PAYATHON 2026

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 数据头;控制请求和通知会产生独立流量
HIDInterrupt IN,可选 Interrupt OUT;也通过 EP0 交换报告数据必须符合 Report Descriptor;使用 Report ID 时,报告包含 1 字节 ID 前缀
MSC,采用 Bulk-Only TransportBulk 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 应用实际收到的有效字节数,并检查以下环节:

  1. 固件的队列或环形缓冲是否足够,能否避免采样与 USB 发送互相阻塞。
  2. 是否大量提交零散的小块数据,增加调用和调度成本。
  3. 异步发送完成前,是否提前修改或复用了发送缓冲区。
  4. PC 是否持续读取,读取超时和应用处理是否造成空档。
  5. 是否出现频繁 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 或调整硬件。

备注:内容仅供参考。