天天看點

USB 協定分析 初始化

現在非常流行USB裝置,時時刻刻都在我們身邊,比如使用U盤,使用MP3,使用手機,都需要與PC的USB通訊。通過USB的接口,使用我們的生活非常友善了,想什麼時候聽歌,就可以插入MP3到PC機那裡,然後再從PC上下載下傳MP3。這個過程在使用者看來是非常的簡單,不需要安裝驅動程式,不需要斷掉PC 機的電源。真正展現“科技以人為本”的思想。使用這麼友善的U盤或者MP3,那麼我們又需要去問個為什麼了,為什麼會這麼友善呢?往往友善的背後,就意味着需要工程師做大量的工作,才會讓大家使用USB這麼友善。下面就來詳細地分析USB的協定。

USB通訊是非常複雜的,剛剛協定定義就厚厚的一本,要完全地去看完,并且了解它,是很費時間的事情。希望本文可以提供給你一個非常好的指導,讓你深入地了解USB 的協定。USB的協定是主從協定,在所有通訊裡,隻有一個主要器,其它都是從裝置。最多能接127個從裝置,因為協定裡隻保留了7bit作為裝置位址。所有的USB資料交流都是由主要器發起,其它從裝置進行響應。現在就以WINDOWS上的USB通訊來學習一下USB的通訊協定,後面所有提到的資料,都是 WINDOWS上的USB驅動程式發下來的資料。

為了把所有通訊資料都顯示出來,我找到了一個USB的ARM開發闆,通過這個開發闆,就可以把主要器所有資料列印出來,并作相應的分析。同時,使用這個開發闆,也可以用來調試龍芯的主要器驅動程式的調試。

當USB的開發闆加電時,就會先初始化USB的連接配接,但沒有插入PC的連接配接線,這時開發闆就會從序列槽輸出下面的字元:

USB Suspend

USB Resume

從上面看到,從裝置的USB一直不斷地挂起和喚醒,直到插入PC的連接配接線。當插入連接配接線到PC時,就會收到主要器發來的資訊。下面的資料,就是開發闆與USB主要器交流的資料。

1.收到主要器的擷取裝置描述符配置包。

Setup m=0,n=0,val=37

80 06 00 01 00 00 40 00

這是主要器發來第一個配置包資料。由于主要器不知道USB裝置裝置描述符有多長,是以包的最後裡的長度是0x0040,也就是64個位元組長度。

REQUEST_STANDARD=0x6

USB_DEVICE_DESCRIPTOR_TYPE(0)

根據USB的協定,分析上面的資料,就知道它是擷取裝置描述符。為了實作即插即用,就需要對插入來的裝置進行擷取描述資訊,才知道這個USB的裝置是什麼樣的裝置,是U盤,還是HID的鍵盤。是以,USB裝置就傳回下面的資料給主要器:

USB_DataInStage,cnt=18,EP0Data.Count=18

12 01 10 01 00 00 00 40 00 80 00 80 00 01 04 2C 4A 01

這條資料,就是USB的裝置描述符,描述了這個裝置使用什麼USB的協定版本,這裡是1.1的版本,還有廠家辨別、産品辨別,以及廠家、産品和序列号等字元串的偏移位址。

USB_EVT_IN,USB_DataInStage

USB_DataInStage,cnt=0,EP0Data.Count=0

USB_EVT_OUT

通過裝置描述符,就可以讓主要器知道這個裝置是使用什麼版本的USB協定,是高速的裝置,還是低速的裝置,是誰産生的,是什麼産品,然後作業系統就可以通過這些資訊去找到相應的驅動程式,如果作業系統沒有找到相應的驅動程式,就會提示使用者插入CD光牒,或者其它方來安裝相應的驅動程式。

2.收到主要器的設定裝置位址配置包。

Setup m=0,n=0,val=37

00 05 01 00 00 00 00 00

這條資料,根據USB的協定,就可以知道它是設定USB裝置的位址配置包。它的作用,就是配置設定USB裝置的位址,由于USB總線上可以有127個裝置,那麼每個裝置都需要配置設定一個唯一的位址才通訊,這跟網卡的MAC位址的作用是一樣的。其實,就像配置設定門牌号,讓大家看到那個門牌就知道什麼房了。

從下面的資料分析來看,是配置設定位址為1.

REQUEST_STANDARD=0x5

USB_DeviceAddress=129

USB_EVT_IN,USB_SetAddress(1)

3.收到主要器的擷取裝置描述符配置包。

