IO设备模型框架

Tutorial: 卓越工程师 Category: 嵌入式 Published: 2026-10-23 11:05:29 Views: 1 Likes: 0 Comments: 0

265.3.1IO设备模型框架

源码边界说明: 本节是 I/O 设备模型框架的概念导入,没有新增或修改应用层业务代码。视频只展示框架图、设备注册/访问接口和 Windows 设备管理器,因此不把 RT-Thread 内核或 BSP 驱动源码复制到文首冒充业务源码;正文结合课程配套 13IO_DEVICE 工程说明这些接口如何衔接。

一、RT-Thread的设备模型框架

RT-Thread 设备模型的核心目的,是在应用程序与具体硬件驱动之间建立一套统一约定。应用通常先按名称取得设备句柄,再用统一接口打开、读写或控制设备;底层驱动则创建具体设备对象、填入操作函数并完成注册。

驱动侧:创建设备对象 → 绑定操作函数 → 注册设备名称
                                        ↓
应用侧:按名称查找设备 → 打开设备 → 读 / 写 / 控制 → 关闭设备

“统一接口”不表示所有硬件的能力完全相同,而是让它们都通过同一种入口形式被管理。UART、ADC、PWM、I2C 等设备的控制命令、数据单位和运行模式仍由各自的设备类别与驱动决定。

1.1 设备模型框架介绍

这里的两个关键词分别是:

  • 设备模型:用一个设备对象描述名称、类型、状态、打开次数、回调以及 init/open/close/read/write/control 等操作。
  • 设备框架:规定应用层、通用设备管理代码、某类设备的公共驱动代码和芯片级驱动怎样协作。

课程说它“仿照 Linux 驱动框架”,可以理解为两者都采用了分层、设备对象、注册和统一访问接口等思想,但不能进一步推导为“内部实现与 Linux 完全相同”。RT-Thread 面向 MCU,结构、对象模型、初始化机制和 API 都有自己的实现;学习它有助于建立驱动分层思维,却不能代替 Linux 驱动本身的学习。

RT-Thread 标准版与 Nano 版的课程语境也要分清:

项目RT-Thread NanoRT-Thread 标准版
主要目标资源非常受限的 MCU,只保留精简实时内核在内核之上提供可裁剪的设备框架、组件和软件包生态
设备概念官方驱动文档所述 Nano 版本不包含标准版的设备模型启用 RT_USING_DEVICE 后使用统一设备对象与接口
外设访问可由用户直接编写或调用芯片 HAL/LL 驱动可通过设备框架访问,也不意味着技术上禁止直接调用 HAL
取舍ROM/RAM 占用更小,但需要自行组织更多外设代码增加一定代码与对象开销,换取复用、移植和统一管理能力

因此,选择 Nano 不是因为“HAL 只能在 Nano 中使用”,选择标准版也不等于所有外设会自动可用。最终仍要根据 Flash、RAM、驱动需求和项目维护成本裁剪配置。

本课程接下来的 UART、PIN、ADC、PWM、I2C 等实验,是从不同设备类别观察同一套框架:先学应用接口,再沿注册过程回看底层原理。PIN 是 RT-Thread 对引脚设备的命名;I2C 才是规范写法,课程思维导图中的 “IIC” 是常见口语写法。

1.2 IO设备

I/O 是 Input/Output(输入/输出),这里指能与系统交换数据或控制信息的设备,不是专指 GPIO 引脚。

GPIO 的完整名称是 General-Purpose Input/Output(通用输入输出)。GPIO 可以作为一种 I/O 资源,但 I/O 设备的范围远大于 GPIO。判断输入与输出时,通常以处理器或应用程序为观察中心:

类型数据方向示例
输入设备外部世界 → 系统按键、鼠标、麦克风、ADC 采样
输出设备系统 → 外部世界显示器、扬声器、PWM 输出
双向设备两个方向都可传输UART、SPI、I2C、网络接口、存储设备

课程把 MCU 的 UART、ADC、SPI、I2C、PWM、PIN 等都作为 I/O 设备来理解是合适的。视频提到 DMA 后又立即说“DMA 就算了”,原因是 DMA 更准确地说是数据搬运机制/控制器:它能帮助外设与内存传输数据,但通常不是应用层按普通 I/O 设备语义独立读写的对象。

课程用电脑上的 CH340 串口举例时,要把两侧硬件分开:

电脑应用(串口助手)
        ↓ 选择 COM 号
Windows 串口驱动与设备枚举
        ↓ USB
CH340 USB 转串口芯片
        ↓ TX / RX / GND(TTL 电平 UART)
