USB枚举移植适配:从Host驱动到设备识别 - 第1节
1.1 USB总线发展史:从1.0到4.0的演进
通用串行总线(Universal Serial Bus,USB)自1996年正式发布以来,已成为电子设备最通用的有线连接标准。其演进历程不仅反映了数据传输速率的飞跃,更体现了电源管理、协议架构和物理层设计的持续革新。
| 版本 | 发布年份 | 理论最大速率 | 编码方式 | 核心特性 |
|---|---|---|---|---|
| USB 1.0 / 1.1 | 1996 / 1998 | 1.5 Mbps (Low-Speed) 12 Mbps (Full-Speed) |
NRZI + 位填充 | 热插拔、即插即用、总线供电 |
| USB 2.0 | 2000 | 480 Mbps (Hi-Speed) | NRZI + 位填充 | 微帧调度、事务分割(Split Transaction) |
| USB 3.2 Gen 1×1 | 2008 (USB 3.0) | 5 Gbps (SuperSpeed) | 8b/10b | 双总线架构(USB 2.0 + SuperSpeed)、全双工 |
| USB 3.2 Gen 2×2 | 2017 (USB 3.2) | 20 Gbps | 128b/132b | 双通道传输、Type-C接口强制 |
| USB4 Gen 3×2 | 2019 | 40 Gbps | 128b/132b | 基于Thunderbolt 3协议、隧道化传输 |
关键演进节点:USB 2.0引入的微帧(Microframe)机制将帧周期从1ms缩短为125μs,为等时传输提供了更精细的调度粒度;USB 3.0的双总线架构是向后兼容性的经典设计——保留完整的USB 2.0差分对(D+/D-),同时新增SuperSpeed差分对(SSTX/SSRX)。
嵌入式移植视角:在Host驱动适配时,USB 2.0的EHCI(Enhanced Host Controller Interface)与USB 3.0的xHCI(eXtensible Host Controller Interface)寄存器模型完全不同。xHCI统一管理所有速度等级的设备,而EHCI仅处理Hi-Speed事务,Full/Low-Speed需依赖UHCI/OHCI配合。
1.2 物理层与电气特性
1.2.1 差分信号与线缆结构
USB物理层采用差分信号传输以抑制共模噪声。USB 2.0及以下版本使用一对差分线(D+/D-)进行半双工通信;USB 3.x/4.0在此基础上增加独立的SuperSpeed发送(SSTX+/-)和接收(SSRX+/-)差分对,实现全双工传输。
| 信号组 | 线缆数量 | 方向 | 适用版本 | 电气特性 |
|---|---|---|---|---|
| VBUS | 1 | Host → Device | 所有版本 | 4.75~5.25 V,最大电流随版本提升(0.5A→0.9A→3A/5A) |
| GND | 1 | 公共地 | 所有版本 | 参考地平面 |
| D+/D- | 2 | 半双工 | USB 1.0/2.0 | 差分阻抗 90Ω ±15%,信号摆幅 0~3.3V |
| SSTX+/- | 2 | Host → Device | USB 3.x/4 | 差分阻抗 90Ω ±15%,摆幅 0.8~1.2V |
| SSRX+/- | 2 | Device → Host | USB 3.x/4 | 差分阻抗 90Ω ±15%,摆幅 0.8~1.2V |
电气适配陷阱:在嵌入式Host移植中,USB 2.0的D+/D-上拉/下拉电阻配置是设备识别的关键。Full-Speed设备在D+线上接1.5kΩ上拉至3.3V,Low-Speed设备则在D-线上接上拉。Host端D+/D-各接15kΩ下拉电阻,检测上拉信号以识别设备插入和速度等级。
1.2.2 速度识别与终端匹配
设备插入后,Host通过检测D+/D-的电压状态判断设备速度等级。USB 2.0规范定义了三种速度的电气识别方式:
- Low-Speed (1.5 Mbps):D-线上拉,D+线保持低电平
- Full-Speed (12 Mbps):D+线上拉,D-线保持低电平
- Hi-Speed (480 Mbps):先以Full-Speed上拉D+,随后在Host发起的复位信号中通过Chirp J/K序列协商切换到Hi-Speed
1.3 USB拓扑结构:Host、Hub与Device
1.3.1 分层星形拓扑
USB采用分层星形(Tiered Star)拓扑结构,由Host Controller作为根节点,通过Hub扩展下游端口,最多支持7层(Tier),包括根Hub在内。每个物理设备(Function)占用一个唯一地址(0~127),地址0保留给未配置的设备。
| 拓扑角色 | 功能描述 | 关键约束 |
|---|---|---|
| Host Controller | 总线主控,发起所有事务,管理带宽分配和电源 | 每个Host最多127个设备地址 |
| Root Hub | 集成在Host Controller内部,提供物理端口 | 通常2~4个端口,支持过流保护 |
| Hub | 端口扩展器,转发事务、管理电源、检测设备插入/移除 | 最多5层级联,每端口支持不同速度 |
| Function (Device) | 实现具体功能的外设,如HID、Mass Storage、CDC | 复合设备可包含多个Function |
枚举路径约束:在枚举过程中,Host通过Hub的端口状态变化(Port Change)检测设备插入。每个Hub端口有独立的状态寄存器,Host轮询Hub中断端点获取状态变化。设备地址分配必须在Set Address阶段完成,且地址0在复位后必须立即释放。
1.3.2 复合设备与多功能设备
USB设备可分为两种复合形态:
- 复合设备(Composite Device):单个物理设备包含多个Function(接口),每个Function由不同的驱动程序管理。例如USB摄像头同时包含Video和Audio接口。
- 复合设备(Compound Device):物理上包含一个内置Hub和一个或多个Function,例如带Hub功能的键盘。
1.3.3 Host驱动与设备枚举的桥梁
在嵌入式系统中,Host驱动(xHCI/EHCI/OHCI)负责管理拓扑中的事务调度和状态机。设备枚举是Host驱动与设备逻辑之间的第一次握手,其核心步骤包括:
- 设备插入检测:Hub端口状态寄存器检测到信号变化(D+/D-上拉)
- 复位与速度协商:Host发送复位信号(SE0),设备在复位期间完成速度识别
- 获取设备描述符:Host通过控制传输(Setup包)读取设备描述符的前8字节(获取最大包长度)
- 分配地址:Host发送Set Address请求,设备切换到新地址
- 完整枚举:读取配置描述符、接口描述符、端点描述符,加载对应驱动程序
移植适配要点:在裸机或RTOS环境下,Host驱动需要实现完整的控制传输状态机(Setup → Data → Status)。xHCI的TRB(Transfer Request Block)环管理机制与EHCI的QH(Queue Head)/TD(Transfer Descriptor)链表差异显著,移植时需重点关注链表维护和中断处理。
/* USB 设备枚举核心流程伪代码(Host 驱动侧) */
void usb_enumerate_device(uint8_t hub_port, uint8_t hub_addr) {
// 1. 检测端口状态变化
port_status_t status = hub_read_port_status(hub_addr, hub_port);
if (status.connection_change) {
// 2. 复位端口
hub_set_port_feature(hub_addr, hub_port, PORT_RESET);
delay_ms(10); // 复位持续时间至少 10ms
hub_clear_port_feature(hub_addr, hub_port, PORT_RESET);
// 3. 读取端口速度
uint8_t speed = hub_get_port_speed(hub_addr, hub_port);
// 4. 分配设备地址(地址 0 为默认)
uint8_t device_addr = allocate_device_address();
// 5. 获取设备描述符前 8 字节(获取 bMaxPacketSize0)
device_descriptor_t desc;
usb_control_transfer(0, 0x80, GET_DESCRIPTOR,
DEVICE_DESCRIPTOR, 0, 8, &desc);
// 6. 设置设备地址
usb_control_transfer(0, 0x00, SET_ADDRESS,
device_addr, 0, 0, NULL);
delay_ms(2); // 地址切换稳定时间
// 7. 完整读取设备描述符
usb_control_transfer(device_addr, 0x80, GET_DESCRIPTOR,
DEVICE_DESCRIPTOR, 0, 18, &desc);
// 8. 读取配置描述符并解析接口/端点
configuration_descriptor_t cfg_desc;
usb_control_transfer(device_addr, 0x80, GET_DESCRIPTOR,
CONFIGURATION_DESCRIPTOR, 0, 9, &cfg_desc);
// 9. 选择配置
usb_control_transfer(device_addr, 0x00, SET_CONFIGURATION,
cfg_desc.bConfigurationValue, 0, 0, NULL);
// 10. 加载驱动
usb_driver_bind(device_addr, &desc);
}
}
常见枚举失败场景:
- 复位时序不足:某些Hub芯片需要更长的复位保持时间(10~20ms),过短会导致设备无法进入配置状态
- 地址冲突:未正确释放地址0或重复分配地址,导致设备无响应
- 最大包长度误判:设备描述符前8字节中的bMaxPacketSize0字段决定了控制传输的数据阶段大小,读取错误将导致后续传输失败
- Hub级联深度超限:超过5层Hub级联时,Host将忽略该设备