文章目录
【全文大纲】 : https://blog.csdn.net/Engineer_LU/article/details/135149485
1 . 前言
- 1 . USB协议较为广泛,这里按以往风格描述物理层,协议层,应用层
- 2 . 基于本人习惯简洁风格,此篇文章尽量把USB协议描述清晰
- 3 . 接下来从物理层,协议层,应用层,从下往上深入浅出剖析
2 . USB外形类分

3 . 物理层
-
USB2.0的结构简洁,VCC, D+, D-, GND通信.
-
主机的两个下拉电阻阻值为15K,从机的D+ D-上拉电阻为1.5K
-
通讯依靠D+,D-作为差分
-
对于低速,全速设备USB通信四种状态,逻辑0与逻辑1和JK状态不加以区分
J K SE0 SE1 D + 0 1 0 1 D - 1 0 0 1 -
对于高速设备,做了8b/10b编码,把逻辑0与逻辑1和JK状态区分开来
-
下图为USB2.0的结构 :

-
当设备接入时,通过检测从机的上拉电阻接在了D- 或 D+来判断设备所属模式
模式 上拉电阻接入 (1.5K) 速度 低速 D- 1.5M 全速 D+ 12M 高速 D+ 480M -
当设备拔出时,通过检测从机的D- 和 D+ 的上拉来判断设备拔出
模式 上拉电阻接入 (1.5K) 拔出 无
4 . 协议层
4.1: USB协议层结构
- USB协议层结构图解,一次传输由多个事务组成,一个事务一般由三个包组成,、一个包由多个字段构成

