USB概述及协议基础

时间:2024-04-15 19:30:00

USB概述及协议基础

USB的拓扑结构

USB是一种主从结构的系统。主机叫做Host,从机叫做Device(也叫做设备)。

 

通常所说的主机具有一个或者多个USB主控制器(host controller)和根集线器(root hub)。主控制器主要负责数据处理,而根集线器则提供一个连接主控制器与设备之间的接口和通路。另外,还有一类特殊的USB设备-------USB集线器(USB hub),它可以对原有的USB口在数量上进行扩展,就可以获得更多的USB口。

 

注意:集线器只能扩展出更多接口的USB口,而不能扩展出更多的带宽。带宽是共享一个USB主控制器的。

 

通常,PC上有多个USB主控制器和多个USB口。每个主控制器下有一个根集线器,根集线器通常具有一个或者几个USB口。当你有多个不同的USB设备都需要较大的数据带宽时,可以考虑将它们分别接到不同的主控制器的根集线器上,以避免带宽不足。

 

理论上,一个USB主控制器最多可接127个设备,这是因为协议规定每个USB设备具有一个7 bit的地址(取值范围为0~127,而地址0是保留给未初始化的设备使用的)。

 

USB描述符及其之间的关系

USB只是一个总线,只提供了一个数据通路而已,USB总线驱动程序并不知道一个设备具体如何操作,有哪些行为。具体的一个设备实现什么功能,要由设备自己来决定,那么,USB主机是如何知道一个设备的功能以及行为呢?这就是要通过描述符来实现了。描述符中记录了设备的类型、厂商ID和产品ID(通常依靠它们来加载对应的驱动程序)、端点情况、版本号等众多信息。

 

USB只是一个总线,只提供了一个数据通路而已,USB总线驱动程序并不知道一个设备具体如何操作,有哪些行为。具体的一个设备实现什么功能,要由设备自己来决定,那么,USB主机是如何知道一个设备的功能以及行为呢?这就是要通过描述符来实现了。描述符中记录了设备的类型、厂商ID和产品ID(通常依靠它们来加载对应的驱动程序)、端点情况、版本号等众多信息。

 

一个USB设备只有一个设备描述符。设备描述符里决定了该设备有多少种配置,每种配置有一个配置描述符;而在每个配置描述符中又定义了该配置里有多少个接口,每个接口都有一个接口描述符;在接口描述符里又定义了该接口有多少个端点,每个端点都有一个端点描述符;端点描述符定义了端点的大小、类型等。如果有类特殊描述符,它跟在相应的接口描述符之后。

 

在主机获取描述符时,首先获取设备描述符,接着再获取配置描述符,然后根据配置描述符中的配置集合的总长度,一次将配置描述符、接口描述符、类接口描述符(如果有)、端点描述符一次读回。对于字符串描述符,是单独获取的。主机通过发送获取字符串描述符的请求以及描述符的索引号、语言ID来获取相对应的字符串描述符。

 

设备有一个设备地址,USB主机依靠这个设备地址来访问设备。而在设备内部还会分的更细,它会分出一些端点出来等。就是说,如果USB主机要和USB设备通信,光有设备地址是不够的,还需要一个端点地址。有了设备地址和端点地址,就能准确地对端点发送和读取数据了。而配置和接口,是为了更方便地管理端点而抽象出来的概念。一个设备可以有多个设备,但是同一时间只能有一个配置有效,每个配置下又可以有多个接口。当我们需要不同的功能时,只要选择不同的配置即可。同一个端点号不能出现在同一配置下的两个或多个不同的接口中。同一个端点号可用在不同的配置中。

 

USB设备的枚举过程

USB主机在检测到USB设备插入后,就要对设备进行枚举了。为什么要枚举呢?枚举就是从设备读取各种描述符信息,这样主机就可以根据这些信息来加载合适的驱动程序,从而知道设备是什么样的设备,如何进行通信等。调试USB设备,很重要的一点就是USB的枚举过程,只要枚举成功了,剩下的工作就不多了。

 

在说枚举之前,先介绍一下USB的一种传输模式-----控制传输。这种传输在USB中是非常重要的,它要保证数据的正确性,在设备的枚举过程中都是使用控制传输。控制传输分为三个过程:建立过程、可选的数据过程及状态过程。

 

