USB协议栈核心:数据包结构、传输类型与帧机制

大家好,我是你们的讲师。今天咱们聊聊USB协议栈最核心的部分——数据包结构、四种传输类型,还有帧与微帧机制。说实话,这部分内容我当年啃协议规范时也花了不少功夫。但只要你搞懂了这些,后面写驱动、移植协议栈就会顺手很多。

一、USB数据包结构:从比特到事务

USB通信的最小单位是数据包。一个数据包由若干字段组成,我习惯把它拆成三层来看:

  • 同步字段(SYNC):8位,用于时钟同步。说白了就是告诉接收端“我要发数据了,准备好”。
  • 包标识符(PID):8位,其中4位是类型码,4位是校验码。PID决定了这个包是令牌包、数据包还是握手包。
  • 数据字段:根据包类型不同,长度可变。比如数据包的数据段是0~1023字节。
  • CRC校验:令牌包用5位CRC,数据包用16位CRC。嗯,这里要注意,CRC算错了整个包就会被丢弃。
  • 包结束(EOP):单端0信号,表示包传输完毕。

关键点:USB是主机驱动的协议。所有传输都由主机发起,设备只能被动响应。这一点和I2C、SPI完全不同,你写驱动时一定要记住。

数据包组合起来形成事务。一个事务通常包含三个阶段:

  1. 令牌阶段:主机发送令牌包,指定要访问的设备地址、端点号和传输方向。
  2. 数据阶段:数据发送方(主机或设备)发送数据包。
  3. 握手阶段:接收方返回握手包(ACK/NAK/STALL),表示数据是否成功接收。

我在项目中遇到过一个问题:设备总是返回NAK,导致主机反复重试。后来发现是端点FIFO没及时清空。你看,理解事务流程对调试有多重要。

四、USB帧与微帧机制:时间基准

USB是时分复用的总线。全速/低速模式下,主机每1毫秒生成一个帧(Frame);高速模式下,每帧又分成8个微帧(Microframe),每个125微秒。

为什么要分帧?说白了就是为了给等时传输和中断传输提供确定性的时间槽。主机在每个帧/微帧的开始发送SOF(Start of Frame)包,设备用SOF来同步时钟。

模式 帧周期 每帧微帧数 SOF间隔
低速(1.5 Mbps) 1 ms 1 1 ms
全速(12 Mbps) 1 ms 1 1 ms
高速(480 Mbps) 125 µs 8 125 µs

帧机制直接影响驱动设计。比如你在写音频驱动时,等时传输必须每微帧发送一个数据包,否则就会出现断音。我曾经调试过一个USB摄像头驱动,画面总是卡顿,最后发现是帧同步没处理好,数据包没对齐到微帧边界。

调试技巧:用USB分析仪抓包时,重点关注SOF包的时间戳。如果SOF间隔不稳定,说明主机控制器或USB线缆有问题。我遇到过劣质USB线导致SOF抖动的情况,换了根线就好了。

二、USB传输类型:四种武器

USB定义了四种传输类型,每种都有自己的脾气。我按使用频率给你捋一遍。

1. 控制传输(Control Transfer)

这是USB的“官道”。所有设备都必须支持端点0的控制传输。它用于枚举、配置、获取描述符等管理操作。控制传输分三个阶段:建立阶段、数据阶段(可选)、状态阶段。

我个人习惯把控制传输比作“握手协议”——先发个请求,再传数据,最后确认。写驱动时,控制传输通常由USB核心层帮你封装好了,你只需要调用usb_control_msg()之类的API。

2. 批量传输(Bulk Transfer)

批量传输的特点是“可靠但没保障”。它保证数据无误送达,但不保证带宽和延迟。适合U盘、打印机这类对实时性没要求的设备。

批量传输在总线空闲时才能发送,如果总线繁忙,它就得排队。我在做U盘驱动时发现,如果同时挂载多个U盘,批量传输的吞吐量会明显下降。解决办法是调整端点FIFO大小,或者用多线程提交URB。

3. 中断传输(Interrupt Transfer)

别被名字骗了,USB中断传输不是硬件中断,而是主机轮询。设备告诉主机“我多久需要被查询一次”,主机就按这个间隔来读取数据。

中断传输适合鼠标、键盘这类输入设备。轮询间隔可以设置,全速模式下最小1ms,高速模式下最小125µs。嗯,这里要注意:轮询间隔设得太小会浪费总线带宽,设得太大又会导致输入延迟。

4. 等时传输(Isochronous Transfer)

等时传输是“实时但不可靠”的。它保证带宽和延迟,但不保证数据正确性——没有重传机制。适合音频、视频这类流式数据。

等时传输没有握手阶段,数据丢了就丢了。我在做USB麦克风驱动时,等时传输的缓冲区大小必须精确匹配帧周期,否则就会出现杂音或丢帧。

传输类型 可靠性 带宽保证 延迟 典型应用
控制 高 无 不确定 枚举、配置
批量 高 无 不确定 U盘、打印机
中断 高 有 确定 鼠标、键盘
等时 低 有 确定 音频、视频

避坑指南:我曾经在移植USB协议栈时,把批量端点的最大包大小设错了。全速批量端点最大包是64字节,高速是512字节。设小了性能差,设大了设备不认。一定要查datasheet确认。

三、USB协议栈核心知识结构

下面这张图是我自己整理的USB协议栈核心知识结构,从物理层到传输层,帮你建立整体认知。

USB协议栈核心知识结构图 USB协议栈核心知识结构 物理层 低速 1.5 Mbps | 全速 12 Mbps | 高速 480 Mbps | 超高速 5 Gbps 包层(Packet Layer) SYNC | PID | 数据字段 | CRC | EOP 事务层(Transaction Layer) 令牌阶段 → 数据阶段 → 握手阶段 传输层(Transfer Layer) 控制传输 | 批量传输 | 中断传输 | 等时传输 帧/微帧层(Frame Layer) SOF包 | 帧周期 1ms | 微帧 125µs | 带宽分配 说明:USB协议栈从物理层到帧层逐层封装,驱动开发主要关注传输层和帧层 理解每层的职责,能帮你快速定位驱动问题出在哪个环节

五、传输类型与帧机制的配合

你想想看,主机怎么在1ms内调度这么多传输?答案是轮询调度。主机控制器维护一个帧列表,每个帧/微帧里安排不同的传输事务。

  • 等时传输:优先安排,因为要保证带宽。每个微帧最多发3个等时事务。
  • 中断传输:按轮询间隔安排。比如鼠标设置10ms轮询,那就每10帧查一次。
  • 控制传输:有固定带宽预留,但优先级低于等时和中断。
  • 批量传输:剩下的带宽全给它。所以U盘在系统空闲时跑得快,忙时跑得慢。

实战建议:写驱动时,如果你发现设备响应慢,先别急着优化代码。用USB分析仪看看总线上是不是有太多批量传输在抢带宽。我曾经优化过一个打印机驱动,最后发现是同一个USB控制器上挂了两个U盘在拷贝大文件。

好了,这一章的内容就到这里。数据包结构、四种传输类型、帧与微帧机制,这三块是USB协议栈的基石。你把这些搞透了,后面看USB控制器驱动、设备驱动源码时就会轻松很多。