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传输事务由三个阶段组成:
- 令牌阶段:主机发令牌包,指定设备和端点
- 数据阶段:数据发送或接收
- 握手阶段:接收方回应ACK/NAK/STALL
举个例子,主机从批量端点读数据:
主机发 IN令牌包 → 设备发 数据包 → 主机发 ACK握手包
如果设备没准备好:
主机发 IN令牌包 → 设备发 NAK握手包 → 主机重试
你想想看,这个机制保证了数据传输的可靠性。但代价就是延迟不确定。所以实时性要求高的场合,得用中断传输或同步传输。
3.5 实战经验总结
做USB开发这么多年,我总结了几条经验:
选型建议:
- 控制传输:只用于枚举和配置,别拿来传大数据
- 批量传输:适合U盘、串口,注意缓冲区大小
- 中断传输:适合HID设备,轮询周期要合理
- 同步传输:适合音视频,记得加纠错
最后说一句:USB协议看着复杂,但核心就是这四种传输方式。把它们的脾气摸透了,大部分问题都能迎刃而解。下次咱们聊聊设备枚举的细节,那个过程更有意思。
公众号:蓝海资料掘金营,微信deep3321