一、驱动开发基础

MSC驱动模板

  • 新的MSC驱动模板相比之前更加简洁,仅保留核心部分。
  • 模板中已由系统完成class创建等初始化工作,开发者无需重复实现。
  • class代表设备类型,在MSC初始化时已被创建,后续设备均属于该class。

动态编译与静态编译

  • 动态编译允许内核启动后动态加载或卸载模块,便于调试,避免每次修改代码都需重启内核。
  • 静态编译则将驱动直接编入内核镜像,适用于最终产品发布。
  • 开发流程通常为:先动态编译进行调试,完成后转为静态编译集成到内核。
  • 当驱动导致内核崩溃时,仍需重启系统恢复。

ioctl接口机制

  • ioctl用于实现用户空间对设备的控制命令传递,功能强大且灵活。
  • 命令由多个字段组合而成,包括type、number、direction和size。
  • 使用宏(如_IOR、IOWR)来构造命令值,其中IOWR表示同时读写数据的操作较少见。

驱动开发:

内核驱动开发结束后即为

应用端开发:

二、设备与驱动分离架构

platform总线设计思想

  • platform总线用于管理没有物理总线对应的片上外设资源
  • 设备与驱动分离可实现一对多或多对多匹配,减少重复代码。
  • 同一类设备共享同一套驱动方法,只需定义不同的资源信息即可。
  • 类比圆珠笔写字,不同颜色的笔不需要重新学习书写方式。

资源描述冗余问题 

  • 传统方式将设备资源配置硬编码在C文件中,导致内核体积膨胀。
  • 不同开发板即使使用相同芯片也需维护各自的device.c文件,造成大量冗余。
  • 官方内核需支持众多开发板,因此包含大量未使用的设备描述代码。

三、设备树(Device Tree)机制

引入背景与优势

  • 为解决设备资源配置冗余问题,引入设备树机制,将硬件描述从内核代码中剥离
  • 设备树由Linus Torvalds推动发展,采用独立的DTS文件描述硬件资源。(以开发板为单位)
  • 内核启动时加载DTB文件解析硬件信息,实现“一次编译,多平台运行”。

文件格式与结构

  • DTS:设备树源文件,文本格式,可读性强。
  • DTSI:头文件形式的设备树片段,用于共享公共定义。
  • DTB:DTS经DTC工具编译后的二进制文件,供内核加载使用。
  • 设备树呈树状结构,根节点为"/",每个大括号代表一个节点或子节点。
  • 节点包含属性(property),采用键值对形式描述特征,类似JSON格式。

设备树:描述硬件资源 --- 以开发板为单位
.dts 描述板级资源的设备树文件(一般一个开发板对应一个文件)
.dtsi 描述SOC级的设备树文件(类似于.c对应的.h文件)
.h C语言的头文件 --- 设备树编译支持预处理C文件
.dtb 最终编译完成后生成的可加载的设备树文件
(类似与a.out)

SOC级与板级描述

  • DTSI文件描述SOC级资源,如CPU、PWM、I2C控制器等芯片固有组件。
  • DTS文件描述板级外设,如LED、传感器、LCD背光等具体连接信息。
  • 开发板DTS文件通过include引用对应SOC的DTSI文件,实现信息复用。

四、设备树实践操作

自定义设备树文件创建

  • 复制现有开发板DTS文件作为新板型的基础模板。
  • 修改文件名以区分不同开发板,例如从imx6ull.dts改为pute.dts。
  • 需在Makefile中添加新DTS文件对应的DTB生成规则。
新加自己的设备树文件:
cp arch/arm/boot/dts/imx6ull-alientek-emmc.dts
arch/arm/boot/dts/pute.dts
修改arch/arm/boot/dts/Makefile 新增一行:
        pute.dtb(加到对应的SOC)
make dtbs 编译所有的设备树文件
make pute.dtb 只编译pute.dts
cp arch/arm/boot/dts/pute.dtb /home/linux/tftpboot/

编译与部署流程

  • 修改arch/arm/boot/dts/Makefile,加入新设备树目标。
  • 执行make dtbs编译所有设备树文件,或指定目标单独编译。
  • 使用TFTP将生成的DTB文件下载至内存并启动测试。
  • 启动成功表明设备树基本结构无误,可在其基础上进一步修改适配。

五、开发流程规范

移植与验证原则

  • 在移植任何驱动或系统组件时,应首先确保原始版本能正常编译运行。
  • 验证基础功能无误后再进行定制化修改,便于问题定位。
  • 启动命令参数实为三个地址:kernel镜像地址、DTB地址和unused参数地址。

六、设备树(DTS)语法与结构

节点与属性定义

  • DTS文件由节点(node)和属性组成,节点可嵌套形成树状结构。
  • 属性以键值对形式存在,每行以分号结尾。
  • 节点可包含子节点(sub-node),形成层级关系。

属性合并与覆盖规则

  • 多个相同名称的节点存在时,其属性会进行合并
  • 属性名重复,后定义的值会覆盖先定义的值,遵循“后写覆盖先写”原则。
  • 此机制类似于变量赋值,支持部分重写或完全重写。

通用与自定义属性

  • 通用属性(如reg、compatible、status)需遵循标准命名规范。
  • 自定义属性可自行命名,适用于特定场景且无标准定义的情况。
  • 建议使用语义清晰的名称以提高代码可读性。

地址与大小描述

  • reg属性用于描述寄存器地址和大小,格式为。
	ptled{
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "ptled0";
		name1 = "led1";
		status = "okay";
		reg = <0x020E0068 0x4
			   0X020E02F4 0x4 
			   0X0209C004 0x4 
			   0x0209C000 0x4>;	
	};
  • #address-cells 和 #size-cells 定义了地址和大小字段所占的单元数。
  • 多个地址-大小对可在reg中连续列出。

七、设备树在驱动中的应用

节点查找与数据读取

  • 使用of_find_node_by_path函数根据路径查找设备树节点。
  • 查找结果返回struct device_node指针,用于后续操作。
  • 利用of_property_read_string等函数读取节点属性值。

数据类型处理

  • 支持读取字符串、整型数组等多种数据类型。
  • of_property_read_u32_array用于读取32位整型数组。
  • 所有读取操作基于已解析的设备树内存结构,无需重新解析文件。

错误处理与常量指针

  • 函数返回值用于判断操作是否成功,需进行错误检查。
  • 使用const修饰指针参数,确保指向的数据不可修改,符合只读要求。

八、GPIO子系统介绍与使用

子系统概念

  • GPIO子系统是内核提供的标准化接口,封装底层寄存器操作。
  • 类似I2CSPI等其他外设也提供相应子系统接口。

引脚配置方法

  • 在设备树中通过pinctrl节点定义引脚功能及电气属性。
  • 使用pinmux设置引脚复用功能,避免与其他设备冲突。
  • 每个引脚配置需唯一,防止资源竞争。

GPIO操作流程

  • 使用of_get_named_gpio获取设备树中定义的GPIO编号。
  • 调用gpio_request请求GPIO使用权。
  • 设置方向(gpio_direction_output)并控制电平(gpio_set_value)。
  • 使用完成后调用gpio_free释放资源。

驱动实现优化

  • 将GPIO操作集成到驱动初始化和控制函数中。
  • 取代直接寄存器访问,提升代码可移植性和可维护性。
  • 支持输入模式下的电平读取(gpio_get_value)。
Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