STM32 的 UART 引脚与片内 USART 外设
  • 拔掉 CH340 后,Windows 中对应 COM 端口消失,是因为电脑的 USB 总线检测到设备离线。
  • CH340 把电脑的 USB 通信转换成 MCU 可使用的 UART 电平信号;它不是 STM32 片内 UART 本身。
  • Windows 设备管理器显示 COM 端口,并不代表 RT-Thread 自动发现了外接 CH340。STM32 侧是否存在 uart1 等逻辑设备,取决于 BSP 配置、参与编译的驱动和启动时的注册过程。
  • 串口接线至少要共地,并通常交叉连接 TX→RX、RX←TX;还要确认双方电平兼容,不能把 RS-232 电平直接接到 STM32 的 3.3 V UART 引脚。

1.3 设备模型框架的分层

官方文档把位于应用与硬件之间的 I/O 设备框架分成三层:I/O 设备管理层、设备驱动框架层、设备驱动层。若把框架外部的应用程序和硬件也一起画出来,整条路径才呈现为五个位置:

位置主要职责课程工程中的例子
应用程序按名称查找并通过统一 API 使用设备用户线程、main()、后续串口实验代码
I/O 设备管理层管理已注册设备对象,提供查找、打开、读写、控制等统一入口rt_device_find()、rt_device_open() 等
设备驱动框架层抽取同类设备的公共逻辑,并把统一设备操作映射到该类驱动RT-Thread serial、SPI、I2C、PIN 等框架
设备驱动层实现某款 MCU/控制器特有的寄存器、时钟、中断和 DMA 操作STM32 UART、GPIO、I2C 驱动及 HAL
硬件真正执行收发、采样或输出STM32 USART、GPIO、ADC、外接 SPI Flash 等

不要把“设备驱动框架层”和“设备驱动层”简单合并后就忽略两者的边界:

  • 框架层解决“同一类设备有哪些共同行为”,例如串口的缓冲、打开模式和统一读写入口。
  • 驱动层解决“这颗芯片具体怎样工作”,例如 USART 寄存器地址、IRQ、DMA 通道和 GPIO 复用。
  • 对逻辑非常简单的设备,驱动也可以直接注册到 I/O 设备管理层,不一定必须经过某个专用子框架。

一次典型调用可理解为:应用调用 rt_device_write(),设备管理代码通过设备对象中登记的 write 操作找到串口框架,串口框架再调用 STM32 驱动提供的底层发送操作,最后才访问 USART/DMA 硬件。统一入口负责“转发”,真正驱动硬件的是下层实现。本工程没有启用 RT_USING_DEVICE_OPS,所以 struct rt_device 中保存的是各个独立操作函数指针;启用该配置的版本才通过统一的 ops 函数表保存这些入口。

1.3.1 设备管理器

课程借 Windows 设备管理器帮助理解“集中管理设备”,这个类比只在宏观层面成立:上层看到统一的设备名称/属性,底层驱动把设备交给系统管理。但两者的发现机制并不相同。

RT-Thread 的 I/O 设备管理层不是一个会自动扫描电路板的硬件探测器。在本课程 13IO_DEVICE 工程中,串口注册的真实链路是:

board.h / rtconfig.h 选择启用哪些串口与串口组件
        ↓
rt_hw_board_init() → hw_board_init()
        ↓
启用 RT_USING_SERIAL 时调用 rt_hw_usart_init()
        ↓
STM32 驱动为每个已配置 UART 准备对象与 stm32_uart_ops
        ↓
rt_hw_serial_register(..., "uart1", ...)
        ↓
串口框架绑定通用的 rt_serial_init/open/read/write/control 等操作
        ↓
rt_device_register(..., "uart1", ...)
        ↓
设备对象进入 RT-Thread 设备对象容器

rt_device_register() 在 RT-Thread 5.0.2 中主要完成以下工作:检查设备指针和重名、把对象初始化为 RT_Object_Class_Device、记录名称与能力标志,并把引用计数和打开标志清零。它注册的是软件设备对象,不会凭空检测引脚上是否焊接了模块,也不会验证外接器件一定能正常通信。

应用调用 rt_device_find("uart1") 时,只是在设备对象容器中按名称查找。找不到通常意味着设备没有注册、名称不一致、配置宏未启用、驱动未参与构建或初始化失败,并不应先假定 UART 硬件损坏。启用 FinSH/MSH 时可用 list_device 查看已经注册的设备名称、类型和引用次数。

