3、端点描述符详解:bEndpointAddress、bmAttributes、wMaxPacketSize、bInterval 的配置策略与约束

端点描述符,说白了就是端点的「身份证」。USB 主机靠它来识别这个端点到底是干啥的、怎么传数据、一次传多少、多久传一次。我刚开始调 USB 驱动时,总觉得这玩意儿就是个固定模板,填对了就行。后来踩了几个坑才明白——这几个字段的配置,直接决定了你的批量传输能跑多快、稳不稳。

今天咱们就把这四个关键字段掰开揉碎了讲。我会结合自己项目里遇到的实际问题,告诉你哪些地方容易翻车,怎么配置才靠谱。

3.1 bEndpointAddress:方向与端点号

bEndpointAddress 是一个字节,高 1 位表示方向,低 4 位表示端点号。结构很简单:

位 含义 取值
Bit 7 方向 0 = OUT(主机到设备),1 = IN(设备到主机)
Bit 6-4 保留 必须为 0
Bit 3-0 端点号 0x00 ~ 0x0F(共 16 个)

举个例子:0x81 表示端点 1 IN,0x02 表示端点 2 OUT。

我的习惯:批量传输端点我一般把 IN 和 OUT 的端点号设成一样,比如 EP1 IN 和 EP1 OUT。这样主机驱动里处理逻辑更清晰,不容易搞混。但要注意——控制传输的端点 0 是固定的,别去动它。

这里有个容易忽略的点:同一个端点号可以同时有 IN 和 OUT 两个方向,但它们是两个独立的描述符。USB 规范允许这样做,很多设备也是这么干的。不过我个人建议,如果硬件资源允许,尽量把 IN 和 OUT 分开用不同的端点号,调试时更方便定位问题。

我曾经踩过的坑:有一次做高速数据采集卡,我把 EP1 同时配成了 IN 和 OUT,结果主机驱动里用 libusb 提交 URBs 时,总是莫名其妙地超时。查了两天才发现——芯片内部 FIFO 是共享的,IN 和 OUT 同时跑时互相干扰。后来改成 EP1 IN + EP2 OUT,问题立刻解决。

3.2 bmAttributes:传输类型

bmAttributes 决定端点的传输类型。对于批量传输,这个字段必须设为 0x02。但实际项目中,我见过有人不小心配成中断传输(0x03),结果主机枚举时直接报错。

值 传输类型 典型应用
0x00 控制传输 枚举、标准请求
0x01 等时传输 音频、视频流
0x02 批量传输 存储、数据采集
0x03 中断传输 HID、键盘、鼠标

嗯,这里要注意:批量传输没有带宽保证,主机总线空闲时才传。所以如果你的应用对实时性要求高,比如音频流,别用批量传输,老老实实选等时或中断。

关键点:批量传输的 bmAttributes 固定为 0x02。别问为什么,规范就是这么定的。你改了这个值,主机就不认你这个端点了。

3.3 wMaxPacketSize:一次能传多少

wMaxPacketSize 是端点单次传输的最大数据包大小。这个值直接影响吞吐量——配小了带宽浪费,配大了硬件可能扛不住。

不同 USB 速度下,批量传输的 wMaxPacketSize 有固定上限:

USB 速度 wMaxPacketSize 上限 实际常用值
全速 (12 Mbps) 64 字节 64
高速 (480 Mbps) 512 字节 512
超高速 (5 Gbps) 1024 字节 1024

我见过有人在全速设备上配了 512 字节的 wMaxPacketSize,结果主机枚举时直接返回错误。原因很简单——全速批量传输最大包就是 64 字节,你配大了硬件根本发不出去。

性能调优建议:高速设备一定要配 512 字节。我做过对比测试,配 512 字节时批量传输吞吐量能到 40+ MB/s,配 64 字节时只有不到 10 MB/s。差距就是这么明显。

另外,wMaxPacketSize 的低 11 位是包大小,高 5 位用于等时传输的微帧数。批量传输时高 5 位必须为 0。这个细节我见过有人搞错,结果主机解析描述符时直接跳过这个端点。

3.4 bInterval:轮询间隔

bInterval 这个字段,批量传输和中断传输的含义完全不同。很多人一开始搞混,包括我自己。

  • 中断传输:bInterval 表示主机轮询端点的间隔,单位是毫秒(全速)或 125µs 的倍数(高速)。
  • 批量传输:bInterval 没有实际意义,主机不会按这个间隔去轮询。但规范要求高速批量端点的 bInterval 必须为 0。

说白了,批量传输的 bInterval 就是个「摆设」。但你别乱填——高速设备必须填 0,全速设备可以填 0 或任意值,但主机不会理它。

我曾经犯过的错:做高速 USB 摄像头时,我把批量端点的 bInterval 填成了 1,想着「轮询快一点」。结果主机枚举时直接报「描述符错误」,设备都认不出来。后来查规范才发现——高速批量端点 bInterval 必须为 0,非 0 值会被视为无效描述符。

3.5 配置策略总结

下面这张图是我自己总结的端点描述符配置流程,每次做新项目我都会对照着检查一遍:

端点描述符配置决策流程 确定端点用途 选择传输类型 (bmAttributes) 批量传输 (0x02) 中断传输 (0x03) 配置 wMaxPacketSize bInterval 必须为 0 配置 wMaxPacketSize bInterval 按需配置 配置 bEndpointAddress 提示:批量传输的 bInterval 在高速下必须为 0,全速下无实际意义但建议也填 0

3.6 实战配置示例

下面是一个高速批量端点的描述符配置示例,我直接从项目代码里摘出来的:

// 高速批量 IN 端点描述符
0x07,           // bLength: 描述符长度
0x05,           // bDescriptorType: 端点描述符
0x81,           // bEndpointAddress: EP1 IN
0x02,           // bmAttributes: 批量传输
0x00, 0x02,     // wMaxPacketSize: 512 字节 (0x0200)
0x00,           // bInterval: 高速批量必须为 0

// 高速批量 OUT 端点描述符
0x07,           // bLength
0x05,           // bDescriptorType
0x01,           // bEndpointAddress: EP1 OUT
0x02,           // bmAttributes: 批量传输
0x00, 0x02,     // wMaxPacketSize: 512 字节
0x00            // bInterval: 0
注意:wMaxPacketSize 是小端格式。512 字节 = 0x0200,在描述符里写成 0x00, 0x02。我见过有人写成 0x02, 0x00,结果主机解析成 2 字节,数据根本传不出去。

好了,关于端点描述符的四个关键字段,咱们就聊到这儿。配置时记住一句话:批量传输的 wMaxPacketSize 往大了配,bInterval 往 0 填,bmAttributes 别乱改,bEndpointAddress 注意方向位。按这个思路来,基本不会出大问题。