Setup m=0,n=0,val=37

80 06 00 01 00 00 12 00

再次收到擷取裝置描述符的配置包,由于第一次不知道裝置描述符有多大,因而總是發送一個最大資料的包,就是64個位元組大,現在知道描述符的大小為0x0012個大小了,就把它發送下來了。是以USB裝置再次回應裝置描述符就可以了。

REQUEST_STANDARD=0x6

USB_DEVICE_DESCRIPTOR_TYPE(1)

USB_DataInStage,cnt=18,EP0Data.Count=18

12 01 10 01 00 00 00 40 00 80 00 80 00 01 04 2C 4A 01

在這裡再次回應裝置描述符。

USB_EVT_IN,USB_DataInStage

USB_DataInStage,cnt=0,EP0Data.Count=0

4.收到配置描述符包

Setup m=0,n=0,val=45

80 06 00 02 00 00 09 00

根據USB協定可以知道,這是一個配置描述符的包,也就是讓USB裝置發送本裝置有多少個配置方式給主要器。由于USB的裝置是多種多樣,滿足不同的使用者需要的。比如滑鼠和鍵盤,就是不同的裝置了。還有MP3播放器,還有各種數位相機等等,都是不一樣的裝置了。就可以通過下面的方式來說明這個配置有多少種方式,主要是通訊的方式。

REQUEST_STANDARD=0x6

USB_CONFIGURATION_DESCRIPTOR_TYPE(2)(Offset=0x0)

USB_DataInStage,cnt=9,EP0Data.Count=9

09 02 22 00 01 01 00 01 32

這裡就是USB裝置傳回配置描述符給主要器的,它主要說明了這個裝置有多少個配置,比如定義端點的類型,端點的傳送方式,還有這個裝置使用USB總線的電源多少。

USB_EVT_IN,USB_DataInStage

USB_DataInStage,cnt=0,EP0Data.Count=0

5.收到字元串描述符包

Setup m=0,n=0,val=45

80 06 00 03 00 00 FF 00

這裡收到擷取字元串描述符。由于在裝置描述符裡已經說明字元串描述符在那裡,主要是偏移位址,比如04就是廠家的描述符。

REQUEST_STANDARD=0x6

USB_STRING_DESCRIPTOR_TYPE(3)(Offset=0x0)

USB_DataInStage,cnt=4,EP0Data.Count=4

04 03 09 04

這裡傳回偏移位址為0的字元串描述,其實那裡是儲存字元串描述符的語言描述辨別,這裡英語的辨別,0X0409。

USB_EVT_IN,USB_DataInStage

USB_DataInStage,cnt=0,EP0Data.Count=0

6.收到字元串描述符包

Setup m=0,n=0,val=45

80 06 4A 03 09 04 FF 00

這時收到擷取字元串描述符,根據偏移位址,就知道它是想傳回0x4A的字元串,也就是字元串描述符裡的偏移位址。

REQUEST_STANDARD=0x6

USB_STRING_DESCRIPTOR_TYPE(4)(Offset=0x4A)

USB_DataInStage,cnt=36,EP0Data.Count=36

24 03 43 00 41 00 49 00 32 00 30 00 30 00 37 00 30 00 33 00 32 00 35 00 20 00 31 00 2E 00 30 00 2E 00 30 00

USB裝置傳回0x4A的字元串給PC。這裡是我放置的字元串:

CAI20070325 1.0.0

它作為産品的序列号。由于采用UNICODE編碼,所有高位位元組全是0。由于USB協定是使用小端格式來發送資料,是以都低位在前,高位在後。這樣在PC那裡就可以看到USB裝置的産品序列号了。又前進了一步。

USB_EVT_IN,USB_DataInStage

USB_DataInStage,cnt=0,EP0Data.Count=0

7.收到第二個配置描述符包

Setup m=0,n=0,val=45

80 06 00 02 00 00 FF 00

這裡收到是第二個配置描述符包,與第一個的差別是傳回長度不同。

第一個配置包傳回的長度是9個位元組,而這裡的長度是255。

REQUEST_STANDARD=0x6

USB_CONFIGURATION_DESCRIPTOR_TYPE(5)(Offset=0x0)

USB_DataInStage,cnt=34,EP0Data.Count=34

09 02 22 00 01 01 00 01 32 09 04 00 00 01 03 00 00 6E 09 21 00 01 00 01 22 24 00 07 05 81 03 40 00 20

在這裡傳回全部配置描述給PC,讓PC知道USB裝置所有的配置。在這裡包括裝置配置,接口配置,端點配置,還有裝置特别配置資訊。

