4. USB传输类型(上):控制传输与批量传输
USB的传输类型,说白了就是四种不同的数据搬运方式。每种方式都有自己的脾气秉性,用在不同的场景里。今天咱们先聊两种:控制传输和批量传输。这两种在实际项目中碰得最多,坑也最多。
4.1 控制传输(Control Transfer)——USB的“控制通道”
控制传输是所有USB设备都必须支持的传输类型。它走的是端点0(Endpoint 0),这是一个双向的、默认存在的端点。设备一上电,主机第一件事就是通过控制传输来枚举它。
控制传输的核心特征:
- 双向性:一次传输可以包含主机到设备、设备到主机两个方向的数据。
- 可靠性:硬件层面有CRC校验和重试机制,丢包了会自动重传。
- 带宽保证:USB协议保证控制传输至少占用10%的带宽。
- 数据量小:每次传输的数据包最大64字节(高速模式),通常就几个字节。
控制传输的典型场景:
- 设备枚举(获取描述符、设置地址、配置设备)
- 标准设备请求(如清除端点暂停、获取状态)
- 类特定请求(如HID设备的Set_Report、大容量存储设备的SCSI命令)
- 厂商自定义请求(用于固件升级、调试等)
4.1.1 控制传输的四个阶段
控制传输不是一股脑把数据扔过去就完事。它分四个阶段,每个阶段都有明确的分工。我刚开始做USB驱动时,就因为这个阶段没搞清楚,调试了整整两天。
| 阶段 | 方向 | 内容 | 说明 |
|---|---|---|---|
| 建立阶段 | 主机→设备 | Setup包(8字节) | 包含请求类型、请求码、参数等 |
| 数据阶段 | 双向 | 数据包(0~64字节) | 可选,有些请求不需要数据 |
| 状态阶段 | 设备→主机 | 零长度包或状态信息 | 确认传输成功或失败 |
| 握手阶段 | 双向 | ACK/NAK/STALL | 硬件自动处理,软件通常不感知 |
建立阶段那个8字节的Setup包,结构是固定的。我贴一段代码,你一看就明白:
// USB控制传输的Setup包结构
typedef struct {
uint8_t bmRequestType; // 请求类型:方向、类型、接收者
uint8_t bRequest; // 请求码
uint16_t wValue; // 值字段
uint16_t wIndex; // 索引字段
uint16_t wLength; // 数据阶段长度
} usb_setup_packet_t;
// 举个例子:获取设备描述符
// bmRequestType = 0x80 (主机→设备, 标准请求, 设备接收)
// bRequest = 0x06 (GET_DESCRIPTOR)
// wValue = 0x0100 (描述符类型=设备, 索引=0)
// wIndex = 0x0000
// wLength = 0x0012 (设备描述符长度18字节)
我的经验:调试控制传输时,用逻辑分析仪抓一下Setup包的前两个字节,基本就能判断问题出在哪。bmRequestType的bit7是方向位,bit5-6是类型位,bit0-4是接收者位。很多初学者搞混这个,导致设备不响应。
4.1.2 控制传输的三种类型
控制传输根据数据阶段的有无和方向,又分成三种子类型:
- 控制读取(Control Read):主机发Setup包,然后设备发数据,最后主机发状态。比如获取描述符。
- 控制写入(Control Write):主机发Setup包,然后主机发数据,最后设备发状态。比如设置配置。
- 无数据控制(No-Data Control):只有Setup包和状态阶段。比如清除端点暂停。
为什么会这样设计?你想想看,USB是主从架构,所有传输都由主机发起。控制传输的四个阶段,保证了主机能精确控制每一次交互的节奏。
避坑指南:我曾经在一个项目中,设备端处理控制传输时,数据阶段返回的数据长度和Setup包中wLength不一致。结果主机直接STALL了端点,设备就断连了。记住:数据阶段返回的数据长度不能超过wLength,但可以少于wLength(返回短包表示传输结束)。
4.2 批量传输(Bulk Transfer)——U盘的“数据高速公路”
批量传输,顾名思义,就是用来传输大批量数据的。它没有固定的带宽保证,但保证数据一定送到。U盘、移动硬盘这些大容量存储设备,用的就是批量传输。
批量传输的核心特征:
- 可靠性:硬件层面有CRC16校验、重试机制、错误重传。
- 无带宽保证:只有在总线空闲时才能传输,优先级低于控制传输和中断传输。
- 数据量大:每次传输最大包长:全速64字节,高速512字节,超高速1024字节。
- 流控制:通过NAK握手包实现,设备忙时可以告诉主机“等一下”。
批量传输的典型场景:
- 大容量存储设备(U盘、移动硬盘、SD卡读卡器)
- 打印机数据传输
- 扫描仪图像数据传输
- 数据采集卡的高速数据上传
4.2.1 批量传输的流程
批量传输比控制传输简单,只有三个阶段:
- 令牌阶段:主机发OUT或IN令牌包,指定端点。
- 数据阶段:主机发数据(OUT)或设备发数据(IN)。
- 握手阶段:接收方发ACK(成功)、NAK(忙,请重试)或STALL(错误,别重试了)。
这里有个关键点:批量传输的NAK机制。设备收到令牌后,如果数据还没准备好(IN端点)或缓冲区满(OUT端点),就回一个NAK。主机收到NAK后,会等一会儿再重试。这个机制保证了设备不会被数据淹没。
// 批量传输的典型流程(主机读取数据)
// 1. 主机发 IN 令牌包
// 2. 设备发数据包(最多512字节,高速模式)
// 3. 主机发 ACK 确认
//
// 如果设备数据没准备好:
// 1. 主机发 IN 令牌包
// 2. 设备发 NAK 握手包
// 3. 主机等待一段时间后重试
// 批量传输的端点描述符示例
typedef struct {
uint8_t bLength; // 描述符长度 = 7
uint8_t bDescriptorType; // 描述符类型 = 0x05 (端点)
uint8_t bEndpointAddress;// 端点地址:bit7=方向(0:OUT,1:IN), bit3-0=端点号
uint8_t bmAttributes; // 传输类型:0x02 = 批量
uint16_t wMaxPacketSize; // 最大包长
uint8_t bInterval; // 批量传输忽略此字段
} usb_endpoint_descriptor_t;
// 例如:U盘的批量IN端点
// bEndpointAddress = 0x81 (端点1, IN方向)
// bmAttributes = 0x02 (批量传输)
// wMaxPacketSize = 0x0200 (512字节)
4.2.2 批量传输的可靠性机制
批量传输的可靠性,靠的是三样东西:
| 机制 | 说明 | 效果 |
|---|---|---|
| CRC校验 | 数据包尾部有CRC16校验码 | 检测传输错误 |
| 重试机制 | 接收方发现CRC错误,不回ACK,发送方超时重传 | 纠正传输错误 |
| 数据切换 | 使用DATA0/DATA1交替,防止丢包或重复 | 保证数据顺序和完整性 |
数据切换这个机制,我多说两句。USB协议用了一个叫“数据切换同步”的技术:每个数据包都有一个PID(Packet ID),DATA0和DATA1交替出现。发送方发DATA0,接收方确认后,下一次发DATA1。如果接收方发现PID不对,就知道丢包了,会要求重传。
我的经验:调试U盘驱动时,最头疼的就是数据切换不同步。有一次设备端固件升级后,数据切换逻辑写反了,结果主机一直收不到正确的数据。用USB分析仪一看,PID序列全是乱的。修复方法很简单:在端点复位后,强制将数据切换重置为DATA0。
4.2.3 批量传输在U盘中的应用
U盘用的是Bulk-Only Transport(BOT)协议,它把批量传输封装成命令/数据/状态三个阶段:
- 命令阶段(CBW):主机通过批量OUT端点发送命令块包装(Command Block Wrapper),包含SCSI命令。
- 数据阶段:根据命令方向,通过批量IN或OUT端点传输数据。
- 状态阶段(CSW):设备通过批量IN端点返回命令状态包装(Command Status Wrapper),报告命令执行结果。
举个例子,读取U盘的一个扇区:
// 读取U盘扇区的BOT流程
// 1. 主机发CBW(批量OUT端点)
// - dCBWSignature = 0x43425355 ("USBC")
// - dCBWTag = 0x00000001 (命令标签)
// - dCBWDataTransferLength = 0x00000200 (512字节)
// - bmCBWFlags = 0x80 (设备→主机)
// - bCBWLUN = 0x00 (逻辑单元号)
// - bCBWCBLength = 0x0A (SCSI命令长度)
// - CBWCB = SCSI READ(10) 命令
// 2. 设备返回数据(批量IN端点)
// - 512字节的扇区数据
// 3. 设备返回CSW(批量IN端点)
// - dCSWSignature = 0x53425355 ("USBS")
// - dCSWTag = 0x00000001 (与CBW匹配)
// - dCSWDataResidue = 0x00000000 (剩余数据长度)
// - bCSWStatus = 0x00 (成功)
避坑指南:我曾经在一个项目中,U盘读写偶尔会卡死。排查了很久,发现是主机在发送CBW后,设备还没准备好就收到了数据请求。问题出在:主机没有等待设备处理完CBW就发了IN令牌。解决方案是在CBW和IN之间加一个微秒级的延时,或者用中断传输来查询设备状态。
4.3 控制传输 vs 批量传输:一张表说清楚
| 对比项 | 控制传输 | 批量传输 |
|---|---|---|
| 端点 | 端点0(固定) | 任意批量端点(1~15) |
| 方向 | 双向(同一端点) | 单向(IN或OUT) |
| 带宽保证 | 至少10% | 无保证 |
| 最大包长(高速) | 64字节 | 512字节 |
| 可靠性 | 高(CRC+重试) | 高(CRC+重试+数据切换) |
| 典型应用 | 枚举、设备管理 | U盘、打印机、数据采集 |
| 数据量 | 小(通常≤64字节) | 大(可连续传输MB级数据) |
4.4 本章知识结构图
下面这张图,把控制传输和批量传输的核心知识点串起来了。我建议你把它存下来,做项目时对照着看。
控制传输和批量传输,是USB协议里最常用的两种传输方式。控制传输是USB设备的“神经系统”,负责管理和配置;批量传输是“数据动脉”,负责搬运大量数据。理解了这两者,你就掌握了USB通信的基石。