3. USB 设备状态机:状态定义与转换条件
USB 协议定义了一套严谨的设备状态机,用于管理设备从物理连接到正常工作的完整生命周期。理解这六个状态及其转换条件,是分析枚举日志、定位故障的根本前提。任何枚举失败,最终都可以归结为设备未能正确进入或停留在某个预期状态。
3.1 六种核心状态定义
USB 2.0 规范明确定义了以下六种设备状态,设备在任何时刻只能处于其中一种状态:
| 状态名称 | 状态标识 | 核心特征 | 电源要求 |
|---|---|---|---|
| Attached | 连接态 | 设备物理连接到主机或 Hub 端口,VBUS 供电生效 | VBUS (4.75V-5.25V) |
| Powered | 供电态 | 设备检测到 VBUS,内部上电复位完成,可响应总线事件 | VBUS + 内部稳压 |
| Default | 默认态 | 设备接收到复位信号(SE0),默认地址为 0,可接收控制传输 | VBUS + 100mA 最大 |
| Address | 地址态 | 主机分配唯一非零地址,设备以新地址通信 | VBUS + 100mA 最大 |
| Configured | 配置态 | 主机发送 Set Configuration 请求,设备加载描述符并进入功能模式 | VBUS + 配置所需电流 |
| Suspended | 挂起态 | 总线空闲超过 3ms,设备进入低功耗模式 | VBUS + 2.5mA 最大 |
故障排查要点: 在枚举日志中,如果看到设备反复在 Default 和 Address 之间跳转,通常意味着地址分配失败或设备未能正确响应 Set Address 请求。这是最常见的枚举故障模式之一。
3.2 状态转换条件与流程图
状态转换由主机和设备的交互事件驱动。下图展示了完整的六状态转换关系,每个箭头代表一个明确的触发条件:
3.3 状态转换条件详解
每个状态转换都有明确的触发条件,这些条件在枚举日志中表现为特定的事件序列:
3.3.1 Attached → Powered
- 触发条件:设备检测到 VBUS 电压稳定在 4.75V 以上,且内部上电复位(POR)完成。
- 日志特征:Hub 检测到端口状态变化(Port Change),主机读取端口状态寄存器。
- 故障点:VBUS 电压不足、设备内部电源管理 IC 故障、去耦电容短路。
3.3.2 Powered → Default
- 触发条件:主机通过 Hub 向端口发送复位信号(SE0,即 D+ 和 D- 同时拉低),持续时间至少 10ms。
- 日志特征:主机发送
SetPortFeature(PORT_RESET),随后检测到复位完成位。 - 故障点:复位信号被 Hub 或线缆衰减、设备 D+/D- 线路短路、设备未正确识别复位。
关键指标:复位信号持续时间必须在 10ms 到 20ms 之间。过短(<10ms)设备可能不响应,过长(>20ms)可能导致设备误判为挂起。
3.3.3 Default → Address
- 触发条件:主机通过默认地址 0 发送
Set Address请求,设备在收到后返回 ACK,并立即切换到新地址。 - 日志特征:主机发送
SETUP(Set Address, addr=0x02),设备回复 ACK。 - 故障点:设备未正确解析 Set Address 请求、地址冲突、设备固件未实现地址切换。
3.3.4 Default/Address → Configured
- 触发条件:主机发送
Set Configuration请求,携带配置描述符中的 bConfigurationValue。 - 日志特征:主机发送
SETUP(Set Configuration, value=0x01),设备回复 ACK 并加载功能驱动。 - 故障点:设备不支持所选配置、配置描述符解析错误、设备内部功能模块初始化失败。
3.3.5 任意状态 → Suspended
- 触发条件:总线空闲(无 SOF 帧或数据活动)持续超过 3ms。
- 日志特征:主机停止发送 SOF,设备进入低功耗模式。
- 故障点:主机进入系统挂起、Hub 端口电源被切断、设备未正确进入低功耗导致电流超标。
3.3.6 Suspended → Powered
- 触发条件:设备检测到 Resume 信号(K 状态,即 D+ 高、D- 低)持续 20ms,或主机主动恢复总线活动。
- 日志特征:主机发送
ClearPortFeature(PORT_SUSPEND),设备退出低功耗模式。 - 故障点:设备无法被唤醒、唤醒信号被 Hub 过滤、设备固件唤醒逻辑异常。
3.4 状态转换与枚举流程的对应关系
将状态机映射到实际的枚举流程中,可以更直观地理解每个阶段:
| 枚举阶段 | 设备状态 | 主机操作 | 关键请求 |
|---|---|---|---|
| 物理连接 | Attached → Powered | 检测端口状态变化 | GetPortStatus |
| 复位 | Powered → Default | 发送复位信号 | SetPortFeature(PORT_RESET) |
| 获取设备描述符 | Default | 读取前 8 字节 | GetDescriptor(DEVICE, 0) |
| 分配地址 | Default → Address | 分配非零地址 | Set Address |
| 获取完整描述符 | Address | 读取全部描述符 | GetDescriptor(DEVICE/CONFIG) |
| 配置设备 | Address → Configured | 选择配置 | Set Configuration |
常见陷阱: 有些设备在 Default 状态下会错误地响应非零地址的请求,这通常是因为固件未正确实现地址过滤。在枚举日志中,如果看到设备在 Set Address 之前就回复了非默认地址的请求,说明设备状态机实现有缺陷。
3.5 状态机故障排查速查表
当枚举失败时,根据设备卡住的状态可以快速定位问题范围:
| 卡住状态 | 典型日志特征 | 可能原因 | 排查方向 |
|---|---|---|---|
| Powered | 端口状态显示连接,但无复位事件 | Hub 未检测到设备、复位信号未发出 | 检查 Hub 驱动、端口电源、线缆完整性 |
| Default | 复位完成,但 GetDescriptor 超时 | 设备未响应控制传输、地址 0 冲突 | 检查设备端点 0 实现、USB 时钟、D+/D- 上拉电阻 |
| Address | Set Address 成功,但后续请求无响应 | 设备未切换到新地址、地址被占用 | 验证设备地址切换逻辑、检查地址池 |
| Configured | Set Configuration 成功,但功能异常 | 配置值不匹配、内部初始化失败 | 检查配置描述符、设备功能模块 |
| Suspended | 总线空闲后设备无法恢复 | 唤醒信号未识别、低功耗模式锁死 | 检查 Resume 信号、设备唤醒逻辑 |