RTT-IO设备模型

1. 为什么需要 IO 设备模型?

不同芯片厂商的驱动库存在差异。若应用层直接调用厂商驱动,后续更换芯片、升级驱动或替换外设时,应用代码也要跟着修改,可移植性很差。

RT-Thread 通过统一的 IO 设备模型,将应用程序与底层硬件驱动隔离。上层只调用统一接口,底层硬件如何实现由驱动负责。

2. 三层结构

复制代码
应用程序
   ↓
IO 设备管理层
   ↓
设备驱动框架层
   ↓
具体设备驱动层
   ↓
硬件
  • IO 设备管理层:向应用提供统一的 openreadwrite 等接口。

  • 设备驱动框架层:抽象同类设备的共同功能,例如串口、SPI、I2C。

  • 设备驱动层:直接操作寄存器或厂商 HAL 库,完成硬件控制。

简单设备也可以跳过"设备驱动框架层",直接注册到设备管理器。

3. 设备对象

在 RT-Thread 中,设备也是内核对象。每个设备都有一个 struct rt_device 结构体,其中包含:

  • 设备类型、名称、状态、打开次数等基本信息;

  • initopenclosereadwritecontrol 等操作函数;

  • 接收完成 rx_indicate 与发送完成 tx_complete 回调函数;

  • 驱动私有数据 user_data

可以把它理解为:设备对象描述设备信息,函数指针决定设备如何工作。

4. 设备常用流程

应用层访问设备的一般流程:

复制代码
查找设备 → 初始化 → 打开 → 读/写/控制 → 关闭

常用接口如下:

复制代码
rt_device_t dev;
​
/* 1. 按名称查找设备 */
dev = rt_device_find("uart1");
​
/* 2. 打开设备 */
rt_device_open(dev, RT_DEVICE_OFLAG_RDWR);
​
/* 3. 写数据 */
rt_device_write(dev, 0, "hello\r\n", 7);
​
/* 4. 关闭设备 */
rt_device_close(dev);

代码解析: rt_device_find("uart1") 按名称获取串口设备句柄;得到 dev 后再以读写模式打开。rt_device_write() 中的 0 是偏移量,串口属于字符设备,通常传 0 即可;7 是字符串 "hello\r\n" 的字节数。设备用完必须关闭,以便驱动减少打开计数并在计数为 0 时释放硬件资源。实际项目应在每一步检查返回值或 dev != RT_NULL

接口 作用
rt_device_find() 根据名称获取设备句柄
rt_device_init() 初始化设备
rt_device_open() 打开设备
rt_device_read() 从设备读取数据
rt_device_write() 向设备写入数据
rt_device_control() 使用命令控制设备
rt_device_close() 关闭设备

5. 驱动开发流程

驱动层通常需要完成以下工作:

复制代码
创建设备对象 → 实现操作函数 → 注册设备

注册接口:

复制代码
rt_device_register(dev, "device_name", flags);

代码解析: 这是驱动层注册设备的接口。dev 是已创建并完成操作函数适配的设备对象;"device_name" 是应用层通过 rt_device_find() 查找时使用的唯一名称;flags 用于声明只读、读写、中断收发等能力。注册成功后,上层才能通过名称访问该设备。

注册后,应用层便能通过设备名称查找并使用该设备。

动态创建的设备,在不再使用时可通过 rt_device_destroy() 销毁;设备也可通过 rt_device_unregister() 从设备管理器中注销。

6. 读写接口的理解

复制代码
rt_device_read(dev, pos, buffer, size);
rt_device_write(dev, pos, buffer, size);

代码解析: dev 指向目标设备,buffer 是读出数据的存放位置或待发送数据的位置,size 是期望读写的数据量。pos 对块设备表示块偏移,对串口等字符设备通常无意义。返回值是实际读写量;若返回 0,需进一步查看当前线程的 errno

  • 字符设备,如串口:pos 通常没有实际意义,可传 0

  • 块设备,如 Flash、SD 卡:pos 表示块偏移,size 通常以块为单位。

  • 返回值表示实际读写的数据量;返回 0 时,需要结合 errno 判断错误原因。

7. 数据收发回调

接收回调

设备收到数据后,可通过接收回调通知应用层:

复制代码
rt_device_set_rx_indicate(dev, rx_callback);

代码解析:rx_callback 注册为接收通知函数。驱动接收到数据时会调用它,并把收到的数据长度传给上层;回调中应只做轻量通知,实际数据读取应交给线程调用 rt_device_read() 完成。

回调触发后,上层线程应及时调用 rt_device_read() 读取数据。

发送完成回调

当 DMA 或 FIFO 等硬件完成发送后,可通过发送完成回调通知上层:

复制代码
rt_device_set_tx_complete(dev, tx_callback);

代码解析:tx_callback 注册为发送完成通知函数。DMA 或 FIFO 发送结束后,驱动调用该函数;此时上层才可以安全复用或释放对应的发送缓冲区。

上层可在回调中释放发送缓冲区,或准备下一次发送。

总结

RT-Thread IO 设备模型的核心价值是:用统一接口屏蔽不同硬件和驱动的差异。

对应用开发者来说,只需掌握"查找、打开、读写、控制、关闭";对驱动开发者来说,则需要实现设备操作函数并完成注册。这样既降低了应用与硬件的耦合,也让代码更容易移植和维护。

相关推荐
VALENIAN瓦伦尼安教学设备1 小时前
设备状态检测振动分析实训台案例分析
大数据·数据库·人工智能·嵌入式硬件·算法
米饭不加菜1 小时前
一些常用模块简介
经验分享·笔记·嵌入式硬件
aichitang20242 小时前
希尔伯特空间中的正交性
人工智能·学习·机器学习·ai·泛函分析
for_ever_love__2 小时前
python基础语法学习: 正则表达式
python·学习·正则表达式
佳児素花痴╮2 小时前
STM32U575RIT6(USART)学习
stm32·嵌入式硬件·学习
sunshine22 girl2 小时前
Angular7,9,学习笔记三 dom操作
笔记·学习·angular
春风解人意2 小时前
从零开始学习嵌入式P28----线程
c语言·学习·算法
MartinYeung53 小时前
[论文学习]MAC:多智能体宪章学习
人工智能·学习·macos
小+不通文墨3 小时前
使用vscode里的U8G2 库编写OLED菜单控制
经验分享·笔记·单片机·嵌入式硬件·学习
oshan20124 小时前
零基础STM32入门教程--12-I2C 串行通迅
stm32·单片机·嵌入式硬件