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-电平变化)、复位信号、低速/全速/高速识别
枚举失败排查要点: 主机侧枚举失败通常发生在L1(物理连接/复位失败)或L2(描述符解析超时/校验错误)。L3绑定失败不会导致枚举中断,但会导致设备无法被系统识别。

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的关键枚举步骤包括:

  1. 检测到设备连接后,分配一个临时地址(通常为0)。
  2. 发送 Get_Descriptor(Device) 请求,获取设备描述符前8字节(用于确定最大包长度)。
  3. 发送 Set_Address 请求,分配唯一地址。
  4. 再次发送 Get_Descriptor(Device) 获取完整设备描述符。
  5. 发送 Get_Descriptor(Configuration) 获取配置描述符及所有接口/端点描述符。
  6. 根据描述符信息选择配置(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固件栈是设备侧的灵魂。它必须实现以下核心功能:

  1. Setup包解析:识别bmRequestType、bRequest、wValue、wIndex、wLength。
  2. 标准请求处理:Get_Descriptor、Set_Address、Set_Configuration、Get_Configuration、Get_Status、Clear_Feature等。
  3. 描述符管理:设备描述符、配置描述符、接口描述符、端点描述符、字符串描述符的存储与返回。
  4. 状态机维护:Default → Address → Configured 的状态转换。
固件调试建议: 在枚举阶段,建议在固件中增加调试输出(如串口打印),记录每次收到的Setup请求和返回的数据长度。对比USB分析仪抓包,可以快速定位描述符返回错误或状态机异常。

2.3 USB分层模型

USB协议栈采用分层设计,从物理层到功能层逐层封装。理解分层模型有助于定位枚举失败的具体层次。

USB分层模型与枚举数据流 Client Driver (功能层) 解析类描述符,实现设备功能 USB Driver (USBD) (协议层) 枚举调度、标准请求、描述符解析 Host Controller (HC) (物理层) 复位、SOF、事务处理、电平检测 Application / Class Logic (功能层) 实现具体设备功能 USB Firmware Stack (协议层) Setup解析、描述符返回、状态机 Device Controller (DC) (物理层) SIE、端点硬件、复位检测 枚举请求 (Setup包、Get_Descriptor、Set_Address) 枚举响应 (描述符数据、状态包) 枚举失败分层定位原则 物理层失败:复位/电平异常 → 检查硬件连接、DC寄存器 协议层失败:描述符超时/校验错误 → 检查固件状态机、描述符长度

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
关键时序点: 步骤5-7中,设备描述符前8字节的bMaxPacketSize0必须与DC硬件配置一致。如果DC配置的端点0最大包长度为64,但描述符中填写8,主机将使用8字节包长通信,导致后续数据截断。