USB 通信协议

USB通信协议概述

一、SUB概述

        USB协议有两种:USB1.1和USB2.0。(现在有3.0了)

        USB2.0和USB1.1完全兼容。USB1.1支持的 数据传输率为12Mbps和1.5Mbps(用于慢 速外设),USB2.0支持的数据传速率可达 480Mbps。在普通用户看来,USB系统就是 外设通过一根USB电缆和PC机连接起来。 通常把外设称为USB设备,把其所连接的 PC机称为USB主机。将指向USB主机的数据传输方向称为上行通信,把指向USB设备的 数据传输方向称为下行通信。

        USB网络采用阶梯式星形拓扑结构,如图1。一个USB网络中只能有一个主机。主机内设置了一个根集线器,提供了主机上的初始附属点。

 

图1 USB主机和USB设备的连接

        主机定时对集线器的状态进行查询。当一个新设备接入集线器时,主机会检测到集线器状态改变,主机发出一个命令使该端口有效并对其进行设置。位于这个端口上的设备进行响应,主机收到关于设备的信息,主机的操作系统确定对这个设备使用那种驱动程序,接着设备被分配一个唯一标识的地址,范围从0~127,其中0为所有的设备在没有分配惟一地址时使用的默认地址。主机向它发出内部设置请求。当一个设备从总线上移走时,主机就从其可用资源列表中将这个设备删除。

二、USB系统基本概念

2.1、 USB主机

        USB的所有数据通信(不论是上行通信还是下行通信)都由USB主机启动,所以USB主机在整个数据传输过程中占据着主导地位。在USB系统中只允许有一个主机。从开发人员的角度看,USB主机可分为三个不同的功能模块:客户软件、USB系统软件和USB总线接口。

(1) 客户软件 :

        客户软件负责和USB设备的功能单元进行通信,以实现其特定功能。一般由开发人员自行开发。客户软件不能直接访问USB设备,其与USB设备功能单元的通信必须经过USB系统软件和USB总线接口模块才能实现。客户软件一般包括USB设备驱动程序和界面应用程序两部分。

        USB设备驱动程序负责和USB系统软件进行通信。通常,它向USB总线驱动程序发出I/O请求包(IRP)以启动一次USB数据传输。此外,根据数据传输的方向,它还应提供一个或空或满的数据缓冲区以存储这些数据。
         界面应用程序负责和USB设备驱动程序进行通信,以控制USB设备。它是最上层的软件,只能看到向USB设备发送的原始数据和从USB设备接收的最终数据。

(2) USB系统软件:

        USB系统软件负责和USB逻辑设备进行配置通信,并管理客户软件启动的数据传输。USB逻辑设备是程序员与USB设备打交道的部分。USB系统软件一般包括USB总线驱动程序和USB主控制器驱动程序这两部分。这些软件通常由操作系统提供,开发人员不必掌握。

(3) USB总线接口:

       USB总线接口包括主控制器和根集线器两部分。根集线器为USB系统提供连接起点,用于给USB系统提供一个或多个连接点(端口)。主控制器负责完成主机和USB设备之间数据的实际传输,包括对传输的数据进行串行编解码、差错控制等。该部分与USB系统软件的接口依赖于主控制器的硬件实现,开发人员不必掌握。

2.2、USB设备

        一个USB设备由三个功能模块组成:USB总线接口、USB逻辑设备和功能单元。这里的USB总线接口指的是USB设备中的串行接口引擎(SIE);USB逻辑设备被USB系统软件看作是一个端点的集合;功能单元被客户软件看作是一个接口的集合。SIE、端点和接口都是USB设备的组成单元。为了更好地描述USB设备的特征,USB提出了设备架构的概念。从这个角度来看,可以认为USB设备是由一些配置、接口和端点组成的,即一个USB设备可以含有一个或多个配置,在每个配置中可含有一个或多个接口,在每个接口中可含有若干个端点。其中,配置和接口是对USB设备功能的抽象,实际的数据传输由端点来完成。在使用USB设备前,必须指明其采用的配置和接口。这个步骤一般是在设备接入主机时设备进行自举时完成的,我们在后面会进一步介绍。USB设备使用各种描述符来说明其设备架构,包括设备描述符、配置描述符、接口描述符、端点描述符以及字符串描述符,它们通常被保存在USB设备的固件程序中。  

①设备:

        设备代表一个USB设备,它由一个或多个配置组成。设备描述符用于说明设备的总体信息,并指明其所含的配置的个数。一个USB设备只能有一个设备描述符。     

②配置:

        一个USB设备可以包含一个或多个配置,如USB设备的低功耗模式和高功耗模式可分别对应一个配置。在使用USB设备前,必须为其选择一个合适的配置。配置描述符用于说明USB设备中各个配置的特性,如配置所含接口的个数等。USB设备的每一个配置都必须有一个配置描述符。

