参考文章:https://www.jianshu.com/p/3afc1eb5bd32
一、前言USB通用串行总线(Universal Serial Bus)在计算机系统中使用的很广泛,相比较RS232、RS485、CAN总线而言,USB的协议层比较复杂,但是物理层的原理很相似。我们先看看物理层的区别。
二、物理层
USB总线和RS485和CAN总线类似,都是使用两根差分信号线(D+、D-)进行数据传输,在我之前的一篇关于CAN总线仲裁的文章中有介绍,CAN总线上有两种状态:显性和隐性,通过线与运算可以实现优先级仲裁工作。与CAN总线不同的是,对于USB FULL SPEED DEVICE来说USB总线拥有4中状态:J、K、SE0、SE1,对应的差分线状态为:
Data J state:D+=1,D-=0;
Data K state:D+=0,D-=1;
SE0:D+=D-=0;
SE1:D+=D-=1;
USB物理层使用这4种状态来进行信号传输,也就是在总线上有这4种状态,在分析USB的物理层数据格式之前,先使用RS232协议作为开门砖。由于RS232总线只有一根数据线,且传输的是数字信号,所以总线只能表述两种状态:高电平、低电平,所以使用RS232进行数据传输的信号格式如下图:
.
空闲状态下RS232总线上的状态为高电平,启动传输的时候总线上会先产生一个起始位(Start Bit),表示数据传输的开始,后续传输8位的数据,传输完成数据之后继续传输一个停止位(Stop Bit),表示数据传输的结束。上面这个过程就是一个完整的RS232物理层的数据帧,这个帧只包含8个位的有效数据,如果需要传输多个字节的数据,需要在上一层协议中进行传输控制。
理解了RS232协议之后,USB总线的物理层也就简单了。USB总线也有空闲状态,空闲状态下总线处于J状态。开始进行数据传输的时候USB总线也发出一个类似于RS232协议中的“起始位”的信号,叫做SYNC(同步域),用于通知接收设备开始进行数据传输,同步域由多个位的信号构成。结束数据传输的时候USB也会发出一个类似于RS232协议中"停止位"的信号,叫做EOP(结束域),处于EOP时,总线处于SE0状态若干个位的时间。在SYNC和EOP之间的就是有效的数据,这个数据就USB协议层的内容了,我们物理层先不管这个USB协议层的内容。下图就是USB物理层的信号结构:
可以看出USB物理层的传输以SYNC开始以EOP结束,加上中间的有效数据,整个过程称为一个Packet(包)。Packet是USB中的最小传输单位,和上面介绍的RS232中的一个数据帧一样,但是不同的是RS232的一个最小传输单位只有一个字节,而USB的最小传输单位Packet可以包含多个字节的数据。现在我们大致了解了USB总线物理层的构成了,下面可以探究一下USB协议层的结构。
三、USB协议层3.1、Packet
下图是一个典型的USB Packet格式:
剥离掉物理层的东西(空闲状态、同步域、结束域),剩下的就是USB协议层的东西了,当然这个帧格式只是USB协议格式中的一种,物理层和协议层的对应关系如下图:
3.2、域
一个Packet包含多个域:标识域(PID)、地址域(ADDR)、端点域(ENDP)、帧号域(FRAM)、数据域(DATA)、校验域(CRC)。
1、标识域(PID),由四位标识符+四位标识符反码构成,表明包的类型和格式,这是一个很重要的部分,这里可以计算出,USB的标识码有16种:
2、地址域(ADDR):七位地址,代表了设备在主机上的地址,地址000 0000被命名为零地址,是任何一个设备第一次连接到主机时,在被主机配置、枚举前的默认地址,由此可以知道为什么一个USB主机只能接127个设备的原因。
3、端点域(ENDP),四位,由此可知一个USB设备有的端点数量最大为16个。
4、帧号域(FRAM),11位,每一个帧都有一个特定的帧号,帧号域最大容量0x800,对于同步传输有重要意义(同步传输为四种传输类型之一)。
5、数据域(DATA):长度为0~1023字节,在不同的传输类型中,数据域的长度各不相同,但必须为整数个字节的长度
6、校验域(CRC):对令牌包和数据包(对于包的分类请看下面)中非PID域进行校验的一种方法,CRC校验在通讯中应用很泛,是一种很好的校验方法,至于具体的校验方法这里就不多说,请查阅相关资料,只须注意CRC码的除法是模2运算,不同于10进制中的除法。
一个USB Packet通常包含多个上述的域,有的是必有的例如标识域(PID),有的可以没有,Packet的类型通过标识域(PID)来鉴别:
1、令牌包:可分为输入包、输出包、设置包和帧起始包(注意这里的输入包是用于设置输入命令的,输出包是用来设置输出命令的,而不是放据数的) 其中输入包、输出包和设置包的格式都是一样的:
SYNC+PID+ADDR+ENDP+CRC5(五位的校验码)
帧起始包的格式:
SYNC+PID+11位FRAM+CRC5(五位的校验码)
2、数据包:分为DATA0包和DATA1包,当USB发送数据的时候,当一次发送的数据长度大于相应端点的容量时,就需要把数据包分为好几个包,分批发送,DATA0包和DATA1包交替发送,即如果第一个数据包是 DATA0,那第二个数据包就是DATA1。但也有例外情况,在同步传输中(四类传输类型中之一),所有的数据包都是为DATA0,格式如下:
SYNC+PID+0~1023字节+CRC16
3、握手包:结构最为简单的包,格式如下 SYNC+PID 。
每包数据的最后都会有一个包结束符EOP(End-of-Packet),对于高速设备和全速/低速设备也是不一样的。全速/低速设备的EOF是一个大约为2个数据位宽度的单端0(SE0)信号。SE0的意思就是,D+和D-同时都保持为低电平。由于USB使用的是差分数据线,通常都是一高一低的,而SE0不同,是一种都为低的特殊状态。SE0用来表示一些特殊的意义,例如包结束、复位信号灯。USB集线器对USB设备进行复位的操作,就是通过将总线设置为SE0状态大约10ms来实现的。对于高速设备的EOF,使用故意的位填充错误来表示。那么如何判断一个位填充错误是真的位错误还是包结束呢?这个由CRC校验来判断。如果CRC校验正确,则说明这个位填充错误是EOP,否则,说明传输出错。
3.3、Transaction
USB协议中的最小传输单位 Packet 已经定义好了,但是USB的应用层不是直接使用 Packet 进行数据传输工作,USB应用层的传输是基于Transfer(传输)的,也就是想发送、接收数据都是基于Transfer,而一个Transfer是由若干个Transaction(事务)组成,而Transaction(事务)才是由多个Packet组成的,协议结构如下图:
我们先说说Transaction(事务)。事务分别有IN事务、OUT事务和SETUP事务三大事务,每一种事务都由令牌包、数据包、握手包三个阶段构成,这里用阶段的意思是因为这些包的发送是有一定的时间先后顺序的,事务的三个阶段如下:
1、令牌包阶段:启动一个输入、输出或设置的事务
2、数据包阶段:按输入、输出发送相应的数据
3、握手包阶段:返回数据接收情况,在同步传输的IN和OUT事务中没有这个阶段,这是比较特殊的。
事务的三种类型如下(以下按三个阶段来说明一个事务):
1、 IN事务:
令牌包阶段——主机发送一个PID为IN的输入包给设备,通知设备要往主机发送数据;
数据包阶段——设备根据情况会作出三种反应(要注意:数据包阶段也不总是传送数据的,根据传输情况还会提前进入握手包阶段)
1) 设备端点正常,设备往入主机里面发出数据包(DATA0与DATA1交替);
2) 设备正在忙,无法往主机发出数据包就发送NAK无效包,IN事务提前结束,到了下一个IN事务才继续;
3) 相应设备端点被禁止,发送错误包STALL包,事务也就提前结束了,总线进入空闲状态。
握手包阶段——主机正确接收到数据之后就会向设备发送ACK包。
2、 OUT事务:
令牌包阶段——主机发送一个PID为OUT的输出包给设备,通知设备要接收数据;
数据包阶段——比较简单,就是主机会给设备送数据,DATA0与DATA1交替
握手包阶段——设备根据情况会作出三种反应
1)设备端点接收正确,设备往入主机返回ACK,通知主机可以发送新的数据,如果数据包发生了CRC校验错误,将不返回任何握手信息;
2)设备正在忙,无法接收主机发出数据包就发送NAK无效包,通知主机再次发送数据;
3)相应设备端点被禁止,发送错误包STALL包,事务提前结束,总线直接进入空闲状态。
3、SETUT事务:
令牌包阶段——主机发送一个PID为SETUP的输出包给设备,通知设备要接收数据;
数据包阶段——比较简单,就是主机会设备送数据,注意,这里只有一个固定为8个字节的DATA0包,这8个字节的内容就是标准的USB设备请求命令(共有11条)
握手包阶段——设备接收到主机的命令信息后,返回ACK,此后总线进入空闲状态,并准备下一个传输(在SETUP事务后通常是一个IN或OUT事务构成的传输)
3.4、Transfer
再说说Transfer(传输),传输由OUT、IN、SETUP事务构成,传输有四种类型,中断传输、批量传输、同步传输、控制传输,其中中断传输和批量转输的结构一样,同步传输有最简单的结构,而控制传输是最重要的也是最复杂的传输。
1、中断传输:由OUT事务和IN事务构成,用于键盘、鼠标等HID设备的数据传输中 。
2、批量传输:由OUT事务和IN事务构成,用于大容量数据传输,没有固定的传输速率,也不占用带宽,当总线忙时,USB会优先进行其他类型的数据传输,而暂时停止批量转输。
3、同步传输:由OUT事务和IN事务构成,有两个特殊地方,第一,在同步传输的IN和OUT事务中是没有返回包阶段的;第二,在数据包阶段所有的数据包都为DATA0 。
4、控制传输:最重要的也是最复杂的传输,控制传输由三个阶段构成(初始设置阶段、可选数据阶段、状态信息步骤),每一个阶段可以看成一个的传输,也就是说控制传输其实是由三个传输构成的,用来于USB设备初次加接到主机之后,主机通过控制传输来交换信息,设备地址和读取设备的描述符,使得主机识别设备,并安装相应的驱动程序,这是每一个USB开发者都要关心的问题。
1)、初始设置步骤:就是一个由SETUP事务构成的传输;
2)、可选数据步骤:就是一个由IN或OUT事务构成的传输,这个步骤是可选的,要看初始设置步骤有没有要求读/写数据(由SET事务的数据包阶段发送的标准请求命令决定);
3)、状态信息步骤:顾名思义,这个步骤就是要获取状态信息,由IN或OUT事务构成构成的传输,但是要注意这里的IN和OUT事务和之前的INT和OUT事务有两点不同:
(1) 传输方向相反,通常IN表示设备往主机送数据,OUT表示主机往设备送数据;在这里,IN表示主机往设备送数据,而OUT表示设备往主机送数据,这是为了和可选数据步骤相结合;
(2) 在这个步骤里,数据包阶段的数据包都是0长度的,即SYNC+PID+CRC16
在Transfer(传输)之上就可以进行应用层的传输了。首先介绍一个概念叫做“描述符”,顾名思义,描述符就是描述事物的一道符!这道符不是写在纸上的,而是存在内存中的一个数据结构。用在USB设备中,描述符有很多种,包括:设备描述符、配置描述符、接口描述符、端点描述符、HID描述符。
设备描述符(Device Descriptor),用于描述USB设备,每个USB设备只有一个设备描述符,包含了设备类型、设备遵循的协议、厂商ID、产品id、序列号等,一个完整的设备描述符如下:
DEVICE DESCRIPTOR
bLength: 18
bDescriptorType: 0x01 (DEVICE)
bcdUSB: 0x0200
bDeviceClass: Vendor Specific (0xff)
bDeviceSubClass: 255
bDeviceProtocol: 255
bMaxPacketSize0: 64
idVendor: Marvell Semiconductor, Inc. (0x1286)
idProduct: Unknown (0x812a)
bcdDevice: 0x0000
iManufacturer: 3
iProduct: 2
iSerialNumber: 0
bNumConfigurations: 1
配置描述符(Configuration Descriptor),用于配置设备,一个USB设备可以拥有多个配置描述符,但是同一时间只能有一种配置生效,配置描述符如下:
CONFIGURATION DESCRIPTOR
bLength: 9
bDescriptorType: 0x02 (CONFIGURATION)
wTotalLength: 121
bNumInterfaces: 4
bConfigurationValue: 1
iConfiguration: 0
Configuration bmAttributes: 0xc0 SELF-POWERED NO REMOTE-WAKEUP
1... .... = Must be 1: Must be 1 for USB 1.1 and higher
.1.. .... = Self-Powered: This device is SELF-POWERED
..0. .... = Remote Wakeup: This device does NOT support remote wakeup
bMaxPower: 250 (500mA)
接口描述符(Interface Descriptor),USB interface用来处理一类 USB 逻辑连接, 例如一个鼠标, 一个键盘, 或者一个音频流. 一些 USB 设备有多个接口。也就是复合设备,例如一个 USB 扬声器可能有 2 个接口: 一个 USB 键盘给按钮和一个 USB 音频流。一个接口描述符包含多个端点描述符。
INTERFACE DESCRIPTOR (2.0): class Vendor Specific
bLength: 9
bDescriptorType: 0x04 (INTERFACE)
bInterfaceNumber: 2
bAlternateSetting: 0
bNumEndpoints: 2
bInterfaceClass: Vendor Specific (0xff)
bInterfaceSubClass: 0x00
bInterfaceProtocol: 0x00
iInterface: 8
端点描述符(Endpoint Descriptor),USB的通讯都是基于端点的,一个endpiont只能承载一个方向的数据。endpiont分为如下几种:
- CONTROL
控制端点被用来允许对 USB 设备的不同部分存取. 通常用作配置设备, 获取关于设备的信息, 发送命令到设备, 或者获取关于设备的状态报告. 这些端点在尺寸上常常较小. 每个 USB 设备有一个控制端点称为"端点 0", 被 USB 核用来在插入时配置设备. 这些传送由 USB 协议保证来总有足够的带宽使它到达设备.
- INTERRUPT
中断端点传送小量的数据, 以固定的速率在每次 USB 主请求设备数据时. 这些端点对 USB 键盘和鼠标来说是主要的传送方法. 它们还用来传送数据到 USB 设备来控制设备, 但通常不用来传送大量数据. 这些传送由 USB 协议保证来总有足够的带宽使它到达设备.
- BULK
块端点传送大量的数据. 这些端点常常比中断端点大(它们一次可持有更多的字符). 它们是普遍的, 对于需要传送不能有任何数据丢失的数据. 这些传送不被 USB 协议保证来一直使它在特定时间范围内完成. 如果总线上没有足够的空间来发送整个 BULK 报文, 它被分为多次传送到或者从设备. 这些端点普遍在打印机, 存储器, 和网络设备上.
- ISOCHRONOUS
同步端点也传送大量数据, 但是这个数据常常不被保证它完成. 这些端点用在可以处理数据丢失的设备中, 并且更多依赖于保持持续的数据流. 实时数据收集, 例如音频和视频设备, 一直都使用这些端点.