4、USB协议分层:物理层、链路层、事务层、应用层,各层职责与数据流

说实话,很多初学者一上来就被USB的协议栈吓住了。我当年刚接触USB时,看着那几层协议也是一脸懵。但后来在实际项目中摸爬滚打,才真正体会到——分层设计,是USB能活到今天的关键原因

你想想看,USB从1.0走到现在的USB4,速度翻了上万倍,但上层软件基本不用改。为什么?就是因为每一层各司其职,下层变了,上层不受影响。今天我就带你一层层拆开看看。

4.1 物理层(Physical Layer)—— 最底层的"搬运工"

物理层,说白了就是负责把0和1变成电信号,再从电信号变回0和1。我习惯把它比作"快递小哥"——他不关心包裹里是什么,只负责安全送到。

物理层的主要职责:

  • 信号编码:USB 2.0用NRZI编码,USB 3.0用8b/10b编码。嗯,这里要注意,NRZI编码有个特点——连续6个1就会强制插入0,防止时钟失锁。我在调试一个U盘项目时,就遇到过因为编码理解错误导致眼图不过的情况。
  • 电气特性:差分信号D+/D-,电压摆幅、驱动能力、终端匹配。USB 2.0高速模式下,信号摆幅只有400mV。
  • 连接检测:通过D+/D-上的上拉/下拉电阻,检测设备插入、拔出、速度识别。
  • 时钟恢复:从数据流中提取时钟,同步收发。

避坑指南:我曾经在一个项目中,因为PCB走线没做差分阻抗匹配(要求90Ω±15%),导致高速模式完全无法建立。后来重新layout才解决。所以,物理层的问题往往是最难查的——它不像软件能打log,只能靠示波器一点点看。

4.2 链路层(Link Layer)—— 数据的"打包工"

链路层在物理层之上,负责把原始比特流组织成有意义的"包"。我个人觉得,这一层是USB协议中最"脏活累活"的一层。

链路层的主要职责:

  • 包封装:将数据加上SOP(包起始)、EOP(包结束)、PID(包标识符)等字段。USB的包类型有:令牌包、数据包、握手包、特殊包。
  • CRC校验:令牌包用5位CRC,数据包用16位CRC。我建议你在做IP时,CRC生成器一定要单独验证——我吃过这个亏,CRC算错了,主机就是不认。
  • 位填充:USB 2.0要求在连续6个1后插入一个0,防止接收端失锁。接收端要负责去掉这些填充位。
  • 错误检测与重试:发现CRC错误,就要求对方重传。

个人经验:链路层的状态机设计,我建议用三段式写法(当前状态、下一状态、输出逻辑)。这样调试时一目了然。我曾经用一段式写过一个USB 2.0的链路层,后来改bug改到怀疑人生。

4.3 事务层(Transaction Layer)—— 传输的"调度员"

事务层,是USB协议中最核心的一层。它管理着"谁在什么时候可以发数据"。你想想看,USB是主从架构,所有通信都由主机发起。事务层就是负责把这些"会话"组织好。

事务层的主要职责:

  • 事务类型管理:USB定义了三种事务类型——批量传输、中断传输、等时传输。控制传输比较特殊,它由多个事务组成。
  • 令牌生成与解析:主机发出令牌包(IN/OUT/SETUP),设备根据令牌包决定是收还是发。
  • 数据流控制:通过NAK、ACK、STALL等握手包,控制数据流动。比如设备来不及处理,就回NAK让主机重试。
  • 超时处理:如果收不到响应,事务层要负责超时重试或报错。
事务类型 典型应用 特点
控制传输 枚举、配置 可靠,分阶段(建立-数据-状态)
批量传输 U盘、打印机 可靠,带宽不保证
中断传输 鼠标、键盘 可靠,保证轮询间隔
等时传输 音频、视频 不可靠,保证带宽

注意:事务层的状态机设计,一定要考虑所有可能的异常情况。我曾经在一个项目中,设备端的事务层状态机没有处理"收到重复令牌"的情况,结果在压力测试下直接挂死。后来加了一个"令牌序列号检查"才解决。

4.4 应用层(Application Layer)—— 用户看到的"功能"

应用层,就是USB设备对外展现的功能。比如U盘的文件系统、摄像头的视频流、鼠标的坐标数据。这一层,说白了就是"用户想干什么"。

应用层的主要职责:

  • 类协议实现:USB定义了各种设备类——HID类(人机交互)、Mass Storage类(大容量存储)、Audio类(音频)等。每个类都有标准化的描述符和命令集。
  • 端点配置:每个设备可以有多个端点(Endpoint),每个端点对应一个数据通道。端点0是默认控制端点,其他端点由设备描述符定义。
  • 数据解析与封装:将应用数据打包成USB事务层能识别的格式,或者将收到的数据解析成应用能用的格式。
  • 电源管理:支持挂起、唤醒等电源状态。

我的建议:做应用层时,一定要先搞清楚你的设备属于哪个类。不要自己发明协议——USB的类协议已经非常成熟了。我见过一个团队,非要自己定义一套通信协议,结果兼容性一塌糊涂。后来老老实实改用HID类,问题全解决了。

4.5 数据流:从应用到物理的"旅程"

好了,四层都讲完了。我们来看看数据是怎么从应用层一路走到物理层的。我习惯用"发快递"来比喻:

  1. 应用层:你要寄一个包裹(比如一个文件)。你写好地址、贴好标签。
  2. 事务层:快递公司把包裹分成若干个小包,每个小包编上号(事务ID),决定是发往哪个城市(端点)。
  3. 链路层:每个小包再套上信封(包封装),写上寄件人和收件人(PID、地址),贴上防拆封条(CRC)。
  4. 物理层:快递员骑着电动车,把信封送到目的地(电信号传输)。

接收端则反过来:物理层收到电信号,链路层拆信封检查封条,事务层把小包拼回大包,应用层取出文件。

实际项目中的经验:调试USB协议栈时,我建议你从链路层开始验证。先确保包能正确收发,再往上走。我曾经犯过一个错误——应用层调了半天发现数据不对,最后查出来是链路层的CRC生成器有个bit顺序反了。嗯,从那以后,我每层都单独写测试用例。

4.6 小结

USB的四层协议,每一层都有明确的职责边界。物理层管信号,链路层管包,事务层管会话,应用层管功能。理解了这个分层思想,你就能看懂USB协议栈的代码了。

下一章,我们会深入链路层,手把手教你实现一个USB 2.0的包收发器。到时候,我会把我踩过的坑都告诉你。