建立(Setup)过程都是由USB主机发起的。它开始于一个SETUP令牌包,后面紧跟一个DATA0数据包,接着就是数据过程。如果是控制读传输,那么数据过程是输入数据;如果是控制写传输,那么数据过程是输出数据。如果在建立过程中,指定了数据长度为0,则没有数据过程。数据过程之后是状态过程。状态过程刚好与数据过程的数据传输方向相反:如果是控制读传输,则状态过程是一个输出数据包;如果是控制写传输,则状态过程是一个输入数据包。状态过程用来确认所有的数据是否都已经正确传输完成。

如下两图:

1

控制读

 

2

控制写

 

下面介绍枚举的详细过程:

①USB主机检测到USB设备插入后,就会先对设备复位。USB设备在总线复位后其地址为0,这样主机就可以通过地址0和那些刚刚插入的设备通信。USB主机往地址为0的设备的端点0发送获取设备描述符的标准请求(这是一个控制传输的建立过程)。设备收到该请求后,会按照主机请求的参数,在数据过程将设备描述符返回给主机。主机在成功获取到一个数据包的设备描述符并且确认没有错误后,就会返回一个0长度的确认数据包(状态过程)给设备,从而进入到接下来的设置地址阶段。这里需要注意的是,第一次主机只会读取一个数据包的设备描述符。标准的描述符有18字节,有些USB设备的端点0大小不足18字节(但至少具有8字节),在这种情况下,USB主机也是只发送一次数据输入请求,多余的数据将不会再次请求。因此,如果当设备端点0大小不足18字节时,就需要注意到这个问题。也就是说在第一次获取设备描述符时,只需要返回一次数据即可,不要再等主机继续获取剩余数据(如果还有),因为主机不会这么干的。当主机成功获取到设备描述符的前8字节之后(USB协议规定端点0最大包长至少要有8字节),它就知道端点0的最大包长度了,因为端点0最大包长度刚好在设备描述符的第八字节处。

 

②主机对设备又一次复位。这时就进入到了设置地址阶段。USB主机往地址为0的设备的端点0发出一个设置地址的请求(控制传输的请求过程),新的设备地址包含在建立过程的数据包中。具体的地址由USB主机负责管理,主机会分配一个唯一的地址给刚接入的设备。USB设备在收到这个建立过程之后,就直接进入到状态过程,因为这个控制传输没有数据过程。设备等待主机请求状态返回(一个输入令牌包),收到输入令牌包后,设备就返回0长度的状态数据包。如果主机确认该状态包已经正确收到,就会发送应答包ACK给设备,设备在收到这个ACK之后,就要启用新的设备地址了。这样设备就分配到了一个唯一的设备地址,以后主机就通过它来访问该设备。需要注意的是,像D12这样的USB接口芯片,会自动等待状态过程主机的ACK之后才启用新地址,所以要在返回0长度的状态包之前,将地址写到D12芯片的地址寄存器中,D12芯片等主机返回ACK后,才会使用新的地址。

 

③主机再次获取设备描述符。这次跟第一次有点不一样,首先是主机不再使用地址0来访问设备,而是使用新的设备地址;另外,这次需要获取全部的18字节的设备描述符。如果你的端点0最大包长小于18字节,那就会有多次请求数据输入(即发送多个IN令牌包)。

 

④主机获取配置描述符。配置描述符总共为9字节。主机在获取到配置描述符后,根据配置描述符中所描述的配置集合总长度,获取配置集合。获取配置描述符和获取配置描述符集合的请求是差不多的,只是指定的长度不一样。有些主机干脆不单独获取配置描述符,而是直接使用最大长度来获取配置描述符集合,因为设备实际返回的数据可以少于指定的字节数。配置集合包括配置描述符、接口描述符、类特殊描述符(如果有)、端点描述符等。接口描述符、类特殊描述符、端点描述符是不能单独获取的,必须跟随配置描述符以一个集合的方式返回一并返回。

 

如果有字符串描述符,还要获取字符串描述符。另外,像HID设备还有报告描述符等,它们是单独获取的。

 

USB的包结构及传输过程

