USB幀概念
如上圖所示,在USB1.1規(guī)范當中,把USB總線時間按幀劃分,每一幀占用時間是1ms;
每一幀內(nèi)的最開始處是SOF token,在SOF內(nèi)包含有11位的幀號;
每一幀的SOF幀號相比前一幀的SOF幀號加1,直到11位最大值以后回到零,如此循環(huán)往復;
每一幀的最后是很短時間的EOF Interval,EOF即為end of frame,作為當前幀的結(jié)束;
USB幀內(nèi)傳輸(transfer)
如上圖所示的同步傳輸和批量傳輸可以看出,USB規(guī)范定義傳輸?shù)幕締挝皇莟ransaction,多個transaction構(gòu)成1個transfer。這些transaction就發(fā)生在usb幀內(nèi),如下圖所示:
圖中可以看到有3個USB 幀的數(shù)據(jù)傳輸, 每1幀都以SOF開始:
在第1幀內(nèi),USB主機先和設(shè)備1的端點2通信也就是通過前述的transaction或者transfer,接下來再和設(shè)備2的端點2通信,然后再和設(shè)備5的端點3通信;最后我們可以看到這一幀剩下的時間沒有任何通信;
在第2幀內(nèi),主機先和設(shè)備1的端點2通信,接下來再和設(shè)備2的端點0通信,然后再和設(shè)備5的端點3通信,最后也是剩余的空閑時間;
在第3幀內(nèi),主機先和設(shè)備1的端點2通信,接下來再和設(shè)備2的端點0通信,然后是剩余的空閑時間。
從上面的圖形我們需要明白的是,每一幀內(nèi)會發(fā)生多少次傳輸是由主機來決定;USB是一種主從通信的架構(gòu),每次傳輸由主機發(fā)起,通過類似查詢的機制,被尋址到的端點才會發(fā)出數(shù)據(jù)或者告訴主機現(xiàn)在無數(shù)據(jù)可發(fā)。
另外,同步傳輸和中斷傳輸?shù)膬?yōu)先級比批量傳輸和控制傳輸更高,主機總是先處理前兩者,每一幀剩下的時間才會處理后兩者;另外,每一幀總會保留一些時間給控制傳輸使用,防止因為USB帶寬耗盡而不能處理其他事務(wù)的情況發(fā)生比如USB枚舉。
USB封包類型
USB通信都以封包的形式進行,例如前面提到的基本通信單位transaction就由多個封包構(gòu)成。下圖是USB封包格式:
下圖列出了USB1.1標準的封包類型:
從上面的圖片可以看到,封包當中有一個4位寬度的字段PID,也就是packet identifier,上面的表格總共列出了10個PID也就是總共有10個類型,同時又分成4大類:
Token類,包含OUT/IN/SOF/SETUP封包;OUT/IN token專門用于數(shù)據(jù)交換,SOF如前所述用于幀起始指示;SETUP專門用于控制傳輸。
Data類,包含DATA0/DATA1 共2個封包,與前述OUT/IN token相配合,專門用于數(shù)據(jù)交換;當通信需要多次transaction的時候,DATA0和DATA1交替發(fā)生,以用于可能發(fā)生的錯誤恢復。
Handshake類,包含ACK/NAK/STALL封包,都專門用于數(shù)據(jù)交互時的應(yīng)答;ACK表示DATA0或者DATA1封包已經(jīng)被收到;NAK表示當前不能夠收取Data類封包;STALL表示當前通信發(fā)生了錯誤且需要主機進一步采取措施來恢復通信。
Special類,只包含PRE封包;這一類封包毋須關(guān)心,只需要知道它的作用是用于全速總線兼容低速設(shè)備。
什么是HID設(shè)備?
HID設(shè)備即人機交互設(shè)備,常見例子如鍵盤、鼠標以及游戲控制類設(shè)備。
HID設(shè)備專門用于主機和人類交互的場景,比如主機根據(jù)使用者在鍵盤按下的鍵值或者鼠標的移動做出響應(yīng)。
HID主機必須做到快速響應(yīng),以防止使用者可能覺察得到的響應(yīng)延遲。
HID設(shè)備產(chǎn)生的數(shù)據(jù)以report的形式向主機發(fā)送,其格式由USB HID規(guī)范定義;HID規(guī)范的核心是定義了現(xiàn)實世界的各種物理對象或者物理單位并指明其用途;可以查詢HID相關(guān)規(guī)范比如Usage Page以及Usage ID的定義。
HID Report主要使用中斷和控制兩種傳輸;其中,中斷傳輸主要 用于低時延場合比如按鍵或者鼠標的移動,而控制傳輸主要用于對時延要求不高的場合比如鍵盤上的大小寫狀態(tài)燈或者數(shù)字鍵盤鎖定狀態(tài)燈等等。
HID transaction
如前所述,HID Report主要使用中斷傳輸向主機發(fā)送有時延要求的數(shù)據(jù),下面以中斷傳輸為例看一下具體流程如下圖:
從圖中可以看到有3個階段:
首先是主機發(fā)送token;如果是設(shè)備向主機發(fā)送數(shù)據(jù)也就是上圖左側(cè),主機發(fā)送IN token,IN token包含設(shè)備地址以及端點地址,起到類似查詢的作用,意思是詢問某個設(shè)備端點是否有數(shù)據(jù);類似地如圖右側(cè),如果是主機向設(shè)備發(fā)送數(shù)據(jù),主機發(fā)出OUT token,其含義是通知某個設(shè)備端點即將有數(shù)據(jù)發(fā)送給該端點;
接下來是數(shù)據(jù)階段;如上圖左側(cè),被尋址到的設(shè)備端點交替發(fā)送DATA0/DATA1,這是設(shè)備有數(shù)據(jù)發(fā)送的情況;如果設(shè)備無數(shù)據(jù)可發(fā),該端點就發(fā)送NAK,之后本次的中斷傳輸就結(jié)束了;如果設(shè)備遇到的某一種錯誤而期望主機之后進行錯誤處理,該端點就發(fā)送STALL,同樣也結(jié)束了本次中斷傳輸;如上圖右側(cè),因為是主機向設(shè)備發(fā)送數(shù)據(jù),這個階段就直接由主機交替發(fā)送DATA0/DATA1;
最后是握手階段;如上圖左側(cè),如果主機正確接收了DATA0/DATA1,就回應(yīng)ACK;如果主機接收到了DATA0/DATA1但是發(fā)現(xiàn)CRC錯誤或者DATA0/DATA1的順序錯誤,或者由于線纜質(zhì)量低劣沒有收到DATA0/DATA1,都不會回應(yīng)ACK,在下一次中斷傳輸該端點會進行數(shù)據(jù)重發(fā);如上圖右側(cè),如果設(shè)備端點回應(yīng)ACK表示DATA0/DATA1被正確接收,如果設(shè)備端點回應(yīng)NAK表示該端點現(xiàn)在不能接收數(shù)據(jù),通常的原因是上一次的數(shù)據(jù)還沒有從端點取走;如果設(shè)備端點回應(yīng)STALL表示該端點遇到錯誤通信不能繼續(xù),需要主機之后進行錯誤處理;如果設(shè)備端點沒有任何回應(yīng),下一次中斷傳輸主機會重發(fā)數(shù)據(jù)。
從前面可以看出,中斷傳輸都是由主機發(fā)起的,以IN token或者OUT token開始,類似一種查詢機制;數(shù)據(jù)要低延遲發(fā)送,查詢的時間間隔就非常重要,這個參數(shù)通常是在端點描述符里進行描述,并且是在枚舉階段主機獲取到這個參數(shù),之后主機就按照該設(shè)備端點期望的查詢間隔周期性地開始查詢。
nRF5 HID composite example
現(xiàn)在結(jié)合nRF5 SDK HID composite example具體講述前面的內(nèi)容。
如上圖,在nRF5 SDK v17.1的HID composite例程當中,可以看到端點8被定義成interrupt IN類型的端點,其查詢間隔是1ms,即USB1.1定義的最小查詢間隔;這意味著主機將會以1ms的時間間隔來查詢該端點。
另外,從上圖的按鍵處理代碼可以看到,當DK板按鍵被按下以后,會向主機發(fā)出X方向為+3的位移量;同時還會啟動溢出時間為5ms的APP_Timer,溢出后同樣也是會向主機發(fā)出X方向為+3的位移量;這樣形成的效果就是當DK板按鍵被按下并且一直保持,PC桌面的光標會快速向右移動。
下面再結(jié)合USB協(xié)議分析儀具體看一下和上述代碼相對應(yīng)的USB總線上的傳輸情況。
可以看到,在USB總線上主機每5ms成功取到一次數(shù)據(jù),也可以看到DATA0和DATA1是交替發(fā)送;同時還可以看到在主機成功取到數(shù)據(jù)之前,還有4次也就是4毫秒的時間內(nèi)該端點回復NAK;這樣算下來,HID匯報率是200Hz。
假如我們修改代碼如下,也就是把APP_Timer的溢出時間修改成1ms,是否就可以達到1000 Hz的匯報率呢?
和上述代碼相對應(yīng)的USB總線傳輸情況如下:
從上圖可以看到,在多數(shù)的USB 1ms幀內(nèi),該端點都交替回復了DATA數(shù)據(jù)包,但是總有少數(shù)的USB幀內(nèi),該端點回復NAK表示無數(shù)據(jù)可發(fā),所以總體來看匯報率是低于1000Hz。
nRF52 USB – 批量和中斷傳輸
如果需要理解上述匯報率低于1000Hz的原因,有必要了解nRF52 USB外設(shè)工作原理,如下圖:
從上圖我們需要理解以下幾點:
當easy dma還沒有寫入數(shù)據(jù)到端點的時候,nRF52 USB外設(shè)端點會自動發(fā)送NAK;
當啟動easy dma并且數(shù)據(jù)已經(jīng)被寫入端點以后,主機再次查詢時,端點就會把數(shù)據(jù)以DATA封包的形式交替發(fā)出;
當DATA封包發(fā)送前后,會產(chǎn)生2個事件,分別是EVENTS_ENDEPIN[n] 和EVENTS_EPDATA;EVENTS_ENDEPIN[n]表示easy dma所指向的RAM地址內(nèi)的數(shù)據(jù)已經(jīng)被端點使用完畢,現(xiàn)在用戶可以操作這一段RAM比如改寫等操作;EVENTS_EPDATA表示端點內(nèi)數(shù)據(jù)已經(jīng)成功發(fā)送到了主機。
如何提高匯報率到1000Hz?
如果主機每次查詢時候,端點內(nèi)的數(shù)據(jù)已經(jīng)準備好,就可以保證主機每次都可以取到數(shù)據(jù),也就可以達到1000Hz匯報率;如何保證端點內(nèi)的數(shù)據(jù)及時準備好呢?很明顯,可以利用前面提到的端點外設(shè)事件EVENTS_ENDEPIN[n] 和EVENTS_EPDATA;即一旦當前的數(shù)據(jù)已經(jīng)成功發(fā)送,就立即啟動easy dma準備好新的端點數(shù)據(jù),在下一幀內(nèi)主機就可以取到新的數(shù)據(jù),如此循環(huán)往復匯報率就能夠達到1000Hz;非常方便的是,USB驅(qū)動已經(jīng)有事件APP_USBD_HID_USER_EVT_IN_REPORT_DONE供應(yīng)用層代碼使用;通過使用該事件,就可以達到上述目的。
在原始的例程代碼當中,該事件并無特殊處理,只是翻轉(zhuǎn)DK板上的LED燈;現(xiàn)在只需在這個事件內(nèi)加入寫端點的代碼也就是每次向右移動3個位移量。需要指出的是,我們需要使用APP Scheduler模塊的API,把寫端點的操作從USB事件隊列處理循環(huán)當中移出,并放到該循環(huán)之后。
改進后的代碼片段如下:
如上圖,當事件APP_USBD_HID_USER_EVT_IN_REPORT_DONE 發(fā)生后也就是主機取到數(shù)據(jù)之后,調(diào)用APP Scheduler模塊API,將函數(shù)HIDMouseReportHandler放入APP Scheduler模塊事件隊列;
如上圖所示,真正寫端點的操作發(fā)生在USB事件隊列處理循環(huán)之后,位于APP Scheduler模塊API app_sched_execute內(nèi)。
更改后的USB總線傳輸情況如下:
從上圖可以看出,HID 匯報率的確達到了1000 Hz。在USB每一幀內(nèi),主機都成功取到了一次數(shù)據(jù),也就是HID Report所定義的X方向和Y方向位移量以及滾輪數(shù)據(jù),可以看到每一次X方向的位移量都是+3。
從時間戳上也可以看出,HID Report相隔的時間也是精確的1ms,例如 19.868.554 -> 19.869.554-> 19.870.554 -> ->19.871.554 ……
補充1:USB協(xié)議如何保證正確傳輸?
首先,從前面的講述以及上面的圖片可以看到,USB封包除了Handshake類沒有CRC校驗以外,其他封包都包含CRC檢驗碼,token類使用CRC5,數(shù)據(jù)類使用CRC16,這樣可以保證USB封包的正確傳輸;其次,雖然Handshake類沒有CRC并不意味數(shù)據(jù)完整性會丟失,通過以下3個例子我們就可以理解:
DATA封包成功傳輸
左邊第i次傳輸:
發(fā)送方sequence bit是0,因此發(fā)送方發(fā)出DATA0封包,接收方正確接收以后其sequence bit切換到1;
接收方發(fā)出ACK,發(fā)送方正確接收以后其sequence bit切換到1;
右邊第i+1次傳輸:
發(fā)送方sequence bit已經(jīng)為1,因此發(fā)送方發(fā)出DATA1封包,接收方正確接收以后其sequence bit切換到0;
接收方發(fā)出ACK,發(fā)送方正確接收以后其sequence bit切換到0;
如此循環(huán)往復,數(shù)據(jù)封包在發(fā)方和收方之間正確傳遞。
DATA封包錯誤或者未被接受

