3. USB数据传输基础:传输类型、端点与管道、数据包结构

大家好,我是老张。做USB开发这么多年,我见过太多工程师在数据传输这块栽跟头。说白了,USB协议最核心的就是怎么把数据从A点搬到B点。今天咱们就把这层窗户纸捅破。

3.1 四种传输类型:各有各的脾气

USB定义了四种传输类型。为什么是四种?因为外设的需求五花八门。鼠标不需要高速,但要求实时;U盘需要高速,但偶尔卡一下没关系。你想想看,一种传输方式肯定搞不定。

3.1.1 控制传输(Control Transfer)

控制传输是USB的"官方语言"。每个USB设备都必须支持。它主要用于设备枚举、配置、状态查询这些管理操作。

特点:

  • 双向传输:主机发请求,设备回数据
  • 有保证的传输:硬件层面会重试
  • 带宽占用小:最多占用10%的帧时间
  • 数据量小:通常不超过64字节(全速)

控制传输的结构很固定,分三个阶段:

  • 建立阶段(Setup):主机发8字节的请求包
  • 数据阶段(Data):可选,传数据
  • 状态阶段(Status):确认传输结果

我记得刚入行时调试一个HID设备,枚举死活过不去。折腾了两天,最后发现是控制传输的wLength字段填错了。嗯,这种细节问题最坑人。

3.1.2 批量传输(Bulk Transfer)

批量传输,说白了就是"尽力而为"。它保证数据一定送到,但不保证什么时候送到。适合打印机、U盘、数据采集卡这类设备。

特点:

  • 非实时:带宽空闲时才传
  • 错误重传:硬件自动处理
  • 数据量大:一次最多512字节(高速)或1024字节(超高速)
  • 独占带宽:可以占满整个帧时间

我在项目中遇到过一个问题:一个高速数据采集设备,用批量传输往PC发数据。结果发现只要PC端CPU负载一高,数据就丢包。后来查了半天,原来是驱动层的缓冲区太小。我建议你设计时至少留4KB的缓冲区。

3.1.3 中断传输(Interrupt Transfer)

别被名字骗了。USB的中断传输不是硬件中断,而是主机轮询。设备想发数据?得等主机来问。

轮询周期:

  • 低速:10-255ms
  • 全速:1-255ms
  • 高速:125μs-4095×125μs
  • 超高速:125μs-4095×125μs

中断传输适合鼠标、键盘这类设备。数据量小,但要求延迟可控。我曾经调试一个游戏鼠标,轮询周期设成了10ms,结果玩家反映移动不跟手。改成1ms后,问题就解决了。

3.1.4 同步传输(Isochronous Transfer)

同步传输最特殊。它不保证数据正确,但保证准时送达。适合音频、视频这类流媒体数据。

重要提醒:

  • 没有重传机制:丢包就丢了
  • 没有ACK/NAK握手:只管发不管收
  • 带宽预留:最多占90%的帧时间
  • 数据校验:只有CRC,没有重传

做USB麦克风时我就吃过这个亏。同步传输丢包了,音频里会出现"咔咔"的杂音。后来加了前向纠错(FEC)才搞定。你想想看,音频数据丢了就丢了,总不能等重传吧?那声音就卡住了。

3.2 端点与管道:数据的出入口

端点(Endpoint)是USB设备内部的数据收发点。每个端点都有一个编号和方向。管道(Pipe)是主机到端点的逻辑连接。

3.2.1 端点类型

端点类型 传输类型 方向 典型应用
端点0 控制传输 双向 枚举、配置
批量端点 批量传输 IN/OUT U盘、串口
中断端点 中断传输 IN/OUT 鼠标、键盘
同步端点 同步传输 IN/OUT 音频、视频

端点0是每个USB设备的标配。它就像设备的"控制台",所有管理操作都通过它完成。我个人习惯在调试时先确认端点0能不能正常工作,这是基础中的基础。

3.2.2 管道机制

管道分两种:

  • 消息管道:用于控制传输,数据有结构
  • 流管道:用于批量、中断、同步传输,数据无结构

流管道的数据没有固定格式,就是一串字节。消息管道的数据有固定格式,比如控制传输的Setup包。我曾经见过有人把流管道当消息管道用,结果数据解析全乱了。

3.3 数据包结构:USB的"信封"

USB数据包分三种:令牌包、数据包、握手包。它们组合起来完成一次传输事务。

3.3.1 令牌包(Token Packet)

令牌包由主机发出,告诉设备要做什么。格式如下:

SYNC(8bit) + PID(8bit) + ADDR(7bit) + ENDP(4bit) + CRC5(5bit) + EOP
  • SYNC:同步序列,让设备对齐时钟
  • PID:包标识符,告诉设备是IN、OUT还是SETUP
  • ADDR:设备地址,最多127个设备
  • ENDP:端点号,最多16个端点
  • CRC5:5位校验码

3.3.2 数据包(Data Packet)

数据包就是实际传输的数据。格式:

SYNC(8bit) + PID(8bit) + DATA(0-1024字节) + CRC16(16bit) + EOP

数据包有DATA0和DATA1两种PID。它们交替使用,实现数据同步。为什么这么做?防止丢包后数据重复。我记得有一次调试,发现设备老是收到重复数据,查了半天是数据切换位没处理好。

3.3.3 握手包(Handshake Packet)

握手包最简单,就一个PID:

SYNC(8bit) + PID(8bit) + EOP

握手包有三种:

  • ACK:数据接收成功
  • NAK:设备忙,无法接收
  • STALL:端点出错,需要主机干预

调试小技巧:

用USB分析仪抓包时,多留意握手包。如果看到大量NAK,说明设备处理不过来。如果看到STALL,说明端点配置有问题。我曾经靠这个技巧,半小时就定位了一个批量传输卡死的问题。

3.4 传输事务:一次完整的对话

一次USB传输事务由三个阶段组成:

  1. 令牌阶段:主机发令牌包,指定设备和端点
  2. 数据阶段:数据发送或接收
  3. 握手阶段:接收方回应ACK/NAK/STALL

举个例子,主机从批量端点读数据:

主机发 IN令牌包 → 设备发 数据包 → 主机发 ACK握手包

如果设备没准备好:

主机发 IN令牌包 → 设备发 NAK握手包 → 主机重试

你想想看,这个机制保证了数据传输的可靠性。但代价就是延迟不确定。所以实时性要求高的场合,得用中断传输或同步传输。

3.5 实战经验总结

做USB开发这么多年,我总结了几条经验:

选型建议:

  • 控制传输:只用于枚举和配置,别拿来传大数据
  • 批量传输:适合U盘、串口,注意缓冲区大小
  • 中断传输:适合HID设备,轮询周期要合理
  • 同步传输:适合音视频,记得加纠错

最后说一句:USB协议看着复杂,但核心就是这四种传输方式。把它们的脾气摸透了,大部分问题都能迎刃而解。下次咱们聊聊设备枚举的细节,那个过程更有意思。


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