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

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

相关推荐
小弥儿3 小时前
GitHub今日热榜 | 2026-09-18:腾讯双项目上榜
学习·开源
天空'之城4 小时前
单片机基础核心知识点汇总(四十)
单片机·嵌入式硬件
smartpi_ai4 小时前
离线语音模块误识别问题全面解决方案:命令词设计、防误触与灵敏度调优指南
人工智能·单片机·语音识别
LCG元4 小时前
STM32F103 驱动 HX711 称重实战:DOUT 就绪时序、增益切换陷阱与两点标定精度实测
stm32·单片机·嵌入式硬件
知识分享小能手5 小时前
深度学习学习教程,从入门到精通,深度生成模型 —— 知识点详解与代码实现(20)
人工智能·深度学习·学习
m4Rk_6 小时前
【论文阅读】Agent 记忆机制(74):CompassMem——从相似度检索走向事件图上的记忆导航
论文阅读·人工智能·学习·开源·github
小李不想当小白6 小时前
DMA直接存储器存取(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·分享
朝发如雪6 小时前
快速掌握单片机基础(6)(数据处理与传送指令)(跳转与分支指令)(栈操作与混合编程)(程序状态寄存器操作指令)(内存映射)
单片机·arm
blue_ice .6 小时前
Verilog DDS 数字信号发生器:DAC 输出模拟波形与 ADC 回采观测
开发语言·单片机·嵌入式硬件·fpga开发
深圳市恒锐丰科技杨生7 小时前
EG4318 / EG4319 / EG4320 铅酸电池三段式充电副边管理芯片|屹晶 EGmicro
嵌入式硬件·硬件工程