左邊第i次傳輸:
發(fā)送方sequence bit是0,因此發(fā)送方發(fā)出DATA0封包,接收方收到錯誤的DATA0封包或者由于端點緩沖區(qū)尚未清空因此沒有接收該DATA0封包,因此接收方的sequence bit保持不變?yōu)?;
接收方發(fā)出NAK,發(fā)送方正確接收以后發(fā)現(xiàn)是NAK而不是ACK,其sequence bit也保持不變?yōu)?;
右邊第i次重傳:
發(fā)送方sequence bit仍舊是0,因此發(fā)送方仍舊發(fā)出上一次的DATA0封包,接收方正確接收以后其sequence bit切換到1;
接收方發(fā)出ACK,發(fā)送方正確接收以后其sequence bit也切換到1;
因此,某個封包在沒有正確傳輸?shù)那闆r下可以在下一次重試的時候成功傳輸。
Handshake封包錯誤

左邊第i次傳輸:
發(fā)送方sequence bit是0,因此發(fā)送方發(fā)出DATA0封包,接收方正確接收以后其sequence bit切換到1;接收方發(fā)出ACK但該封包由于信號質(zhì)量的原因未被發(fā)送方正確接收,因此發(fā)送方的sequence bit未能切換到1即保持不變?yōu)?;
中間第i次重傳:
發(fā)送方sequence bit仍舊是0,因此發(fā)送方仍舊發(fā)出上一次的DATA0封包,接收方因sequence bit已經(jīng)為1而識別到該DATA0封包是重傳封包因此將其忽略;接收方仍舊發(fā)出ACK,發(fā)送方正確接收以后其sequence bit也切換到1;
右邊第i+1次傳輸:
發(fā)送方sequence bit已經(jīng)為1,因此發(fā)送方發(fā)出DATA1封包,接收方正確接收以后其sequence bit切換到0;接收方發(fā)出ACK,發(fā)送方正確接收以后其sequence bit切換到0;
如上所述,ACK封包雖然沒有CRC檢錯能力,但是即使在其沒有正確傳輸?shù)那闆r下,收發(fā)雙方在這之后還是可以恢復收發(fā)同步。
補充2:主機如何調(diào)度中斷傳輸和同步傳輸
如前所述,USB是一種主從通信的架構(gòu),每次傳輸由主機通過類似查詢的機制發(fā)起,被尋址到的端點發(fā)出響應(yīng),從機在任何時候都不能夠也不可以主動發(fā)起通信;另外,同步傳輸和中斷傳輸優(yōu)先級比批量傳輸和控制傳輸更高,主機總是先處理前兩者,每一幀剩下的時間才會處理后兩者;所有這一切都是由主機的調(diào)度機制來保證的。
總體上,在主機端把USB的四種傳輸方式按照兩類來執(zhí)行調(diào)度,分別是周期性調(diào)度和異步調(diào)度:周期性調(diào)度針對有時間緊迫性的傳輸也就是同步傳輸和中斷傳輸,比如HID設(shè)備,音頻類設(shè)備,這些設(shè)備必須在一定的時間內(nèi)完成數(shù)據(jù)傳輸,否則用戶會感覺到操作延遲或者聽到音樂卡頓。
異步調(diào)度針對數(shù)據(jù)傳輸沒有時間要求而只需數(shù)據(jù)能夠正確傳遞,也就是批量傳輸和控制傳輸。這類設(shè)備比如優(yōu)盤等設(shè)備,只要用戶能夠把文件正確復制即可,如果此時USB帶寬還有較大冗余用戶也不會感覺耗時。
下面我們通過一個實際的USB主機實現(xiàn)也就是EHCI來理解上述機制。