USB的包结构及包的分类

USB是串行总线,所以数据是一位一位地在数据线上传递的。既然是一位一位地传递,就存在着一个数据位先后的问题。USB使用的是LSB在前的方式,即先出来的是最低位数据,接下来是次低位,最后是最高位(MSB)。一个包,又被分成了很多个域(field),而LSB、MSB就是以域为单位来划分的。

 

另外还有一个数据传输方向的问题,因为在USB系统中,主机处于主导地位,所以把从设备到主机的数据叫做输入,从主机到设备的数据叫做输出。

 

USB总线上传输数据总是以包为基本单位的。一个包被分成不同的域。根据不同类型的包,所包含的域是不一样的。但是不同的包有个共同的特点,就是都要以同步域开始,紧跟着一个包标识符PID(Packet Of Identifier),最终以包结束符EOP(End Of Packet)来结束这个包。

 

包标识符PID是用来标识一个包的类型的。它总共有8位,其中USB协议使用的只有4位(PID0~PID3),另外4位(PID4~PID7)是PID0~PID3的取反,用来校验PID。USB协议规定了4类包,分别是:令牌包(toker packet,PID1~0为01),数据包(data packet,PID1~PID0为11),握手包(handshake packet,PID1~PID0位10)和特殊包(special packet,PID1~PID0为00)。不同类的包又分成几种具体的包。

 

下表是USB2.0协议中规定的各种PID,其中有些是在USB1.1协议中没有的,用*号标出。

3

 

令牌包

令牌包用来启动一次USB传输。因为USB是主从结构的拓扑结构,所以所有的数据传输都是由主机发起的,设备只能被动地接听数据(唯一的例外是支持远程唤醒的设备能够主动改变总线的状态让集线器感知到设备的唤醒信号,但是这个过程并不传送数据,只是改变一下总线的状态)。这就需要主机发送一个令牌来通知哪个设备进行响应,如何响应。

 

令牌包有4种,分别为输出(OUT)、输入(IN)、建立(SETUP)和帧起始(SOF Start Of Frame)。

  • 输出令牌包用来通知设备将要输出一个数据包。

  • 输入令牌包用来通知设备返回一个数据包。

  • 建立令牌包只用在控制传输中,它跟输出令牌包的作用一样,也是通知设备将要输出一个数据包,两者的区别在于:SETUP令牌包后只使用DATA0数据包,且只能发到设备的控制端点,并且设备必须要接收,而OUT令牌包没有这些限制。

  • 帧起始包在每帧(或微帧)开始时发送,它以广播的形式发送,所有USB全速设备和高速设备都可以接收到SOF包。USB全速设备每毫秒产生一个帧,而高速设备每125us产生一个微帧。

USB主机会对当前帧号进行计数,在每次帧开始时通过SOF包发送帧号。SOF中的帧号是11位的。在4个令牌包中,只有SOF令牌包之后不跟随数据传输,其他的都有数据传输。下图是SOF令牌包的结构。每个令牌包,最后都有一个CRC5的校验,它只校验PID之后的数据,不包括PID本身,因为PID本身已经有4个取反的位进行校验了。

 

4

 

下图是OUT、IN、SETUP令牌包的结构。其中,地址域是要访问的设备的地址,端点域是要访问的端点号。

5

 

数据包

顾名思义,数据包就是用来传输数据的。在USB1.1协议中,只有两种数据包:DATA0包和DATA1包。在USB2.0中又增加了DATA2和MDATA包,主要用在高速分裂事务和高速高带宽同步传输中。

 

数据包都具有同样的结构:一个同步域,后面跟整数字节的数据,然后是CRC16校验,最后是包结束符EOP,如下图所示:

6

 

之所以有不同类型的数据包,是用在当握手包出错时纠错。下面以DATA0包和DATA1包的切换为例进行具体的解释。