③接口:

        一个配置可以包含一个或多个接口,如对一个光驱来说,当用于文件传输时使用其大容量存储接口;而当用于播放CD时,使用其音频接口。接口是端点的集合,可以包含一个或多个可替换设置,用户能够在USB处于配置状态时,改变当前接口所含的个数和特性。接口描述符用于说明USB设备中各个接口的特性,如接口所属的设备类及其子类等。USB设备的每个接口都必须有一个接口描述符。

④端点:

        端点是USB设备中的实际物理单元,USB数据传输就是在主机和USB设备各个端点之间进行的。端点一般由USB接口芯片提供,例如Freescale的MC68HC908JB8。USB设备中的每一个端点都有唯一的端点号,每个端点所支持的数据传输方向一般而言也是确定的:或是输入(IN)或是输出(OUT),也有些芯片提供的端点的数据方向是可以配置的,例如MC68HC908JB8包含有两个用于数据收发的端点:端点1和端点2。其中端点1只能用于数据发送,即支持输入(IN),端点2既能用于数据发送也可用于数据接收,即支持输入(IN)和输出(OUT)操作。需要注意的是,在这里数据的传输方向是站在主机的立场上来看得。比如端点1只能发送数据,在主机看来是端点1向主机输入数据,即IN操作;当端点2配置为接收数据时,主机向端点2输出数据,即OUT操作。这一点是初学者比较容易产生混淆的地方。

       利用设备地址、端点号和传输方向就可以指定一个端点,并和它进行通信。0号端点比较特殊,它有数据输入IN和数据输出OUT两个物理单元,且只能支持控制传输。

⑤字符串

        在USB设备中通常还含有字符串描述符,以说明一些专用信息,如制造商的名称、设备的***等。它的内容以UNICODE的形式给出,且可以被客户软件所读取。对USB设备来说,字符串描述符是可选的。

⑥管道

        在USB系统结构中,可以认为数据传输是在主机软件(USB系统软件或客户软件)和USB设备的各个端点之间直接进行的,它们之间的连接称为管道。管道是在USB设备的配置过程中建立的。管道是对主机和USB设备间通信流的抽象,它表示主机的数据缓冲区和USB设备的端点之间存在着逻辑数据传输,而实际的数据传输是由USB总线接口层来完成的。

       管道和USB设备中的端点一一对应。一个USB设备含有多少个端点,其和主机进行通信时就可以使用多少条管道,且端点的类型决定了管道中数据的传输类型,如中断端点对应中断管道,且该管道只能进行中断传输。传输类型在后面会介绍。不论存在着多少条管道,在各个管道中进行的数据传输都是相互独立的

三、USB物理特性

   3.1、USB接口

      USB使用一根屏蔽的4线电缆与网络上的设备进行互联。数据传输通过一个差分双绞线进行,这两根线分别标为D+和D-,另外两根线是Vcc和Ground,其中Vcc向USB设备供电。使用USB电源的设备称为总线供电设备,而使用自己外部电源的设备叫做自供电设备。为了避免混淆,USB电缆中的线都用不同的颜色标记,如表1所示。(现在为了支持主机,设备的互换,增加了一条线,OTG,也就有了mini USB)

引脚编号       信号名称 缆线颜色
1 Vcc   
2 Data-(D-)   
3 Data+(D+)    绿
4  Ground   
表1    USB缆线的信号与颜色

        从一个设备连回到主机,称为上行连接;从主机到设备的连接,称为下行连接。为了防止回环情况的发生,上行和下行端口使用不同的连接器所以USB在电缆和设备的连接中分别采用了两种类型的连接头,即图16-2所示的A型连接头和B型连接头。每个连接头内的电线号与图16-2的引脚编号是一致的。A型连接头,用于上行连接,即在主机或集线器上有一个A型插座,而在连接到主机或集线器的电缆的一端是A型插头。在USB设备上有B型插座,而B型插头在从主机或集线器接出的下行电缆的一端。采用这种连接方式,可以确保USB设备、主机/集线器和USB电缆始终以正确的方式连接,而不出现电缆接入方式出错,或直接将两个USB设备连接到一起的情况。


图2   USB连接头

3.2、USB信号

(1) 差分信号技术特点
             传统的传输方式大多使用“正信号”或者“负信号”二进制表达机制,这些信号利用单线传输。用不同的信号电平范围来分别表示1和0,它们之间有一个临界值,如果在数据传输过程中受到中低强度的干扰,高低电平不会突破临界值,那么信号传输可以正常进行。但如果遇到强干扰,高低电平突破临界值,由此造成数据传输出错。差分信号技术最大的特点是:必须使用两条线路才能表达一个比特位,用两条线路传输信号的压差作为判断1还是0的依据。这种做法的优点是具有极强的抗干扰性。倘若遭受外界强烈干扰,两条线路对应的电平同样会出现大幅度提升或降低的情况,但二者的电平改变方向和幅度几乎相同,电压差值就可始终保持相对稳定,因此数据的准确性并不会因干扰噪声而有所降低。

(2)USB通信的格式

