USB-HID 设备扫盲
这是一篇关于USB协议的分析,重点分析HID(human interface device)类设备。本文档是在开发 CH567 HID 设备之后成文。
USB 扫盲
USB 速度
USB速度有4种,分为低速(Low-Speed)、全速(Full-Speed)、高速(Hi-Speed)和超高速(SuperSpeed)。其中的总线速度依次大约是1.5Mb/s, 12Mb/s和480Mb/s。这并不是指实际的数据传输速率。
USB 插入识别
USB的HOST,其D+与D-会有15K的电阻下拉,在没有设备的时候会是低电平。低速设备D-有1.5k电阻上拉。全速设备D+有1.5k电阻上拉。以此来分辨不同的设备速度类型。
USB 端点
根据USB规范,设备端点是USB设备中一个独特的可寻址部分,它作为主机和设备间通信流的信息源或库。可以简单理解为逻辑上的不同数据通道。分为IN 端点和 out 端点。以 host 的视角为中心,in 端点就是输入到 host 的数据,out 端点就是 host 输出的数据。
USB 传输类型
有4种传输类型。控制传输、中断传输、批量传输、同步传输。这些不同的传输方式各有优势,需要根据不同的应用需求选择不同的传输类型。
中断传输:这种传输非常适合需要使用高度可靠的方式来传输少量数据的设备。它通常用于HID设计。这种传输的名称可引起误会。实际上,它并不是一个中断,但使用了一个轮询率。进行该传输时,主机将在预计时间间隔内检查数据。通过及时检测错误并重新传输数据,该传输可确保数据操作的准确性。
控制传输:控制传输通过总线发送和接收设备的信息。它的优点是可以保证传输准确。它能够立即检测到错误的发生,并重新发送数据。
批量传输:这些端点支持批量传输,即是在高度可变的时间内传输大量数据并且可用任何带宽空间的传输。它们是USB设备的最通用传输类型。因为用于批量传输的带宽并不是固定的,该传输的传送时间也是可变的。传送时间取决于总线上的可用带宽,由于该因素,便不能预期实际的传送时间。通过及时检测错误并重新传输数据,该传输可确保数据操作的准确性。批量传输非常适合对时间没有严格要求的大量数据传输。
同步传输:这些端点支持同步传输,即具有预定带宽的连续性实时传输。由于同步传输没有错误恢复机制和握手数据包,它们需要支持容忍错误的数据流。错误由CRC字段检测,但不会被修改。因此,同步传输可保证传输速度,但以数据的准确性作为代价。流式音乐或视频即是使用同步端点的应用示例,因为我们的耳朵和眼睛通常忽略偶尔被错过的数据。
USB 链路编码
USB2.0采用NRZI 编码(Non-Return-to-Zero Inverted Code)。在 USB 中,电平翻转代表逻辑 0,电平不变代表逻辑1。在 USB 中,每个 USB 数据包,最开始都有个同步域(SYNC),这个域固定为 0000 0001,这个域通过 NRZI 编码之后,就是一串方波(NRZI 遇 0 翻转,遇 1 不变),接受者可以用这个 SYNC 域来同步之后的数据信号。此外,因为在 USB 的 NRZI 编码下,逻辑 0 会造成电平翻转,所以接受者在接受数据的同时,根据接收到的翻转信号不断调整同步频率,保证数据传输正确。但是,这样还是会有一个问题,就是虽然接受者可以主动和发送者的频率匹配,但是两者之间总会有误差。假如数据信号是 1000 个逻辑 1,经过 USB 的 NRZI 编码之后,就是很长一段没有变化的电平,在这种情况下,即使接受者的频率和发送者相差千分之一,就会造成把数据采样成 1001 个或者 999 个 1了。
USB中用Bit-Stuffing来同步时钟信号
USB 对这个问题的解决办法,就是强制插 0,也就是传说中的 bit-stuffing,如果要传输的数据中有 6个连续的 1,发送前就会在第 6 个 1 后面强制插入一个 0,让发送的信号强制出现翻转,从而强制接受者进行频率调整。
接受者只要删除 6 个连续 1 之后的 0,就可以恢复原始的数据了。
USB 包
USB通讯由一系列的帧构成。为了保证这些帧的分割准确性,每个帧开始都有一个SYNC(同步域),后面跟着Start-of-Frame(SOF),一个字节,数据为0xA5.后面跟着一个或者多个数据包。
- 数据包内有数据包ID(PID) — (8 位:4 个类型位和4 个错误检测位)。这些位将数据传输定义为IN/OUT/SETUP/SOF
- 可选的设备地址 — (7 位:最多可支持127 个设备)
- 可选的端点地址 — (4 位:最多支持16 个端点)。USB 规范支持多达32 个端点。虽然4 位地址最多仅支持16
个端点,但我们具有一个IN PID 和一个OUT PID,它们各自使用了端点地址1 到16,因此共有32 个端点。请注
意,它表示端点的地址,而不是端点的编号 - 可选的加载数据 — (0 到1023 字节)
- 可选的CRC — (5 或16 位)
USB 设备识别的协议
这里以 HID 设备为例,分析 USB 设备在被 host 识别的过程中的数据包交互。
描述符
描述符按照树形结构的层级,每个描述符有上下级关系。一个简单 HID 设备具有以下描述符: 设备描述符、配置描述符、接口描述符、端点描述符。还有一些字符串类的描述符,用来显示本设备的名字。
- 设备描述符:该描述符可以认为是本设备的身份证,里面包含了生产设备商的ID,和本身产品的ID,和支持的协议类型。host 首先读取这个描述符就好像警察盘问你先让你掏身份证一样。
偏移 | 字段 | 大小(字节) | 说明 |
---|---|---|---|
0 | bLength | 1 | 该描述符的长度 = 18 个字节 |
1 | bDescriptorType | 1 | 描述符类型 = 设备(01h) |
2 | bcdUSB | 2 | USB 规范版本(BCD) |
4 | bDeviceClass | 1 | 设备类别 |
5 | bDeviceSubClass | 1 | 设备子类别 |
6 | bDeviceProtocol | 1 | 设备协议 |
7 | bMaxPacketSize0 | 1 | 端点0 的最大数据包大小 |
8 | idVendor | 2 | 供应商ID(VID,由USB-IF 分配) |
10 | idProduct | 2 | 产品ID(PID,由制造商分配) |
12 | bcdDevice | 2 | 设备释放编号(BCD) |
14 | iManufacturer | 1 | 制造商字符串索引 |
15 | iProduct | 1 | 产品字符串索引 |
16 | iSerialNumber | 1 | 序列号字符串索引 |
17 | bNumConfigurations | 1 | 受支持的配置数量 |
- 配置描述符:该描述符会提供特定设备配置的信息,如接口数量、设备由总线供电还是自供电、设备能否启动一个远程唤醒以及设备功耗。
偏移 | 字段 | 大小(字节) | 说明 |
---|---|---|---|
0 | bLength | 1 | 该描述符的长度 = 9个字节 |
1 | bDescriptorType | 1 | 描述符类型 = 配置(02h) |
2 | wTotalLength | 2 | 总长度包括接口和端点描述符在内 |
4 | bNumInterfaces | 1 | 本配置中接口的数量 |
5 | bConfigurationValue | 1 | SET_CONFIGURATION请求所使用的配置值,用于选择该配置 |
6 | iConfiguration | 1 | 描述该配置的字符串索引 |
7 | bmAttributes | 1 | 位7:预留(设置为1) 位6:自供电 位5:远程唤醒 |
8 | bMaxPower | 1 | 本配置所需的最大功耗(单位为2 mA) |
- 接口描述符:一个接口描述符介绍了包含在配置中的特定接口。该接口的端点数量将显示在该描述符中。接口描述符也包含有关设备的USB 类别的信息。一个USB 设备可以属于多个预定义类别。一个USB 设备类别指出了设备功能,并有助于加载用于该特定功能的合适驱动器。
偏移 | 字段 | 大小(字节) | 说明 |
---|---|---|---|
0 | bLength | 1 | 该描述符的长度 = 9 个字节 |
1 | bDescriptorType | 1 | 描述符类型 = 接口(04h) |
2 | bInterfaceNumber | 1 | 该接口基于零的索引 |
3 | bAlternateSetting | 1 | 备用设置值 |
4 | bNumEndpoints | 1 | 该接口所使用的端点数量(不包含EP0) |
5 | bInterfaceClass | 1 | 接口类别 |
6 | bInterfaceSubclass | 1 | 接口子类别 |
7 | bInterfaceProtocol | 1 | 接口协议 |
8 | iInterface | 1 | 该接口字符串描述符索引 |
- 端点描述符:在一个设备中所使用的全部端点都有自己的描述符。该描述符会提供主机必须获取的端点信息。这些信息包括端点的方向、传输类型和数据包的最大尺寸。
偏移 | 字段 | 大小(字节) | 说明 |
---|---|---|---|
0 | bLength | 1 | 该描述符长度 = 7个字节 |
1 | bDescriptorType | 1 | 描述符类型 = 端点(05h) |
2 | bEndpointAddress | 1 | 位3 … 0:端点数量 位6 … 4:预留,复位为零 位7:端点的方向。控制端点可以忽略该位。 0 = OUT端点 1 = IN端点 |
3 | bmAttributes | 1 | 位1 … 0:传输类型 00 = 控制 01 = 同步 10 = 批量 11 = 中断 如果该端点不是同步端点,那么位5到位2将被预留,必须将这些位设置为零。如果该端点是同步的,这些位将按如下内容定义: 位3…2:同步类型 00 = 无同步 01 = 异步 10 = 自适应 11 = 同步 位5…4:用途类型 00 = 数据端点 01 = 反馈端点 10 = 隐式反馈数据端点 数值11表示保留 |
4 | wMaxPacketSize | 2 | 该端点的数据包最大尺寸 |
6 | bInterval | 1 | 中断端点的轮询间隔,单位为ms(对于同步端点,该间隔为1 ms;控制或批量端点可能忽略该字段) |