主机和设备都会维护自己的一个数据包类型切换机制:当数据包成功发送或者接收时,数据包类型切换。当检测到对方所使用的数据包类型不对时,USB系统认为这发生了一个错误,并试图从错误中恢复。数据包类型不匹配主要发生在握手包被损坏的情形。当一端已经正确接收到数据并返回确认信号时,确认信号却在传输过程中被损坏。这时,另一端就无法知道刚刚发送的数据是否已经成功,这时它只好保持自己的数据包的类型不变。如果对方下一次使用的数据包类型跟自己的不一致,则说明它刚刚已经成功接收到数据包了(因为它已经做了数据包切换,只有正确接收才会如此);如果对方下一次使用的数据包类型跟自己的一致,则说明对方没有切换数据包类型,也就是说,刚刚的数据包没有发送成功,这是上一次的重试操作。

 

握手包

握手包用来表示一个传输是否被对方确认,握手包只有同步域、PID和EOP,是最简单的一种数据包,如下图所示:

7

 

握手包有ACK、NAK、STALL和NYET。

  • ACK表示正确接收数据,并且有足够空间来容纳数据。主机和设备都可以用ACK来确认,而NAK、STALL、NYET只有设备能够返回,主机不能使用这些握手包。

  • NAK表示没有数据需要返回,或者数据正确接收但是没有足够的空间来容纳它们。当主机收到NAK时,知道设备还未准备好,主机会在以后合适的时机进行重试传输。

  • STALL表示设备无法执行这个请求,或者端点已经被挂起了,它表示一种错误的状态,设备返回STALL后,需要主机进行干预才能解除这种STALL状态。

  • NYET只在USB2.0的高速设备输出事务中使用,它表示设备本次数据成功接收,但是没有足够的空间来接收下一次数据。主机在下一次输出数据时,将先使用PING令牌包来试探设备是否有空间接收数据,以避免不必要的带宽浪费。

需要注意的是,返回NAK并不表示数据出错,只是说明设备暂时没有数据传输或者暂时没有能力接收数据。当USB主机或者设备检测到数据出错时,将什么都不返回。这时等待接收握手包的一方就会收不到握手包从而等待超时。

 

特殊包

特殊包是一些在特殊场合使用的包。总共有4种:PRE、ERR、SPLIT和PING。其中PRE、SPLIT和PING是令牌包,ERR是握手包。

 

PRE是通知集线器打开其低速端口的一种前导包。PRE只使用在全速模式中。平时,为了防止全速信号使低速准备误动作,集线器是没有将全速信号传递给低速设备的。只有当收到PRE令牌包时,才打开其低速端口。PRE令牌包与握手包的结构一样,只有同步域、PID和EOP。当需要传送低速事务时,主机首先发送一个PRE令牌包(以全速模式发送)。对于全速设备,将会忽略这个令牌包。集线器在收到这个令牌包后,打开其连接了低速设备的端口。接着,主机就会以低速模式给低速设备发送令牌包、数据包等。

 

PING令牌包与OUT令牌包具有一样的结构,但是PING令牌包后并不发送数据,而是等待设备返回ACK或者NAK,以判断设备是否能够传送数据。只有在USB2.0高速环境中才会使用PING令牌包,它只被使用在批量传输和控制传输的输出事务中。直接使用OUT令牌包发送数据时,不管设备是否有空间接收数据,都会在OUT令牌包之后跟着发送一个数据包,如果设备没有空间接收数据,就返回一个NAK。这样的结果就是浪费了总线带宽,白白传送了数据。在高速设备中增加了这个PING机制,主机先用PING令牌包试试设备是否有空间接收数据,而不用事先把数据发送出去。

 

SPLIT令牌包是高速事务分裂令牌包,通知集线器将高速数据包转化为全速或者低速数据包发送给其下面的端口。

ERR握手包是在分裂事务中表示错误使用。

 

如何处理数据包

这么多类型的包以及传输过程,我们该怎么去处理呢?其实,如果使用现成的USB接口芯片,很多过程,USB接口芯片都已经处理好了,可以不用太关心这些细节,只要知道有这么一个过程就行了。

 

当USB接口芯片正确接收到数据时,如果有空间保存,则它将数据保存并返回ACK,同时,设置一个标志表示已经正确接收到数据;如果没有空间保存数据,则自动会返回NAK。

 

收到输入请求时,如果有数据需要发送,则发送数据,并等待接收ACK。只有当数据成功发送出去(即接收到应答信号ACK)之后,它才设置标志,表示数据已发送成功;如果无数据需要发送,则它自动返回ACK。

 

