1. USB协议基础:USB总线架构、USB物理层与电气特性、USB数据包格式

大家好,我是你们的老朋友。今天咱们来聊聊USB协议的基础。说实话,很多做驱动的兄弟一上来就扎进代码里,结果遇到热插拔问题就抓瞎。我个人习惯是,先把底层的这些概念吃透,后面写代码才心里有底。

1.1 USB总线架构:从拓扑到角色

USB的总线架构,说白了就是一颗星形拓扑。你想想看,一个主机(Host)下面挂着一堆设备(Device),中间通过集线器(Hub)来扩展。这个结构其实挺像咱们公司的组织架构——老板(Host)发号施令,员工(Device)只能乖乖听话。

这里有个关键点:USB是主从架构。所有通信都由Host发起,Device永远不能主动说话。我在项目中遇到过新手写驱动,试图让设备主动上报数据,结果发现根本发不出去——嗯,这就是没搞懂架构。

核心角色:

  • Host(主机):唯一的主控者,负责调度所有传输
  • Device(设备):被动响应,不能主动发起事务
  • Hub(集线器):负责扩展端口,检测设备插拔

我建议你记住一个数字:USB 2.0最多支持127个设备(包括Hub在内)。为什么是127?因为地址字段只有7位,0被保留给未配置的设备。这个细节在枚举过程中特别重要。

USB Host Root Hub Hub 1 Hub 2 Device 1 Device 2 Device 3 Host Root Hub Hub Device USB 总线星形拓扑结构

1.2 USB物理层与电气特性

物理层这块,说白了就是两根差分信号线(D+和D-)加上Vbus和GND。USB 2.0用半双工差分传输,速度最高480Mbps。我刚开始做驱动时,总觉得电气特性是硬件工程师的事,直到有一次设备在长线缆上频繁掉线——查了半天,原来是D+和D-的阻抗没匹配好。

避坑指南:我曾经在调试一个USB摄像头时,发现插上后主机能识别,但一传数据就断连。最后用示波器一看,D+信号线上的上升沿太缓了——线缆超过了5米。记住:USB 2.0的线缆建议不超过5米,否则信号完整性会出问题。

电气特性上,有几个关键参数你得记住:

参数 USB 1.0/1.1 USB 2.0 说明
速度 1.5 Mbps (Low) / 12 Mbps (Full) 480 Mbps (High) High Speed需要额外握手
信号电平 0~3.3V 0~3.3V (HS: 400mV差分) HS模式下摆幅更小
终端电阻 D+或D-上拉1.5kΩ D+和D-各接45Ω到地 用于速度识别
线缆最大长度 3m (Low) / 5m (Full) 5m 超过需加Hub中继

为什么会这样?因为USB用D+和D-上的上拉电阻来识别设备速度。Full Speed设备在D+上拉,Low Speed在D-上拉。High Speed设备先以Full Speed握手,再切换到HS模式。我记得第一次看这个协议时,觉得这个设计挺巧妙的——不用额外引脚就能区分速度。

1.3 USB数据包格式:从字节到事务

USB的数据传输,说白了就是包(Packet)的交换。每个包由同步字段(SYNC)、包标识符(PID)、数据字段和CRC组成。我建议你把这个结构刻在脑子里,因为后面写驱动时,所有错误排查都要回到包级别。

USB包的基本结构:

+--------+--------+--------+--------+--------+
| SYNC   |  PID   |  DATA  |  CRC   |  EOP   |
| (8位)  | (8位)  | (0-1024字节)| (5/16位)|       |
+--------+--------+--------+--------+--------+

PID(Packet Identifier)是包的类型标识,一共4位,但为了可靠性,会取反后重复4位。嗯,这里要注意:PID决定了包的类型和传输方向。常见的PID有:

  • 令牌包(Token):OUT (0xE1)、IN (0x69)、SETUP (0x2D) —— 主机发起的传输方向
  • 数据包(Data):DATA0 (0xC3)、DATA1 (0x4B) —— 数据交替翻转,用于错误检测
  • 握手包(Handshake):ACK (0xD2)、NAK (0x5A)、STALL (0x1E) —— 设备响应状态
  • 特殊包(Special):PRE (0x3C)、ERR (0x3C) —— 用于低速设备通信

我个人习惯把USB传输分成三个层次:

  1. 包(Packet):最底层的数据单元,上面已经讲了
  2. 事务(Transaction):由令牌包+数据包+握手包组成,完成一次数据交换
  3. 传输(Transfer):由多个事务组成,实现一次完整的读写操作

举个例子,你往U盘里写一个文件,底层其实是无数个OUT事务在跑。每个OUT事务包含:主机发OUT令牌包 → 主机发DATA0/DATA1数据包 → 设备回ACK/NAK握手包。如果设备回了NAK,说明它忙,主机得重试。

注意:我曾经在调试一个USB打印机驱动时,发现打印到一半就卡住了。抓包一看,设备连续回了3个NAK,然后主机就放弃了。原因是打印机缓存满了,但驱动没做重试逻辑。记住:NAK不是错误,是设备在说“我还没准备好”,驱动应该重试而不是放弃。

数据包里的DATA0和DATA1交替机制,是USB保证数据可靠性的关键。每次成功的事务后,主机和设备都会翻转数据包PID。如果收到重复的PID,说明上一包没被确认,需要丢弃。这个机制在USB 2.0的High Speed模式下尤其重要,因为480Mbps的速率下,噪声干扰的概率更高。

好了,USB协议基础就聊到这儿。这些概念虽然基础,但后面讲热插拔、枚举流程、驱动开发时,全都要用到。你想想看,如果连包格式都搞不清,怎么排查设备枚举失败的问题?

我的建议:刚开始学USB驱动时,别急着写代码。先拿一个USB分析仪(比如Beagle USB 480)抓抓包,看看真实的USB通信长什么样。我当年就是这么干的——看了一周的抓包数据,后面写驱动基本没出过大错。


公众号:蓝海资料掘金营,微信deep3321