2. USB系统架构:主机侧与设备侧分层模型
USB系统的成功枚举依赖于主机与设备两侧严格的分层协作。理解USB系统架构是分析枚举失败的根本前提。本章从主机侧、设备侧以及分层模型三个维度,拆解USB系统的软硬件栈。
2.1 主机侧架构
USB主机侧由硬件控制器、核心驱动与客户端驱动三层构成,每一层对枚举流程都有直接影响。
| 层次 | 组件 | 职责 | 枚举相关关键点 |
|---|---|---|---|
| L3 | Client Driver | 实现具体设备类功能(HID、Mass Storage、CDC等) | 解析设备描述符中的bDeviceClass字段,决定是否绑定 |
| L2 | USB Driver (USBD) | 管理总线、枚举流程、地址分配、配置选择 | 发送标准请求(Get_Descriptor、Set_Address),解析描述符 |
| L1 | Host Controller (HC) | 物理层信号、帧起始包(SOF)、事务处理 | 检测设备连接(D+/D-电平变化)、复位信号、低速/全速/高速识别 |
2.1.1 Host Controller (HC) 层
Host Controller是USB总线的物理主控,负责产生帧起始包(SOF)、管理总线状态、检测设备插入/拔出、执行总线复位。常见的HC类型包括:
- EHCI:高速(480 Mbps)控制器,兼容全速/低速通过事务翻译器(TT)。
- OHCI/UHCI:全速/低速控制器,多用于旧平台或嵌入式系统。
- xHCI:USB 3.x 控制器,支持超速(5/10 Gbps)并向后兼容。
在枚举初期,HC检测到D+/D-电平变化后,会触发复位信号(至少10ms),然后进入帧起始包发送阶段。如果复位信号时序不满足USB规范(例如复位时间不足10ms),设备可能无法进入默认状态,导致枚举失败。
2.1.2 USB Driver (USBD) 层
USBD是操作系统核心USB子系统,负责枚举流程的调度。它通过控制传输(端点0)发送标准请求,并解析设备返回的描述符。USBD的关键枚举步骤包括:
- 检测到设备连接后,分配一个临时地址(通常为0)。
- 发送
Get_Descriptor(Device)请求,获取设备描述符前8字节(用于确定最大包长度)。 - 发送
Set_Address请求,分配唯一地址。 - 再次发送
Get_Descriptor(Device)获取完整设备描述符。 - 发送
Get_Descriptor(Configuration)获取配置描述符及所有接口/端点描述符。 - 根据描述符信息选择配置(
Set_Configuration)。
Set_Address 后,必须在新地址上响应后续请求。如果固件未正确切换地址,USBD将无法收到描述符数据,枚举超时失败。
2.1.3 Client Driver 层
Client Driver是设备类功能的具体实现,例如HID驱动、大容量存储驱动、CDC ACM驱动等。它通过USBD提供的接口与设备通信。Client Driver在枚举完成后被加载,但它的存在与否不影响枚举成功与否。如果系统缺少对应的Client Driver,设备枚举成功但无法正常工作(设备管理器显示“未知设备”)。
2.2 设备侧架构
USB设备侧由固件(Firmware)和设备控制器(Device Controller)组成。设备侧必须严格按照USB规范响应主机请求。
| 层次 | 组件 | 职责 | 枚举相关关键点 |
|---|---|---|---|
| L3 | Application / Class Logic | 实现设备功能(如数据采集、存储、人机交互) | 不直接参与枚举,但需提供描述符数据 |
| L2 | USB Firmware Stack | 处理标准请求、管理端点、维护设备状态 | 解析Setup包、填充描述符缓冲区、处理Set_Address/Set_Configuration |
| L1 | Device Controller (DC) | 物理层收发、串行接口引擎(SIE)、端点硬件 | 检测复位信号、响应SOF、处理令牌包 |
2.2.1 Device Controller (DC) 层
Device Controller是设备端的硬件模块,通常集成在MCU内部(如STM32的USB IP、NXP的USB-OTG)。它负责:
- 检测USB总线状态(复位、挂起、恢复)。
- 解析主机发送的令牌包(SETUP、IN、OUT)。
- 管理端点0的控制传输。
- 处理CRC校验和位填充。
DC的寄存器配置直接影响枚举。例如,端点0的最大包长度必须与描述符中的 bMaxPacketSize0 一致,否则主机在读取描述符时会因数据长度不匹配而失败。
2.2.2 Firmware 层
USB固件栈是设备侧的灵魂。它必须实现以下核心功能:
- Setup包解析:识别bmRequestType、bRequest、wValue、wIndex、wLength。
- 标准请求处理:Get_Descriptor、Set_Address、Set_Configuration、Get_Configuration、Get_Status、Clear_Feature等。
- 描述符管理:设备描述符、配置描述符、接口描述符、端点描述符、字符串描述符的存储与返回。
- 状态机维护:Default → Address → Configured 的状态转换。
2.3 USB分层模型
USB协议栈采用分层设计,从物理层到功能层逐层封装。理解分层模型有助于定位枚举失败的具体层次。
2.4 分层模型与枚举失败的关系
枚举失败可以按分层模型快速定位:
| 失败现象 | 可能原因 | 涉及层次 | 排查方向 |
|---|---|---|---|
| 主机未检测到设备连接 | D+/D-电平异常、上拉电阻缺失 | 物理层(HC/DC) | 检查硬件原理图、上拉电阻值(1.5kΩ) |
| 复位后无响应 | 复位时序不足、DC未正确初始化 | 物理层(DC) | 检查固件复位处理、DC寄存器配置 |
| Get_Descriptor超时 | 端点0最大包长度不匹配、描述符地址错误 | 协议层(Firmware) | 核对bMaxPacketSize0、描述符存储位置 |
| Set_Address后无响应 | 固件未切换地址、地址设置延迟 | 协议层(Firmware) | 检查Set_Address处理逻辑、地址切换时机 |
| 配置描述符解析失败 | 描述符长度错误、接口/端点描述符缺失 | 协议层(Firmware) | 使用USB分析仪对比描述符原始数据 |
| 设备枚举成功但无法工作 | 缺少Client Driver、类描述符错误 | 功能层(Client/Class) | 检查系统驱动安装、bDeviceClass/bInterfaceClass |
2.5 主机与设备交互的典型枚举时序
以下是一个完整的枚举时序示例,展示主机与设备各层的交互过程:
1. 设备插入,HC检测到D+/D-电平变化
2. HC发送复位信号(至少10ms)
3. 设备检测到复位,进入Default状态
4. HC开始发送SOF包
5. USBD发送 Get_Descriptor(Device) 请求(地址0,端点0)
6. 设备固件解析Setup包,返回设备描述符前8字节
7. USBD解析bMaxPacketSize0,确定后续传输最大包长度
8. USBD发送 Set_Address(addr=1) 请求
9. 设备固件切换地址,返回0长度状态包
10. USBD使用新地址发送 Get_Descriptor(Device) 请求
11. 设备返回完整设备描述符(18字节)
12. USBD发送 Get_Descriptor(Configuration) 请求
13. 设备返回配置描述符及所有接口/端点描述符
14. USBD根据描述符选择配置,发送 Set_Configuration(1)
15. 设备进入Configured状态,枚举完成
16. USBD加载对应的Client Driver