USB 描述符怎么写都不对?别只抄例程,看看 bLength 与 wTotalLength
摘要:USB 描述符复制了例程,只是改了个 PID,电脑就提示“设备描述符请求失败”?不是 USB 栈坏了,而是描述符长度(bLength)或总长度(wTotalLength)算错了。本文拆解 USB 描述符的“字节级”潜规则。
一、问题描述(现象)
**用 CubeMX 生成的 HID 例程,枚举一切正常;
只是加了一个自定义 Usage Page,设备管理器就开始报黄叹号;
Bus Hound 抓包显示 GET_DESCRIPTOR 返回的数据长度不对。**
很多工程师的排查方向是:
-
是不是 PID/VID 冲突了?
-
描述符数组是不是没对齐?
-
换个 USB 端口试试?
二、原理分析
1. 物理模型
USB 主机在枚举时,会先问设备:“你多长?”
Host: GET_DESCRIPTOR (wLength = 64)
Device: [Descriptor Data...]
2. 核心参数
-
bLength:当前这个描述符结构体的实际字节数。
-
wTotalLength:整个配置描述符集合(Configuration + Interface + Endpoint)的总字节数。
3. 反直觉真相
USB 主机根本不相信你给的长度,它会自己算。
-
如果你在代码中修改了某个描述符(比如多加了一个 Endpoint)。
-
但忘了更新
wTotalLength。 -
结果:主机算出的长度和实际发送的不一致 → 枚举失败。
三、工程级解决方案
方案 1:手算长度(最稳,推荐)
以 Configuration Descriptor 为例:
// 标准配置描述符长度 = 9 字节
// 接口描述符 = 9 字节
// 端点描述符 = 7 字节
// 如果有两个端点:
wTotalLength = 9 + 9 + 7 + 7 = 32;
切记: wTotalLength是小端格式(低字节在前)。
方案 2:利用编译器自动算(进阶)
如果是 C 语言,可以用结构体技巧。
typedef struct {
USB_ConfigDescTypeDef config;
USB_InterfaceDescTypeDef interface;
USB_EndpointDescTypeDef ep_in;
USB_EndpointDescTypeDef ep_out;
} __attribute__((packed)) USBD_CDC_DescTypedef;
#define USBD_CDC_DESC_SIZE sizeof(USBD_CDC_DescTypedef)
方案 3:用工具验证
不要只靠眼睛看。
-
USBView (Windows):直接展开描述符树,看每一项的长度是否正确。
-
Bus Hound:查看 GET_DESCRIPTOR 返回的原始字节流。
四、选型避坑建议
-
bLength 必须精确:
-
Device Descriptor = 18 bytes
-
Configuration Descriptor = 9 bytes
-
Interface Descriptor = 9 bytes
-
Endpoint Descriptor = 7 bytes
-
-
字符串描述符:
-
bLength包含字符串长度 × 2 + 2(Unicode)。
-
-
不要混用:HID 描述符有自己的长度计算方法,不能直接套用 CDC/MSC。
五、总结 Checklist
-
[ ] 是否检查了每个描述符的
bLength是否正确? -
[ ] 修改描述符后,是否更新了
wTotalLength? -
[ ]
wTotalLength是否按小端格式填写? -
[ ] 是否用 USBView/Bus Hound 验证过原始数据?
六、写在最后(关注我,少走弯路)
我是 gqqsherry,一个拒绝调包、专注底层逻辑的嵌入式工程师。
USB 描述符是“写一次,调三天”的典型代表,一个字节的错误就能让整个设备变砖。
关注我的专栏《嵌入式底层避坑指南》,我会持续更新 USB、CAN、UART 等外设的真实调试案例和量产级解决方案。
👉 下一篇预告:《USB CDC 虚拟串口识别不了?别只怪驱动,看看 VID/PID 与 INF》
References
-
USB 2.0 Specification – Chapter 9 (Device Framework)
-
Microsoft Docs – USB Descriptor Overview
如果你在写 USB 描述符时遇到过“怎么改都报错”的情况,欢迎在评论区晒出你的描述符代码片段。
原创文章,转载请注明出处。
更多推荐


所有评论(0)