通俗易懂的USB協議詳解(轉…
但是usb的介面協議實在有點費解,linux uhci驅動作者之一Alan Stern曾經就說過“The USB documentation
is downright evil. Most of it is just crap, written by a committee. You're better off ignoring
most of it ”。
本文將從整體上介紹usb協議,包括usb host ,usb hub,usb function。希望能給讀者一個總體上的瞭解。也
因此,文章將分成相應的三部分講解。
一。usb function
1。初識usb.usb是一種序列介面協議,它靠d+,d-兩條資料線構成的差分線來進行資料傳輸,這讓我們非常感興
趣它到底和我們通常熟悉兩線rs232/485有何區別。瞭解這種區別有助於我們對usb作一個深入的瞭解。那麼讓
我們回想一下到底一個兩線rs232的資料是如何傳送的,如圖一:
在這裡我們的重點在於,我們發現要在序列口傳送資料一個最體碼的要求恐怕就是:要知道資料傳輸何時開始,
何時結束。即如何delimit.那麼rs232怎麼做的。顯然,在idle(空閒)時,即無資料傳送時,資料線處於高電
平,等到有資料開始傳送,傳送方首先拉低資料線(start),表示資料傳輸開始,接受端也因為這個“start”訊號
開始準備接受即將到來的資料,類似一次握手,隨後,在兩者之間的資料傳送開始,結束後主方再次拉高資料
線,表示結束傳輸,自此兩者重新進入Idle狀態。等待下一輪傳送開始。
瞭解了rs232,那麼我們自然想到usb如何做到這個呢,既然是序列位流傳輸,也理所當然的解決這個問題。沒錯,
Usb協議必然要解決這個問題,讓我們作一個類似rs232的比較吧!類似於rs232,usb的傳輸楨如圖二:
(這裡我們暫時忽略這個傳輸所代表的意義)為了說明問題,我們對一些問題簡化,我們定義這樣幾個狀態:
假設D+,D-分別表示usb訊號線的電平訊號。那麼對於usb full speed function(high speed ,low
speed是不
同的),我們定義差分資料線上可能出現的四個狀態:
Data J state:D+=1,D-=0;
Data K state:D+=0,D-=1;
SE0:D+=D-=0;
SE1:D+=D-=1;
那麼上面的圖中,相應的也可表示為圖三:
這個對usb full speed function來說,idle狀態將處於Data J state,se0表示一楨結束。看這個圖是不是很像
我們熟悉的rs232。沒錯!!!他們確實很相似。在無資料傳輸時,它們都處於Idle狀態,當要開始傳輸資料時,
先發一個sync(同步訊號,rs232為start,usb為一sync位元組,見協議說明)訊號進行“握手”,而後開始傳輸,
當傳輸要結束時,發一stop訊號(usb為一個se0狀態表示要結束傳輸),最後又進入idle態等待新的傳輸。不過,
你可能更加註意到,他們還是不同的。不同在於usb是按packet進行傳輸的,就是說它傳輸的最小單位是packet,
而rs232是按位元組傳送的,也即它的最小傳送單位是位元組。既然是按pakcet傳送,想想我們相較於rs232的按位元組
單位傳輸,我們可以得到哪些“好處”。想想看,pakcet的好處不就在於我們可以靈活的定義資料的傳送格式,
傳送方式,從而可以適應各種各樣的序列裝置,這不就是所謂的“通用序列匯流排”嗎?
簡介:從本節開始,我們將介紹usb的傳輸機制。這節先介紹usb現有傳輸方式的背景知識,做為對下節將要展開
的四種傳輸型別,描述符,等相關知識的一個導引。
2。usb傳輸。
我們在上一節中瞭解到了usb的“packet”的感念,瞭解到了usb傳送一個packet總是以sync開始,以eop結束,
這個稱為delimiter,即標記packet的始末。有了packet,我們就可以在usb總線上傳輸資料了。但是這還不夠,
比如資料傳送方向,即傳回usb主機還是傳下usb從機,資料傳送的地址,資料傳送的型別(這些後面我們將會知
道)這些資訊在傳輸之前是必須搞清楚的,那麼這個資訊如何得知呢,看來這就需要我們定一套基於packet的
“協議”了。主機與從機在傳輸中均遵循這套“協議”,那麼這些問題就可以迎刃而解。事實上,usb的一次數
據傳輸總是遵循這樣的“協議”的:
首先,主機發第1個packet給從機,宣告資料傳送方向,資料傳輸地址,資料傳輸型別。
其次,主機發第2個至第n個packet載有實際資料
最後,從機返回一個packet是一個ACK包,報告資料傳輸的結果,比如接受出錯或成功等資訊,這樣主機
就可以藉此瞭解到這次傳輸情況,從而有可能來作出相應措施如決定是否重發。
這裡我們考慮的是主機發資料給從機的情況,那麼從機發資料給主機時,是不是也可以這樣呢?當然可以,比如
從機要發資料給主機時,也可以採取同主機類似的方式:
首先,從機發第1個packet給主機,宣告資料傳送方向,資料傳輸地址,資料傳輸型別。
其次,從機發第2個至第n個packet載有實際資料
最後,主機返回一個packet是一個ACK包,報告資料傳輸的結果,比如接受出錯或成功等資訊,這樣從機就可以
藉此瞭解到這次傳輸情況,從而有可能來作出相應措施如決定是否重發。
基本上可以歸結為一個“三段式”傳輸
這裡有人可能注意到了,對這樣一個傳輸機制,從機和主機的功能將是一樣的,因為這樣的實現機制,從機
可能在某一時刻是主機,某一時刻又可能是從機,因為他們要實現同樣的功能。這樣實現起來的複雜性也將是
一樣的。
注:這裡概念或許容易混淆,其實,我們這裡的主機(master)和從機(slaver)是一個transceiver,即可收可發。
相應的,在某一時刻,master在發資料,我們稱其為transmitter,在接受時我們稱為receiver.對slaver同樣。
我們可能還注意到了,usb這種按pakcet傳輸的方式在實現時已經很複雜了(至少比rs232要複雜多吧),至少我
們目前看來主從機功能一樣這樣的實現方式似乎還是可行,但是後面我們談到usb host時將會了解到host的功
能是如何的複雜,以至於讓一個usb function 也帶上如此的功能成本和實現複雜性將陡然上升。作為面向廣範
應用的usb,這是我們不允許的。我們期望的是一個使用usb 的udisk,使用usb的光碟機,使用usb的耳麥等等這些
東西不要因為usb而變得昂貴,複雜。
正是因為這個原因,usb從機的傳輸髮式便由上面的方式改成了下面的方式進行:
首先,主機發第1個packet給從機,宣告資料傳送方向,資料傳輸地址,資料傳輸型別。
其次,從機收到主機送來的第一個packet後,再發第2個至第n個packet載有實際資料
最後,主機返回一個packet是一個ACK包,報告資料傳輸的結果,比如接受出錯或成功等資訊,這樣從機
就可以藉此瞭解到這次傳輸情況,從而有可能來作出相應措施如決定是否重發。
而對於usb 主機傳輸方式保持不變。
對於這樣的改變,我們馬上就有疑問了:這個改變的傳輸方式是和未改變之前的等價嗎。當然,不全等價。問題
在哪裡?仔細觀察一下便知,兩者區別在於第一個packet是由誰發起的。未改變之前,第一個packet總是由要傳
送資料的一方發起,而改變之後的第一個Packet總是由主機發起。這樣,就變成如果從機要傳送資料給主機時,
總是由主機發起(第一個packet),然後從機開始傳送。
可能初次接觸我們會感覺怪怪的,怎麼從機要給主機發送資料前反而要主機先發packet給從機。
這樣行嗎?
我們要說這樣是可以的,因為通常一次傳輸互動的產生,並非無來由的產生,這些都是由程式設計師控制的,控制usb
何時收,何時發,及發給誰!!!
這裡我們就注意到了,usb function(總是作為從機)的功能一下從原來與主機具有相同功能的tranceiver變成
了現在僅具傳送(或接收)功能的transmitter(或Receiver)實現的複雜性及成本可想而知也就相應得減小了。
簡介:本節介紹usb full speed function的四種傳輸型別。
上節中我們瞭解到了usb host 與usb function 之間採用的是一種“非對稱”的傳輸,也就是說,無論usb接受
數據還是傳送資料,都是由usb host首先發起。即傳輸的第一個packet總是由usb host發出的。這個packet將聲
明本次即將進行的資料傳輸方向,資料傳輸地址和資料傳輸型別。
: 或許你已經注意到了,一個usb host埠並不是僅僅支援一個Usb function.如圖1,
通過usb hub,一個usb host埠可以連線usb滑鼠,usb鍵盤,Usb寫字板......。要連線這麼多東西在同一個
usb host上,我們通常會有一個基本問題,即usb host如何識別這些被連線在它的埠上的裝置呢。正如通常的
主從式通訊系統一樣,如rs485多機通訊,我們通常是用一個特定的地址標誌每一個從裝置。對這裡的usb,我們
採用同樣的方法,將為每個掛接在該usb host上的usb function指定一個特定地址,通過這個特定地址來識別
每個usb function.看來這將是一個usb function在資料傳輸之前必須解決的問題--得到它的地址分配。
這個“地址指定”的過程需要usb host通知usb function才能完成,這個互動過程就是一個控制式傳輸。通過
這個“控制式傳輸”,usb host將指定地址給usb function ,以為即將進行的正式通訊做好準備工作。這裡細心
的讀者可能已經注意到了,既然usb host總要分配地址給usb function才能進行正式的資料傳輸工作,那麼
usb host將如何與一個初始時未分配地址的usb function進行互動來分配地址呢。這裡,是這樣解決的:
usb協議保留了一個“通用地址”0,usb host 通過這個地址0來和初始未分配地址的usb function進行通訊,進行一
些初始的準備工作,諸如這裡的為它非配一個特定地址。後面我們就會了解到,usb除了配置地址外,還有一些
其它引數需要事先主從雙方達成共識。這些引數也都是通過控制式傳輸完成的。一個Usb 的控制式傳輸如圖二:
一個Usb的控制式傳輸分為兩個或三個階段進行傳輸:setup stage,data stage(視情況而定),status stage。
- 首先是setup stage,聯絡上節所說的Usb傳輸模式,usb Host總是先發起第一個packet--這裡它
- 首先發起setup,
- 之後發起以data0為起始的setup data,
- 最後usb function迴應ack結束一次互動。
- 其次如果有data stage,類似的,還是按照上節說的usb傳輸模式,
-
- usb host總是先發起第一個Packet--Out(或in),
- 之後usb host(或usb function)發起以data1為起始的payload data,
- 最後Usb fuction(或usb host)迴應ack結束一次互動。
- 如果資料未傳完,繼續data stage,同上繼續。
-
- 最後是status stage,類似的,
- usb host首先發起第一個Packet--in(或out),
- 之後usb function(或usb host)發起以data1為起始的Null data(0長度),
- 最後Usb host(或usb function)迴應ack結束一次互動。
如此,整個控制式傳輸結束。 你或許有疑問,data stage為什麼進行了多次而非一次完成?實際上,usb總是將
一批大量的資料分成了許多小段來進行傳輸,稱為一個pay load。這樣傳輸的目的是容易對傳輸進行控制。既然
一次大量的資料總是被分成一段一段來分次傳輸,那麼這裡就出現了一個需要事先確定的引數(wMaxPacketSize):即每次即這個小段有多大。這個引數如地址指派一樣,正式傳輸之前需要事先達成共識。通過控制式傳輸,現在我們
已經完成了usb function的地址指定等引數的設定工作,下一步可以進行正式的資料傳輸了。
我們終於等到usb function 配置完成,現在我們的任務是要傳送一批資料,這裡可以使用批量資料傳輸(bulk Transactions)。 一個批量傳輸總是按照如圖所示方式進行,
- 首先,usb host發起第一個Packet--in(或out),表示要開始資料傳輸了。
- 其次,usb function(或usb host)發起以data1(或data0)為起頭的payload data,開始一次互動。
- 再其次,usb host(或Usb function)發起ack迴應這次互動。
如果資料還為傳完,繼續上述過程,即:
- 首先,usb host再次發起一個Packet--in(或out),表示又要開始資料傳輸了。
- 其次,usb function(或usb host)發起以data0(或data1)為起頭的payload data,開始又一次互動。
- 再次,usb host(或Usb function)發起ack迴應這次互動。
這裡的疑問依然是為什麼一次可能傳完的資料為什麼分成多次進行傳輸,原因在上次介紹控制式傳輸式已經說明。
後面我們就會明白,為什麼這樣可以方便控制傳輸過程。 仔細看看控制式的data stage採用的傳輸方式,是否
就是批量傳輸方式呢?!注意,每次payload data的“牽頭人”(preamble)在輪番掉換,先是data1,接著data0,
再是data1,......如此接替,只要有一次互動出現問題,這個接替規則就會被打破進而被Usb host識別而發現
傳輸異常。所以這個交替的“牽頭人”規則是可靠資料傳輸的所採取的措施之一。
在批量資料傳輸中,觸發一次批量資料傳輸總是“被動”的,就是說需要資料傳輸時Usb host並不會主動發起
傳輸,而是需要得到你的指令。當你告訴它:“一切ok,讓我們開始吧!” 這時它才開始資料傳輸。這種方式
顯然在某些情況下並不適合。比如音視訊流,你無法要求它聽從你的“指揮”,讓它等你發指令給usb host,
然後開始一次傳輸。我們需要的是一種“及時”傳輸。一個好的方案就是設定一個timer,按照tick發起usb傳輸。
這個tick通常以1ms(usb full speed)為最小單位。這時,可以設定為每次1ms tick出現,usb host“自動”發起一
次資料傳輸。那麼這種方案具體如何來實現呢?看來最基本的要素便是一個發出tick的timer,而這個“timer”
需要usb host和usb function(事實上還要包括usb hub)雙方均能“看到”,從而協調工作,否則單方面的
timer又有何意義?這個"timer"(或tick)在usb中使用一個特殊的packet實現,即是SOF。這個SOF由USB HOST
相當精確的以每1.00 ms ±0.0005 ms的時間週期傳送給usb device,來在二者之間定時。從而usb function能
夠“及時”的瞭解到“現在時刻”。 現在我們在usb host和usb function之間建立起了“對時”機制。那麼接下
來看看剛才設想的“自動”傳輸如何實現。事實上,一旦usb host及usb function雙方建立了一種時間機制,那
麼這種“自動”傳輸是很容易實現的。usb 實現同步式傳輸或中斷式傳輸總是以一種類似於批量資料傳輸的方式
進行的,唯一不同的是傳輸的觸發不再是“被動”的,而是由SOF所建立的tick觸發。
- 首先,時間到達,usb host發起第一個Packet--in(或out),表示要開始資料傳輸了。
- 其次,usb function(或usb host)發起以data1(或data0)為起頭的payload data,開始一次互動。
- 再其次,如果是中斷式傳輸,usb host(或Usb function)發起ack迴應這次互動,如果是同步式傳輸,
- 該步跳過。
如此重複上述步驟,即usb host等待下一個tick到達,並開始新一輪的互動。
這裡我們注意到了,同步式傳輸和中斷式傳輸二者雖然都是時間觸發,但是中斷式傳輸需要ack應答,而相反,
同步式傳輸不需要。這個最大的區別決定了同步式傳輸是一種非可靠傳輸,但是因此換來了更多的usb傳輸時間。
也因此,同步式傳輸的 payload data(對應wMaxPacketSize )通常相較於其他傳輸方式比較大,因為它消掉了
ack所佔有資料傳輸時間。這裡還有一個地方值得注意的是tick的設定,這個tick也是需要事先usb host 和
usb function達成共識的引數之一。