USB_EVT_IN,USB_DataInStage

USB_DataInStage,cnt=0,EP0Data.Count=0

8.收到字元串描述符包

Setup m=0,n=0,val=45

80 06 00 03 00 00 FF 00

收到PC的字元串描述符,後面裝置就傳回。

REQUEST_STANDARD=0x6

USB_STRING_DESCRIPTOR_TYPE(6)(Offset=0x0)

USB_DataInStage,cnt=4,EP0Data.Count=4

04 03 09 04

傳回裝置描述符的語言定義。

USB_EVT_IN,USB_DataInStage

USB_DataInStage,cnt=0,EP0Data.Count=0

9.收到字元串描述符包

Setup m=0,n=0,val=45

80 06 2C 03 09 04 FF 00

收到PC需要産品字元串。

REQUEST_STANDARD=0x6

USB_STRING_DESCRIPTOR_TYPE(7)(Offset=0x2C)

USB_DataInStage,cnt=30,EP0Data.Count=30

1E 03 42 00 69 00 67 00 53 00 6C 00 6F 00 70 00 65 00 33 00 44 00 20 00 48 00 49 00 44 00

這裡USB裝置傳回産品字元串給PC了。

USB_EVT_IN,USB_DataInStage

USB_DataInStage,cnt=0,EP0Data.Count=0

10.收到字元串描述符包

Setup m=0,n=0,val=45

80 06 00 03 00 00 FF 00

這裡收到字元串描述符。

REQUEST_STANDARD=0x6

USB_STRING_DESCRIPTOR_TYPE(8)(Offset=0x0)

USB_DataInStage,cnt=4,EP0Data.Count=4

04 03 09 04

傳回語言辨別。

USB_EVT_IN,USB_DataInStage

USB_DataInStage,cnt=0,EP0Data.Count=0

11.收到字元串描述符包

Setup m=0,n=0,val=45

80 06 2C 03 09 04 FF 00

收到PC需要産品字元串。

REQUEST_STANDARD=0x6

USB_STRING_DESCRIPTOR_TYPE(9)(Offset=0x2C)

USB_DataInStage,cnt=30,EP0Data.Count=30

1E 03 42 00 69 00 67 00 53 00 6C 00 6F 00 70 00 65 00 33 00 44 00 20 00 48 00 49 00 44 00

這裡USB裝置傳回産品字元串給PC了。

USB_EVT_IN,USB_DataInStage

USB_DataInStage,cnt=0,EP0Data.Count=0

12.收到字元串描述符包

USB_EVT_OUT

Setup m=0,n=0,val=37

80 06 00 03 00 00 FF 00

這裡收到字元串描述符。

REQUEST_STANDARD=0x6

USB_STRING_DESCRIPTOR_TYPE(10)(Offset=0x0)

USB_DataInStage,cnt=4,EP0Data.Count=4

04 03 09 04

傳回語言辨別。

USB_EVT_IN,USB_DataInStage

USB_DataInStage,cnt=0,EP0Data.Count=0

×××××××××××××××××××××××××××××××××××××××××××××

13.收到字元串描述符包

Setup m=0,n=0,val=45

80 06 2C 03 09 04 FF 00

收到PC需要産品字元串。

REQUEST_STANDARD=0x6

USB_STRING_DESCRIPTOR_TYPE(11)(Offset=0x2C)

USB_DataInStage,cnt=30,EP0Data.Count=30

1E 03 42 00 69 00 67 00 53 00 6C 00 6F 00 70 00 65 00 33 00 44 00 20 00 48 00 49 00 44 00

這裡USB裝置傳回産品字元串給PC了

USB_EVT_IN,USB_DataInStage

USB_DataInStage,cnt=0,EP0Data.Count=0

14.收到字元串描述符包

Setup m=0,n=0,val=45

80 06 00 03 00 00 FF 00

REQUEST_STANDARD=0x6

USB_STRING_DESCRIPTOR_TYPE(12)(Offset=0x0)

USB_DataInStage,cnt=4,EP0Data.Count=4

04 03 09 04

USB_EVT_IN,USB_DataInStage

USB_DataInStage,cnt=0,EP0Data.Count=0

Setup m=0,n=0,val=45

80 06 2C 03 09 04 FF 00

REQUEST_STANDARD=0x6

USB_STRING_DESCRIPTOR_TYPE(13)(Offset=0x2C)

USB_DataInStage,cnt=30,EP0Data.Count=30

