4. 设备驱动框架总览

RT-Thread 的设备驱动框架是一套“以对象为中心”的统一抽象层。它把 GPIO、UART、SPI、I2C、ADC、PWM、传感器等外设统一抽象为“设备对象”,向上提供标准的 open/close/read/write/control 接口,向下由具体驱动实现硬件操作。理解这套模型,是后续编写任何驱动的前提。

一、I/O 设备模型的核心思想

RT-Thread 的 I/O 设备模型借鉴了 UNIX 的“一切皆文件”思想,但更贴近嵌入式场景。它通过 rt_device 结构体描述一个设备,通过设备类型区分不同外设,通过设备操作集 rt_device_ops 绑定具体驱动函数。应用层只依赖抽象接口,不关心底层是寄存器操作还是外设库调用。

核心公式:设备对象 = 设备类型 + 设备操作集 + 设备状态 + 私有数据

这种设计带来三个直接好处:一是应用代码与硬件解耦,更换芯片时上层几乎不改;二是驱动可以分层复用,例如串口驱动可复用通用框架;三是设备可被统一管理、查找和调试。

二、设备对象与设备类型

设备对象由 struct rt_device 定义,关键成员包括:type(设备类型)、flag(访问权限)、open_flag(打开标志)、ref_count(引用计数)、device_id(设备 ID)、ops(操作集)、user_data(私有数据)。设备类型是框架识别设备类别的关键,常见类型如下表所示。

设备类型宏定义典型设备常用接口
字符设备RT_Device_Class_Char串口、控制台read/write/control
块设备RT_Device_Class_BlockSD卡、Flashread/write/control
网络设备RT_Device_Class_NetIf以太网、WiFinetdev 接口
MTD 设备RT_Device_Class_MTDNOR/NAND Flashread/write/erase
CAN 设备RT_Device_Class_CANCAN 控制器send/recv
RTC 设备RT_Device_Class_RTC实时时钟control
声音设备RT_Device_Class_Sound音频编解码read/write
图形设备RT_Device_Class_GraphicLCD 屏control
杂项设备RT_Device_Class_Miscellaneous看门狗、ADCcontrol

设备操作集 rt_device_ops 是驱动与框架之间的契约,通常包含 init、open、close、read、write、control 六个回调。驱动开发者只需实现其中必要的部分,未实现的回调可置为 RT_NULL。

三、设备注册与查找机制

设备注册使用 rt_device_register(),它把设备对象挂入内核设备链表,并分配设备 ID。注册后设备即可被查找。查找使用 rt_device_find(),按名称在链表中匹配,返回设备句柄。典型流程如下:

/* 1. 定义并初始化设备对象 */
static struct rt_device dev;

/* 2. 注册设备,名称为 "uart2" */
rt_device_register(&dev, "uart2", RT_DEVICE_FLAG_RDWR);

/* 3. 应用层查找设备 */
rt_device_t uart2 = rt_device_find("uart2");
if (uart2 == RT_NULL) {
    rt_kprintf("find uart2 failed\n");
}

/* 4. 打开设备并读写 */
rt_device_open(uart2, RT_DEVICE_OFLAG_RDWR);
rt_device_write(uart2, 0, buf, len);
rt_device_close(uart2);

注册与查找是“名称—对象”的映射关系。名称必须唯一,重复注册会失败。查找失败通常意味着驱动未注册、名称拼写错误或初始化顺序不对。

四、驱动分层设计思想

RT-Thread 驱动框架强调分层:最上层是应用接口层,中间是设备框架层,下层是硬件驱动层。框架层负责设备管理、引用计数、权限检查、接口分发;驱动层只负责具体硬件操作。以串口为例,应用调用 rt_device_write(),框架层找到设备并调用其 ops->write,最终进入串口驱动的发送函数。

RT-Thread I/O 设备模型:分层、注册与查找 应用层 / 组件层 rt_device_find / open / read / write / control 设备框架层(I/O 管理器) 设备链表 · 引用计数 · 权限检查 · 接口分发 · 设备类型识别 字符设备驱动 UART / 控制台 ops: init/open/read/write 块设备驱动 SD卡 / SPI Flash ops: read/write/control 杂项/网络设备驱动 ADC / 看门狗 / 以太网 ops: control / netdev 注册:rt_device_register() 挂入设备链表 | 查找:rt_device_find() 按名称匹配 | 调用:框架分发到 ops 回调

分层设计的关键在于“框架不碰硬件,驱动不碰管理”。框架层不直接操作寄存器,驱动层不直接维护设备链表。这样,新增一款芯片时,只需实现新的驱动层,框架层和应用层保持不变。

五、典型场景与常见误区

假设要新增一个 ADC 设备。正确做法是:定义 rt_device 对象,实现 control 回调处理采样命令,调用 rt_device_register(&adc_dev, "adc0", RT_DEVICE_FLAG_RDONLY) 注册,应用层通过 rt_device_find("adc0") 获取句柄后使用 rt_device_control() 读取采样值。

常见误区一:注册前未初始化设备对象,导致 ops 为空指针。误区二:名称重复注册,第二次注册失败但未检查返回值。误区三:在驱动层直接调用应用层接口,破坏分层。误区四:忘记在 close 中释放硬件资源,造成引用计数与硬件状态不一致。
调试建议:使用 list_device 命令查看当前已注册设备及类型;若 rt_device_find() 返回 RT_NULL,优先检查注册顺序、名称拼写和初始化返回值。