当前位置: 首页 > news >正文

USB 描述符怎么写都不对?别只抄例程,看看 bLength 与 wTotalLength

摘要:USB 描述符复制了例程,只是改了个 PID,电脑就提示“设备描述符请求失败”?不是 USB 栈坏了,而是描述符长度(bLength)或总长度(wTotalLength)算错了。本文拆解 USB 描述符的“字节级”潜规则。


一、问题描述(现象)

**用 CubeMX 生成的 HID 例程,枚举一切正常;

只是加了一个自定义 Usage Page,设备管理器就开始报黄叹号;

Bus Hound 抓包显示 GET_DESCRIPTOR 返回的数据长度不对。**

很多工程师的排查方向是:

  1. 是不是 PID/VID 冲突了?

  2. 描述符数组是不是没对齐?

  3. 换个 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 返回的原始字节流。


四、选型避坑建议

  1. bLength 必须精确

    • Device Descriptor = 18 bytes

    • Configuration Descriptor = 9 bytes

    • Interface Descriptor = 9 bytes

    • Endpoint Descriptor = 7 bytes

  2. 字符串描述符

    • bLength包含字符串长度 × 2 + 2(Unicode)。

  3. 不要混用: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 描述符时遇到过“怎么改都报错”的情况,欢迎在评论区晒出你的描述符代码片段。

原创文章,转载请注明出处。

http://www.jsqmd.com/news/974260/

相关文章:

  • 后端开发效率提升技巧:让编码更轻松
  • AI-Shoujo HF Patch终极指南:一键解锁70+插件与完整汉化 [特殊字符]✨
  • Wand-Enhancer:免费解锁Wand专业版功能的终极增强工具
  • 成都市2026年黄金回收白银回收铂金回收 5 家高性价比门店实地测评盘点 - 马刺总冠军
  • 3种高级方案深度解析pywencai项目:从量化数据采集到自动化分析系统
  • 魔兽争霸3终极优化解决方案:Warcraft Helper完全使用指南
  • 从STM32迁移到GD32F303?手把手教你用RT-Thread点亮第一个多线程应用
  • Colab工程化实践:构建可复现、抗中断的远程GPU工作站
  • ArcGIS工具箱实战:手把手教你定制自己的MODIS数据处理工具(附完整Python代码)
  • 告别付费限制:5分钟解锁Wand所有高级功能
  • 告别在线排队!手把手教你用NCBI BLAST+ 2.11.0在Windows本地搭建自己的序列比对工作站
  • 别再手动算温度了!用STM32CubeMX+MAX31865搞定PT100铂电阻,附三线制接线避坑指南
  • 注意力机制与最优传输的数学本质及GOAT实现
  • 深入解析FPGA架构:从查找表到逻辑单元与布线资源
  • 嵌入式信号处理避坑指南:你的滤波器阶数真的选对了吗?
  • COM3D2 MaidFiddler终极指南:实时修改女仆属性的完整教程
  • 如何用AI轻松征服2048游戏?这款智能助手让你胜率提升85%
  • Django REST后端 + Vue前端的可运行电商毕设项目(含数据导入、部署步骤和后台管理)
  • 3分钟免费解密微信聊天记录:WechatDecrypt终极解决方案
  • 现代数据科学中的正则表达式实战:从清洗到生产就绪
  • 基于U-Net网络的肺部图像分割
  • STM32F103C8T6智慧大棚实战工程:OneNET云直连+光照/温湿度/CO₂/土壤墒情四合一采集与远程开关控制
  • ZYNQ开发者效率翻倍:VSCode插件全攻略(从Testbench生成到TCL语法高亮)
  • DeTikZify:AI驱动的科学图表自动TikZ代码生成解决方案
  • 你的NFS配置安全吗?详解Ubuntu上/etc/exports权限设置的5个常见误区与正确姿势
  • 3分钟掌握科研数据提取:WebPlotDigitizer从图表图像中智能提取数值数据
  • 3ds Max可编辑衣柜模型:带预览图、分组结构与材质预留的实用家具资源
  • Beyond Compare过滤.DS_Store和__pycache__,Mac/Win双平台保姆级配置
  • PRISM架构:白盒Transformer的信号-噪声分解技术解析
  • 2026上饶市权威认证贵金属回收 TOP5+黄金回收白银回收铂金回收门店地址电话推荐