📖 引言
USB(Universal Serial Bus)是现代计算机系统中最重要的外设接口之一,但其通信机制却常常令人困惑。本文将从最核心的Host主导原则出发,深入解析USB通信的各个层面,包括设备架构、端点机制、枚举过程等关键概念。无论你是嵌入式开发者、系统工程师,还是对USB原理感兴趣的爱好者,本文都将为你提供清晰、系统的理解框架。
1. 🎯 最重要的原则:USB由Host主导
USB不是普通的双向串口,而是一个严格的主从架构总线系统。
1.1 Host与Device的角色
在USB总线上:
- Host:发起并调度所有通信
- Device :不能随意向总线发送数据
- Device只能准备数据,然后等待Host访问相应端点
- 即使摄像头有新画面,也不能直接"主动推给PC"
- 实际上是PC按调度周期发起IN Transaction,设备返回已经准备好的数据
1.2 RK3568发送视频的实际流程
#mermaid-svg-EfsiFxMLXPUWVi5W{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-EfsiFxMLXPUWVi5W .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-EfsiFxMLXPUWVi5W .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-EfsiFxMLXPUWVi5W .error-icon{fill:#552222;}#mermaid-svg-EfsiFxMLXPUWVi5W .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EfsiFxMLXPUWVi5W .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-EfsiFxMLXPUWVi5W .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EfsiFxMLXPUWVi5W .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EfsiFxMLXPUWVi5W .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-EfsiFxMLXPUWVi5W .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EfsiFxMLXPUWVi5W .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EfsiFxMLXPUWVi5W .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EfsiFxMLXPUWVi5W .marker.cross{stroke:#333333;}#mermaid-svg-EfsiFxMLXPUWVi5W svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EfsiFxMLXPUWVi5W p{margin:0;}#mermaid-svg-EfsiFxMLXPUWVi5W .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-EfsiFxMLXPUWVi5W .cluster-label text{fill:#333;}#mermaid-svg-EfsiFxMLXPUWVi5W .cluster-label span{color:#333;}#mermaid-svg-EfsiFxMLXPUWVi5W .cluster-label span p{background-color:transparent;}#mermaid-svg-EfsiFxMLXPUWVi5W .label text,#mermaid-svg-EfsiFxMLXPUWVi5W span{fill:#333;color:#333;}#mermaid-svg-EfsiFxMLXPUWVi5W .node rect,#mermaid-svg-EfsiFxMLXPUWVi5W .node circle,#mermaid-svg-EfsiFxMLXPUWVi5W .node ellipse,#mermaid-svg-EfsiFxMLXPUWVi5W .node polygon,#mermaid-svg-EfsiFxMLXPUWVi5W .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-EfsiFxMLXPUWVi5W .rough-node .label text,#mermaid-svg-EfsiFxMLXPUWVi5W .node .label text,#mermaid-svg-EfsiFxMLXPUWVi5W .image-shape .label,#mermaid-svg-EfsiFxMLXPUWVi5W .icon-shape .label{text-anchor:middle;}#mermaid-svg-EfsiFxMLXPUWVi5W .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-EfsiFxMLXPUWVi5W .rough-node .label,#mermaid-svg-EfsiFxMLXPUWVi5W .node .label,#mermaid-svg-EfsiFxMLXPUWVi5W .image-shape .label,#mermaid-svg-EfsiFxMLXPUWVi5W .icon-shape .label{text-align:center;}#mermaid-svg-EfsiFxMLXPUWVi5W .node.clickable{cursor:pointer;}#mermaid-svg-EfsiFxMLXPUWVi5W .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-EfsiFxMLXPUWVi5W .arrowheadPath{fill:#333333;}#mermaid-svg-EfsiFxMLXPUWVi5W .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-EfsiFxMLXPUWVi5W .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-EfsiFxMLXPUWVi5W .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EfsiFxMLXPUWVi5W .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-EfsiFxMLXPUWVi5W .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EfsiFxMLXPUWVi5W .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-EfsiFxMLXPUWVi5W .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-EfsiFxMLXPUWVi5W .cluster text{fill:#333;}#mermaid-svg-EfsiFxMLXPUWVi5W .cluster span{color:#333;}#mermaid-svg-EfsiFxMLXPUWVi5W div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-EfsiFxMLXPUWVi5W .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-EfsiFxMLXPUWVi5W rect.text{fill:none;stroke-width:0;}#mermaid-svg-EfsiFxMLXPUWVi5W .icon-shape,#mermaid-svg-EfsiFxMLXPUWVi5W .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EfsiFxMLXPUWVi5W .icon-shape p,#mermaid-svg-EfsiFxMLXPUWVi5W .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-EfsiFxMLXPUWVi5W .icon-shape .label rect,#mermaid-svg-EfsiFxMLXPUWVi5W .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EfsiFxMLXPUWVi5W .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-EfsiFxMLXPUWVi5W .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-EfsiFxMLXPUWVi5W :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} RK3568用户态程序准备图像
提交给UVC Gadget驱动
驱动把usb_request挂到IN端点
PC Host按周期发起IN事务
RK3568 USB控制器返回视频数据
1.3 IN/OUT方向定义
这里"IN/OUT"永远以Host为参照:
| 方向 | 数据流向 | 典型应用 |
|---|---|---|
| IN | Device → Host | USB摄像头视频、USB麦克风音频 |
| OUT | Host → Device | USB音箱播放数据、PC发送控制命令 |
| EP0 | 双向(控制传输) | PC发送控制命令、设备枚举 |
⚠️ 重要提示:这是非常容易混淆、面试也经常问的地方!
2. 🏗️ Host侧和Device侧架构
以RK3568连接Windows PC为例:
2.1 Windows PC(Host侧)
Windows PC(Host)
├─ xHCI Host Controller
├─ USB Core
├─ Hub Driver
└─ UVC/UAC Class Driver
↕ USB线
2.2 RK3568(Device侧)
RK3568(Device)
├─ DWC3 Device Controller
├─ UDC Driver
├─ Gadget Core
├─ Composite Framework
├─ UVC Function
└─ UAC2 Function
2.3 重要名词解释
| 名词 | 描述 | 位置 |
|---|---|---|
| Host Controller | PC侧的USB控制器,一般是xHCI | Host侧 |
| UDC | USB Device Controller,设备侧的控制器抽象 | Device侧 |
| Gadget | Linux把"本机模拟成USB外设"的框架 | Device侧 |
| Function | 复合设备中的一项功能,例如UVC、UAC、ACM | Device侧 |
| Composite Device | 一个物理USB设备包含多个Function | Device侧 |
2.4 最终设备结构
我们的最终设备大致是:
一个USB Device
└─ 一个Configuration
├─ UVC Function
│ ├─ VideoControl Interface
│ └─ VideoStreaming Interface
└─ UAC2 Function
├─ AudioControl Interface
└─ AudioStreaming Interface
3. 🔍 Device、Interface和Endpoint的区别
3.1 Device(设备)
表示整个物理设备,包含:
- VID/PID(厂商ID/产品ID)
- USB版本
- 设备类别
- 厂商、产品、序列号字符串
- Configuration数量
3.2 Configuration(配置)
表示设备的一套运行配置。大部分设备只有一个Configuration。Host通过SET_CONFIGURATION选择它。
3.3 Interface(接口)
表示一种逻辑功能。UVC摄像头通常不止一个Interface:
| Interface类型 | 功能 |
|---|---|
| VideoControl Interface | 控制摄像头参数(亮度、对比度等) |
| VideoStreaming Interface | 传输视频数据 |
Interface还可以有多个Alternate Setting:
| Alternate Setting | 状态 |
|---|---|
| Setting 0 | 不传视频,没有等时端点 |
| Setting 1 | 启用视频端点 |
| Setting 2 | 启用更大带宽的视频端点 |
3.4 Endpoint(端点)
Endpoint是Device内部的数据通道。
端点地址格式
bit 7:方向(1=IN,0=OUT)
bit 3:0:端点编号
示例
| 端点地址 | 端点编号 | 方向 | 完整描述 |
|---|---|---|---|
0x81 |
1 | IN | Endpoint 1 IN |
0x82 |
2 | IN | Endpoint 2 IN |
0x02 |
2 | OUT | Endpoint 2 OUT |
0x00 |
0 | OUT | EP0 OUT方向 |
0x80 |
0 | IN | EP0 IN方向 |
注意:端点号相同但方向不同,可以表示不同端点:
- EP1 IN = 0x81
- EP1 OUT = 0x01
4. ⭐ EP0为什么特殊
每个USB设备都必须有Endpoint 0。
4.1 EP0的作用
EP0用于Control Transfer,承担:
- 获取描述符
- 设置设备地址
- 选择Configuration
- 发送标准请求
- 发送Class请求
- UVC/UAC参数协商
4.2 为什么必须有EP0?
设备刚插入时,Host还不知道它是什么设备,但一定知道它存在EP0,因此枚举才能从EP0开始。
4.3 控制传输三个阶段
控制传输通常分为三个阶段:
SETUP阶段 → 可选DATA阶段 → STATUS阶段
例如Host读取Device Descriptor:
- SETUP:Host说明"我要读取Device Descriptor"
- DATA:Device返回描述符
- STATUS:Host确认传输完成
4.4 SETUP包结构
SETUP包固定为8字节:
c
struct usb_ctrlrequest {
uint8_t bRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
};
后面分析UVC请求时会反复遇到这个结构体。
5. 🔄 设备插入后的枚举过程
5.1 三个关键状态
先记住三个状态:
Default → Addressed → Configured
5.2 典型枚举过程
#mermaid-svg-n969rTPSwqgxAQRA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-n969rTPSwqgxAQRA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-n969rTPSwqgxAQRA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-n969rTPSwqgxAQRA .error-icon{fill:#552222;}#mermaid-svg-n969rTPSwqgxAQRA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-n969rTPSwqgxAQRA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-n969rTPSwqgxAQRA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-n969rTPSwqgxAQRA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-n969rTPSwqgxAQRA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-n969rTPSwqgxAQRA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-n969rTPSwqgxAQRA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-n969rTPSwqgxAQRA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-n969rTPSwqgxAQRA .marker.cross{stroke:#333333;}#mermaid-svg-n969rTPSwqgxAQRA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-n969rTPSwqgxAQRA p{margin:0;}#mermaid-svg-n969rTPSwqgxAQRA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-n969rTPSwqgxAQRA .cluster-label text{fill:#333;}#mermaid-svg-n969rTPSwqgxAQRA .cluster-label span{color:#333;}#mermaid-svg-n969rTPSwqgxAQRA .cluster-label span p{background-color:transparent;}#mermaid-svg-n969rTPSwqgxAQRA .label text,#mermaid-svg-n969rTPSwqgxAQRA span{fill:#333;color:#333;}#mermaid-svg-n969rTPSwqgxAQRA .node rect,#mermaid-svg-n969rTPSwqgxAQRA .node circle,#mermaid-svg-n969rTPSwqgxAQRA .node ellipse,#mermaid-svg-n969rTPSwqgxAQRA .node polygon,#mermaid-svg-n969rTPSwqgxAQRA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-n969rTPSwqgxAQRA .rough-node .label text,#mermaid-svg-n969rTPSwqgxAQRA .node .label text,#mermaid-svg-n969rTPSwqgxAQRA .image-shape .label,#mermaid-svg-n969rTPSwqgxAQRA .icon-shape .label{text-anchor:middle;}#mermaid-svg-n969rTPSwqgxAQRA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-n969rTPSwqgxAQRA .rough-node .label,#mermaid-svg-n969rTPSwqgxAQRA .node .label,#mermaid-svg-n969rTPSwqgxAQRA .image-shape .label,#mermaid-svg-n969rTPSwqgxAQRA .icon-shape .label{text-align:center;}#mermaid-svg-n969rTPSwqgxAQRA .node.clickable{cursor:pointer;}#mermaid-svg-n969rTPSwqgxAQRA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-n969rTPSwqgxAQRA .arrowheadPath{fill:#333333;}#mermaid-svg-n969rTPSwqgxAQRA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-n969rTPSwqgxAQRA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-n969rTPSwqgxAQRA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-n969rTPSwqgxAQRA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-n969rTPSwqgxAQRA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-n969rTPSwqgxAQRA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-n969rTPSwqgxAQRA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-n969rTPSwqgxAQRA .cluster text{fill:#333;}#mermaid-svg-n969rTPSwqgxAQRA .cluster span{color:#333;}#mermaid-svg-n969rTPSwqgxAQRA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-n969rTPSwqgxAQRA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-n969rTPSwqgxAQRA rect.text{fill:none;stroke-width:0;}#mermaid-svg-n969rTPSwqgxAQRA .icon-shape,#mermaid-svg-n969rTPSwqgxAQRA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-n969rTPSwqgxAQRA .icon-shape p,#mermaid-svg-n969rTPSwqgxAQRA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-n969rTPSwqgxAQRA .icon-shape .label rect,#mermaid-svg-n969rTPSwqgxAQRA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-n969rTPSwqgxAQRA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-n969rTPSwqgxAQRA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-n969rTPSwqgxAQRA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 设备接入
Host检测到连接
Host对端口复位
Device进入Default状态
地址为0
Host通过EP0读取
Device Descriptor
Host发送SET_ADDRESS
Device进入Addressed状态
Host读取完整描述符
Host根据Class/Subclass/Protocol
选择驱动
Host发送SET_CONFIGURATION
Device进入Configured状态
Class Driver开始发送
UVC/UAC专用请求
5.3 描述符读取顺序
├─ Device Descriptor
├─ Configuration Descriptor
├─ Interface Descriptor
├─ Endpoint Descriptor
└─ String Descriptor
5.4 关键理解
一个关键点是:Host不是看到设备外观后认出摄像头,而是根据描述符识别它。
如果描述符声明:
- Video Class
- VideoControl Interface
- VideoStreaming Interface
- MJPEG Format
- 1280×720 @ 30fps
- Isochronous IN Endpoint
Windows才会把它当作UVC摄像头,并加载系统内置的类驱动。
所谓"免驱",不是设备不需要驱动,而是设备遵守标准Class规范,Host使用系统已有的Class Driver。
6. 📦 Packet、Transaction、Transfer
这三个概念不能混用。
6.1 Packet(包)
USB总线上最基础的数据单元,例如:
- Token Packet
- Data Packet
- Handshake Packet
6.2 Transaction(事务)
由若干Packet组成的一次总线事务。
例如普通IN事务:
IN Token → DATA Packet → ACK
6.3 Transfer(传输)
面向软件的一次完整数据传输,可以由多个Transaction组成。
6.4 四种USB传输类型
| 类型 | 可靠性 | 带宽 | 特点 | 常见用途 |
|---|---|---|---|---|
| Control | 有校验和重试保证 | 基本访问 | 用于枚举和控制 | EP0、枚举、控制 |
| Bulk | 有校验和重试 | 使用剩余带宽 | 可靠的大数据传输 | U盘、打印机 |
| Interrupt | 有校验和重试 | 周期性调度 | 保证最大延迟 | 键盘、鼠标 |
| Isochronous | 通常不重传 | 保留周期带宽 | 保证带宽,不保证可靠 | 摄像头、麦克风 |
等时传输不重传的原因:实时音视频更在意"及时到达",过期的视频帧或音频样本即使重传,通常也没有播放价值。
7. ⚡ USB速度和时间单位
7.1 USB 2.0常见速度
| 速度等级 | 速率 | 典型应用 |
|---|---|---|
| Low Speed | 1.5 Mbps | 键盘、鼠标 |
| Full Speed | 12 Mbps | 音频设备、老式摄像头 |
| High Speed | 480 Mbps | 高速存储、高清摄像头 |
7.2 调度单位
| 速度等级 | 调度单位 | 时长 |
|---|---|---|
| Full Speed | 1帧 | 1 ms |
| High Speed | 1个Microframe | 125 μs |
注意:每秒有8000个High-Speed Microframe。
7.3 带宽注意事项
480 Mbps只是线路速率,不等于应用可以获得60 MB/s。协议开销、总线调度和端点类型都会减少实际可用带宽。
8. 📝 今日练习
练习1:解释方向
请判断下面每条数据流是IN还是OUT:
- RK3568摄像头向PC发送一帧图像。
- PC向RK3568发送"调整亮度"请求的数据阶段。
- PC向RK3568模拟的USB音箱发送PCM音频。
- RK3568模拟麦克风向PC发送PCM音频。
练习2:解析Endpoint Descriptor
给定原始字节:
07 05 81 01 00 04 01
字段布局:
bLength:1字节bDescriptorType:1字节bEndpointAddress:1字节bmAttributes:1字节wMaxPacketSize:2字节,小端序bInterval:1字节
请回答:
- 端点编号是多少?
- 是IN还是OUT?
bmAttributes & 0x03等于1,是什么传输类型?wMaxPacketSize是多少?bInterval是多少?
练习3:理解枚举
用自己的话回答:
- 为什么USB设备必须有EP0?
- 为什么"免驱设备"仍然需要Host驱动?
- Host在什么时候知道设备是摄像头?
SET_ADDRESS和SET_CONFIGURATION分别改变什么?- 摄像头有新画面时,为什么不能直接向USB总线发送?
9. 📚 总结
本文系统性地讲解了USB通信的核心原理:
- Host主导原则是USB架构的基石
- 设备架构分为Host侧和Device侧
- Device/Interface/Endpoint构成USB设备的层次结构
- EP0作为控制端点,负责设备枚举和配置
- 枚举过程是设备被Host识别和配置的关键流程
- Packet/Transaction/Transfer是USB数据传输的三个层次
- 传输类型决定了数据的可靠性和实时性
理解这些概念对于开发USB设备、调试USB问题以及深入理解现代计算机外设系统都至关重要。
💡 提示:建议结合实际的USB协议分析工具(如Wireshark的USB捕获功能)来加深理解,观察真实的枚举过程和数据传输。