本文翻译自 https://embeddedinn.com/articles/tutorial/usb-2-0/#history-of-revisions
USB(Universal Serial Bus,通用串行总线)是一种以主机为中心的 4 线总线协议,旨在提供标准、低成本的接口,具有自识别、动态连接的外围设备,可自动将功能映射到驱动程序和配置,并具有低协议开销、保证带宽和低延迟,适用于电话、音频、视频等要求苛刻的应用。
USB规范由包括惠普、英特尔、LSI、微软、瑞萨和意法爱立信在内的多家主要厂商联合制定。该规范及其认证由USB开发者论坛(USBIF)负责维护。
版本历史

版本 1.0(1996 年 1 月 15 日)
- 低速传输速率为 1.5 Mbps
- 全速传输速率为 12 Mbits/s。
修订版 1.1(1998 年 9 月 23 日)
- 改进了规格,是第一个被广泛使用的USB版本。
修订版 2.0(2000 年 4 月 27 日)
- 高速传输速率为 480 Mbits/s。
修订版 3.0(2008 年 11 月 17 日)
- 超高速 USB(原始数据吞吐量高达 5.0 Gbit/s)
本文将重点介绍高速 USB 协议修订版,俗称 USB 2.0。
基础知识
总线拓扑结构
基本 USB 遵循如下图所示的层级星型拓扑结构。

主机位于层级结构的顶端,所有“功能”最终都连接到主机。“hub”用于扩展“root hub”(主机的最终端点)的连接性。hub是每一层级的中心,每一层级都以hub为中心构建星型拓扑结构。

USB协议中每个操作都有一个最大延时。为此,最大延时限制了层级数量为7层——包括顶层(根/主机)。包含Hub和其他功能的组合称为“复合设备”。由于不允许再增加层级,因此第7层不应包含Hub。
系统组件
USB系统由三个主要部分组成:USB设备、USB主机和USB接口。
USB 设备可以是提供额外 USB 连接点的Hub集线器,也可以是为系统提供额外功能的设备。
任何USB系统中都只有一个主机。与主机系统连接的USB接口称为主机控制器。主机控制器可以用硬件、固件或软件实现。主机系统内部集成了一个根Hub,用于提供一个或多个连接点。
USB接口是总线的物理接口,由总线的电气和机械规范进行描述。
USB互连和信号传输
互连
USB 通过铜线使用 4 线物理接口实现规定的协议速度。该总线还为设备供电。

电缆的最大长度取决于信号延迟和信号完整性。标准电缆符合如图所示的颜色编码。

信号传导
USB协议采用电缆D+和D-线上的差分信号传输。数据使用NRZI协议编码,并以J(电流流入D+线)和K(电流流入D-线)表示。
在NRZI编码中,0流通过每比特时间翻转一次JK键来表示,而1流则保持D+和D-线的先前状态。单端零(SE0)信号通过同时驱动D+和D-线来发出。

