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 批量传输的流程

批量传输比控制传输简单,只有三个阶段:

  1. 令牌阶段:主机发OUT或IN令牌包,指定端点。
  2. 数据阶段:主机发数据(OUT)或设备发数据(IN)。
  3. 握手阶段:接收方发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)协议,它把批量传输封装成命令/数据/状态三个阶段:

  1. 命令阶段(CBW):主机通过批量OUT端点发送命令块包装(Command Block Wrapper),包含SCSI命令。
  2. 数据阶段:根据命令方向,通过批量IN或OUT端点传输数据。
  3. 状态阶段(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传输类型(上)—— 控制传输与批量传输 控制传输(Control Transfer) 批量传输(Bulk Transfer) 端点0(双向) 四个阶段 建立阶段 数据阶段 状态阶段 握手阶段 应用:枚举、设备管理、类请求 单向(IN/OUT) 三个阶段 令牌阶段 数据阶段 握手阶段 NAK流控 应用:U盘、打印机、数据采集 核心要点:控制传输用于管理,批量传输用于数据搬运。两者都可靠,但控制传输有带宽保证,批量传输没有。

控制传输和批量传输,是USB协议里最常用的两种传输方式。控制传输是USB设备的“神经系统”,负责管理和配置;批量传输是“数据动脉”,负责搬运大量数据。理解了这两者,你就掌握了USB通信的基石。