2. USB系统架构:主机、设备与集线器的角色
USB系统说白了就是一个主从结构的通信网络。主机说了算,设备听指令,集线器负责把总线扩展出去。这个架构从USB 1.0一直沿用到现在,基本没变过。
我刚开始做USB驱动时,总觉得这个架构太简单了——不就一个主控带一堆设备嘛。后来踩了几个坑才明白,这套看似简单的架构里藏着不少设计智慧。
2.1 主机(Host)——总线的掌控者
主机是整个USB系统的核心。它负责管理总线上的所有事务,包括设备枚举、数据传输、电源管理等。在PC上,主机就是USB主控制器(Host Controller)加上根集线器(Root Hub)。
主机的关键职责有这些:
- 检测设备插拔——通过集线器端口状态变化来发现设备
- 分配地址——每个设备在枚举时获得唯一地址(1-127)
- 调度传输——按帧/微帧为单位,安排所有传输事务
- 管理电源——控制端口供电,处理挂起/唤醒
关键概念:USB总线上的所有传输都由主机发起。设备从不主动发送数据,只能被动响应主机的请求。这一点和I2C、SPI很像,但USB的调度机制要复杂得多。
我记得第一次调试一个USB摄像头时,发现图像数据总是丢帧。查了半天,原来是主机的帧调度出了问题——等时传输的带宽预留不够。嗯,从那以后我养成了习惯:做USB项目先算带宽预算。
2.2 设备(Device)——功能的提供者
设备就是挂在USB总线上的各种外设。键盘、鼠标、U盘、摄像头、打印机……它们都遵循同样的设备框架。
每个USB设备都包含以下逻辑结构:
| 层级 | 说明 | 我见过的坑 |
|---|---|---|
| 设备层 | 描述设备整体信息(VID/PID、版本等) | 有些山寨设备VID/PID乱填,导致驱动加载失败 |
| 配置层 | 定义设备的工作模式(如高功耗/低功耗) | 配置描述符里的bMaxPower写错,主机直接拒绝 |
| 接口层 | 代表一个功能单元(如音频、HID) | 复合设备接口顺序搞反,驱动认不出 |
| 端点层 | 数据传输的终点,每个端点有方向、类型 | 端点0以外的端点描述符漏了,枚举到一半卡死 |
设备通过描述符向主机报告自己的身份和能力。主机在枚举阶段逐层读取这些描述符,然后加载对应的驱动程序。
个人经验:调试设备枚举问题时,我习惯用逻辑分析仪抓取主机发起的GET_DESCRIPTOR请求。看看主机读到了什么、设备返回了什么,问题往往一目了然。比单纯看驱动日志高效得多。
2.3 集线器(Hub)——总线的扩展器
集线器负责把USB总线扩展出更多端口。每个USB系统中至少有一个根集线器(集成在主控制器里),外部还可以级联集线器。
集线器的工作说白了就是两件事:
- 信号中继——把上游的信号整形、放大后转发到下游端口
- 端口管理——检测设备插拔、控制电源、处理复位和挂起
集线器本身也是一个USB设备,有自己的地址和端点。主机通过集线器类请求来管理它,比如读取端口状态、设置端口特性等。
避坑指南:我曾经在一个项目中用了劣质集线器,导致高速设备(USB 2.0)只能跑全速(12 Mbps)。查了三天才发现是集线器的信号质量太差,眼图都闭合了。从那以后,我选集线器芯片只认TI、Microchip、Cypress这几家。
2.4 通信模型——谁跟谁说话?
USB的通信模型是主机-设备的点对点关系。集线器只是透明转发,不参与数据内容的理解。
一次典型的USB传输过程是这样的:
- 主机软件(驱动)准备好数据,提交给主控制器
- 主控制器按调度把数据打包成事务(Transaction)
- 事务通过集线器转发到目标设备
- 设备解析事务,执行操作,返回响应
- 响应原路返回给主机软件
你想想看,这个模型和网络通信很像——主机是服务器,设备是客户端。但USB的实时性要求更高,所以引入了帧的概念来保证确定性。
2.5 系统架构总览
下面这张图展示了USB系统的完整架构。我建议你把它记在脑子里,后面所有调试和分析都离不开这个框架。
2.6 三种角色的协作关系
主机、设备、集线器三者配合,构成了完整的USB系统。它们的协作可以总结为:
- 主机制定规则、发起通信、管理资源
- 设备遵守规则、响应请求、提供功能
- 集线器扩展总线、透明转发、管理端口
这个架构的好处是:设备不需要知道总线上还有谁,集线器不需要理解数据内容,主机掌控一切。说白了就是集中管理、分布执行。
调试要点:遇到USB通信问题时,先判断是哪个角色出了问题。主机问题看驱动日志和主控制器状态;设备问题抓描述符和端点配置;集线器问题查端口状态和信号质量。分而治之,效率最高。
我记得有一次调试一个USB集线器,下游设备总是间歇性断开。用USB分析仪抓包发现,集线器在设备枚举完成后没有正确转发SOF包。换了一颗集线器芯片就好了——嗯,有时候硬件问题就是这么直接。
好了,USB系统架构就讲到这里。理解了这个框架,后面讲枚举、传输、协议栈的时候,你就能知道每个环节发生在哪个角色身上。这是整个USB调试的基础,值得花时间吃透。