主机和设备之间的同步由两端的数字锁相环 (DPLL) 维持。然而,长时间的 1 序列会导致 DPLL 失去同步,因此,每连续 6 个 1 之后会“填充”一个 0。这称为比特填充。
“比特时间”取决于函数运行的速度模式。例如,低速运行的设备的数据速率为 1.5Mbps,因此一个比特时间就是 (1/1.5 微秒)。
速度模式是通过 D+ 和 D- 线中的终端电阻来区分的。
对于低速器件,D- 线末端会连接一个 1.5K 欧姆的上拉电阻;而对于全速/高速器件,D+ 线也会连接类似的上拉电阻。因此,高速器件最初会像全速器件一样工作。该器件会生成一个“high speed chirp”,即一组 15 个 JK 对,用于向主机表明其为高速器件(480Mb/s)。
一旦检测到速度模式,主机便会启动“枚举”过程,这实际上就是配置该功能以供使用。但在深入了解枚举细节之前,我们需要了解一些基本术语。这将在下一节中进行解释。
协议基础知识
基本术语
- 信号模式(Signalling mode),USB2.0 支持下面三种模式
- 低速:1.5Mb/s
- 全速:12Mb/s
- 高速:480Mb/s
- 主机(Host)
是指连接 USB 设备的计算机系统(也可以是嵌入式系统)。主机发起所有通信,并且是总线的主控端。在总线拓扑结构中,只能有一个 USB 主机。 - 设备(Device)
是指连接到主机的 USB 功能设备。一个设备可能包含多种功能,这些功能可能同时可用,也可能不可用。这将在后续章节中详细讨论。 - 设备地址(Device Address)
主机在设备连接时为其分配的唯一 7 位地址。主机使用此地址将所有通信定向到目标设备。设备首次连接到主机时,其地址为 0(默认地址)。 - 端点(End Point)
是设备中主机可以与之通信的最细粒度的分类。端点通常实现为一个缓冲区,主机向该缓冲区发送数据(无论是否与特定协议相关)。
每个 USB 设备都应该有一个默认端点 0 (EP0),主机使用该端点来识别设备的功能并向其发送配置信息。
最多可以有 31 个端点,包括 EP0。除 EP0 外,所有端点均为单向的,每个端点都由一个唯一的 4 位端点编号及其方向标识。端点方向由 8 位端点地址的低半字节表示。因此,EP1 IN 的地址为 0x10,EP1 Out 的地址为 0x18。
- 管道(Pipe)
主机上某一点与设备上某一点之间的逻辑连接。一个设备最多可以有 31 个管道,包括终止于 EP0 的默认管道。
USB 管道分为两种类型:单向流管道(可以传输任何格式的数据)和流管道(可以传输没有指定格式的数据)。
EP0 默认始终是消息管道,而到其他端点的管道都是流管道。

- 接口和配置(Interface and configuration)
接口和配置是功能内部各项能力的两级层次结构。一个功能(设备)可以有多个配置,每个配置又可以关联多个接口。每个接口可以关联多个端点。在任何给定时间点,主机只能加载一个配置。接口对端点进行逻辑分组,从而简化了设备驱动程序的加载。
主机通过设备描述符、接口描述符和端点描述符读取设备的配置和接口功能。这些信息是在主机向设备发出某些标准命令时传递给主机的。
设备运行期间,主机可以加载或卸载特定的接口/配置。

- 设备类别(Device Class)
USB 设备被分为不同的类别,以便于驱动程序的开发和分类。但是,并没有规定设备必须属于某个类别规范。经过认证的设备仍然可以使用完全专有的驱动程序。然而,该设备必须符合 SUB 2.0 规范第 9 章中规定的基本协议行为,并满足电气和机械兼容性要求。 - 帧(Frame)
无论目标设备如何,USB总线上的所有通信都以帧为单位进行分段传输。对于低速/全速设备,帧宽为1毫秒;而对于高速设备,帧宽为125微秒。每个帧都由一个11位帧号标识,该帧号以称为帧起始信息(SOF)的特殊数据包的形式发送。帧内可以包含多个数据包,这些数据包将被发送到不同的设备。数据包的实际传输过程将在以下章节中进行详细说明。


- 挂起(Stall)
当终端设备未准备好发送或接收数据时,它会发送 NAK响应
。而如果终端设备收到错误的命令或由于某些内部错误而无法响应,则设备会发送 STALL 握手响应。收到 STALL 握手响应后,主机必须采取纠正措施来恢复终端设备的功能。
- 超时(Timeout)
连接到拓扑结构最后一层(第 7 层)的高速设备最多需要 736 Bit时间才能响应任何主机请求。如果在 816 bit时间内未检测到信号,主机将超时。超时后,主机将发出重置指令并尝试重新枚举该设备。
USB基本通信流程

USB总线上的所有数据通信都以数据包的形式进行。通信由主机发起,主机向设备和端点发送一个令牌数据包,后续数据包将指向该令牌数据包。令牌数据包包含一个8位数据包标识符(PID),用于标识即将发生的事务类型。

PID 实际上为 4 位宽。低半字节是高半字节的补码,用于进行有效性检查。
令牌包之后会有一个方向与令牌包中指定的方向相同的数据包。对于不同类型的令牌,这是一个可选阶段,相关说明请参见本文档的相应部分。
交易完成后,会向其发送握手包。
主机每毫秒(LS 或 FS 设备)或每 125 微秒( HS 设备)广播帧开始 (SoF) 数据包。
因此,每笔传输(transfer )都包含多笔事务(transactions)。每笔事务分为三个阶段。
- 令牌阶段(token stage:):主机向目标设备发出令牌数据包(token packet),以便进行后续通信。该令牌数据包还指示传输方向。
- 数据阶段(Data Stage):实际数据传输通过数据包(data packet)进行。
- 状态阶段(status stage):数据传输通过握手得到确认。