图3 在USB电缆上使用双向不归零编码和差动信号的传输

       USB的数据包使用反向不归零编码(NRZI)。图3描述了在USB电缆段上传输信息的步骤。反向不归零编码由传送信息的USB代理程序完成;然后,被编码的数据通过差分驱动器送到USB电缆上;接着,接收器将输入的差分信号进行放大,将其送给解码器。使用该编码和差动信号传输方式可以更好地保证数据的完整性并减少噪声干扰。


图4      反向不归零编码 

        使用反向不归零编码方式可以保证数据传输的完整性,而且不要求传输过程中有独立的时钟信号。反向不归零编码不是一个新的编码方式。它在许多方面都有应用。图4给出了一个数据流和编码之后的结果。在反向不归零编码时,遇到“0”转换,遇到“1”保持。反向不归零码必须保持与输入数据的同步性,以确保数据采样正确。反向不归零码数据流必须在一个数据窗口被采样,无论前一个位时间是否发生过转换。解码器在每个位时间采样数据以检查是否有转换。

图5     在USB电缆上使用双向不归零编码和差动信号的传输

        若重复相同的“1”信号一直进入时,就会造成数据长时间无法转换,逐渐的积累,而导致接收器最终丢失同步信号的状况,使得读取的时序会发生严重的错误。因此,在NRZI编码之间,还需执行所谓的位填充的工作。位填充要求数据流中如果有连续的六个“1”就要强行转换。这样接收器在反向不归零码数据流中最多每七个位就检测到一次跳转。这样就保证了接收器与输入数据流保持同步。反向不归零码的发送器要把“0”(填充位)插到数据流中。接收器必须被设计成能够在连续的六个“1”之后识别一个自动跳转,并且立即扔掉这六个“1”之后的“0”位。

       图5的第一行是送到接收器的原始数据。注意数据流包括连续的八个“1”。第二行表示对原始数据进行了位填充,在原始的第六个和第七个“1”之间填入了一个“0”。第七个“1”延时一个位时间让填充位插入。接收器知道连续六个“1”之后将是一个填充位,所以该位就要被忽略。注意,如果原始数据的第七个位是“0”,填充位也同样插入,在填充过的数据流中就会有两个连续的“0”。

3.3、检测设备连接和速度

       在USB设备连接时,USB系统能自动检测到这个连接,并识别出其采用的数据传输速率。USB采用在D+或D-线上增加上拉电阻的方法来识别低速和全速设备。USB支持三种类型的传输速率:1.5Mb/s的低速传输、12Mb/s的全速传输和480Mb/s的高速传输。如图6所示。当主控制器或集线器的下行端口上没有USB设备连接时,其D+和D-线上的下拉电阻使得这两条数据线的电压都是近地的(0V);当全速/低速设备连接以后,电流流过由集线器的下拉电阻和设备在D+/D-的上拉电阻构成的分压器。由于下拉电阻的阻值是15KΩ,上拉电阻的阻值是1.5KΩ,所以在D+/D-线上会出现大小为(Vcc*15/(15+1.5))的直流高电平电压。当USB主机探测到D+/D-线的电压已经接近高电平,而其它的线保持接地时,它就知道全速/低速设备已经连接了。保持接地时,它就知道全速/低速设备已经连接了。

图6:USB连接图

四、USB通信协议

4.1、 数据包

       包(Packet)是USB系统中信息传输的基本单元,所有数据都是经过打包后在总线上传输的。USB包由五部分组成,即同步字段(SYNC)、包标识符字段(PID)、数据字段、循环冗余校验字段(CRC)和包结尾字段(EOP),包的基本格式如下:

同步字段(SYNC) PID字段 数据字段 CRC字段 包结尾字段(EOP)

       4.1.1、SYNC字段:由8位组成,作为每个数据封包的前导,用来产生同步作用,使USB设备与总线的包传输率同步,它的数值固定为00000001。

       4.1.2、PID字段:用来表示数据封包的类型。包标识符中的校验字段是通过对类型字段的每个位求反码产生的, PID字段如下图所示:

PID0 PID1 PID2 PID3 ~PID0 ~PID1 ~PID2 ~PID3
        表2中列出了信息包的类型,包括令牌、数据、握手或特殊四种信息包类型。

封包类型 PID名称 PID编码 意义
令牌 OUT 0001B 从主机到设备的数据传输
令牌 IN 1001B 从设备到主机的数据传输
令牌 SOF 0101B 帧的起始标记与帧码
令牌 SETUP 1101B 从主机到设备。表示要进行控制传输
数据 DATA0 0011B 偶数数据封包
数据 DATA1 1011B 奇数数据封包
握手 ACK 0010B 接收器收到无错误的数据封包
握手 NAK 1010B 接收器无法接收数据或发射器无法送出数据
握手 STALL 1110B

端点产生停滞的状况

