转载:http://blog.csdn.net/walkrainning/article/details/27216967
USB概述
USB是什么呢?一说USB是You SB的意思,即“你傻B”的意思。另一种说法是USB其实是美国的弟弟,因为美国叫USA,USB当然是他的弟弟了。
那么USB到底是什么呢?其实USB是通用串行总线(Universal Serial Bus)的简写,它已经有了10多年的历史了。USB协议出现过的版本有USB1.0,USB1.1,USB2.0。由于USB是主从模式,设备与设备之间、主机与主机之间不能互连,为了解决这个问题,扩大USB的使用范围,就出现了USB OTG(On The Go)。USB OTG的做法是,同一个设备,在不同的场合下可以在主机或从机之间切换。在USB1.0和USB1.1版本中,只支持1.5Mbps的低速(Low Speed)模式和12Mbps的全速(Full Speed)模式。在USB2.0中,又加入了480Mbps的高速(High Speed)模式。值得注意的是,USB2.0并不是高速设备的代名词,详述请看《误区:关于USB2.0和USB高速传输模式》一文。
USB具有很多优点,例如即插即用,容易使用,方便携带,传输速度快,可扩展性强,标准统一,价格便宜等等。目前流行的USB设备有移动硬盘,数码相机,MP3,U盘,USB鼠标、键盘、游戏杆,USB MIDI键盘,USB摄相头,USB打印机,USB扫描仪,USB声卡,USB话筒,USB网卡,USB显示器,USB电话,具有USB口的各种仪表仪器等等,只要是能跟电脑打交道的,就基本上可以通过USB来实现,足见USB功能的强大。
然而USB也有一些缺点,例如传输距离短,开发、调试较困难等等。当然,它还有一个更大的缺点,那就是你发现要找出它的缺点很件很令人头疼的事情。
要开发USB,一个网站是开发者必须要知道的,那就是小组:http://group.ednchina.com/93/。呵呵,不好意思,搞错了,其实是USB开发者论坛,网址是Http://www.usb.org。此外,还有其它一些USB相关的网站也不错,例如驱动程序开发网:http://www.driverdevelop.com/,程序员联合开发网:http://www.pudn.com/等等。
要开发USB,看书是少不了的。推荐一本叫做《计算机USB系统原理及其主/从机设计》(马伟 编著,北京航天航空大学出版社)的书,我觉得这本书写得很不错,而且也不贵,只要29.5¥,如果你去http://www.2688.com网站购书的话,还可以打7.5折,只需22¥。至于协议,我还是推荐大家看英文原版的,这样理解起来更准确。当然现在也有好多协议翻译的书,大家可以在网上搜索一下。也有很多电子版的,这样可以节省一些钱。
我们的口号是:“现在USB技术已经很流行了,就像以前的串口一样。以前的电子工程师不会搞串口通信就落伍了,而现在的电子工程师如果不会搞USB通信,那就落伍了。电子工程师门,还等什么,赶紧加入小组来学习USB吧……”,恩,这个口号有点长,将就一下吧。
USB的连接模型
USB是一种主从结构。主机叫做Host,从机叫做Device(也叫做设备),集线器也被当作一种特殊的设备处理。USB的数据交换只能发生在主机和设备之间,主机和主机,设备和设备之间不能互连。为了在物理上区分主机和设备,使用了不同的插头和插座,这个在USB的连接器一节中会讲到。所有的数据传输都由主机主动发起,而设备只是被动的负责应答。例如,在读数据时,USB先发出读命令,设备收到该命令后,才返回数据。在USB OTG中,一个设备可以在从机和主机之间切换,这样就可以实现设备与设备之间的连接,大大增加了USB的使用范围。但这时依然没有脱离这种主从关系,两个设备之间必然有一个作为主机,另一个作为从机。USB OTG增加了一种MINI USB接头,比普通的4线USB多了一个ID表识线,用来表明它是主机还是设备,这个以后会讲到。
USB的拓扑结构为金字塔型。由一个USB主控制器出发,下面接USB集线器,USB集线器将一个USB口扩展为多个USB口,多个USB口又可以通过集线器为更多个接口。但USB协议中对集线器的层数是有限制的,USB1.1规定最多为4层,USB2.0规定最多为6层。理论上,一个USB主控制器最多可接127个设备,这是由数据包中的7位地址位决定的,但是实际上不会接这么多的设备。我们所说的一个USB主控制器可以连接多个USB设备,并不是直接简单的将多个设备并联或者串联,而是要由集线器负责端口扩展,才能连接更多的设备。在我们的电脑上,也有一个(或者多个,视USB主控制器的个数而定)集线器,它叫做根集线器,直接连在USB主控制器上。在设备管理器中,我们可以看到USB主控制器和根集线器。如下图所示。USB数据传输路径如下:USB主控制器发出数据包,通过根集线器,再通过下面的集线器(如果有的话),再发给USB设备;设备返回数据,交给它上层的集线器,上层的集线器再交给更上层的集线器,直到USB主控制器为止。而USB主控制器就可以跟CPU打交道了。在标准的PC机上,USB主控制器是挂在PCI总线上的。在Windows中,USB由各种驱动程序负责管理,最后由驱动程序产生功能设备(FDO),这就是我们所看到的实际设备了。我们的应用程序就可以通过Windows提供的各种API进行访问USB设备了,例如CreateFile,ReadFile,DeviceIOControl等等。
USB的电气特性
标准的USB使用4根线:5V电源线(Vbus),差分数据线负(D-),差分数据线正(D+),地(Gnd)。在USB OTG中,又增加了一种mini接口,使用的是5根线,比标准的USB多了一根身份识别(ID)线。USB使用的是差分传输模式,有两根数据线,分别是D+和D-。在USB的低速和全速模式中,采用的是电压传输模式。而在高速模式下,则是电流传输模式。关于具体的高低电平门限值,请参看USB协议。为了防止出现长时间的0或者1(这样不利于时钟信号的提取),在发送数据前要经过位填充处理。然后再将数据串行化,发送到数据线上,由两根数据线的差分值来表示0或者1。而在接收端,则刚好是相反的过程。接收端采样数据线,将数据并行化,并同时去掉未填充,然后解析数据。通常,我们使用现成的USB芯片,像位填充,串行化这些芯片内部的硬件已经帮我们做好了,因此通常我们并不用关心这些细节。在设备接收数据时,芯片的串行接口引擎(SIE)会接收属于自己地址的数据,并根据相应的端口号,放到相应的缓冲区内,并返回ACK给主机进行确认,然后产生中断请求,通知我们的程序,已经收到数据包了。在我们还未处理完缓冲区的数据之前,如果再收到对该端点的输出请求,USB芯片将会使用NAK返回,告诉主机端点现在忙,主机检测到NAK后,过段时间会重试输出数据,直到超时为止;发送数据时,用户将数据写入USB芯片的缓冲区,并通知USB芯片缓冲区内数据可用,然后USB芯片检测到主机请求对应的端点输入时,它就会将数据返回,数据发送完毕并收到主机的ACK确认之后,产生中断请求通知应用程序数据已经发送完毕。如果USB芯片已经收到了输入请求,但是用户程序还未填充好缓冲区,它也会用NAK返回,告诉主机数据还未准备好。主机收到NAK后,过段时间会重试,直到超时为止。在USB协议中规定,设备在未配置之前,可以从Vbus上最多获取100mA的电流;在配置之后,最多可从Vbus上获取500mA的电流。Vbus是5V的电压,具体的参数请参看USB协议。
USB的线缆以及插头、插座
USB是一个标准的协议,因此对线缆、插头、插座等有严格的规范要求。
在最初的标准里,USB接头有4条线:电源,D-,D+,地线。我们暂且把这样的
叫做标准的USB接头吧。后来OTG出现了,又增加了miniUSB接头。而miniUSB接头
则有5条线,多了一条ID线,用来标识身份用的。标准USB口只有A型和B型。其中每
一型又分为插头和插座,例如A型插头,A型插座等。我们平常电脑上用的那种插
座叫做A型USB插座,而相应的插头,叫做A型插头,例如U盘上那种。而像打印机
上面那个插座,则是B型插座(比较四方的,没电脑上面那种扁),相应的插头,就
是B型插头。也许你见过一头方一头扁的USB延长线,没错了,扁的那头就叫做A型
插头,而方的那头,就叫做B型插头,而相应的被插的那两个插座,就分别是A型
插座和B型插座了。A型插头是插不进B型插座的,反之亦然。
miniUSB也分为A型,B型,但增加了一个AB型(不是血型呀,别搞错了,没有
O型^_^)。既然它叫做miniUSB,那么当然它就是很小的了,主要是给便携式设备
用的,例如MP3、手机、数码相机等。USB是一主多从结构,即一个时刻只能有一
台主机。像PC机就是一个主机,其它的只能是设备,因而两个设备之间是无法直
接进行通信的。而USB OTG(on the go)的出现,则解决了这个矛盾:一个设备可
以在某种场合下,改变身份,以主机的形式出现。因而就出现了AB型的miniUSB插
座,不管是A型miniUSB插头,还是B型miniUSB插头,都可以插进去,而靠里面多
出的那条ID线来识别它的身份:是主机还是从机。这样两个USB设备就可以直接连
接起来,进行数据传送了。 像我们MP3上用的那中miniUSB插座,就是B型的
miniUSB插座(注意,有一类miniUSB插座,似乎不是USB规范里面的,因为miniUSB
接头应该有5条线,而这种插座只有4条线)。
更详细的数据,例如封装尺寸等,请参看USB协议。
附图:各种USB插头和插座,来自USB协议。
USB设备的检测
USB主机是如何检测到设备的插入的呢?首先,在USB集线器的每个下游端口的D+和D-上,分别接了一个15K欧姆的下拉电阻到地。这样,在集线器的端口悬空时,就被这两个下拉电阻拉到了低电平。而在USB设备端,在D+或者D-上接了1.5K欧姆上拉电阻。对于全速和高速设备,上拉电阻是接在D+上;而低速设备则是上拉电阻接在D-上。这样,当设备插入到集线器时,由1.5K的上拉电阻和15K的下拉电阻分压,结果就将差分数据线中的一条拉高了。集线器检测到这个状态后,它就报告给USB主控制器(或者通过它上一层的集线器报告给USB主控制器),这样就检测到设备的插入了。USB高速设备先是被识别为全速设备,然后通过HOST和DEVICE两者之间的确认,再切换到高速模式的。在高速模式下,是电流传输模式,这时将D+上的上拉电阻断开。
一个简单的实验:只用一个上拉电阻接在USB的+5V和D+或者D-上,WINDOWS也会提示发现新硬件,但是无法找到驱动程序。这时去设备管理器里面看,有显示未知USB设备,并且其VID和PID为0。根据这个,我们可以简单的判断设备是否枚举成功。如下图所示,分别是枚举不成功和枚举成功的图。
USB设备的枚举过程
USB主机在检测到USB设备插入后,就要对设备进行枚举了。为什么要枚举呢?枚举就是从设备读取一些信息,知道设备是什么样的设备,如何进行通信,这样主机就可以根据这些信息来加载合适的驱动程序。调试USB设备,很重要的一点就是USB的枚举过程,只要枚举成功了,那么就已经成功大半了。
在说枚举之前,先大概说一下USB的一种传输模式——控制传输。这种传输在USB中是非常重要的,它要保证数据的正确性,在设备的枚举过程中都是使用控制传输。控制传输分为三个过程:①建立过程。②可选的数据过程。③状态过程。建立(Setup)过程都是由USB主机发起,它开始于一个Setup令牌包,后面紧跟一个DATA0包。如果是控制输入传输,那么数据过程就是输入数据;如果是控制输出传输,那么数据过程是输出数据。如果在设置过程中,指定了数据长度为0,则没有数据过程。数据过程之后是状态过程。状态过程刚好与数据过程的数据传输方向相反:如果是控制输入传输,则状态过程是一个输出数据包;如果是控制输出传输,则状态过程是一个输入数据包。状态阶段用来确认所有的数据都已经正确传输。
好了,下面我们来看看枚举的详细过程。
首先,USB主机检测到USB设备插入后,就会先对设备复位。设备复位后,USB主机就会对地址为0的设备发送获取设备描述符的标准请求。所有的USB设备在总线复位后其地址都为0,这样主机就可以跟那些刚刚插入的设备通过地址0通信。主机在建立阶段发出获取设备描述符的输入请求,设备收到该请求后,在数据过程将设备描述符返回给主机。主机在成功获取到一个数据包的设备描述符后并且确认没有什么错误后(注意:有些USB设备的端点0大小不足18字节(但至少具有8字节),而标准的设备描述有18字节,在这种情况下,USB设备只能暂时按最大包将部分设备描述符返回,而主机在成功获取到前面一部分描述符后,就不会再请求剩下的设备描述符部分,而是进入设置地址阶段),就会返回一个0长度的状态数据包给设备。
然后主机再对设备复位一下,接下来就会进入到设置地址阶段。这时USB主机发出一个设置地址的请求(建立过程,设置地址无数据过程),地址包含在建立包中,具体的地址USB主机会负责管理,它会分配一个唯一的地址给新的设备。USB设备在收到地址后,返回0长度的状态包,主机收到0长度的状态包之后,会返回一个ACK给设备。设备在收到这个ACK之后,就可以启用新的地址了。这样设备就分配到了一个唯一的设备地址,以后主机就通过它来进行访问该设备。
然后主机再次获取设备描述符,这次跟第一次可能有点不一样,这次需要获取完全部的18个字节的设备描述符。当然,如果你的端点0缓冲大于18字节的话,那就跟第一次的情形一样了。
接下来,主机就会获取配置描述符。配置描述符总共为9字节。主机在获取到配置描述符后,根据里面的配置集合总长度,再获取配置集合。配置集合包括配置描述符,接口描述符,端点描符等等。
如果有字符串描述符的话,还要获取字符串描述符。另外HID设备还有HID描述符等。使用BUS HOUND以及通过串口返回信息,很容易看到具体的过程。总之是主机请求什么,你的程序就响应什么。
下面这些数据是使用BUS HOUND抓的,这个是在WIN2000下抓到的,如果在WINXP下,就看不到设置地址之前的数据。
写了注释下面的部分就是主机和设备之间的数据通信,而其它的则是主机跟根集线器之间的通信数据。
Device Phase Data Description Cmd.Phase.Ofs(rep) Delta
------ ----- -------------------------------------------------- ---------------- ------------------ -----
14.1 DI 02 . 1.1.0 3.4sc
14.0 CTL a3 00 00 00 01 00 04 00 GET STATUS 2.1.0(2) 17us
14.0 DI 01 01 01 00 .... 2.2.0 27us
14.0 CTL 23 01 10 00 01 00 00 00 CLEAR FEATURE 4.1.0 110us
14.0 CTL a3 00 00 00 01 00 04 00 GET STATUS 5.1.0(2) 56us
14.0 DI 01 01 00 00 .... 5.2.0 14us
14.0 CTL 23 03 04 00 01 00 00 00 SET FEATURE 7.1.0 109ms
14.1 DI 02 . 8.1.0 90ms
14.0 CTL a3 00 00 00 01 00 04 00 GET STATUS 9.1.0 23us
14.0 DI 03 01 10 00 .... 9.2.0 22us
14.0 CTL 23 01 14 00 01 00 00 00 CLEAR FEATURE 10.1.0 8us
14.0 CTL a3 00 00 00 01 00 04 00 GET STATUS 11.1.0 19ms
14.0 DI 03 01 00 00 .... 11.2.0 34us
/第一次获取设备描述符请求//
14.0 CTL 80 06 00 01 00 00 40 00 GET DESCRIPTOR 12.1.0 239us
/第一次返回设备描述符//
/由于该端点缓冲只有16字节,因此只读到16字节///
14.0 DI 12 01 10 01 00 00 00 10 65 10 36 21 01 00 00 00 ........e.6!.... 12.2.0 67ms
14.0 CTL a3 00 00 00 01 00 04 00 GET STATUS 13.1.0 49us
14.0 DI 03 01 00 00 .... 13.2.0 27us
14.0 CTL 23 03 04 00 01 00 00 00 SET FEATURE 14.1.0 24us
14.1 DI 02 . 15.1.0 112ms
14.0 CTL a3 00 00 00 01 00 04 00 GET STATUS 16.1.0 21us
14.0 DI 03 01 10 00 .... 16.2.0 25us
14.0 CTL 23 01 14 00 01 00 00 00 CLEAR FEATURE 17.1.0 8us
设置地址,地址为2/
14.0 CTL 00 05 02 00 00 00 00 00 SET ADDRESS 18.1.0 19ms
///第二次获取设备描述符请求//
14.0 CTL 80 06 00 01 00 00 12 00 GET DESCRIPTOR 19.1.0 40ms
第二次返回了全部的18字节设备描述符//
14.0 DI 12 01 10 01 00 00 00 10 65 10 36 21 01 00 00 00 ........e.6!.... 19.2.0 75ms
02 01 .. 19.2.16
获取9字节的配置描述符/
14.0 CTL 80 06 00 02 00 00 09 00 GET DESCRIPTOR 20.1.0 44us
返回9字节的配置描述符/
可以看到配置描述符集合长度为0x20(即32)字节//
14.0 DI 09 02 20 00 01 01 00 80 dd .. ...... 20.2.0 50ms
14.0 CTL a3 00 00 00 02 00 04 00 GET STATUS 21.1.0 214ms
14.0 DI 00 01 00 00 .... 21.2.0 34us
//再一次获取设备描述符//
17.0 CTL 80 06 00 01 00 00 12 00 GET DESCRIPTOR 22.1.0 3.8ms
/返回设备描述符
17.0 DI 12 01 10 01 00 00 00 10 65 10 36 21 01 00 00 00 ........e.6!.... 22.2.0 67ms
02 01 .. 22.2.16
//获取配置描述符///
17.0 CTL 80 06 00 02 00 00 09 00 GET DESCRIPTOR 23.1.0 28us
///返回配置描述符///
17.0 DI 09 02 20 00 01 01 00 80 dd .. ...... 23.2.0 50ms
/获取0x20字节的配置描述符集合,包括配置描述符,接口描述符,端点描述符等/
17.0 CTL 80 06 00 02 00 00 20 00 GET DESCRIPTOR 24.1.0 30us
/返回了0x20字节的配置描述符集合/
17.0 DI 09 02 20 00 01 01 00 80 dd 09 04 00 00 02 08 06 .. ............. 24.2.0 83ms
50 00 07 05 82 02 40 00 00 07 05 02 02 40 00 00 P.....@......@.. 24.2.16
获取字符串描述符的语言ID///
17.0 CTL 80 06 00 03 00 00 02 00 GET DESCRIPTOR 25.1.0 29us
///设备返回语言ID,第一字节表示语言ID的长度///
17.0 DI 04 03 .. 25.2.0 45ms
根据长度获取4字节的字符串描述符的语言ID///
17.0 CTL 80 06 00 03 00 00 04 00 GET DESCRIPTOR 26.1.0 17ms
设备返回完整的语言ID
17.0 DI 04 03 09 04 .... 26.2.0 53ms
/获取索引为2的字符串描述符///
17.0 CTL 80 06 02 03 09 04 02 00 GET DESCRIPTOR 27.1.0 27us
//设备返回字符串描述符,第一字节为该字符串描述符的长度//
17.0 DI 12 03 .. 27.2.0 46ms
/根据长度获取索引为2的字符串描述符///
17.0 CTL 80 06 02 03 09 04 12 00 GET DESCRIPTOR 28.1.0 23us
/设备返回完整的0x12字节字符串描述符/
17.0 DI 12 03 32 00 30 00 37 00 31 00 30 00 39 00 38 00 ..2.0.7.1.0.9.8. 28.2.0 70ms
32 00 2. 28.2.16
设置配置///
17.0 CTL 00 09 01 00 00 00 00 00 SET CONFIG 29.1.0 28us
设置接口
17.0 CTL 01 0b 00 00 00 00 00 00 SET INTERFACE 30.1.0 22ms
//获取最大逻辑单元/
17.0 CTL a1 fe 00 00 00 00 01 00 GET MAX LUN 31.1.0 158us
17.0 DI 00 . 31.2.0 53ms
///USB大容量存储设备的命令块封包(CBW)
17.2 DO 55 53 42 43 c8 58 25 81 24 00 00 00 80 00 06 12 USBC.X%.$....... 32.1.0 3.0ms
00 00 00 24 00 00 00 00 00 00 00 00 00 00 00 ...$........... 32.1.16
USB是个通用的总线,端口都是统一的。但是USB设备却各种各样,
例如USB鼠标,USB键盘,U盘等等,那么USB主机是如何识别出不同的
设备的呢?这就要依赖于描述符了。
USB的描述符主要有设备描述符,配置描述符,接口描述符,
端点描述符,字符串描述符,HID描述符,报告描述符等等。
关于报告描述符,请看我以前写的:《USB HID报告及报告描述符简介 》
http://group.ednchina.com/93/198.aspx。
一个USB设备有一个设备描述符,设备描述符里面决定了该设备有多
少种配置,每种配置描述符对应着配置描述符;而在配置描述符中又定义
了该配置里面有多少个接口,每个接口有对应的接口描述符;在接口描
述符里面又定义了该接口有多少个端点,每个端点对应一个端点描述符;
端点描述符定义了端点的大小,类型等等。由此我们可以看出,USB的
描述符之间的关系是一层一层的,最上一层是设备描述符,下面是配置
描述符,再下面是接口描述符,再下面是端点描述符。在获取描述符时,
先获取设备描述符,然后再获取配置描述符,根据配置描述符中的配置
集合长度,一次将配置描述符、接口描述符、端点描述符一起一次读回。
其中可能还会有获取设备序列号,厂商字符串,产品字符串等。
每种描述符都有自己独立的编号,如下:
下面分别详细介绍一下各描述符。
1.设备描述符
2.配置描述符