枚举
识别 USB 设备的功能并将其映射到主机系统中相应的驱动程序的过程称为“枚举”。设备枚举完成后,主机就能了解其速度模式、可用端点数量及其属性和类别等信息。具体步骤如下。
这里我们假设只有一个设备直接插入主机的根集线器。
- 设备插入集线器端口后,总线将复位,SE0(D+ 和 D- 低电平),然后总线上电。
- 接下来,通过数据线上拉电阻检测设备的速度模式。
- 低速器件的 D- 线会连接一个 1.5 K欧姆的电阻,而全速/高速器件的 D+ 线则会连接一个 1.5 K欧姆的电阻。
- 高速设备启动时以全速运行。主机重置总线后,设备会发送一个低频啁啾信号(16个JK对),向主机表明设备已具备高速运行能力。主机会以反向啁啾信号确认此信号。
- 一旦检测到速度模式,主机就会向 EP0 发出名为“GetDescriptor (device)”的“标准请求”,以读取连接设备的功能。

- 描述符的解读是多层次的,因为描述符根据其所传达的信息类型而按层次进行区分,正如上一节所述。
- 首次读取设备描述符后,如果主机支持该设备(由各种因素决定),则总线再次重置,并为该设备提供一个唯一的 7 位地址。
- 将设备置于目标状态后,即可通过其他描述符读取设备的更多详细信息,如下图所示。

- 当读取完设备的所有可用配置后,可以使用设置配置命令。该命令使用索引来指定要选择的设备配置。
- 配置完成后,相应的类驱动程序(稍后会详细介绍类)将被加载,并生成类规范。
设备枚举完毕且端点配置完成后,逻辑通信安排如下图所示。

系统软件主要是根集线器驱动程序,它负责跟踪所有连接到它的设备。任何与配置相关的通信都通过默认管道 EP0 进行。
特定于端点的驱动程序可以通过相应的管道进行通信。




传输模式
如前所述,一个USB设备可以包含多个端点。每个端点可以是输入端点(IN)或输出端点(OUT),具体取决于数据传输方向。另一种分类方法是基于端点上发生的数据传输类型。
USB总线上发生的四种传输类型是:
- 控制权传输(Control Transfer)
- 批量传输(Bulk Transfer)
- 同步传输(Isochronous transfer)
- 中断传输(Interrupt Transfer)
在枚举过程中,处于工作状态的端点(比如,一个USB设备可能总共有16个端点,但是实际只使用了6个端点)将会分配为上面传输类型的一种。比如,端点1 用于 批量传输,端点2使用中断传输。之后的过程中并不会改变传输类型。
控制传输
控制传输用于主机和 EP0 功能(设备)之间的通信。例如,第 9 章中的命令事务,如 SetAddress、Getdescriptor、SetDescriptor 等。
控制传输分三个阶段进行,即
- 建立阶段 setup stage
- 数据阶段 data stage
- 握手阶段handshake stage


建立阶段从主机发送的建立令牌(setup token )开始,然后是包含实际命令信息的数据包,随后是确认阶段。
数据阶段是可选的。接下来会有一个可选的数据阶段,具体取决于所发出的命令类型。例如,`set configuration` 命令将要设置的配置索引嵌入到命令中,因此不需要单独的数据阶段;而 `Getdescriptor` 命令则需要多个数据阶段来传输所有必需的信息。
即使多个设备连接到主机,控制传输始终保证占用总线带宽的 10%。也就是说,在 1 毫秒内(对于高速设备,则为 125 微秒微帧的 20%),始终有 10% 的时间用于控制传输。这是因为如果其他端点出现数据错误,控制传输会用于对这些功能执行纠正措施。
同步传输
这种数据传输模式可以保证访问 USB 总线带宽。
总线带宽
USB 通信以帧的形式进行。每个帧可以包含发往多个设备的传输。带宽是指在 1 毫秒/125 微秒的帧中分配给特定设备的时间。考虑到传输速度模式(1.5/12/480 Mb/s),带宽可以转化为数据量。