特殊 PRE 1100B 使能下游端口的USB总线的数据传输切换到低速的设备
表2       各种信息包的类型与规范 

       4.1.3、数据字段: 是用来携带主机与设备之间要传递的信息,其内容和长度根据包标识符、传输类型的不同而各不相同。在USB包中,数据字段可以包含设备地址、端点号、帧***以及数据等内容。在总线传输中,总是首先传输字节的最低位,最后传输字节的最高位。

        (1) 设备地址(ADDR)数据域ADDR数据域由7位组成,可用来寻址多达127个外围设备。
        (2) 端点(ENDP)数据域: ENDP数据域由4位组成。通过这4个位最多可寻址出32个端点。这个ENDP数据域仅用在IN、OUT与SETUP令牌信息包中。对于慢速设备可支持端点0以及端点1作为中断传输模式,而全速设备则可以拥有16个输入端点(IN)与16个输出端点(OUT)共32个端点。
        (3) 帧***:当USB令牌包的PID为SOF时,其数据字段必须为11位的帧***。帧***由主机产生,且每个数据帧自动加一,最大数值为0x7FF。当帧***达到最大数时将自动从0开始循环。
       (4) 数据:它仅存于DATA信息包内,根据不同的传输类型,拥有不同的字节大小,从0到1023字节(实时传输)。

        4.1.4、循环冗余码CRC:字段由不同数目的位所组成。根据不同的信息包类型,CRC数据域由不同数目的位所组成。其中重要的数据信息包采用CRC16的数据域(16个位),而其余的信息包类型则采用CRC5的数据域(5个位)。其中的循环冗余码校验CRC,是一种错误检测技术。由于数据在传输时,有时候会发生错误,因此CRC可根据数据算出一个校验值,然后依此判断数据的正确性

        4.1.5、包结尾字段:即发送方在包的结尾发出包结尾信号。USB主机根据EOP判断数据包的结束。

4.2、信息包格式

         4.2.1、令牌(token)包:  在USB系统中,只有主机才能发出令牌包。令牌包定义了数据传输的类型,它是事务处理的第一阶段。令牌包中较为重要的是SETUP、IN和OUT这三个令牌包。它们用来在根集线器和设备端点之间建立数据传输。一个IN包用来建立一个从设备到根集线器的数据传送,一个OUT包用来建立从根集线器到设备的数据传输。令牌包格式如下:

8位 4位 4位 7位 4位 5位
SYNC PID ~PID ADDR ENDP CRC5
        4.2.2、数据(data)包: 数据封包含有4个域:SYNC、PID、DATA与CRC16。DATA数据域的位值是根据USB设备的传输速度及传输类型而定,且须以8字节为基本单位。也就是,若传输的数据不足8字节,或传输到最后所剩余的也不足8字节,仍须传输8字节的数据域。格式如下:
8位 4位 4位 0~1023位 16位
SYNC PID ~PID DATA CRC16
       4.2.3、  握手(Handshake)包:握手信息包是最简单的信息包类型。在这个握手信息包中仅包含一个PID数据域而已,它的格式如下所列:
8位 4位 4位
SYNC PID ~PID

4.3、事务

        在USB上数据信息的一次接收或发送的处理过程称为事务处理(Transaction)。事务处理的类型包括输入(IN)事务处理、输出(OUT)事务处理、设置(SETUP)事务处理和帧开始、帧结尾等类型。在输出(OUT)事务处理和设置(SETUP)事务处理中,紧接着SETUP和OUT包后的是DATA包,DATA0和DATA1包是交替地发送的,在DATA包后面,设备将回应一个握手信号,如果设备可以接收数据,就回应ACK包,如果设备忙,就回应NAK包,如果设备出错,则回应STALL包;在IN事务中,IN包后面是设备发来的DATA包或NAK包或STALL包,若设备忙或出错,就发NAK包或STALL包给主机,若设备数据准备好发送,则发DATA包,DATA0和DATA1包也是交替地发送的,紧接着DATA包后面是主机发给设备的握手包,ACK表示主机可以接收数据,NAK包代表主机忙,STALL包代表主机出错。

4.3.1、输入(IN)事务处理

       输入事务处理表示USB主机从总线上的某个USB设备接收一个数据包的过程。

      ①正常的输入事务处理

1. 主机->设备(令牌包) SYNC IN     ADDR   ENDP CRC5
2. 设备->主机(数据包) SYNC DATA0 DATA                  CRC16
3. 主机->设备(握手包) SYNC ACK
       ②设备忙时的输入事务处理   

1. 主机->设备(令牌包) SYNC IN     ADDR   ENDP CRC5
3. 主机->设备(握手包) SYNC NAK
      ③设备出错时的输入事务处理

1. 主机->设备(令牌包) SYNC IN     ADDR   ENDP CRC5
3. 主机->设备(握手包) SYNC STALL

4.3.2、输出(OUT)事务处理

       输入事务处理表示USB主机从总线上的某个USB设备接收一个数据包的过程。

       ①正常的输入事务处理

1. 主机->设备(令牌包) SYNC OUT    ADDR   ENDP CRC5
2. 主机->设备(数据包) SYNC DATA0 DATA                  CRC16
3. 设备->主机(握手包) SYNC ACK

②设备忙时的输入事务处理

