目录
- 一、前言
- [二、为什么项目需要 USB](#二、为什么项目需要 USB)
- [三、USB 软件框架:总线驱动 + 设备驱动](#三、USB 软件框架:总线驱动 + 设备驱动)
- [四、USB 硬件框架:Host-Controller-Hub-Device](#四、USB 硬件框架:Host-Controller-Hub-Device)
- [五、USB 设备识别三阶段](#五、USB 设备识别三阶段)
- [六、四引脚 + 上拉电阻:电平检测原理](#六、四引脚 + 上拉电阻:电平检测原理)
- [七、USBX:Azure RTOS 的 USB 协议栈](#七、USBX:Azure RTOS 的 USB 协议栈)
- 八、学习路线图
- 九、常见坑
- 十、结尾
一、前言
大家好,这里是 Hello_Embed。
前几篇我们把 UART 做到了极致------DMA+IDLE + OOP 封装 + libmodbus 移植。但开发板的 UART2/UART4 是 RS-485/232 封装的,它们之间可以互通,却无法直连 PC。PC 和开发板的通信靠的是 USB。
本篇开始 USB 学习路线:从软硬件框架到 USBX 移植 CDC-ACM,最终让 PC 通过 USB 虚拟串口访问 STM32 上的 libmodbus 从机。
二、为什么项目需要 USB
| UART | USB | |
|---|---|---|
| 速度 | ≤ 2Mbps | 12Mbps(全速) / 480Mbps(高速) |
| 多设备 | 一对一 | 一主机拖 127 设备 |
| 驱动 | 无标准枚举 | 自动枚举 + 描述符匹配驱动 |
| 本项目角色 | 中控↔传感器 (RS-485) | PC↔中控 (USB CDC) |
本项目 USB 的具体用途:STM32H5 通过 USB CDC-ACM(虚拟串口)模拟一个串口设备,PC 端看到的就是一个 COM 口,ModbusPoll 直接当串口用------上层 API 不变,物理层从 UART2 换成了 USB。
三、USB 软件框架:总线驱动 + 设备驱动
┌─────────────────────────────────┐
│ PC 应用程序(ModbusPoll) │
├─────────────────────────────────┤
│ USB 设备驱动程序(CDC-ACM) │ ← Windows 自动匹配
├─────────────────────────────────┤
│ USB 总线驱动程序(USB Bus) │ ← Windows 内置
├─────────────────────────────────┤
│ USB Host 控制器 │ ← 硬件
└───────────┬─────────────────────┘
│ D+/D-
┌───────────▼─────────────────────┐
│ STM32H5 USB Device 控制器 │ ← 硬件
├─────────────────────────────────┤
│ USBX 协议栈(STM32 端) │ ← Azure RTOS
├─────────────────────────────────┤
│ 应用程序(libmodbus 从机) │
└─────────────────────────────────┘
两层关键:
- USB 总线驱动(Windows 内置)------负责识别设备、分配地址
- USB 设备驱动(CDC-ACM 驱动)------负责具体通信协议(虚拟串口就用 usbser.sys)
STM32 端对应的软件是 USBX(Azure RTOS 的 USB 协议栈),它实现 Device 端的 USB 协议,向 PC 报告"我是一个 CDC-ACM 串口设备"。
四、USB 硬件框架:Host-Controller-Hub-Device
PC CPU
└─ USB Host 控制器(内置 Root Hub)
├─ Hub(扩展端口, 最多 6 级)
│ ├─ Device A
│ └─ Device B
└─ Device C(直接连接)
- Host 控制器:发起所有通信,设备被动响应
- Root Hub:集成在 Host 控制器内部,提供物理端口
- Hub:USB 分线器,最多级联 6 级
- Device:被连接的设备(我们的 STM32 就是 Device)
五、USB 设备识别三阶段
USB 设备从插入到可用,经历三个阶段:
1. 物理检测:D+/D- 电平变化。Host 检测到 Device 插入。
2. 枚举(Enumeration):
Host ──获取设备描述符──→ Device ← "我是谁"
Host ──分配地址(1~127)─→ Device ← "你的编号是 X"
Host ──获取配置描述符──→ Device ← "你有哪些功能"
Host ──加载驱动────────→ ← "匹配 CDC-ACM 驱动"
3. 正常通信:驱动加载完成,应用程序可以打开 COM 口通信。
关键是描述符------Device 向 Host 报告自己的身份信息(设备类型、厂商标识、接口类型等)。STM32 端用 USBX 配置这些描述符,告诉 PC"我是 CDC-ACM 串口设备"。
六、四引脚 + 上拉电阻:电平检测原理
USB 接口 4 根引脚:
| 引脚 | 作用 |
|---|---|
| VBUS (5V) | 供电 |
| GND | 地 |
| D+ | 数据正 |
| D- | 数据负 |
设备速度识别靠上拉电阻:
- D+ 接上拉(1.5kΩ 到 3.3V)→ 全速设备(12Mbps)
- D- 接上拉 → 低速设备(1.5Mbps)
STM32H5 的 USB 外设内部已集成上拉电阻,CubeMX 配置时勾选即可。
七、USBX:Azure RTOS 的 USB 协议栈
STM32H5 不裸写 USB 协议,而是用 Azure USBX(ThreadX 生态的一部分,已适配 FreeRTOS)。
USBX 在本项目的角色:
应用程序(ModbusServerTask)
└─ 通过 UART_Device 接口操作
└─ g_usbserial_dev(USB CDC 的 OOP 封装)
└─ USBX CDC-ACM 中间件
└─ USBX Device Stack
└─ STM32H5 USB OTG HAL 驱动
USBX 帮我们做好的事:
- 解析 USB 标准请求(GET_DESCRIPTOR、SET_ADDRESS 等)
- 管理端点(Endpoint)的数据传输
- CDC-ACM 的类协议封装(虚拟串口的线路编码、状态通知)
我们只需要:
- CubeMX 配置 USB OTG 外设 + USBX 中间件
- 稍作源码改造(适配 FreeRTOS 内存管理)
- OOP 封装一个
g_usbserial_dev
八、学习路线图
Note 16: USB 框架 ← 本篇(为什么用 USB + 软硬件架构)
Note 17: USB 协议层(包格式、传输类型、描述符详解)
Note 18: 移植 USBX + CDC-ACM(CubeMX 配置 + 源码改造 + 上机)
Note 19: USB CDC OOP 封装(g_usbserial_dev + ModbusPoll 联调)
4 篇搞定 USB,之后 Note 20 开始 libmodbus 上机实验(USB 后端)。
九、常见坑
9.1 ST-Link 不是 USB Device
ST-Link 是调试器,它自带的虚拟串口走的是 ST-Link 自己的 USB。我们学的是 STM32H5 芯片本身的 USB 外设------用 PA11(D-)/PA12(D+) 引脚。
9.2 USB 和 UART 的"串口"不是一回事
USB CDC-ACM 是模拟串口------PC 端看到的 COM 口,底层走的其实是 USB 包,不是 UART 电平。中间有 USBX + CDC 协议栈做转换。
9.3 CubeMX 配置 USBX 需要 ThreadX 内核(可选)
USBX 原生依赖 ThreadX,但 ST 提供了 FreeRTOS 适配层(ux_stm32_config.h),不需要额外安装 ThreadX。
十、结尾
本篇建立了 USB 的系统框架认知:Host/Device 架构、枚举三阶段、USBX 协议栈的角色。下一篇深入 USB 协议层------包格式、四种传输类型、描述符树,把"设备如何向 Host 描述自己"讲透。