通常只需要根据芯片提供的一些标志,准备要发送的数据到端点,或者从端点读到接收到的数据即可。在USB接口芯片中,通过一些标志可以知道是哪个端点接收或者成功发送了数据。另外,由于控制传输比较特殊,SETUP包也会有相应的标志供我们使用。

 

USB的四种传输类型

虽然USB定义了数据在总线上传输的基本单位是包,但是我们还不能随意地使用包来传输数据,必须按照一定的关系把这些不同的包组织成事务(transaction)才能传输数据。

 

USB事务

那么事务是什么呢?事务通常由两个或者三个包组成:令牌包、数据包和握手包。

  • 令牌包用来启动一个事务,总是由主机发送;

  • 数据包传送数据,可以从主机到设备,也可以从设备到主机,方向由令牌包来指定;

  • 握手包的发送者通常为数据接收者,当数据接收正确后,发送握手包。设备也可以使用NAK握手包来表示数据还未准备好。

USB协议规定了4种传输类型:批量传输、同步传输、中断传输和控制传输。其中,批量传输、同步传输、中断传输每传输一次数据都是一个事务;控制传输包括三个过程:建立过程和状态过程分别是一个事务,数据过程则可能包含多个事务。

 

批量传输

批量传输使用批量事务(bulk transaction)传输数据。一次批量事务有三个阶段:令牌包阶段、数据包阶段和握手包阶段。这里的每个阶段都是一个独立的包。批量传输分为批量读和批量写。批量读使用批量输入事务,批量写使用批量输出事务。

 

批量传输没有规定数据包中数据的意义和结构,具体的数据结构由设备自己定义。批量传输通常用在数据量大、对数据的实时性要求不高的场合,例如USB打印机、扫描仪、大容量存储设备等。

 

首先介绍批量输出事务。主机先发出一个OUT令牌包,这个令牌包中包含了设备地址、端口号。然后,再发送一个DATA包(具体什么类型的DATA包,要看数据切换位),这时地址和端点匹配的设备就会收下这个数据包。然后主机切换到接收模式,等待设备返回握手包。设备解码令牌包、数据包都准确无误,并且有足够的缓冲区来保存数据后,就会使用ACK握手包或者NYET握手包来应答主机(只有高速模式才有NYET握手包,它表示本次数据成功接收,但是没有能力接收下一次传输)。如果没有足够的缓冲区来保存数据,那么它就会返回一个NAK握手包,告诉主机目前没有缓冲区可用,主机会在稍后的时间重试该批量输出事务。如果设备检测到数据正确,但是端点处于挂起状态,则返回一个STALL握手包。如果设备检测到有错误,则不做任何响应,让主机等待超时。

 

再来看看批量输入事务。主机首先发出一个IN令牌包,同样,IN令牌包中包含了设备地址和设备号。然后主机切换到接收数据状态,等待设备返回数据。如果设备检测到错误,那么不做任何响应,主机等待超时。如果此时有地址和端点匹配的设备,并且没有检测到错误,则该设备要做出响应:如果设备有数据需要返回,要么它把一个数据包放到总线上(具体的数据包类型要看数据切换位);如果设备没有数据需要返回,则它直接使用NAK握手包来响应主机;如果该端点处于挂起状态,设备会返回一个STALL握手包。如果主机接收到设备发送的数据包并解码正确后,使用ACK握手包应答设备。如果主机检测到错误,则不做任何响应,设备会检测到超时。主机是不会发送NAK握手包来拒绝接收数据包。主机在收到NAK握手包后,知道设备暂时无数据返回,主机会在稍后的时间里重试该输入事务。

 

另外,USB2.0高速设备中增加了一个PING令牌包,它不发出数据,直接等待设备的握手包。因此PING事务只有令牌包和握手包。

下图是批量事务的流程图。

8

 

下表更详细地给出了一个传输正确的批量输入事务和批量输出事务的数据包图(传送了2字节数据)。

9

一次正确的批量输入事务

 

1

一次正确的批量输出事务

 

中断传输

中断传输是一种保证查询频率的传输。中断端点在端点描述符中要报告它的查询间隔,主机会保证在小于这个时间间隔的范围内安排一次传输。

 