1. 主机->设备(令牌包) SYNC OUT    ADDR   ENDP CRC5
2. 主机->设备(数据包) SYNC DATA0 DATA                  CRC16
3. 设备->主机(握手包) SYNC NAK

③设备出错时的输入事务处理

1. 主机->设备(令牌包) SYNC OUT    ADDR   ENDP CRC5
2. 主机->设备(数据包) SYNC DATA0 DATA                  CRC16
3. 设备->主机(握手包) SYNC STALL

4.3.3、设置(SETUP)事务处理

     输入事务处理表示USB主机从总线上的某个USB设备接收一个数据包的过程。

      ①正常的输入事务处理

1. 主机->设备(令牌包) SYNC SETUP   ADDR   ENDP CRC5
2. 主机->设备(数据包) SYNC DATA0 DATA                  CRC16
3. 设备->主机(握手包) SYNC ACK

     ②设备忙时的输入事务处理   

1. 主机->设备(令牌包) SYNC SETUP    ADDR   ENDP CRC5
2. 主机->设备(数据包) SYNC DATA0 DATA                  CRC16
3. 设备->主机(握手包) SYNC NAK

    ③设备出错时的输入事务处理

1. 主机->设备(令牌包) SYNC SETUP    ADDR   ENDP CRC5
2. 主机->设备(数据包) SYNC DATA0 DATA                  CRC16
3. 设备->主机(握手包) SYNC STALL

4.4、USB的传输类型

       在USB的传输中,制定了4种传输类型:控制传输、中断传输、批量传输以及等时传输。控制传输类型分为2~3个阶段:设置阶段、数据阶段(无数据控制没有此阶段)以及状态阶段。根据数据阶段的数据传输的方向,控制传输又可分为3种类型:控制读取(读取USB描述符)、控制写入(配置USB设备)以及无数据控制。

4.4.1、控制传输

        控制传输是USB传输中最重要的传输。它包含3种类型:控制读取、控制写入以及无数据控制。这3种控制传输类型又分为2~3个阶段:设置阶段、数据阶段(无数据控制没有此阶段)以及状态阶段。

阶段一:设置阶段

         主机从USB设备获取配置信息,并设置设备的配置值。设置阶段的数据交换包含了SETUP令牌封包、紧随其后的DATA0数据封包以及ACK握手封包。它的作用是执行一个设置(概念含糊)的数据交换,并定义此控制传输的内容。

阶段二:数据传输阶段

        数据传输阶段用来传输主机与设备之间的数据。控制读取是将数据从设备读到主机上,读取的数据USB设备描述符。该过程如图16-8所示。对每一个数据信息包而言,首先,主机会发送一个IN令牌信息包,表示要读数据进来。然后,设备将数据通过DATA1数据信息包回传给主机。最后,主机将以下列的方式加以响应:当数据已经正确接收时,主机送出ACK令牌信息包;当主机正在忙碌时,发出NAK握手信息包;当发生了错误时,主机发出STALL握手信息包。

        控制写入则是将数据从主机传到设备上,所传的数据即为对USB设备的配置信息,该过程如图16-9所示。对每一个数据信息包而言,主机将会送出一个OUT令牌信息包,表示数据要送出去。紧接着,主机将数据通过DATA0数据信息包传递至设备。最后,设备将以下列方式加以响应:当数据已经正确接收时,设备送出ACK令牌信息包;当设备正在忙碌时,设备发出NAK握手信息包;当发生了错误时,设备发出STALL握手信息包。

图7  控制读取的IN数据交换的过程示意图           图 8   控制写入的OUT数据交换的过程示意图

阶段三:状态阶段

         状态阶段用来表示整个传输的过程已完全结束。状态阶段传输的方向必须与数据阶段的方向相反,即原来是IN令牌封包,这个阶段应为OUT令牌封包;反之,原来是OUT令牌封包,这个阶段应为IN令牌封包。对于控制读取而言,主机会送出OUT令牌封包,其后再跟着0长度的DATA1封包。而此时,设备也会做出相对应的动作,送ACK握手封包、NAK握手封包或STALL握手封包。相对地对于控制写入传输,主机会送出IN令牌封包,然后设备送出表示完成状态阶段的0长度的DATA1封包,主机再做出相对应的动作:送ACK握手封包、NAK握手封包或STALL握手封包。

4.4.2、实时传输

        实时传输适用于必须以固定速率抵达或在指定时刻抵达,可以容忍偶尔错误的数据上。实时传输一般用于麦克风、喇叭等设备。实时传输只需令牌与数据两个信息包阶段,没有握手包,故数据传错时不会重传。

4.4.3、批量传输

      用于传输大量数据,要求传输不能出错,但对时间没有要求,适用于打印机、存储设备等。

4.4.4、中断传输

      中断传输方式总是用于对设备的查询,以确定是否有数据需要传输。因此中断传输的方向总是从USB设备到主机。

4.5、设备列举