中断传输
中断传输提供了一种机制,可以确保设备和主机之间在周期性的时间间隔内进行通信。
当枚举中断端点时,可以指定轮询间隔,范围从每帧一次到每 255 帧一次。
如果在轮询时设备没有数据要传输,则下一次重试将在下一个服务帧中进行。
每个事务的最大数据包大小为 8 字节(LS)、64 字节(FS)或 1024 字节(HS)。

下图显示了光电鼠标中断端点的端点描述符以及端点的周期性轮询。

关于“保持连接”的说明:
所有连接低速设备的集线器端口都必须生成一个低速保活选通信号,该信号在帧的开头生成,包含一个有效的低速 EOP。在接收到 SOF 的每个帧中,该选通信号必须至少生成一次。此选通信号用于防止总线上没有其他低速流量时,低速设备挂起。
批量传输
当需要在终端和主机之间传输大量非时效性数据时,会使用批量传输。使用批量数据传输机制的设备包括闪存盘(大容量存储设备)、打印机、扫描仪等。对于这些设备而言,数据内容的可靠性比数据传输速率更为重要。因此,批量传输的可用带宽会因连接到主机的其他设备的配置而逐帧变化。

USB可靠性
错误 (Errors)
由于各种原因,USB总线通信可能会出现错误。本节简要概述常见错误以及USB框架实现的相应恢复机制。
错误类型(Error Types)
- PID 错误
8 位 PID 实际上由一个 4 位 PID 和一个 4 位 PID 补码构成,如下图所示。如果出现不匹配,设备将不会响应,并且主机端会发生超时(参考:USB 2.0 规范的 8.3.1 节)。
对所有设备进行 PID 完全解码是强制性的。

- PID 之后的CRC 错误
字段用于计算循环冗余码,该码用于验证接收数据的完整性。任何 CRC 校验错误的数据都将返回 NAK(无效确认)。
令牌包和SOF包的CRC校验码为5位,数据包的CRC校验码为16位。握手包没有CRC校验码。

- 无效命令:
当端点收到“完整但逻辑错误”的数据包(例如错误命令)时,或者设备内部出现故障导致端点无法使用时,端点将停止工作,并且任何发送到该端点的请求都会返回 STALL 握手作为响应。
错误处理(Error Handling)
- NAK
- 当主机收到设备返回的NAK时,前三次重试将由硬件触发。之后,将由软件介入。
- STALL
- 当某个端点发生阻塞( STALL)时,主机必须发送一个 ClearFeature 命令,其中包含指向要清除接口的“功能选择器值”。该命令会发送到控制端点 (EP0)。因此,即使某个端点停止工作,ClearFeature 也会应用于该接口下的所有端点。
- Timeout超时
- 当设备在指定的时间间隔内没有向主机发送任何信号时,主机将通过施加复位信号(在数据线上驱动 SE0 超过 2.5us)来复位设备。

用于同步的标志交替
USB 采用一种称为标志交替的简单机制,以确保主机和设备之间传输的数据同步。
在进行数据传输时,主机使用两种类型的数据令牌,分别是 Data0 和 Data1。初始数据传输使用 Data0 令牌。收到设备的 ACK 确认后,下一条数据将使用 Data1 令牌发送。设备还维护一个序列位,该序列位在成功确认传输后会被翻转。

但是,如果出现问题导致数据不被接受(发送 NAK),则主机和设备会保持序列位状态,并使用前一个数据令牌类型立即进行下一次传输。

假设总线上丢失了 ACK,主机未收到该 ACK,则主机使用相同的令牌(DATA0)重新发送数据。然而,在设备端,由于之前的事务,序列位已被翻转。因此,设备会忽略接收到的数据并发送 ACK。


USB类
枚举完成后,实际的设备通信由主机内部的驱动程序发起,该驱动程序了解设备的行为。USB 协议非常灵活,可以同时使用现有驱动程序和新驱动程序,因此 USB 既可以作为接口,也可以作为通信媒介。这是通过将 USB 设备划分为不同的类,并使用过滤器类型的驱动程序层次结构来实现的。
我们将通过考虑众所周知的存储设备(即 USB 闪存驱动器)的例子来尝试理解 USB 类的概念。
USB闪存盘主要由大容量存储器和连接到USB控制器的存储器控制器组成。