这里所说的中断,跟我们硬件上的中断是不一样的。它不是由设备主动地发出一个中断请求,而是由主机保证在不大于某个时间间隔内安排一次传输。中断传输通常用在数据量不大,但是对时间要求较严格的设备中,例如人机接口设备(HID)中的鼠标、键盘、轨迹球等。中断传输也可以用来不断地检测某个状态,当条件满足后再使用批量传输来传送大量的数据。

 

除了在对端点查询的策略上不一样之外,中断传输和批量传输的结构基本上是一样的,只是中断传输中没有PING和NYET两种包。中断传输使用中断事务(interrupt transaction)。中断事务的流程图如下:

2

 

 

同步传输

同步传输用在数据量大、对实时性要求高的场合,例如音频设备、视频设备等,这些设备对数据延迟很敏感。对于音频或者视频设备来说,对数据的100%正确要求不高,少量数据的错误还是能够容忍的,主要的是要保证不能停顿;所以同步传输是不保证数据100%正确的。当数据错误时,并不进行重传操作。因此同步传输也就没有应答包。数据是否正确,可以由数据包的CRC校验来确认。至于出错的数据如何处理,由软件来决定。同步传输使用同步传输事务(isochronous transaction)来传输数据。下图为流程图:

3

 

控制传输

控制传输与前面三种传输相比,要稍微复杂一些。

 

控制传输分为三个过程:第一个过程是建立过程;第二个过程是可选的数据过程;第三个过程是状态过程。

 

建立过程使用一个建立事务。建立事务是一个输出数据的过程,与批量传输的输出事务相比,有几处不一样:首先是令牌包不一样,建立过程使用SETUP令牌包;其次是数据包类型,SETUP只能使用DATA0包;最后是握手包,设备只能使用ACK来应答(除非出错了,不应答),而不能使用NAK或者STALL来应答,即设备必须要接收建立事务的数据。下图是建立事务的流程图:

4

 

数据过程是可选的,即一个控制传输可能没有数据过程。如果有,一个数据过程可以包含一笔或者多笔数据事务。控制传输所使用的数据事务与批量传输中的批量事务是一样的。要注意的是,在数据过程中,所有的数据事务必须是用一个传输方向的(要么只有控制读传输,要么只有控制写传输)。也就是说,在控制读传输中,数据过程中的所有数据事务都必须是输入的;在控制写传输中,数据过程中的所有数据事务都必须是输出的。一旦数据传输方向发生改变,就会认为进入到了状态过程。数据过程的第一个数据包必须是DATA1包,然后每次正确传输一个数据包后就在DATA0和DATA1之间交替。

 

状态过程也是一笔批量事务,它的传输方向刚好跟前面的数据阶段相反,即控制写传输在状态过程使用一个批量输入事务;控制读传输在状态过程使用一个批量输出事务。状态过程只使用DATA1包。

 

下面是几种控制传输的实例:

5

 

端点类型与传输类型的关系

一个具体的端点,只能工作在一种传输模式下。通常,我们把工作在什么模式下的端点,就叫做什么端点。例如,控制端点、批量端点等。

 

端点0是每个USB设备都必须具备的默认控制端点,它一上电就存在并且可用。设备的各种描述符以及主机发送的一些命令,都是通过端点0传输的。其他端点是可选的,需要根据具体的设备来决定。非0端点只有在Set Config之后才能使用。

 

传输类型与端点支持的最大包长

每个端点描述符中都规定了端点所支持的最大数据包长。主机每次发送数据包,都不能超过端点的最大包长。

  • 对于控制传输的端点,低速模式最大包长固定为8字节,高速模式最大包长固定为64字节,而全速模式可在8、16、32、64字节中选择。

  • 对于同步传输的端点,全速模式最大包长上限为1023字节,高速模式最大包长上限为1024字节,低速模式不支持同步传输。

  • 对于中断传输的端点,低速模式最大包长上限为8字节,全速模式最大包长上限为64字节,高速模式最大包长上限为1024字节。

  • 对于批量传输的端点,高速模式固定为512字节,全速模式最大包长可在8、16、32、64字节中选择,低速模式不支持批量传输

 

参考资料《圈圈教你玩USB》