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

USB HID協(xié)議學(xué)習(xí)入門
慕課嵌入式開發(fā)及應(yīng)用(第四章.USB基本概念及從機(jī)編程方法)

評(píng)論