4.5.1、描述符

        USB描述符就好像是USB外围设备的“身份证”一样,详细地记录着外围设备相关的一切信息。为了描述不同的数据,就需以不同类型的USB描述符来加以描述,它共有以下几种类型:设备描述符、配置描述符、接口描述符和端点描述符,这几个描述符是必须具有的,下面将结合实例详细介绍;其他的描述符,例如,字符串描述符、数种不同的群组描述符以及报告描述符则可以根据不同的设备进行选择。

(1)设备描述符

       设备描述符具有 18 字节的长度,并且是主机向设备请求的第一个描述符。 以下列出设备描述符的范例、数值以及各个字段的意义(以C语言伪代码表示):

[cpp]  view plain  copy
  1. typedef struct _USB_DEVICE_DESCRIPTOR {  
  2.  BYTE bLength;                //0x12, 表示该描述符的字节长度为18个字节,  
  3.  BYTE bDescriptorType;        //描述符类型,1代表设备  
  4.  WORD bcdUSB;  
  5.  BYTE bDeviceClass;  
  6.  BYTE bDeviceSubClass;  
  7.  BYTE bDeviceProtocol;  
  8.  BYTE bMaxPacketSize0;  
  9.  WORD idVendor;  
  10.  WORD idProduct;  
  11.  WORD bcdDevice;  
  12.  BYTE iManufacturer;  
  13.  BYTE iProduct;  
  14.  BYTE iSerialNumber;  
  15.  BYTE bNumConfigurations;  
  16. } USB_DEVICE_DESCRIPTOR;  

        bLength 是本结构的数据长度,这样可以方便以后兼容不同的版本协议。因为不同的结构是不同的长度,这样就可以区分不同的协议了。比如有一天想添加一个字段,那么它的长度就会改变,这时就可以根据不同的长度进行解释不同的协议了。这次返回的结构长度是0x12,也就是18个字节,它的长度是从bLength长度开始,也就是说是完全整个结构的长度。
        bDescriptorType是描述符的类型。它的定义跟主控器发下来描述符的类型是一样的,如下:
类型 类型
DEVICE                              1             ENDPOINT 5          
CONFIGURATION 2 DEVICE_QUALIFIER 6
STRING  3 OTHER_SPEED_CONFIGURATION 7
INTERFACE 4 INTERFACE_POWER1 8
                                               表3、描述符类型
由于返回的是设备描述符,所以就选择了1,也就是包里显示的第二个字节01。用这个类型来区分不同的描述符。

        bcdUSB是USB发布的协议版本。也就是本设备能适用于那种协议,目前USB主要有两个版本,一个是1.10,一个是2.10版本。在本设备里,采用了1.10的协议版本。由于这个字段是采用BCD编码,所以1.10的表示为0x0110的格式,0x0210就表示USB2.10版本。

        bDeviceClass是设备分类。当它的值是0时,表示所有接口在配置描述符里,并且所有接口是独立的。当它的值是1到FEH时,表示不同的接口关联的。当它的值是FFH时,它是厂商自己定义的。在这个设备里,是定义为0。

        bDeviceSubClass是设备子分类码。当前面的bDeviceClass值是0时,这里一定要设置为0。其它就跟据USB-IF组织定义的编码。

        bDeviceProtocol是设备使用的协议。如果使用USB-IF组织定义的协议,就需要设置这里的值。如果不使用,就直接设置为0。如果厂商自己定义的可以设置为FFH。

        bMaxPacketSize0是端点0收发最大的包大小。仅允许设置8,16,32,64中的任何一个大小。

        idVendor是厂商标识。由USB-IF分配的编码。

        idProduct是厂商定义的产品标识。由厂家和产品标识,就可以让操作系统加载不同的驱动程序。

        bcdDevice是用BCD表示的设备发布的版本号。

        iManufacturer是厂商字符串的偏移值。这值主要说明了它在字符串描述符里的偏移位置。如果它设置为0,表示没有厂商字符串。字符串描述符后面会讲到。

        iProduct是产品字符串的偏移值。这值主要说明了它在字符串描述符里的偏移位置。如果它设置为0,表示没有产品字符串。字符串描述符后面会讲到。

        iSerialNumber是***字符串的偏移值。这值主要说明了它在字符串描述符里的偏移位置。如果它设置为0,表示没有***字符串。所有字符串,都是采有UNICODE编码。

        bNumConfigurations是配置描述符的个数。在这里只使用了一个配置,所以设置为1。

2)配置描述符

     配置描述符具有 9 字节的长度,针对设备给予配置的信息。以下列出配置 描述符的范例、数值以及各个字段的意义:

