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 设备模型的核心价值是:用统一接口屏蔽不同硬件和驱动的差异。

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

相关推荐
美味蛋炒饭.1 小时前
Git 版本控制(下)
开发语言·git·学习·总结·后端开发
呼噜想睡觉2 小时前
Bootstrap 组件之响应式导航条实现
学习
菜鸟‍2 小时前
【论文学习】Medical Image Analysis 2024 || 医学图像分割中失败检测方法的比较基准研究:揭示置信度聚合的作用
人工智能·学习
爱音乐的marlon3 小时前
FastAPI 学习笔记03|core 核心层:config 是总控制台,database 是整个工程的地基
笔记·学习·fastapi
wengqidaifeng5 小时前
2026 年电赛(TI 杯)H 题:MaxiCamPro、双主控通信、VOFA+ 遥测与可复现实验方法
c语言·单片机·嵌入式硬件
math_hongfan5 小时前
鸿蒙离线数据缓存高级架构:弱网预加载/离线数据优先级/同步冲突解决/上线后数据合并策略
学习·缓存·华为·架构·harmonyos·鸿蒙
划水的code搬运工小李5 小时前
Simulink学习-自定义Storage Class配置
学习
寻道模式5 小时前
【运维心得】电视KK.RV22.E32B“捉鬼”实录:从炸机到 eMMC 的千里追击(1)
单片机·嵌入式硬件·mos管·开关电源
一尘之中6 小时前
量子计算机能“参禅”吗?从叠加态到不二法门的技术哲学追问
学习·ai写作·量子计算