4.2: USB传输事务关系
- 传输类型与事务数量的关系
| 传输类型 | 典型事务数 | 原因 |
|---|---|---|
| 控制传输 | 2 - N | 固定结构:Setup + (可选Data) + Status |
| 中断传输 | 通常1 | 数据量小,实时性要求 |
| 批量传输 | 1 - N | 根据数据量分割 |
| 同步传输 | 固定N/微帧 | 带宽预留,时间敏感 |
传输类型(Transfer Type)的决定因素
| 决定层面 | 决定因素 | 具体机制 | 示例 |
|---|---|---|---|
| 设备设计时 | 端点描述符 | 在端点描述符的bmAttributes字段的Bits[1:0]中指定: • 00 = 控制传输 • 01 = 等时传输 • 10 = 批量传输 • 11 = 中断传输 | HID设备的输入端点设置为中断传输;U盘的批量端点设置为批量传输 |
| 枚举过程中 | 设备描述符 | bDeviceClass, bDeviceSubClass, bDeviceProtocol字段定义了设备的类、子类和协议,隐式决定了主要传输类型 | 一个音频设备会被识别为需要等时传输 |
| 驱动加载时 | 设备类和协议 | 操作系统根据设备类加载相应驱动,驱动知道如何处理特定传输类型 | HID类驱动知道鼠标使用中断传输,存储设备驱动知道使用批量传输 |
| 应用程序 | 数据特性需求 | 应用根据数据特性选择: • 控制/配置 → 控制传输 • 大文件 → 批量传输 • 实时数据 → 等时传输 • 小量实时 → 中断传输 | 视频会议软件选择等时传输;文件管理器选择批量传输 |
事务类型(Transaction Type)的决定因素,数据量与事务的关系
| 决定层面 | 决定因素 | 具体机制 | 示例 |
|---|---|---|---|
| Setup事务 | 控制传输的结构 | 所有控制传输都必须以Setup事务开始 | 枚举时获取描述符必须用Setup事务发送GET_DESCRIPTOR请求 |
| IN事务 | 数据传输方向 | 需要从设备读取数据时: 1. Setup包的bmRequestType方向位=1 2. 批量/中断/等时传输的读操作 | 鼠标发送移动数据(中断IN)、读取U盘文件(批量IN) |
| OUT事务 | 数据传输方向 | 需要向设备写入数据时: 1. Setup包的bmRequestType方向位=0 2. 批量/中断/等时传输的写操作 | 向打印机发送文档(批量OUT)、配置设备参数(控制OUT) |
| Data事务的数量 | 数据长度和包大小 | 数据事务数 = ceil(数据总长度 / 端点最大包长) | 读取512字节数据,端点最大包长64,需要8个IN事务 |
| Status事务方向 | 数据阶段方向 | • 数据阶段为IN → Status事务为OUT • 数据阶段为OUT → Status事务为IN • 无数据阶段 → Status事务为IN | SET_ADDRESS无数据阶段,Status事务用IN事务 |
-
传输类型主要由:
端点描述符(硬件设计时决定)
设备类规范(协议标准要求)
数据特性(应用需求) -
事务类型主要由:
传输类型(控制传输有固定结构)
数据方向(IN或OUT)
数据量大小(决定事务数量)
协议阶段(Setup/Data/Status) -
简单记忆:
传输类型 = 用什么交通工具(公交、地铁、出租车、专车)
事务类型 = 上下车的具体动作(上车刷卡、找座位、下车刷卡)
-
USB事务类型与字段详解
USB事务一共有setup,in,out事务,另外还有一个status事务,但 status 事务不是USB协议中的标准独立事务类型。它是控制传输的状态阶段,实际上就是一个零长度的IN或OUT事务。因此,我们将重点描述四种基本事务类型。
下表详细描述了Setup、IN、OUT和Status四种事务的组成、作用和各字段:
表1:Setup事务(控制传输的设置阶段),作用 :启动一个控制传输,发送8字节的标准设备请求(如获取描述符、设置地址等)事务组成 包序列 发送方 各字段作用 备注/特殊规则 Setup事务 1. SETUP令牌包 主机 PID(0x2D): 标识为SETUP类型
ADDR: 目标设备地址(通常为0,枚举时)
ENDP: 端点号(总是0,控制端点)
CRC5: 校验地址和端点总是发往设备的控制端点(端点0) 2. DATA0数据包 主机 PID(0xC3): 固定使用DATA0
DATA(8字节): 包含标准请求: bmRequestType, bRequest, wValue, wIndex, wLength
CRC16: 校验8字节数据固定8字节长度,固定DATA0 PID,重置数据切换 3. ACK握手包 设备 PID(0xD2): 确认接收成功 设备必须在接收到有效的Setup包后返回ACK
表2:IN事务(数据从设备到主机),作用 :从设备读取数据,用于控制传输的数据阶段、批量读取、中断传输读取等
事务组成 包序列 发送方 各字段作用 备注/特殊规则 IN事务 1. IN令牌包 主机 PID(0x69): 标识为IN类型
ADDR: 目标设备地址
ENDP: 端点号(指定具体端点)
CRC5: 校验地址和端点主机请求从指定端点读取数据 2. 数据包 设备 PID(DATA0/DATA1/…): 根据数据切换状态
DATA: 要发送给主机的数据
CRC16: 校验数据内容设备可以发送NAK(忙)或STALL(错误)代替数据包 3. 握手包 主机 PID(ACK/…): 确认数据接收状态 主机成功接收后发送ACK;失败则不回应(超时)
表3:OUT事务(数据从主机到设备),作用 :向设备写入数据,用于控制传输的数据阶段、批量写入、中断传输写入等。
事务组成 包序列 发送方 各字段作用 备注/特殊规则 OUT事务 1. OUT令牌包 主机 PID(0xE1): 标识为OUT类型
ADDR: 目标设备地址
ENDP: 端点号(指定具体端点)
CRC5: 校验地址和端点主机准备向指定端点写入数据 2. 数据包 主机 PID(DATA0/DATA1/…): 根据数据切换状态
DATA: 要发送给设备的数据
CRC16: 校验数据内容主机发送实际数据,长度由端点最大包长决定 3. 握手包 设备 PID(ACK/NAK/STALL): 确认数据接收状态 设备成功接收返回ACK;忙则返回NAK;错误则返回STALL
表4:Status事务(控制传输的状态阶段 - 实际是特殊IN/OUT事务),作用 :作为控制传输的最后一个阶段,确认整个控制传输是否成功完成。它不是独立的事务类型,而是零长度的IN或OUT事务。
事务组成 包序列 发送方 各字段作用 备注/特殊规则 Status事务(作为OUT事务时) 1. OUT令牌包 主机 PID(0xE1): OUT类型
ADDR: 目标设备地址
ENDP: 端点号(通常为0)
CRC5: 校验地址和端点仅当数据阶段是IN时,状态阶段用OUT事务 2. DATA1数据包 主机 PID(0x4B): 固定使用DATA1
DATA: 零长度(空)
CRC16: 校验空数据零长度包,固定DATA1 PID,表示传输成功完成 3. ACK握手包 设备 PID(0xD2): 确认状态接收成功 设备确认整个控制传输完成 Status事务(作为IN事务时) 1. IN令牌包 主机 PID(0x69): IN类型
ADDR: 目标设备地址
ENDP: 端点号(通常为0)
CRC5: 校验地址和端点当数据阶段是OUT或没有数据阶段时,状态阶段用IN事务 2. DATA1数据包 设备 PID(0x4B): 固定使用DATA1
DATA: 零长度(空)
CRC16: 校验空数据零长度包,固定DATA1 PID,表示设备处理成功 3. ACK握手包 主机 PID(0xD2): 确认状态接收成功 主机确认整个控制传输完成 -
USB事务类型总结与对比
| 事务类型 | 协议层地位 | 使用场景 | 数据方向 | 数据包PID | 数据长度 |
|---|---|---|---|---|---|
| Setup | 控制传输的必需起始阶段 | 枚举、设备控制请求 | 主机→设备 | 固定DATA0 | 固定8字节 |
| IN | 通用读取事务 | 批量读取、中断读取、控制传输数据阶段(读) | 设备→主机 | DATA0/DATA1交替 | 0-端点最大包长 |
| OUT | 通用写入事务 | 批量写入、中断写入、控制传输数据阶段(写) | 主机→设备 | DATA0/DATA1交替 | 0-端点最大包长 |
| Status | 控制传输的结束阶段 | 控制传输状态确认 | 双向(零长度) | 固定DATA1 | 固定0字节 |
关键理解
关键理解要点
1 . 事务的构成:每种事务都由令牌包 + 数据包 + 握手包组成(等时传输例外,没有握手包)。
2 . Status不是独立事务:它是控制传输的状态阶段,根据数据阶段方向,可以是IN或OUT事务,但必须是零长度且使用DATA1 PID。
3 . PID使用规则:
Setup事务:固定DATA0
Status事务:固定DATA1
其他数据事务:DATA0/DATA1交替(数据切换机制)
4 . 数据方向规则:
数据阶段方向由Setup包的bmRequestType方向位决定
Status阶段方向与数据阶段方向相反(或当无数据阶段时,为IN)
5 . 控制传输的完整流程:控制传输 = Setup事务 + [0-N个数据事务(IN/OUT)] + Status事务(IN/OUT,零长度)
4.3: USB包与字段关系
每个事务一般由三个包构成,各包的字段解析
4.3.1 :令牌包
-
发起并定义一次数据事务的方向和目的地,在USB总线上,永远由主机发起所有通信。令牌包就是主机发出的“命令”,它告诉特定设备:“现在轮到你,我要求你进行一个特定操作”。
定义事务类型:- SETUP: 发起一次控制传输的请求阶段(如:读取设备信息)。
- IN: 要求设备向主机发送数据(如:读取U盘文件)。
- OUT: 通知设备准备从主机接收数据(如:向打印机发送文档)。
- SOF (Start of Frame): 在全速/高速下,每1ms(125μs)广播一次,提供时间基准,用于同步和带宽管理。
指定目标:包含设备地址和端点号,精确地指出与哪一个设备的哪一个“数据通道”(端点)进行通信。
简单比喻:就像一个物流中心的调度员,拿着喇叭喊:“3号仓库的1号门(地址3,端点1),现在请你把货发出来(IN)!”令牌包(Token Packet)字段详解
字段名称 长度(位) 在包中的位置 作用与描述 示例/备注 SYNC 8 (低速/全速)
32 (高速)起始 时钟同步。固定的位模式,用于接收方同步其时钟,准确采样后续数据。 全速:0x80 (二进制 1000 0000),经NRZI编码后为连续的01交替。 PID 8 SYNC之后 包标识符。唯一标识此包为令牌包,并指定其具体类型(IN/OUT/SETUP/SOF)。 0x69 = IN令牌
0xE1 = OUT令牌
0x2D = SETUP令牌
0xA5 = SOF令牌ADDR 7 PID之后 设备地址。指定本次事务要访问的USB设备地址(0-127)。地址0用于默认控制端点,仅在枚举阶段使用。 地址 3 表示与已枚举的、地址为3的设备通信。 ENDP 4 ADDR之后 端点号。与ADDR结合,精确指定目标设备内的特定数据端点(0-15)。端点0是必须存在的控制端点。 端点 1 可能对应设备的某个批量输入端点。 CRC5 5 ADDR和ENDP之后 循环冗余校验。校验ADDR和ENDP字段在传输中是否出错。多项式为 x⁵ + x² + 1。 接收方重新计算CRC5,若与收到的不符,则丢弃整个包。 EOP 信号电平 包末尾 包结束。并非一个数据字段,而是一个特定的信号状态(SE0后接J状态),表示包传输结束。 发送方将D+和D-都拉低(SE0)约2个位时间,再恢复空闲状态(J)。 关键补充说明
PID的构成:PID字节的低4位是类型码,高4位是低4位的反码,用于校验PID本身的有效性。例如,IN的类型码是1001,其PID字节为1001 0110 (0x69)。
SOF令牌的特殊性:SOF(帧起始)包的结构略有不同。它的ADDR和ENDP字段被一个11位的帧号(Frame Number)取代,用于同步和跟踪时间。
令牌包的作用流程:主机通过发送一个令牌包,启动一个完整的事务。例如,发送一个 PID=IN, ADDR=3, ENDP=1 的令牌包,就意味着:“请地址为3的设备,从它的1号端点向我发送数据”。随后才会进入数据包和握手包阶段。
4.3.2 :数据包
-
承载实际需要传输的有效信息,一旦令牌包发出了指令,数据包就负责运送真正的“货物”。它只存在于有数据需要传输的事务中。
关键职责:
- 运输数据:数据可以是命令(Setup包中的8字节请求)、文件内容、鼠标移动坐标、音频流等任何有效载荷。
- 确保顺序:通过 DATA0 和 DATA1(以及高速下的 DATA2/MDATA)的交替切换 (Data Toggle) 机制,发送方和接收方可以检测数据包是否丢失或重复,从而保证数据的正确顺序。
- 数据校验:包含强大的CRC16校验码,接收方可以用它来验证数据在传输过程中是否发生位错误。
简单比喻:就是一辆辆装载着实际货物的卡车,卡车侧面贴着“DATA0”或“DATA1”的标签,用于核对顺序。车厢里装着真正的货物(数据)。
数据包(Data Packet)字段详解
字段名称 长度(位) 在包中的位置 作用与描述 示例/备注 SYNC 8 (低速/全速)
32 (高速)起始 时钟同步。固定的位模式,用于接收方同步时钟,准确采样后续数据。这是接收方识别一个包开始的标志。 同令牌包,是一个特定的边沿信号。 PID 8 SYNC之后 数据包标识符。标识此包为数据包,并指定其数据切换序列类型。这是实现可靠传输、防止丢包或重复的关键。 0xC3 = DATA0
0x4B = DATA1
0x87 = DATA2 (仅高速等时)
0x0F = MDATA (仅高速)DATA 可变(0 ~ 8192位) PID之后 有效载荷。这是实际需要传输的信息主体,长度由具体传输需求和端点能力决定。 可以是设备描述符、文件数据、HID报告、音频采样等。零长度用于状态阶段。 CRC16 16 DATA之后 循环冗余校验。用于校验整个DATA字段在传输过程中是否发生比特错误。多项式为 x¹⁶ + x¹⁵ + x² + 1。 接收方计算DATA字段的CRC16,若与收到值不匹配,则数据包被视为损坏,并期待发送方重传。 EOP 信号电平 包末尾 包结束。一个特定的信号状态(SE0后接J状态),物理上标识当前包的传输结束。 与令牌包和握手包的EOP信号形式相同。 关键补充说明
PID与数据切换(Data Toggle):DATA0和DATA1 PID是数据包可靠性的核心。发送方和接收方各自维护一个“切换位”。成功传输一个数据包后,双方同步地将期待的下一个PID从DATA0切至DATA1,或从DATA1切回DATA0。如果接收方收到一个非预期的PID(如连续两个DATA0),则表明发生了丢包或重复,可据此进行恢复。DATA2和MDATA用于高速等时传输的更复杂序列。
DATA字段的长度限制:
低速:最大 8 字节。
全速:
控制传输:最大 8、16、32 或 64 字节(由设备描述符bMaxPacketSize0指定)。
中断/批量传输:最大 64 字节。
同步传输:最大 1023 字节。
高速:
控制传输:最大 64 字节。
批量传输:最大 512 字节。
中断传输:最大 1024 字节。
等时传输:最大 1024 字节。
特殊的数据包形式:
Setup包:在控制传输中,它是一个DATA字段固定为8字节、PID固定为DATA0的数据包,用于承载主机发送给设备的请求(bmRequestType, bRequest, wValue, wIndex, wLength)。
Status包:在控制传输的状态阶段,它是一个DATA字段长度为0、PID固定为DATA1的数据包,用于确认整个控制传输的完成。
与令牌包字段的对比:数据包没有ADDR(地址)和ENDP(端点)字段,因为这些信息由发起此次数据传输的前置令牌包(IN或OUT)已经指定。数据包使用更强大的CRC16(而非CRC5)来保护其可变长的数据载荷。
4.3.3 :握手包
-
对刚刚完成的事务阶段给予“成功”、“忙”或“出错”的即时反馈。握手包是整个USB通信可靠性和流量控制的基石。它不携带任何应用数据,只传递状态。
关键类型与作用:
- ACK (Acknowledge):确认。接收方(设备或主机)无误地收到了数据包
- NAK (Negative Acknowledge):否定。设备暂时无法发送或接收数据(例如:缓冲区满、尚未准备好)。
- STALL:停滞。端点发生了错误或请求不被支持。主机需要介入修复(如复位端点)。
- NYET:尚未就绪。仅用于高速传输,表示“这次数据收到了,但缓冲区已满,下次发送前请先询问(PING)”。
- 同步传输 :(如音频、视频)没有握手包,为了确保实时性,它牺牲了可靠性(数据出错不重传)。
指定目标:包含设备地址和端点号,精确地指出与哪一个设备的哪一个“数据通道”(端点)进行通信。
简单比喻:就像一个物流中心的调度员,拿着喇叭喊:“3号仓库的1号门(地址3,端点1),现在请你把货发出来(IN)!”握手包(Handshake Packet)字段详解
字段名称 长度(位) 在包中的位置 作用与描述 示例/备注 SYNC 8 (低速/全速)
32 (高速)起始 时钟同步。固定的位模式,用于接收方同步时钟,准确采样后续的PID。虽然握手包很短,但仍需要同步以确保可靠接收。 同其他包,是一个特定的边沿信号。 PID 8 SYNC之后 握手包标识符。这是握手包的核心,唯一的有效信息字段,直接表达事务的状态结果。 0xD2 = ACK
0x5A = NAK
0x1E = STALL
0x96 = NYET (仅高速)EOP 信号电平 包末尾 包结束。一个特定的信号状态(SE0后接J状态),物理上标识当前包的传输结束。 与令牌包和数据包的EOP信号形式相同。 关键补充说明
1 . 握手包的本质:握手包是USB通信中唯一一个只包含PID而不携带任何应用数据的包。它结构简单,没有地址、端点和数据字段,也没有CRC校验(PID自身的校验位已足够)。它的唯一目的就是提供即时状态反馈。
2 . PID的详细解释:
ACK (Acknowledge):成功确认。接收方(可能是主机或设备)无误地收到了数据包(CRC校验正确,且数据切换PID符合预期)。这是事务成功的标志。
NAK (Negative Acknowledge):临时否定。只能由设备发送,表示设备暂时无法处理该事务(例如:输入端点尚无数据可发,或输出端点缓冲区已满)。这不是错误,而是一种流量控制机制,主机会稍后重试。
STALL:功能错误/停滞。只能由设备发送,表示端点遇到了无法自行恢复的错误(如收到了不支持的请求),或已被明确停止(Halted)。这是一个错误状态,通常需要主机通过控制传输进行干预(如发送CLEAR_FEATURE请求来复位端点)。
NYET (Not Yet):尚未就绪。仅用于高速传输,且仅由设备在特定的分割事务或PING协议中发送。它表示“当前数据事务已成功,但接收缓冲区将满,请主机在发送下一个数据包前先进行查询(PING)”。
3 . 握手包的发送方向:
在 IN 事务 中,数据包由设备发送,若主机成功接收,则主机回复 ACK 握手包;若接收出错,主机不回复任何握手包(即超时)。
在 OUT/SETUP 事务 中,数据包由主机发送,若设备成功接收,则设备回复 ACK;若设备忙则回复 NAK;若端点出错则回复 STALL。
4 . 握手包的缺失:等时传输(Isochronous)没有握手包。这是由其实时性特性决定的——为了确保固定的传输速率和低延迟,等时传输宁可容忍偶尔的数据错误,也不愿通过重传机制破坏时序。
握手包与错误处理:
如果接收方检测到数据包的 CRC 错误,它会直接丢弃该包,且不发送任何握手包。发送方会因收不到预期的ACK(或NAK/STALL)而超时,并触发重传机制。
握手包本身没有CRC字段,其完整性依靠其很短的固定长度(只有SYNC和PID)以及PID自身高4位的校验码来保证。
4.4 首次通信
- 当主机识别到有设备接入后,主机会在总线上对 { “地址0” “端点0” } 发送信息,从机接入时,从 { “地址0” “端点0” } 处接收信息,这里的信息包含了主机分配给从机的 新地址,从机回复主机确认后,从机按 新地址 自居,从机找到家了,从此可以和主机愉快的生活了,主机就可以到 新地址 访问了,然后主机会向 新地址 请求完整的 设备描述符,配置描述符 来获取接口,如果接口存在多个,会继续遍历,当主机知晓设备的完整信息后,就完成了 枚举 的过程。
4.5 USB描述符
描述符前言 :
- 在写描述符之前,这里将USB意义简述一下。
- USB协议总是主机发起通信,如果从机的内容比较多,那么主机该如何与从机的某个局部信息进行交互呢?
- 无论从机有多少信息,我们把它分而治之不就可以了吗,就像一省有N市,一市有N镇,一镇有N村,假如你想了解某个人的信息,那么通过省市镇村就可以找到对方进行交互,USB协议里也是一样,逐层解析。
- 所以诞生了设备描述符,配置描述符,接口描述符,端点描述符,报告描述符,特殊描述符,字符串描述符,各描述符都有自己的数据结构,以下详述了各描述符意义。
- 以下引用部分CherryUSB协议栈的数据结构描述