典型的U盘会有一个遵循SCSI协议的内存控制器接口。SCSI协议与USB类似,用于在逻辑块地址级别对内存进行完全控制。因此,枚举完成后,设备(U盘)会作为从设备响应主机发出的SCSI命令。
SCSI 的每笔交易都有 3 个阶段,即
- 命令阶段(Command stage)
- 数据阶段(Data Stage)
- 状态阶段(Status Stage)
文件表和所有逻辑内存处理都由驻留在主机上的 SCSI 驱动程序完成。
SCSI 驱动程序是一个通用驱动程序,与 USB 协议没有任何关系。USB 作为一种总线协议,充当主机和设备之间传输 SCSI 协议数据单元的媒介。这通过在接口级别将设备识别为大容量存储类设备 (MSC 设备) 来实现。一旦设备被识别为具有 MSC 接口,主机就会加载 MSC 驱动程序栈。任何与被指定为 MSC 接口的接口之间的通信都将由 MSC 驱动程序发起,它会向根集线器驱动程序发送请求。根集线器驱动程序 (RHD) 会收集来自不同驱动程序的多个请求并形成帧。
MSC接口驱动程序的主要部分是对实际SCSI驱动程序的封装。SCSI驱动程序会向MSC驱动程序发送请求,这些请求随后由根集线器驱动程序(RHD)通过USB传输。然而,SCSI驱动程序无需了解USB协议或结构。它可以调用MSC驱动程序API,进而向RHD发送请求。另一方面,RHD也无需了解任何SCSI或MSC特有的实现细节。

下图清晰地展示了SCSI命令是如何嵌入到USB传输中的。



因此,基于类的架构使得USB设备能够通过现有的驱动程序进行控制,而该驱动程序的实现与USB协议无关(重用现有驱动程序)。
主机工作流程
到目前为止,我们一直专注于单个设备连接到根中心的情况。然而,该协议的设计能够处理以分层星型拓扑结构连接的多达 126 个设备。在本节中,我们将讨论主机如何调度事务到同时连接到它的多个不同速度模式的设备。

从拓扑图中可以看出,所有设备都通过称为集线器的端口扩展单元连接到根集线器上的一个节点。集线器有一个连接到主机的上游端口和一个下游端口,下游端口为设备提供连接点。
因此,集线器在USB总线拓扑结构中扮演着重要角色,它承担了主机的部分“职责”,例如频繁发送“保持连接”信号。本教程不讨论集线器协议。
一旦多个设备连接到拓扑结构,主机就会给每个设备分配一个唯一的 7 位地址。为此,初始通信将使用设备地址 0(设备在获得实际设备地址之前的默认地址)。主机和集线器之间会协作防止使用地址 0 进行设备寻址。
一旦确定了设备,主机就会根据前面章节中描述的描述符读取设备的“功能”和“要求”,从而对设备进行实际枚举。
如果设备请求的资源超过主机可用资源,则该设备不会被枚举。为了详细说明此用例,假设有三个同步端点(函数)已被枚举,并占用了所有可用带宽。当新连接的设备请求一些不可用的带宽(因为该带宽已被已枚举的设备占用)时,主机将不会枚举该设备。
设备枚举完成后,主机将根据端点类型和从客户端软件收到的请求创建其内部数据结构。以下将对此进行简要说明。
设备枚举完成后,主机上会为该设备生成一个端点数据描述符。这可以看作是链表的头节点,链表中包含要传输到特定设备端点的数据。附加到该端点的是传输描述符,其中包含要发送到端点的数据。客户端软件(设备驱动程序)请求中包含的数据将作为传输描述符附加。
向多个端点发送传输请求的调度是基于帧号进行的。例如,一个中断端点可以请求每 10 帧轮询一次,那么主机就会调度每 10 帧发送一次相应的传输描述符。如图所示,传输描述符可以包含一个 IN 令牌。


本文数据取自Ellisys USB 分析工具和 USB 2.0 规范本身提供的插图。














