STM32 如何选择标准 USB 设备类、端点和传输类型以提高吞吐量?
STM32 需要向 PC 持续传输大量数据,并在传输出错后重试时,可以先评估 CDC ACM 的 Bulk IN 数据通道。CDC ACM 可以使用 Windows 自带的 Usbser.sys,普通字节流传输也不需要在每个 USB 数据包前增加 CDC 专用数据头。实际吞吐量仍取决于 USB 速度、固件缓冲和 PC 端读取方式。
如果后续需要直接控制 Bulk 读写,可以考虑厂商自定义接口配合 Windows 自带的 Winusb.sys。这需要编写设备协议和 PC 应用,但不一定需要自行开发内核驱动。
端点、Pipe 和设备类是什么关系?
Endpoint 是设备侧的数据收发端点,Pipe 是主机侧访问端点的逻辑通道。方向以主机为参照:STM32 → PC 使用 IN,PC → STM32 使用 OUT。所有 USB 设备都必须提供默认控制端点 EP0。Microsoft:USB endpoints and their pipes
需要区分两组术语:
- 传输类型包括 Control、Bulk、Interrupt 和 Isochronous。
- 如果问题中的 Message Types 指规范中的 Message Pipe,它对应 Control;Stream Pipe 则承载 Bulk、Interrupt 或 Isochronous。Arm:USB Concepts
设备类规定接口的功能、请求和数据格式,并约束端点的使用方式。设备类与端点并非一一对应:一个类可以使用多种端点,不同类也可以使用相同的传输类型。端点编号本身通常不能决定吞吐量。
常见设备类使用哪些端点,会增加什么开销?
下表列出与此需求相关的常见方案,EP0 不重复计入数据端点。
| 方案 | 常见端点配置 | 类协议对数据传输的影响 |
|---|---|---|
| CDC ACM 虚拟串口 | Bulk IN、Bulk OUT,以及用于通知的 Interrupt IN | 普通数据通道承载字节流,不要求每个数据包带 CDC 数据头;控制请求和通知会产生独立流量 |
| HID | Interrupt IN,可选 Interrupt OUT;也通过 EP0 交换报告 | 数据必须符合 Report Descriptor;使用 Report ID 时,报告包含 1 字节 ID 前缀 |
| MSC,采用 Bulk-Only Transport | Bulk IN、Bulk OUT | 按命令、可选数据、状态的流程传输,存在 CBW、CSW 等协议开销,面向存储访问 |
| 厂商自定义接口配合 WinUSB | 可以设计 Bulk IN、Bulk OUT | 应用协议格式由自己定义;仍保留 USB 总线自身的开销 |
CDC 的具体配置可以查看 ST 官方 usbd_cdc.c。其中,USBD_CDC_Init() 打开 Bulk 数据端点和 Interrupt 通知端点,发送路径使用应用提供的数据缓冲。
HID 的端点要求见 HID 1.11 规范 §4.4,正文页码 10;Report ID 和报告格式见 §8.1 至 §8.2,正文页码 55。正文页码与 PDF 阅读器显示的文件页序号不同。MSC 的传输规则见 USB-IF:Mass Storage Bulk Only 1.0。
标准设备类增加的字节数不能一概而论。需要根据具体类、子类和协议计算,并区分每个应用消息、每次 USB Transfer 和每个 USB 数据包的开销。
如何确认项目实际使用的端点类型?
先查类规范,再检查设备实际返回的描述符和固件实现。标准规定允许的实现方式,描述符说明当前配置实际采用了什么。
重点查看 Endpoint Descriptor 中的这些字段:
bEndpointAddress:端点编号和方向,bit 7 为 1 表示 IN。bmAttributes & 0x03:0为 Control,1为 Isochronous,2为 Bulk,3为 Interrupt。wMaxPacketSize:单个数据包的最大长度。bInterval:对 Interrupt、Isochronous 等端点具有相应的服务间隔含义,不能统一解释为毫秒。
字段定义可查 Microsoft:USB_ENDPOINT_DESCRIPTOR。
例如,下面是一个 Full-Speed Bulk IN 端点描述符的独立示例,不是完整的 CDC 配置:
#include <stdint.h>
static const uint8_t endpoint_desc[] = {
0x07, // bLength
0x05, // bDescriptorType: Endpoint
0x81, // bEndpointAddress: EP1 IN
0x02, // bmAttributes: Bulk
0x40, 0x00, // wMaxPacketSize: 64, little-endian
0x00 // bInterval
};
uint8_t transfer_type = endpoint_desc[3] & 0x03u;
uint8_t is_in = (endpoint_desc[2] & 0x80u) != 0u;
uint16_t max_packet_size =
(uint16_t)endpoint_desc[4] |
((uint16_t)endpoint_desc[5] << 8);
对于使用 ST 传统 USB Device Library 的工程,可以从 Class/CDC/Src/usbd_cdc.c 和 Class/CDC/Inc/usbd_cdc.h 入手,查找 USBD_LL_OpenEP()、USBD_EP_TYPE_BULK、CDC_IN_EP 以及包长宏。描述符所在文件会随库版本和是否使用组合设备而变化,应以工程实际包含的代码为准。
不能只把 HID 的端点属性改为 Bulk,就期待 Windows HID 驱动继续正常使用;描述符、类行为和主机驱动必须匹配。
STM32 项目应如何选择和验证?
先确认硬件速度,再测 CDC ACM
确认具体 STM32 型号、USB 外设、PHY 和板级连接能够支持的速度,并检查设备实际枚举为 Full-Speed 还是 High-Speed。仅凭 USB 外设名称不足以确定当前连接速度。
Full-Speed Bulk 端点的最大包长上限为 64 字节,High-Speed Bulk 为 512 字节。这是单包限制,不能直接当作每次应用读写的长度限制,也不能据此认定 Full-Speed Bulk 只能达到 64 bytes/ms。Microsoft:USB Bulk Transfer
在满足 Windows 驱动匹配要求时,CDC ACM 可以使用内置 Usbser.sys。自动绑定条件和部署要求需要结合目标 Windows 版本及设备描述符确认。Microsoft:USB serial driver
检查传输链路能否持续供数
建议使用连续的二进制测试数据,统计 PC 应用实际收到的有效字节数,并检查以下环节:
- 固件的队列或环形缓冲是否足够,能否避免采样与 USB 发送互相阻塞。
- 是否大量提交零散的小块数据,增加调用和调度成本。
- 异步发送完成前,是否提前修改或复用了发送缓冲区。
- PC 是否持续读取,读取超时和应用处理是否造成空档。
- 是否出现频繁 NAK、缓冲溢出、重试或应用层丢数。
这些检查有助于定位瓶颈。没有测量时,不能断定 CDC 或 Windows 驱动限制了速度。
需要直接操作 Bulk 时再评估 WinUSB
厂商自定义接口配合 WinUSB,可以让应用直接使用自己的数据协议。符合相应安装条件并提供所需 Microsoft OS Descriptors 时,Windows 可以自动加载内置驱动;应用仍需完成设备发现和读写逻辑。Microsoft:WinUSB device installation
更换驱动不会提高 USB 物理链路速度,也不保证比优化后的 CDC 更快。确定瓶颈位于现有驱动或访问模型时,迁移才有明确依据。
选择传输类型时还有什么限制?
Bulk 适合大量数据并支持总线层面的错误重试,但不保证固定带宽或完成时限。Isochronous 适合重视周期和时延、且允许部分数据丢失的流,不提供 USB 层面的失败重传。不能仅因它缺少部分握手开销,就认定它最合适。Interrupt 也由主机轮询,名称不代表设备可以主动抢占总线。Arm:USB transfer types
目前缺少 STM32 型号、实际连接速度、目标有效数据率及丢包容忍度,因此无法给出可靠的吞吐量数字。已有信息足以支持先用 CDC ACM 的 Bulk IN 建立基线,再根据测量结果决定是否采用 WinUSB 或调整硬件。
备注:内容仅供参考。