1E 03 42 00 69 00 67 00 53 00 6C 00 6F 00 70 00 65 00 33 00 44 00 20 00 48 00 49 00 44 00

USB_EVT_IN,USB_DataInStage

USB_DataInStage,cnt=0,EP0Data.Count=0

15 收到字元串描述符包

USB_EVT_OUT

Setup m=0,n=0,val=45

80 06 00 01 00 00 12 00

這裡收到需要傳回廠商字元串的請求。

REQUEST_STANDARD=0x6

USB_DEVICE_DESCRIPTOR_TYPE(14)

USB_DataInStage,cnt=18,EP0Data.Count=18

12 01 10 01 00 00 00 40 00 80 00 80 00 01 04 2C 4A 01

在這裡傳回裝置的廠商字元串給PC。

USB_EVT_IN,USB_DataInStage

USB_DataInStage,cnt=0,EP0Data.Count=0

16.收到第三個配置描述符包

Setup m=0,n=0,val=45

80 06 00 02 00 00 09 00

REQUEST_STANDARD=0x6

USB_CONFIGURATION_DESCRIPTOR_TYPE(15)(Offset=0x0)

USB_DataInStage,cnt=9,EP0Data.Count=9

09 02 22 00 01 01 00 01 32

USB_EVT_IN,USB_DataInStage

USB_DataInStage,cnt=0,EP0Data.Count=0

17. 收到第四個配置描述符包

Setup m=0,n=0,val=45

80 06 00 02 00 00 22 00

REQUEST_STANDARD=0x6

USB_CONFIGURATION_DESCRIPTOR_TYPE(16)(Offset=0x0)

USB_DataInStage,cnt=34,EP0Data.Count=34

09 02 22 00 01 01 00 01 32 09 04 00 00 01 03 00 00 6E 09 21 00 01 00 01 22 24 00 07 05 81 03 40 00 20

根據長度傳回不同的資料。

USB_EVT_IN,USB_DataInStage

USB_DataInStage,cnt=0,EP0Data.Count=0

18.收到設定描述符包

Setup m=0,n=0,val=45

00 09 01 00 00 00 00 00

REQUEST_STANDARD=0x9

USB_SetConfiguration WB.L =1

USB_Configure(TRUE)

USB_SetConfiguration true

由上面可以知道經過這麼多次來回後,主要器已經配置完成,對這個裝置可以使用了。這時,如果在WINDOWS裡就會看到可以裝置安裝完成,可以使用了。

19.收到設定空閑描述符包

Setup m=0,n=0,val=37

21 0A 00 00 00 00 00 00

收到這個描述符,就表明裝置在空閑狀态。

20.收到HID的報告描述符包

Setup m=0,n=0,val=37

81 06 00 22 00 00 64 00

由于在配置描述符裡,我把這個裝置描述成HID的裝置,是以會收到HID的報告描述符。

REQUEST_STANDARD=0x6

REQUEST_TO_INTERFACE (0x22)

HID_REPORT_DESCRIPTOR_TYPE

USB_DataInStage,cnt=36,EP0Data.Count=36

06 00 FF 09 01 A1 01 19 01 29 08 15 00 25 FF 95 3F 75 08 81 02 19 01 29 08 15 00 25 FF 95 3F 75 08 91 02 C0

這裡就傳回報告描述的類型,說明每次發送資料報告的大小,還有資料的格式。這裡是傳回63個位元組輸出,63個輸入的描述符。

USB_EVT_IN,USB_DataInStage

USB_DataInStage,cnt=0,EP0Data.Count=0

USB_EVT_OUT

到這裡就把USB裝置初始化完成了。從上面可以知道,要想配置一個USB裝置,需要經過20個來回才能完成配置,這個過程是非常多的。如果在調試過程中,隻要任何一個地方出錯,都不會配置成功的。如果再加上硬體的出錯,就需要花費更長的時間了。可見,USB的裝置雖然非常友善使用,但是花費了工程師大量的精力和相當多的時間。因而USB是一個非常值錢的裝置。目前USB裝置已經非常流行,今後PC機與外設的通訊,大部份都是使用USB裝置來完成的。完全會取代序列槽、并口的通訊。我看到有一個廠家開發的USB裝置,就200多種,從USB風扇到USB電話,從USB網絡到USB裝飾品,比如USB接口的彩燈。還有通過USB控制的按摩器等健身器材等等。希望你看到本文之後,又可以開發一款更好的USB裝置到來了。

繼續閱讀