一、USB协议基础回顾:USB 2.0与USB 3.0差异、USB设备架构、端点与管道概念

各位同学,咱们开始上课。今天讲的是USB协议基础,这部分内容虽然基础,但非常重要。我做了这么多年嵌入式开发,发现很多USB驱动的问题,归根结底都是对协议理解不透彻。所以,咱们先把地基打牢。

1.1 USB 2.0 与 USB 3.0 的核心差异

先说说USB 2.0和USB 3.0的区别。很多人以为只是速度翻倍,其实没那么简单。

速度上的差异

USB 2.0 有3种速率:低速1.5Mbps、全速12Mbps、高速480Mbps。USB 3.0 则直接干到5Gbps,也就是SuperSpeed。嗯,这里要注意,USB 3.0的5Gbps是单向的,双向同时传输时理论带宽能到10Gbps。我在项目中遇到过,有人把USB 3.0的5Gbps当成总带宽,结果设计时算错了吞吐量,后来不得不改板子。

物理层的变化

USB 2.0 用的是4根线:VBUS、GND、D+、D-。USB 3.0 在保留这4根线的基础上,额外增加了5根线:SSTX+、SSTX-、SSRX+、SSRX- 以及 GND_DRAIN。说白了,USB 3.0 内部其实是两套独立的收发器——一套兼容USB 2.0,另一套跑SuperSpeed。

传输方式的革命

USB 2.0 是半双工,主机轮询设备。USB 3.0 改成了全双工,而且引入了异步通知机制。你想想看,USB 2.0 时代,设备想发数据得等主机来问;USB 3.0 设备可以直接告诉主机「我有数据了」。这个变化对实时性要求高的场景特别友好。

特性 USB 2.0 USB 3.0
信号线数量 4根(VBUS, GND, D+, D-) 9根(增加SSTX±, SSRX±, GND_DRAIN)
传输模式 半双工,主机轮询 全双工,设备可主动通知
编码方式 NRZI + 位填充 8b/10b 编码
电源管理 全局挂起/恢复 支持链路级电源管理(LPM)

避坑指南: 我曾经在USB 3.0的layout上吃过亏。SSTX和SSRX是差分对,必须等长、包地、远离时钟线。有次我偷懒没严格等长,结果5Gbps速率下眼图完全闭合,降速到2.5Gbps才能用。所以,做USB 3.0硬件设计时,差分对的阻抗控制和等长设计一定要严格。

1.2 USB设备架构:从物理层到功能层

USB设备的架构,我习惯把它分成三层来看:物理层、协议层、功能层。这样分层理解起来特别清晰。

物理层

就是咱们刚才说的那些电气特性。包括连接器、线缆、信号电平、终端电阻等。USB 2.0 高速模式下,D+和D-上需要接45Ω的下拉电阻到地。USB 3.0 的差分对则需要交流耦合电容,一般是0.1μF。

协议层

这一层负责数据的打包、传输、错误检测。USB协议定义了4种传输类型:控制传输、批量传输、中断传输、等时传输。每种传输类型都有固定的包格式。比如控制传输,总是以SETUP包开始,然后是DATA包,最后是STATUS包。

功能层

这一层就是设备具体干什么活了。比如一个USB键盘,功能层就是报告按键信息;一个USB摄像头,功能层就是传输视频流。功能层通过「接口」和「端点」与协议层交互。

个人经验: 在Zephyr中开发USB设备时,我们主要关注功能层和协议层的接口。Zephyr的USB设备栈已经帮我们封装好了协议层的细节,我们只需要配置好端点描述符和接口描述符,然后实现对应的回调函数即可。

1.3 端点与管道:USB通信的最小单元

端点(Endpoint)是USB设备中实际进行数据收发的单元。每个端点都有一个唯一的地址,由端点号和方向组成。比如端点1 IN,就是设备向主机发送数据的端点1。

端点的分类

  • 控制端点(Endpoint 0): 每个USB设备都必须有。用于设备枚举、获取描述符、设置配置等。双向的。
  • 批量端点: 用于大数据量传输,保证数据正确到达,但不保证实时性。比如U盘读写。
  • 中断端点: 用于小数据量、周期性传输,保证延迟。比如鼠标、键盘。
  • 等时端点: 用于实时数据流,不保证数据正确性,但保证带宽和延迟。比如音频、视频。

管道(Pipe)的概念

管道是主机视角的概念。主机通过管道与设备的端点通信。一个管道对应一个端点。说白了,管道就是主机和设备端点之间的逻辑连接。主机通过管道发送或接收数据,管道定义了传输类型、方向、最大包大小等属性。

举个例子:你插上一个USB鼠标。主机会通过控制管道(对应端点0)读取鼠标的设备描述符。然后配置鼠标,建立中断管道(对应鼠标的中断IN端点)。之后鼠标每次移动,数据就通过这个中断管道传给主机。

注意: 在Zephyr中配置端点时,最大包大小必须与硬件支持一致。比如全速模式下,批量端点的最大包大小是64字节;高速模式下是512字节。我曾经在配置一个高速批量端点时,不小心把最大包大小设成了1024,结果枚举失败,折腾了半天才发现是这个问题。

1.4 描述符体系:设备告诉主机「我是谁」

USB设备通过描述符向主机报告自己的身份和能力。描述符是一系列结构化的数据,主机通过标准请求来获取它们。

主要的描述符类型

  • 设备描述符: 描述设备的通用信息,如USB版本、厂商ID、产品ID、设备类等。
  • 配置描述符: 描述设备的配置信息,包括供电方式、最大电流、接口数量等。
  • 接口描述符: 描述一个功能接口,包括接口类、子类、协议、端点数量等。
  • 端点描述符: 描述一个端点的属性,包括端点地址、方向、传输类型、最大包大小等。
  • 字符串描述符: 提供可读的文本信息,如厂商名、产品名、序列号等。

在Zephyr中,这些描述符通常通过宏定义或结构体数组来配置。比如定义一个接口描述符:

/* Zephyr USB设备描述符示例 */
static struct usb_cfg_data my_cfg = {
    .usb_device_description = NULL,
    .interface_config = my_interface,
    .interface_descriptor = &my_interface_desc,
    .cb_usb_status = my_status_callback,
    .interface = {
        .class_handler = my_class_handler,
        .custom_handler = NULL,
        .vendor_handler = NULL,
    },
};

嗯,这里要注意,描述符的顺序和内容必须严格符合USB规范。主机在枚举时会按顺序读取这些描述符,如果顺序错了或者内容不对,枚举就会失败。

避坑指南: 我曾经在做一个复合设备时,接口描述符中的端点数量写错了。接口描述符里声明有3个端点,但实际只配置了2个。结果主机枚举时发现端点描述符数量对不上,直接报错。所以,描述符里的数字一定要和实际配置保持一致。

1.5 小结

今天的内容就这些。咱们回顾一下:USB 2.0和USB 3.0在速度、物理层、传输方式上都有本质区别。USB设备架构分三层,理解分层对开发很有帮助。端点和管道是通信的最小单元,每个端点都有明确的用途。描述符是设备与主机沟通的「身份证」,必须准确配置。

下一章,咱们会深入Zephyr的USB设备栈,看看这些概念在代码里是怎么落地的。到时候我会带着大家一步步配置一个实际的USB设备。今天就到这里,有问题随时问我。