[cpp]  view plain  copy
  1. typedef struct _USB_CONFIGURATION_DESCRIPTOR {  
  2.  BYTE bLength;               //是配置的长度,也就是配置结构的整个长度。在这里9个字节。  
  3.  BYTE bDescriptorType;       //是描述符的类型,这里配置描述符,所以设置为02。具体含义见表3  
  4.  WORD wTotalLength;          //是所有配置设置的结构长度。包括配置描述符、接口描述符、HID或者其它描述符和端点描述符的长度。  
  5.  BYTE bNumInterfaces;        //该配置支持的接口数目(1字节)  
  6.  BYTE bConfigurationValue;   //配置值,作为Set Configuration请求的配置值  
  7.  BYTE iConfiguration;        //是说明配置的字符的偏移值  
  8.  BYTE bmAttributes;          //是配置特性,D7位保留。D6位是说明是否自供电。D5位是否支持远程唤醒。D4—D0是保留,设置为0。  
  9.  BYTE MaxPower;              //是使用的功率,它采用电流来表示。每2mA为单位,比如它的值是50时就表示是100mA的电流消耗。<div class="blogstory">                             //通过这样说明,主控器就知道这个设备是什么样的设备,有多少功能。  
  10. } USB_CONFIGURATION_DESCRIPTOR;</div>  

3)接口描述符

      接口描述符具有 9 字节的长度,用来描述每一个设备的接口特性。以下列 出接口描述符的范例、数值以及各个字段的意义:

[cpp]  view plain  copy
  1. typedef struct _USB_INTERFACE_DESCRIPTOR {  
  2.  BYTE bLength;               //表示该描述符的字节长度为9个字节.  
  3.  BYTE bDescriptorType;       //描述符类型,4代表接口。具体含义见表3  
  4.  BYTE bInterfaceNumber;      //接口数目以0为基值(表示一个接口)。  
  5.  BYTE bAlternateSetting;     //域,交互设置值为0(因为只有一个接口)  
  6.  BYTE bNumEndpoints;         //端点数目设置为2  
  7.  BYTE bInterfaceClass;       //接口群组,$FF表示是供应商说明书  
  8.  BYTE bInterfaceSubClass;    //接口次群组  
  9.  BYTE bInterfaceProtocol;    //接口协议,$FF表示该接口使用的 ;是供应商说明的协议  
  10.  BYTE iInterface;            //接口的字符串描述符的索引,本实例没有                         
  11. }USB_INTERFACE_DESCRIPTOR;  

(4)端点描述符

        端点描述符具有 7 字节长度,用来描述端点的属性以及各个端点的位置。 该实例中有两个端点,bEndpointAddress域的值不同,来区别IN/OUT端点

[cpp]  view plain  copy
  1. typedef struct _USB_ENDPOINT_DESCRIPTOR {  
  2.  BYTE bLength;               //表示该描述符的字节长度为 7 个字节  
  3.  BYTE bDescriptorType;       //描述符类型,5代表端点。具体含义见表3  
  4.  BYTE bEndpointAddress;      //端点地址([0x81=IN,0x02=OUT])  
  5.  BYTE bmAttributes;          //传输类型的属性设置为中断传输 ;(0=控制,1=等时,2=批量,3=中断)  
  6.  WORD wMaxPacketSize;        //域,最大信息包的大小设置为8个字节  
  7.  BYTE bInterval;             //轮询间隔,以ms为单位,在此设置为10m,0xA,                       
  8. }USB_ENDPOINT_DESCRIPTOR;  