Windows 示例中“串口助手从设备管理器取得 COM 口列表”的说法也只是便于入门的简化描述。准确地说,应用通过操作系统提供的设备枚举和文件/通信 API 使用串口;Windows 图形化设备管理器只是这些系统设备信息的一种展示界面,不是所有应用调用都必须经过这个窗口程序。

1.3.2 设备模型框架的用途

设备框架的价值不只是“把很多设备放到一起”,更重要的是隔离变化:

  1. 隔离芯片差异:STM32、NXP、GigaDevice GD32 等芯片的寄存器和中断不同,由各自 BSP/驱动处理。
  2. 复用同类公共逻辑:不同厂商的 UART 都可复用 RT-Thread 串口框架的设备接口、缓冲与回调机制。
  3. 降低应用耦合:应用主要依赖设备名称和标准 API,不必在每个业务文件里直接操作寄存器。
  4. 统一生命周期:注册、查找、打开、访问、关闭和注销都有明确入口,便于诊断和维护。
  5. 支持裁剪:不用的设备类别和驱动可以不启用,避免把所有芯片、所有外设代码都编入固件。

原稿把“GD32”和“兆易创新”列成两种并列芯片不够准确:GD32 是兆易创新(GigaDevice)的 MCU 产品系列。课程真正要表达的是 RT-Thread 需要适配多个芯片厂商、多个 MCU 系列以及每颗芯片上的多类外设。

统一 API 也有边界。例如,字符设备的 read/write 长度通常按字节理解,而块设备可能按块解释 pos 和 size;不同设备的 control 命令更不相同。因此可移植的是访问模式和上层结构,不是所有参数语义都天然完全一致。

1.3.3 设备模型框架使用方法

视频把使用方法分成“设备注册”和“访问 I/O 设备”两部分,方向正确;更准确地说,前者主要属于驱动/BSP 侧,后者主要属于应用侧。

阶段常用接口初学者需要掌握的含义
注册rt_device_register()把已准备好的设备对象按名称加入设备管理系统;同名设备不能重复注册
注销rt_device_unregister()从管理系统移除设备对象;只注销并不等于释放该对象占用的内存
查找rt_device_find()按注册名称取得 rt_device_t 句柄;失败返回 RT_NULL
初始化rt_device_init()调用设备的初始化操作;已激活设备不会重复初始化
打开rt_device_open()按读写、中断、DMA 等模式打开;若尚未初始化,本版本会先尝试初始化
关闭rt_device_close()与打开配对,减少引用计数,并在满足条件时执行底层关闭操作
读取/写入rt_device_read() / rt_device_write()经设备操作函数表转发到具体驱动,返回实际完成的数据量
控制rt_device_control()传递设备类别/驱动支持的命令,例如修改配置或查询状态
接收指示rt_device_set_rx_indicate()登记“有数据到达”时的回调;它本身不读取数据
发送完成rt_device_set_tx_complete()登记发送完成回调;它本身不负责发出数据

应用侧的常见生命周期是:

rt_device_find(name)
        ↓ 成功取得句柄
rt_device_open(dev, mode)
        ↓
rt_device_read / rt_device_write / rt_device_control
        ↓
rt_device_close(dev)

是否需要显式调用 rt_device_init() 要看版本与项目习惯。本课程使用的 RT-Thread 5.0.2 在 rt_device_open() 中会检查激活标志,未初始化时自动调用设备的 init 操作;因此“查找后必须先手工 init 才能 open”不是绝对规则。无论采用哪种写法,都必须检查查找、打开和控制等接口的返回值。

最后回答视频留下的两个问题:

  1. 怎样才算注册了设备? 驱动准备好设备对象及操作函数后,注册链最终成功执行 rt_device_register(),该对象进入设备对象容器,并能用注册名称被 rt_device_find() 找到,才算完成框架意义上的注册。
  2. RT-Thread 内核怎样知道板子上有什么设备? 它主要依赖工程配置、BSP/驱动代码及启动阶段的注册,不会仅靠读取 MCU 型号就知道板上外接了什么。板载 UART、PIN 等由配置宏决定是否创建和注册;外接传感器、Flash 等还需要总线配置、设备实例或探测逻辑。注册成功只代表软件对象存在,实际硬件能否工作仍要由接线、供电、时钟、地址和通信结果验证。

核对依据: 分层定义、注册与访问流程已与 RT-Thread 官方 I/O 设备框架文档交叉核对;注册、查找和操作转发以课程配套 RT-Thread 5.0.2 的 device.c及serial.c为准;Nano 与标准版差异参考官方驱动概览。本工程串口注册链已对照 13IO_DEVICE/drivers/drv_common.c、drv_usart.c、board.h 和 rtconfig.h。