4.3.1 设备描述符
- 设备的基本信息: 设备描述符包含了有关 USB 设备的基本信息,如设备类别、设备子类别、设备协议、最大包大小等。这些信息有助于主机系统正确识别和配置设备
- 供应商和产品标识: 设备描述符包含供应商标识(Vendor ID)和产品标识(Product ID),这两个标识唯一地标识了设备的制造商和产品。这对于主机系统正确识别和匹配设备驱动程序非常重要
- 设备版本信息: 设备描述符包含了设备的版本号,有助于主机系统确定设备的硬件或固件版本。
- 设备支持的 USB 规范版本: 设备描述符指明了设备支持的 USB 规范版本,这有助于主机系统适配与设备进行通信。
- 配置描述符的索引: 设备描述符中包含配置描述符的索引,主机系统可以通过这个索引获取设备的配置信息。
#define USB_DEVICE_DESCRIPTOR_INIT(bcdUSB, bDeviceClass, bDeviceSubClass, bDeviceProtocol, idVendor, idProduct, bcdDevice, bNumConfigurations) \
0x12, /* bLength */ \
USB_DESCRIPTOR_TYPE_DEVICE, /* bDescriptorType */ \
WBVAL(bcdUSB), /* bcdUSB */ \
bDeviceClass, /* bDeviceClass */ \
bDeviceSubClass, /* bDeviceSubClass */ \
bDeviceProtocol, /* bDeviceProtocol */ \
0x40, /* bMaxPacketSize */ \
WBVAL(idVendor), /* idVendor */ \
WBVAL(idProduct), /* idProduct */ \
WBVAL(bcdDevice), /* bcdDevice */ \
USB_STRING_MFC_INDEX, /* iManufacturer */ \
USB_STRING_PRODUCT_INDEX, /* iProduct */ \
USB_STRING_SERIAL_INDEX, /* iSerial */ \
bNumConfigurations /* bNumConfigurations */
设备描述符总结 :
- 设备描述符是设备插入 USB 总线时被主机系统主动请求的第一个描述符。
- 主机系统通过读取设备描述符可以获取设备的基本特性,从而为设备进行正确的配置和管理。
- 设备描述符是 USB 设备描述符的核心组成部分,提供了主机系统获取关于设备基本信息的入口,有助于主机正确识别、配置和与设备进行通信。
4.3.2. 配置描述符
- 配置的基本信息: 配置描述符包含了有关设备配置的基本信息,如配置值、总长度、最大功率需求等。这些信息有助于主机系统了解设备的配置。
- 接口数量: 配置描述符指明了该配置下包含的接口的数量,主机系统可以通过这个信息了解设备提供了多少个功能或服务。
- 供电特性: 配置描述符包含了设备的供电特性,例如设备是否通过总线供电、是否支持远程唤醒等。
- 接口描述符的索引: 配置描述符中包含了接口描述符的索引,主机系统可以通过这个索引获取配置下每个接口的详细信息。
- 分配地址的标志: 配置描述符中的"bAttributes"字段指明了该配置是否是用于分配设备地址的配置,这对于 USB 设备的初始化阶段很重要。
#define USB_CONFIG_DESCRIPTOR_INIT(wTotalLength, bNumInterfaces, bConfigurationValue, bmAttributes, bMaxPower) \
0x09, /* bLength */ \
USB_DESCRIPTOR_TYPE_CONFIGURATION, /* bDescriptorType */ \
WBVAL(wTotalLength), /* wTotalLength */ \
bNumInterfaces, /* bNumInterfaces */ \
bConfigurationValue, /* bConfigurationValue */ \
0x00, /* iConfiguration */ \
bmAttributes, /* bmAttributes */ \
USB_CONFIG_POWER_MA(bMaxPower) /* bMaxPower */
配置描述符总结 :
- 配置描述符提供了主机系统有关设备配置的基本信息。
- 一个 USB 设备可以有一个或多个配置,每个配置可以包含一个或多个接口,每个接口可以包含一个或多个端点。
- 主机系统通过读取配置描述符,能够了解设备的整体结构和提供的功能,从而为设备进行正确的配置和管理。
- 配置描述符是 USB 设备描述符的关键组成部分,有助于主机系统正确识别、配置和与设备进行通信。
4.3.3. 接口描述符
- 接口的基本信息: 接口描述符包含了关于接口的基本信息,如接口号、接口类别、子类别、协议等。
- 端点数量: 接口描述符指明了该接口上包含的端点的数量。每个端点用于不同的数据传输目的,如控制传输、批量传输、中断传输或同步/异步传输等。
- 接口描述符的索引:接口描述符中包含了该接口的索引,主机系统可以通过这个索引获取接口的详细信息。
- 接口的特殊属性: 接口描述符中的一些字段提供了关于接口的特殊属性的信息,如是否支持 HID(Human Interface Device)等。
- 接口关联描述符: 在某些情况下,接口描述符可能包含关联其他接口的信息,用于表示多个接口之间的关系。
// 示例:CDC控制接口(接口0)
0x09, // bLength: 固定9字节
USB_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType: 接口描述符(0x04)
0x00, // bInterfaceNumber: 接口编号(0)
0x00, // bAlternateSetting: 备选设置(通常0)
0x01, // bNumEndpoints: 此接口拥有的端点数量(1个,即INT端点)
0x02, // **bInterfaceClass: 类代码(0x02 = Communication)**
0x02, // **bInterfaceSubClass: 子类(0x02 = Abstract Control Model)**
0x01, // **bInterfaceProtocol: 协议(0x01 = AT Commands)**
0x00, // iInterface: 描述此接口的字符串索引(常为0)
// 示例:CDC数据接口(接口1)
0x09,
USB_DESCRIPTOR_TYPE_INTERFACE,
0x01, // bInterfaceNumber: 接口编号(1)
0x00,
0x02, // bNumEndpoints: 2个端点(BULK IN和OUT)
0x0A, // **bInterfaceClass: 类代码(0x0A = CDC Data)**
0x00, // bInterfaceSubClass: 子类(0)
0x00, // bInterfaceProtocol: 协议(0)
0x00,
接口描述符总结 :
- 接口描述符对于主机系统正确识别和配置设备的每个接口非常重要。
- 主机系统通过读取接口描述符可以了解设备提供了多少个接口,每个接口的功能是什么,以及每个接口上有多少个端点。
- 接口描述符是 USB 设备描述符的一部分,用于提供关于每个接口的基本信息,帮助主机系统正确识别、配置和与设备进行通信。
4.3.4. 端点描述符
- 端点地址(Endpoint Address): 描述端点的地址,包括方向(输入或输出)和端点号。
- 端点类型(Attributes): 描述端点的类型,如控制、同步、异步等。
- 最大包大小(Max Packet Size):表示每个 USB 事务中传输的最大数据包大小。
- 传输间隔(Interval): 对于同步和中断端点,表示两次传输之间的时间间隔。
// 示例:批量OUT端点(地址0x02)
0x07, // bLength: 固定7字节
USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType: 端点描述符(0x05)
CDC_OUT_EP, // bEndpointAddress: 端点地址(0x02,方向OUT)
0x02, // **bmAttributes: 传输类型(0b0010 = 批量传输)**
// (0b0011 = 中断传输,0b0001 = 同步传输)
0x40, 0x00, // wMaxPacketSize: 最大包长(全速模式下64字节)
0x00, // bInterval: 轮询间隔(批量传输忽略,中断传输需设置)
// 示例:中断IN端点(地址0x83)
0x07,
USB_DESCRIPTOR_TYPE_ENDPOINT,
CDC_INT_EP, // bEndpointAddress: (0x83,方向IN)
0x03, // bmAttributes: 中断传输(0b0011)
0x08, 0x00, // wMaxPacketSize: 8字节(状态通知数据量小)
0x0A, // **bInterval: 轮询间隔10个帧(即10ms)**
端点描述符总结 :
- 端点描述符提供了主机系统关于设备上每个端点的详细信息,主机系统根据这些信息来正确配置和管理数据传输。
- 不同类型的端点有不同的用途,例如控制端点用于设备和主机之间的基本控制和配置,而同步和异步端点则用于数据传输等。
- 每个 USB 接口可以包含多个端点,其中包括一个控制端点(Endpoint 0),用于设备和主机之间的基本通信。
- 端点描述符通常跟随在接口描述符之后,为主机系统提供有关每个端点的配置信息。
- 端点描述符是 USB 设备描述符的关键组成部分,提供了主机系统关于设备上每个端点的详细信息,有助于正确配置和管理数据传输。
4.3.5. 报告描述符
- 定义数据结构: 描述HID设备发送和接收的数据报告的具体格式,包括字段数量、大小、用途等。。
- 描述数据项: 定义每个数据项(如按键、坐标、状态等)的用途、逻辑范围、单位等属性。
- 编码方式:使用紧凑的二进制格式描述复杂的数据结构,通过项目序列定义各种数据字段。
- 报告类型: 通常包括输入报告(设备到主机)、输出报告(主机到设备)和特征报告(双向配置数据)。
- 用途说明: 让主机能够正确解析来自HID设备的原始数据,如将字节流转换为有意义的按键事件或鼠标移动。
每个数据项的第一个字节(前缀)定义了它的类型和作用,结构如下:
Bits: [7:6] [5:4] [3:0]
功能: 项目类型 项目标签 数据大小(bSize)
1. 项目类型 (2位,bits 7:6)
值 (二进制) 类型 说明
00 主项目 定义或分组报告的数据域,类似于语言中的“名词”,描述报告里有什么。
01 全局项目 描述数据的通用属性(如逻辑范围、单位),其状态会持续影响后续所有项目,直到被改变,类似于“形容词”。
10 局部项目 描述特定用途的属性(如用法、字符串索引),其影响范围通常到下一个主项目为止,类似于“修饰语”。
11 保留 未使用。
2. 项目标签 (4位,bits 5:4 与 bits 3:0 的一部分)
与项目类型结合,共同确定项目的具体含义。例如,0x81 (1000 0001) 表示一个全局项目(类型10),标签是Input。
3. 数据大小 (2位,bits 3:2)
定义该项目后跟随的数据字节数:
bSize 数据字节数
00 0字节
01 1字节
10 2字节
11 4字节
static const uint8_t hid_mouse_report_desc[] = {
// ===== 全局项:设置使用场景 =====
0x05, 0x01, // Usage Page (Generic Desktop) // 全局项:声明主功能在“通用桌面”页面
0x09, 0x02, // Usage (Mouse) // 局部项:具体用途是“鼠标”
0xA1, 0x01, // Collection (Application) // 主项:开始一个“应用集合”,描述整个鼠标功能
// ===== 第一层:鼠标按键 =====
0x09, 0x01, // Usage (Pointer) // 局部项:用途是指针设备
0xA1, 0x00, // Collection (Physical) // 主项:开始一个“物理集合”,描述物理上的指针
// --- 定义3个独立按键(每个占1 bit) ---
0x05, 0x09, // Usage Page (Button) // 全局项:切换到“按键”页面
0x19, 0x01, // Usage Minimum (Button 1) // 局部项:最小用途ID是按键1
0x29, 0x03, // Usage Maximum (Button 3) // 局部项:最大用途ID是按键3(共3个按键)
0x15, 0x00, // Logical Minimum (0) // 全局项:数据的逻辑最小值是0(未按下)
0x25, 0x01, // Logical Maximum (1) // 全局项:数据的逻辑最大值是1(按下)
0x95, 0x03, // Report Count (3) // 全局项:接下来有3个字段
0x75, 0x01, // Report Size (1) // 全局项:每个字段占1个比特
0x81, 0x02, // Input (Data,Var,Abs) // 主项:定义3个1比特的输入字段,代表按键1-3
// --- 定义5个填充位(占5 bit) ---
0x95, 0x01, // Report Count (1) // 全局项:接下来有1个字段(组)
0x75, 0x05, // Report Size (5) // 全局项:该字段(组)占5个比特
0x81, 0x01, // Input (Cnst,Var,Abs) // 主项:定义5个比特的常量输入字段(填充,无意义)
// ===== 第二层:鼠标移动和滚轮 =====
0x05, 0x01, // Usage Page (Generic Desktop) // 全局项:切回“通用桌面”页面
0x09, 0x30, // Usage (X) // 局部项:用途是X轴
0x09, 0x31, // Usage (Y) // 局部项:用途是Y轴
0x09, 0x38, // Usage (Wheel) // 局部项:用途是滚轮(垂直滚轮)
0x15, 0x81, // Logical Minimum (-127) // 全局项:数据逻辑最小值是-127
0x25, 0x7F, // Logical Maximum (127) // 全局项:数据逻辑最大值是127
0x75, 0x08, // Report Size (8) // 全局项:每个字段占8个比特(1字节)
0x95, 0x03, // Report Count (3) // 全局项:接下来有3个字段(X, Y, Wheel)
0x81, 0x06, // Input (Data,Var,Rel) // 主项:定义3个8比特的**相对值**输入字段
0xC0, // End Collection // 主项:结束“物理集合”
0xC0 // End Collection // 主项:结束“应用集合”
};
报告描述符总结 :
- 报告描述符是在 HID 类别中定义的,典型的 HID 设备如键盘或鼠标通常会包含一个或多个报告描述符。
- 主机系统通过读取报告描述符可以了解 HID 设备生成数据报告的结构,从而正确解析和处理来自 HID 设备的数据。
- 报告描述符在 HID 设备中起到了定义设备数据报告结构的关键作用,它是 USB 设备描述符的一部分,用于描述 HID 设备生成的数据报告的格式和内容。
4.3.6. 特殊描述符
-
String Descriptor(字符串描述符): USB 设备可以包含多个字符串描述符,用于提供设备的可读文本信息,如制造商名称、产品名称、序列号等。
-
Interface Association Descriptor(接口关联描述符): 这是一种辅助描述符,用于关联多个接口。例如,一个设备可能同时提供音频输入和输出,这两个功能可能被关联在一起。
-
Endpoint Companion Descriptor(端点伴随描述符): 在 USB 3.0 中引入,提供了关于端点的额外信息,如是否支持超速传输等。
-
BOS (Binary Object Store) Descriptor(二进制对象存储描述符): 用于提供设备的固件升级和其他二进制对象的信息。
-
Device Capability Descriptor(设备能力描述符): 提供了关于设备支持的一些特殊功能和能力的信息,如 USB PD(USB Power Delivery)的支持等。
特殊描述符总结 :
- 一般HID,CDC设备会带有特殊描述符,补充描述
- 这些描述符可能是标准描述符,也可能是设备特定的描述符。
- 特殊描述符的存在使得设备能够提供额外的信息,或者支持一些规范中未明确定义的功能。
- 需要查阅设备的技术文档或者 USB 设备的特定规范以了解特殊描述符的具体细节和用途,因为它们的内容和用途在不同类型的设备中可能有很大的差异。
4.3.7. 字符串描述符
-
语言标识符描述符(Language ID Descriptor): 这是一种特殊的字符串描述符,用于指示设备支持的语言。语言标识符描述符的索引为零,它的值是一个语言标识符列表,表示设备支持的语言。
-
字符串描述符: 每个字符串描述符包含了一个 UTF-16 编码的字符串,用于表示特定的信息,如制造商名称、产品名称、序列号等。字符串描述符的索引值是非零的,每个索引值对应一种特定的字符串。
// 字符串描述符数组(注意:索引0是语言ID)
// 索引0:语言ID(例如美式英语 0x0409)
USB_LANGID_INIT(USBD_LANGID_STRING),
// 索引1:制造商字符串
0x14, 0x03, 'C',0, 'h',0, 'e',0, 'r',0, 'r',0, 'y',0, 'U',0, 'S',0, 'B',0,
// 索引2:产品字符串
0x26, 0x03, 'C',0, 'h',0, ..., 'D',0, 'E',0, 'M',0, 'O',0,
// 索引3:序列号字符串
0x16, 0x03, '2',0, '0',0, '2',0, '2',0, '1',0, '2',0, '3',0, '4',0, '5',0, '6',0,
字符串描述符总结 :
- 字符串描述符的格式包括一个字节的长度信息,后面是一个或多个包含 UTF-16 编码的字符。
- USB 规范规定字符串描述符的字符编码必须为 UTF-16LE(Little Endian)。
- 例如,如果要获取设备的制造商名称,主机系统可以向设备发送请求,指定索引值为设备制造商名称的字符串描述符,设备会回复一个包含制造商名称的字符串描述符。
- 字符串描述符提供了对设备信息的人类可读访问,有助于用户和操作系统正确识别和管理连接到系统的 USB 设备。
- 字符串描述符是 USB 设备描述符中的一种类型,用于提供设备的人类可读的文本信息。
4.6 数据通信
USB协议里从机不会主动发起通信,因为主机很多事情做,总线却只有一个,所以当主机 “忙完”, 就会主动向从机发起通信,通信过程包含三种情况。