如上圖,EHCI標準當中定義了2個寄存器分別是PeriodicListBase和 FRINDEX,前者相當于指向某一段RAM空間的基地址,F(xiàn)RINDEX相當于索引值,兩者組合起來的地址叫Periodic Frame List Element Address;該地址指向一個有1024/512/256個元素的數(shù)組當中的某個元素,這個數(shù)組叫做Periodic Frame List;而每個元素的值將指向一個對象叫做Isochronous Transfer Descriptor,該對象就是專門用于同步傳輸?shù)拿枋龇ㄔO(shè)備地址,端點地址,端點的maximum packet size,數(shù)據(jù)長度及地址,以及下一個傳輸數(shù)據(jù)結(jié)構(gòu)的地址可以是同步傳輸也可以是中斷傳輸……根據(jù)該描述符的內(nèi)容,EHCI主機控制器就可以在同一幀內(nèi)向指定設(shè)備傳輸同步數(shù)據(jù)和中斷數(shù)據(jù)。
上圖還可以看到用于描述中斷傳輸?shù)臄?shù)據(jù)結(jié)構(gòu)interrupt queue head也就是圖中的多邊形,同樣也包括了設(shè)備地址,端點地址,端點的maximum packet size,數(shù)據(jù)長度及地址以及下一個interrupt queue head地址等等參數(shù),借此 EHCI主機控制器就可以向指定設(shè)備傳輸中斷數(shù)據(jù)。
理解了上述內(nèi)容,就可以理解上面圖形的意義:
上面圖形表示現(xiàn)在主機維持了1個同步傳輸和3個中斷傳輸;其中同步傳輸發(fā)生在每1幀也就是相對應(yīng)的設(shè)備端點描述符的polling interval是1;同樣的,另外3個中斷傳輸分別是發(fā)生在每8幀、每4幀、每1幀,這也是由對應(yīng)設(shè)備的端點描述符來指定。
EHCI主機控制器根據(jù)每幀都會自加1的FRINDEX寄存器,結(jié)合PeriodicListBase,就會順序取到Periodic Frame List元素,而每個元素都指向一個Isochronous Transfer Descriptor(iTD);而iTD又會指向interrupt queue head(iQH);所以最終形成和每一幀相對應(yīng)的鏈表如下:
iTD -> iQH(8) -> iQH(4) -> iQH(1);
iTD -> iQH(1);
iTD -> iQH(1);
iTD -> iQH(1);
iTD -> iQH(4) -> iQH(1);
iTD -> iQH(1);
iTD -> iQH(1);
iTD -> iQH(1)
iTD -> iQH(8) -> iQH(4) -> iQH(1);
上述鏈表的執(zhí)行結(jié)果就是1個同步傳輸和3個中斷傳輸;同步傳輸每1幀處理1次;中斷傳輸分別每8幀、每4幀、每1幀處理1次。

前面有講到過,USB主機在每幀的剩下時間才會處理控制和批量傳輸。這是通過一個異步鏈表來實現(xiàn)的,鏈表的頭位于AsyncListAddr寄存器內(nèi),異步鏈表是一個首尾相接的環(huán)形鏈表,鏈表的元素也是前述類似的queue head數(shù)據(jù)結(jié)構(gòu)。這樣主機控制器就可以在每一幀執(zhí)行完周期性調(diào)度以后的剩余時間內(nèi)執(zhí)行異步調(diào)度也就是控制傳輸和批量傳輸了。
審核編輯 黃宇
-
usb
+關(guān)注
關(guān)注
60文章
8441瀏覽量
284620 -
HID
+關(guān)注
關(guān)注
2文章
139瀏覽量
48907
發(fā)布評論請先 登錄
智能天線的基本概念
伺服與變頻的基本概念
嵌入式的基本概念及其應(yīng)用
USB基本概念及從機編程方法介紹
總線的基本概念及其分類簡析
電磁兼容基本概念
采用HID類的USB人機接口設(shè)計
USB HID協(xié)議學習入門
慕課嵌入式開發(fā)及應(yīng)用(第四章.USB基本概念及從機編程方法)
USB/HID及其基本概念
評論