4. USB类协议详解:HID类(键盘鼠标)、Mass Storage类(U盘)、CDC类(虚拟串口)、Video类(摄像头)
好,咱们进入USB协议栈里最接地气的部分——类协议。说白了,USB协议只定义了底层怎么传数据,但传上去的数据是什么含义?键盘按下了哪个键?U盘里存了什么文件?这些事USB主机根本不知道。所以就有了「类协议」这个东西,它给设备分了类,定好了数据格式。
我个人习惯把这四个类称为「四大金刚」:HID、Mass Storage、CDC、Video。你平时用的键盘鼠标、U盘、串口转USB线、摄像头,全在这四个类里。今天咱们一个一个拆开讲。
4.1 HID类——键盘鼠标的「隐形语言」
HID,全称Human Interface Device,人机交互设备。名字挺高大上,其实就是键盘、鼠标、游戏手柄这些东西。我刚开始做USB时,觉得HID协议很简单,不就是发几个按键码吗?后来踩了坑才发现,细节多得很。
4.1.1 HID的「报告描述符」
HID设备最核心的东西,不是数据本身,而是「报告描述符」(Report Descriptor)。它告诉主机:我要发什么数据,每个字节是什么含义。
举个例子,一个标准键盘的报告描述符,大概长这样:
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0xE0, // Usage Minimum (224)
0x29, 0xE7, // Usage Maximum (231)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x81, 0x02, // Input (Data,Var,Abs)
0x19, 0x00, // Usage Minimum (0)
0x29, 0x65, // Usage Maximum (101)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x65, // Logical Maximum (101)
0x75, 0x08, // Report Size (8)
0x95, 0x06, // Report Count (6)
0x81, 0x00, // Input (Data,Array)
0xC0 // End Collection
这段描述符定义了键盘的输入报告:8个字节的修饰键(Ctrl、Shift等),6个字节的按键码。嗯,这里要注意,键盘最多同时按6个键,不是随便按的。
4.1.2 键盘与鼠标的数据格式
键盘的报告格式很简单:
| 字节 | 含义 |
|---|---|
| 0 | 修饰键(Ctrl=0x01, Shift=0x02, Alt=0x04, Win=0x08) |
| 1 | 保留(必须为0) |
| 2-7 | 按键码(最多6个同时按下) |
鼠标的报告格式稍微复杂点:
| 字节 | 含义 |
|---|---|
| 0 | 按键状态(bit0=左键, bit1=右键, bit2=中键) |
| 1 | X轴位移(有符号数) |
| 2 | Y轴位移(有符号数) |
| 3 | 滚轮(有符号数) |
你想想看,鼠标移动的数据是有符号的,正数往右/往下,负数往左/往上。这个设计很巧妙,因为鼠标是相对定位设备,不是绝对定位。
4.2 Mass Storage类——U盘的「文件系统」
Mass Storage类,说白了就是U盘、移动硬盘这些东西。它用的协议叫Bulk-Only Transport(BOT),就是通过批量端点传数据。
我记得第一次做U盘项目时,以为只要把数据写到Flash里就行了。结果发现,USB主机根本不关心你用什么存储介质,它只认SCSI命令。
4.2.1 SCSI命令集
Mass Storage设备本质上是一个SCSI命令的翻译器。主机发SCSI命令过来,设备执行后返回结果。常用的SCSI命令有:
- INQUIRY:查询设备信息(厂商、产品名、版本)
- READ CAPACITY:读取总容量
- READ (10):读取指定扇区
- WRITE (10):写入指定扇区
- TEST UNIT READY:检查设备是否就绪
一个典型的READ命令格式:
CBW(命令块包裹):
dCBWSignature: 0x43425355
dCBWTag: 0x00000001
dCBWDataTransferLength: 0x00000200
bmCBWFlags: 0x80(设备到主机)
bCBWLUN: 0x00
bCBWCBLength: 10
CBWCB: READ(10)命令内容
OP Code: 0x28
LBA: 起始逻辑块地址
Transfer Length: 要读的扇区数
4.2.2 文件系统与扇区
Mass Storage协议只负责扇区级的读写,不关心文件系统。FAT32、NTFS、exFAT这些,都是主机端的事。设备只需要做到:主机说读扇区100,我就把扇区100的数据返回去。
但有个细节要注意:扇区大小。标准U盘用512字节扇区,但大容量设备(超过2TB)需要用4K扇区。这个在READ CAPACITY命令里要返回正确值。
4.3 CDC类——虚拟串口的「伪装术」
CDC类,全称Communication Device Class。最常见的应用就是USB转串口,比如CH340、CP2102这些芯片。它让USB看起来像是一个传统的RS232串口。
我个人觉得CDC类是最容易实现的USB类协议,因为它不需要处理复杂的文件系统或报告描述符。但坑也不少。
4.3.1 抽象控制模型(ACM)
CDC类分两个子类:
- ACM(Abstract Control Model):虚拟串口,最常用
- EEM(Ethernet Emulation Model):虚拟网卡
ACM设备需要两个接口:
- 通信接口(Control Interface):用于设置波特率、数据位等参数
- 数据接口(Data Interface):用于收发实际数据
通信接口使用控制端点(端点0),数据接口使用批量端点。数据格式很简单:发什么就收什么,没有额外协议头。
4.3.2 波特率设置与流控
设置波特率时,主机发送SET_LINE_CODING请求:
struct line_coding {
uint32_t dwDTERate; // 波特率,如115200
uint8_t bCharFormat; // 数据位:0=5, 1=6, 2=7, 3=8
uint8_t bParityType; // 校验:0=None, 1=Odd, 2=Even
uint8_t bDataBits; // 停止位:0=1, 1=1.5, 2=2
};
嗯,这里要注意,很多初学者会把波特率设成固定值。但实际上,主机可能会随时改变波特率,设备必须能动态响应。
4.4 Video类——摄像头的「数据洪流」
Video类,全称USB Video Class(UVC)。摄像头、视频采集卡都用这个协议。它最大的特点就是数据量大,对实时性要求高。
我记得第一次做UVC摄像头项目时,被数据量吓了一跳。一个1080p30帧的摄像头,每秒要传将近1.5Gbps的原始数据。USB 2.0的480Mbps根本扛不住,所以必须用压缩。
4.4.1 视频流格式
UVC支持多种视频格式:
| 格式 | 描述 | 典型带宽 |
|---|---|---|
| MJPEG | Motion JPEG,每帧独立压缩 | 中等 |
| H.264 | 高效视频编码,帧间压缩 | 低 |
| YUY2 | 未压缩的YUV 4:2:2格式 | 极高 |
| NV12 | YUV 4:2:0格式,常用于硬件编码 | 高 |
为什么会有这么多格式?说白了就是带宽和画质的权衡。YUY2画质最好,但USB 2.0下只能跑VGA分辨率。MJPEG画质稍差,但能跑1080p。
4.4.2 等时传输与帧同步
Video类使用等时传输(Isochronous Transfer),因为它对实时性要求高,可以容忍偶尔丢包,但不能容忍延迟。
等时传输的帧结构:
每个USB帧(1ms)内包含多个等时事务:
- 事务1: 视频数据包(含帧头)
- 事务2: 视频数据包
- ...
- 事务N: 视频数据包(含帧尾)
帧头标记:
- FID(Frame ID):0/1交替,表示新帧开始
- EOF(End of Frame):1表示当前帧结束
4.4.3 视频控制与参数调节
UVC除了传视频数据,还支持控制功能,比如:
- 亮度(Brightness):范围0-255
- 对比度(Contrast):范围0-10
- 饱和度(Saturation):范围0-200
- 自动曝光(Auto Exposure):开关控制
这些控制通过视频控制接口(VC Interface)实现,使用控制端点发送请求。每个控制项都有对应的Unit和Terminal,主机通过查询这些Unit来获取支持的控制范围。
你想想看,一个摄像头可能有几十个可调参数,但并不是所有参数都支持。所以UVC协议要求设备上报自己的控制能力,主机再根据能力来显示调节界面。
小结
这四个类协议,每个都有自己的特点:
- HID:靠报告描述符定义数据格式,灵活但容易出错
- Mass Storage:用SCSI命令操作扇区,简单但要注意兼容性
- CDC:模拟串口,实现简单但流控细节多
- Video:数据量大,等时传输,帧同步是关键
我个人建议,初学者先从CDC类入手,因为它最简单,能快速看到效果。然后做HID类,理解报告描述符。接着做Mass Storage,掌握SCSI命令。最后挑战Video类,处理大数据量和实时性问题。
嗯,今天就讲到这里。下一章咱们聊聊USB的电源管理和枚举过程,这两个是调试USB设备时最常遇到的问题。