- 控制传输: 用于在主机和设备之间进行基本的命令和配置
- 批量传输: 用于需要高带宽但不要求低延迟的数据传输
- 中断传输: 用于传输具有周期性且对延迟敏感的小量数据
- 同步传输: 用于实时传输音频、视频等对数据完整性要求不高但对实时性要求较高的应用
主机一开始请求过程会发送令牌包(TOKEN),请求中声明有数据要传输则有数据过程(长度描述不为0),最后由从机发起状态过程,结束本次通信。
如果主机向从机获取数据,那么主机获取到数据后,状态过程由主机发起结束。
4.7 :设备类型描述
- HID设备是人机交互设备,鼠标键盘这些为HID设备
- CDC设备是数据通信类,例如USB模拟串口这类
HID设备的数据在报告描述符内描述数据结构,然后就可以通过传输包来交互数据,报告描述符网上很多,若是不需要深入了解,可以直接用现成的。
5 . 小结
-
传输都由主机发起,一共四种传输(控制传输,中断传输,批量传输,同步传输),一次传输分为事务三种(setup事务,in事务,out事务),事务下面是包,都分别是令牌包、数据包(可选)、握手包(可选)组成,包下面是具体的字段,然后setup事务是枚举阶段用的,还有无其他阶段不确定,然后in事务是用来获取设备的数据的,out事务是主动往设备发数据,USB的4种传输类型,每种传输由一种或多种事务组成,事务有3种基本类型:SETUP、IN、OUT,关于SETUP事务:不仅用于枚举,正常运行时也用于类特定命令(如HID的Set_Report、CDC的Set_Line_Coding等)。
控制传输 = 1个SETUP事务 + 0个或多个IN/OUT事务(DATA阶段)+ 1个STATUS事务(也是一个IN或OUT事务)
其他传输(中断/批量/同步)= N个连续的IN事务或N个连续的OUT事务 -
一开始是设备描述符,用来描述设备的vid,pid,设备的usb版本然后是配置描述符
-
配置描述符里一般直接描述接口描述符,端点描述符,主机通过配置描述符的wTotalLength字段一次性读取整个配置链(配置+接口+端点+类特定描述符)。
-
接口描述符描述设备是什么类型,例如hid,cdc,还是0xff自定义设备,然后端点描述符描述端点地址,端点的缓存区大小,以及端点希望主机要多久查询一次
-
关于每个包里面的字段,再深入就是物理层了,而usb采用NRZI编码,因为usb定义全速和低速要从物理层区分开,所以分为了j和k状态,两者差分状态相反,全速时j为差分1好像,然后k为差分0,然后NRZI编码,是从NR(中间会经过0电平停顿),NRZ(会直接跳变),NRZI(逢0跳变,逢1维持,若连续6个1,则强制加入一个0,以让双方同步,防止频偏误差),物理层是反过来推导的,是先有数据流A,根据数据流做连续6个1做强制插入1个0来同步频偏误差,当这一层进行完后得到新一组数据B,再进行NRZI逢0变化,逢1维持,得到新一组数据C,然后最后根据数据流C进行JK转化差分真正物理层输出
原始数据流A:要发送的二进制比特(如 110111111…)
位填充后数据流B:检测连续6个’1’,插入一个’0’(得到 1101111101…)
NRZI编码后电平序列C:逢’0’跳变,逢’1’维持(得到一个J/K状态序列)
物理输出:将J/K状态转化为D+/D-的差分电压。
技术交流QQ群 : 745662457
群内专注 - 问题答疑,技术研究
321

被折叠的 条评论
为什么被折叠?



