1. 程式人生 > >【轉】USB上電過程 協議分析 初始化

【轉】USB上電過程 協議分析 初始化

最近換了工作,2018.0704進入ime。領導要我弄USB3.0的資料傳輸和虛擬 串列埠。網上搜的比較全面的USB資訊如下:

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協議,是高速的裝置,還是低速的裝置,是誰產生的,是什麼產品,然後作業系統就可以通過這些資訊去找到相應的驅動程式,如果作業系統沒有找到相應的驅動程式,就會提示使用者插入光碟,或者其它方來安裝相應的驅動程式。
 
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裝置到來了。