(5) 字符串描述符
[cpp]  view plain  copy
  1. ypedef struct _USB_STRING_DESCRIPTOR {  
  2.  BYTE bLength;          //是所有数据的长度。具体长度根据bString的长度而定  
  3.  BYTE bDescriptorType;  //是描述类型,这里字符串描述符,所以它是3  
  4.  WORD bString/*[]*/;    //是可变的字符数组。不超过254个应都可以的,并且它是使用UNICODE编码的字符串。美国英语的标识,0x0409。中文的标识=0x0804。<div>                        //通过这个字符串描述符,主控器就知道字符串描述符是使用什么语言说明的了,这样就可以支持全世界的语言标识。</div>} USB_STRING_DESCRIPTOR;  

4.5.2.USB请求

   主机要获取相关信息,需要发送配置包,用C代码定义如下:

[cpp]  view plain  copy
  1. typedef struct _USB_SETUP_PACKET  
  2. {  
  3.  REQUEST_TYPE bmRequestType;  
  4.  BYTE         bRequest;  
  5.  WORD_BYTE    wValue;  
  6.  WORD_BYTE    wIndex;  
  7.  WORD         wLength;  
  8. } USB_SETUP_PACKET;  

       bmRequestType是包含有下面几方面的内容:D7 D6 D5 D4 D3 D2 D1 D0

        在这一个字节里,又按位分为:D7位是表示后面传送数据的方向位。当D7等于0时,表示后面的数据是从主控器发送到USB设备。在PC里,就是从PC机发送到USB的设备。当D7等于1时,表示后面的数据是从USB设备发送到主控器。在PC里,就是从USB设备发送到USB设备。

        D6-D5位是请求主分类型:0 是表示标准的请求。1 是表示类别的请求。2 是表示厂商的请求。3 是保留。

        D4-D0位是表求接收这个包的接口。0 是表示USB设备接收。1 是表示接口接收。2 是表示端点接收。3 是表示其它接收,不知道的。4-31是保留。

     bRequest是本描述符的请求类型,也就是后面发送的数据是什么样的东西。由于USB里有很多配置信息,比如获取设备描述符,又有设置USB地址等等,就是通过这个字节来区分的。

类型 类型
0      GET_STATUS 7     SET_DESCRIPTOR
1 CLEAR_FEATURE 8 GET_CONFIGURATION
2 Reserved for future use 9 SET_CONFIGURATION
3 SET_FEATURE 10 GET_INTERFACE
4 Reserved for future use 11 SET_INTERFACE
5 SET_ADDRESS 12 SYNCH_FRAME
6 GET_DESCRIPTOR    

                                 表4: 请求描述符的类型。

   主控器想读取USB设备的描述符,那么bRequest的值就应该是0x06.

        wValue是根据不同的请求而设置不同的值。一般就是传送参数给设备标明这是什么请求。在上面GET_DESCRIPTOR获取设备描述符里,它的值是00 01。在GET_DESCRIPTOR里这个字段的低字节表示描述符的索引,高字节表示描述符的类型。高字节的类型如表3所示。wValue值在这里的高字节是01,那么它就是设备描述符了。低字节是00,那么它就是表示从偏移地址0开始读取设备描述符。由于在配置描述符里有很多配置,所以低字节在那里就可以用来识别获取同样类型的描述符不同的配置。

        wIndex是根据不同的请求而设置不同的值。一般用来说明端点号或者说明接口标识。在获取描述符里,设置为0,或者是语言ID。在这个发送的描述符里,它是设置为00 00。

        wLength 是根据请求来决定下一阶段发送数据的长度。前面请求第一个字节里,已经说明下一阶段数据传送的方向,这里说明了传送数据的长度。不管是发送数据,还是接收数据,都不要超过这个数据长度,否则主机会出问题,或者设备有问题。

面结合实例分别介绍几个主要的设备请求。

(1) 清除特性(Clear Feature): 

bmRequestType bRequest wValue wIndex wLength data
00000000B(0端点)
00000001B(1端点)
00000010B(2端点)
CLEAR_FEATUR
(01H)
特性选择 0接口端点 0
        该请求中的wValue表示特性选择器,它对应的值为:0=端点,1=设备。当某个特点不允许取消,或该特性根本不存在,或者是指向一个根本不存在的接口或端点时,该请求将会导致设备请求失败。如果端点被固件设为停止状态,主机软件(总线驱动程序)也可以发送一个值为0的CLEAR_FEATURE命令清除该端点的停止状态,本实例中就是这样使用该请求的。

(2) 取得描述符(Get Descriptor)

bmRequestType bRequest wValue wIndex wLength data
10000000B GET_DESCRIPTOR
(06H)
描述符类型与描述符指针 0或语言ID 描述符的长度 各个描述符
        该请求中的wValue的高字节表示要取的描述符类型,低字节表示描述符的索引值,描述的类型有:1表示设备描述符,2表示配置描述符,3表示字符串描述符,4表示接口描述符,5表示端点描述符。wIndex的值为0或语言ID;当要取的描述符是字符串描述符时,该域的值为语言ID;当为其他的描述符时,该域为0。wLength表示要返回的数据长度,如果SETUP阶段的地址使用的是预设地址0(ENDP字段为0),这时的wLength值会大于实际的描述的值。这是为什么呢?原因是用户以预设的地址0来取得设备描述符时,不管设多少字节,用户最多只取其前8字节,即在控制传输过程只有一次数据阶段。但是,如果用户以新的地址(ENDP字段不为0)来取得设备描述符时,这时wLength的值就要注意了。

(3) 设置地址(Set Address)

bmRequestType bRequest wValue wIndex wLength data
10000000B SET_ADDRESS(05H) 设备地址 0 0
        该请求与其他的请求有一个重要的不同点,该请求下,USB设备一直不改变它的地址,直到该请求的状态阶段被成功地完成,而其他请求的操作都是在状态阶段之前完成,可以阅读本实例加深对该点的理解。若特定的设备地址大于127,或者wIndex 或wLength为非0值,那么该请求不执行。

(4) 设置配置(Set Configuration)

bmRequestType bRequest wValue wIndex wLength data
00000000B(0端点)
00000001B(1端点)
00000010B(2端点)
CLEAR_FEATUR
(01H)
特性选择 0接口端点 0
        该请求中的wValue域的低字节表示设置的值,该值必须为0或者与配置描述符中的配置值相匹配。如果设置值等于0,表示设备在地址状态。如果wIndex 或 wLength为非0值,那么该请求不执行。

4.5.3、设备列举

      设备列举可以简单地概括为这样的一个过程:主机通过USB设备请求来取得设备描述符并对该设备进行配置。该过程可以简化为如下5个步骤:第一步,使用预设的地址0取得设备描述符。第二步,设置设备的新地址。第三步,使用新地址取得设备描述符。第四步,取得配置描述符。第五步,设置配置描述符。        设备列举使用的是控制传输。上述的5个步骤必须符合控制传输的基本架构,第一步、第三步和第四步使用的是控制读取,第二步和第五步使用的是无数据控制