摘要:該應用筆記對主機USB控制器MAX3421E版本1和2進行了討論。MAX3421E采用雙緩沖發(fā)送FIFO向USB外設發(fā)送數(shù)據(jù)。在該器件的版本1和2中,需著重考慮OUT傳輸?shù)?a target="_blank">編程。該應用筆記詳細說明了發(fā)送機制。并給出了單緩沖和雙緩沖OUT傳輸?shù)睦獭?
R2:NDFIFO,F(xiàn)IFO數(shù)據(jù)寄存器
R7:SNDBC,字節(jié)計數(shù)寄存器
微控制器對SNDFIFO寄存器R2進行重復寫操作,向FIFO寫入多達64字節(jié)的數(shù)據(jù)。然后,微控制器再對SNDBC寄存器進行寫操作,完成以下3項任務:
圖1. SNDFIFO寄存器和SNDBC寄存器載入一對“ping-pong”FIFO和字節(jié)計數(shù)寄存器
如圖1所示,第一個FIFO字節(jié)不是由物理FIFO產(chǎn)生,而是由用來調(diào)諧兩個內(nèi)部時鐘使其同步的同步寄存器產(chǎn)生,其中一個時鐘用于微控制器,另一個時鐘用于USB邏輯。
若采用MAX3421E版本1,還需要執(zhí)行第6步,因此,每次重復USB OUT傳輸時,同步觸發(fā)器必須重新初始化。
圖2. 雙緩沖OUT數(shù)據(jù)包傳輸流程
右側(cè)循環(huán)(步驟1至步驟4)將USB數(shù)據(jù)裝載到FIFO;而從第5步開始左側(cè)的循環(huán)則通過USB分配FIFO,并處理NAK重試?!癋irst pass?”判斷(步驟3)確保在主控制器載入第一個SNDFIFO后,USB傳輸立即開始。設計該流程圖時使FIFO加載優(yōu)先級高于發(fā)送優(yōu)先級,因此,仍能保持雙緩沖性能。
該流程最好從以下三個方面考慮:
函數(shù)在步驟5中等待傳輸?shù)谝粋€數(shù)據(jù)包,然后在步驟6判斷傳輸結(jié)果。如上所述,如果數(shù)據(jù)包為“NAK'd”,函數(shù)在第10步將未決的BC/FB數(shù)值重新載入FIFO和字節(jié)計數(shù)寄存器中,在步驟4重新發(fā)送數(shù)據(jù)包,流程分支返回至步驟5 (通過步驟1),等待傳輸完成。每產(chǎn)生一個NAK,按步驟5-6-9-10-4-5循環(huán)往復,直至接收到ACK或達到NAK極限值為止。
如果外設應答(ACK) OUT傳輸,函數(shù)在步驟7測試流程是否終止。如果沒有完成,則函數(shù)流程進入步驟8,發(fā)送正在第二個SNDFIFO中等待的下一個數(shù)據(jù)包。在步驟8中,函數(shù)執(zhí)行多個任務:如果步驟10中需要載入“未決的” BC/FC數(shù)值,將這些數(shù)值復制到“當前” BC/FC變量;通過加載當前字節(jié)數(shù),切換第二個SNDFIFO至USB;在步驟4中開始傳輸OUT數(shù)據(jù)。每接收到一個NAK信號,便如上所述循環(huán)操作。若在步驟7至步驟11過程中接收到ACK響應,則函數(shù)退出。
圖3. MAX3421E載入和發(fā)送USB數(shù)據(jù)包的示波器曲線
圖3為所示為用Send_OUT_Record()函數(shù)載入和發(fā)送64字節(jié)OUT數(shù)據(jù)包時疊加的示波器曲線。數(shù)據(jù)的大小為512個字節(jié),由8個64字節(jié)數(shù)據(jù)包組成。由圖中可以看出,與MAX3421E相連的外設不產(chǎn)生NAK信號,因此可對最大傳輸帶寬進行測量,示波器一次就可捕獲整個傳輸過程。
最上面的兩條曲線為SPI主機(SPI硬件具有ARM7)通過SPI端口向MAX3421E SNDFIFO載入64字節(jié)數(shù)據(jù)的曲線。每次寫SNDFIFO時,SS# (從機選擇)線路為低電平,SCK (串行時鐘)脈沖為65 x 8,每當載入命令字節(jié)時,向64字節(jié)數(shù)據(jù)提供8個時鐘脈沖。在該曲線開始之前,第一次SNDFIFO加載結(jié)束,因此,圖3給出了7次SNDFIFO載入時的情況。
第三條曲線是USB D+信號,表示64字節(jié)OUT數(shù)據(jù)包正通過USB從主機MAX3421E向外設傳輸。最下面一條曲線是脈沖,表示ARM7載入MAX3421E HXFR寄存器,開始傳輸。
需要注意的是:在USB數(shù)據(jù)包開始不久,ARM7開始載入第二個SNDFIFO,同時USB數(shù)據(jù)包在總線上進行傳輸。這一雙緩沖過程可大大改善傳輸帶寬。
圖4. 圖3傳輸過程的CATC (LeCroy)和總線分析結(jié)果
如圖4所示,Send_OUT_Record()函數(shù)以6.76Mbps速率傳輸521字節(jié)的數(shù)據(jù)。作為參考,與PC相連的USB全速USB thumb drive采用UHCI USB控制器(USB的唯一附件) 以5.94Mbps速率傳輸512字節(jié)的數(shù)據(jù)。
注: MAX3421E的未來版本將不會再涉及到FIFO的重新載入問題。只需重寫HXFR寄存器,MAX3421E就可重新啟動一個OUT數(shù)據(jù)包。這種改進不再需要Send_OUT_Record()函數(shù)。
引言
MAX3421E是一款USB主機/外設控制器,也就是說,當作為主機工作時,采用雙緩沖發(fā)送FIFO將數(shù)據(jù)發(fā)送至USB外設,這一對兒FIFO由兩個寄存器控制:R2:NDFIFO,F(xiàn)IFO數(shù)據(jù)寄存器
R7:SNDBC,字節(jié)計數(shù)寄存器
微控制器對SNDFIFO寄存器R2進行重復寫操作,向FIFO寫入多達64字節(jié)的數(shù)據(jù)。然后,微控制器再對SNDBC寄存器進行寫操作,完成以下3項任務:
- 指明MAX3421E SIE (串行接口引擎) FIFO中需要發(fā)送的字節(jié)數(shù)。
- 連接SNDFIFO和SNDBC寄存器至USB邏輯,以進行USB通信。
- 清除SNDBAVIRQ中斷標志。如果第二個FIFO可以用于μC裝載的話,那么SNDBAVIRQ將立即重新觸發(fā)。
圖1. SNDFIFO寄存器和SNDBC寄存器載入一對“ping-pong”FIFO和字節(jié)計數(shù)寄存器
如圖1所示,第一個FIFO字節(jié)不是由物理FIFO產(chǎn)生,而是由用來調(diào)諧兩個內(nèi)部時鐘使其同步的同步寄存器產(chǎn)生,其中一個時鐘用于微控制器,另一個時鐘用于USB邏輯。
單緩沖傳輸編程
對于那些帶寬、無嚴格要求的簡易傳輸而言,固件發(fā)送一個數(shù)據(jù)包相對比較簡單。步驟如下:- 將字節(jié)載入SNDFIFO。
- 將字節(jié)載入SNDBC寄存器。
- 向HXFR寄存器寫入OUT PID和端點號,傳輸開始。
- 等待HXFRDNIRQ (主機發(fā)送完成中斷請求)。
- 從HRSL寄存器中讀取傳輸結(jié)果代碼。
- 如果為ACK,表明傳輸已完成。
- 如果為NAK,表明將進入下一步。
- 再次裝載第一個FIFO字節(jié),重新開始傳輸:
- 寫SNDBC = 0,在微控制器控制下返回一個虛擬值,以切換含有OUT數(shù)據(jù)的FIFO。
- 僅將第一個FIFO字節(jié)重新寫入SNDFIFO寄存器,該字節(jié)進入圖1中的SYNC寄存器。
- 再次將重新發(fā)送的數(shù)據(jù)包的字節(jié)數(shù)寫入SNDBC寄存器,這樣可切換FIFO返回至USB側(cè),重新進行USB通信。
- 進入第3步,重新啟動數(shù)據(jù)包。
若采用MAX3421E版本1,還需要執(zhí)行第6步,因此,每次重復USB OUT傳輸時,同步觸發(fā)器必須重新初始化。
雙緩沖傳輸編程
當由多個64字節(jié)數(shù)據(jù)包組成的長數(shù)據(jù)組從USB主機傳輸至USB外設時,利用雙緩沖可以提高性能。當SNDFIFO連接至USB發(fā)送一個數(shù)據(jù)包時,微控制器同時把下一個64字節(jié)數(shù)據(jù)包載入其他SNDFIFO中,從而改善了系統(tǒng)性能。然而,其程序流程要比單緩沖傳輸稍微復雜。某些情況下,需要觸發(fā)FIFO (如得到NAK信號時),重新裝載第一個字節(jié)并重寫字節(jié)計數(shù)寄存器,使其返回初始位置,這就要求程序隨時跟蹤數(shù)據(jù)的兩個緩沖器,圖2給出了雙緩沖傳輸?shù)囊环N可能流程。本應用筆記最后給出了Send_OUT_Record()函數(shù),實現(xiàn)圖2所示流程。圖2. 雙緩沖OUT數(shù)據(jù)包傳輸流程
右側(cè)循環(huán)(步驟1至步驟4)將USB數(shù)據(jù)裝載到FIFO;而從第5步開始左側(cè)的循環(huán)則通過USB分配FIFO,并處理NAK重試?!癋irst pass?”判斷(步驟3)確保在主控制器載入第一個SNDFIFO后,USB傳輸立即開始。設計該流程圖時使FIFO加載優(yōu)先級高于發(fā)送優(yōu)先級,因此,仍能保持雙緩沖性能。
該流程最好從以下三個方面考慮:
- 發(fā)送一個數(shù)據(jù)包(1至64字節(jié)總有效載荷)。
- 發(fā)送兩個數(shù)據(jù)包(65至128字節(jié)總有效載荷)。
- 發(fā)送三個或多個數(shù)據(jù)包(129或更多字節(jié)總有效載荷)。
- ep,為OUT數(shù)據(jù)包分配的終端號
- *pBUF,裝滿數(shù)據(jù)字節(jié)的緩沖器指針
- TBC發(fā)送總字節(jié)數(shù)(緩沖器中的字節(jié)數(shù))
- NAKLimit,在停止和返回之前,從外設接收的連續(xù)NAK響應個數(shù)
發(fā)送一個OUT數(shù)據(jù)包
當發(fā)送的字節(jié)數(shù)等于或小于64字節(jié)時,從步驟1開始循環(huán)。若步驟1的結(jié)果為真,則SPI主機寫SNDFIFO。如果在第10步中需要用到BC和FB,也就是說,外設以“NAK”響應OUT傳輸時,函數(shù)保存字節(jié)數(shù)(BC)和SNDFIFO第一個字節(jié)(FB)。由于這是第一次數(shù)據(jù)傳輸,函數(shù)從第4步開始進行OUT傳輸。直至沒有字節(jié)需要發(fā)送時再返回步驟1,然后函數(shù)在第5步等待傳輸完成,并在第6步測試設備響應。在該點上如果得到ACK,步驟7檢測到?jīng)]有數(shù)據(jù)需要發(fā)送,則函數(shù)將在步驟11返回。然而,如果該點得到NAK,則在步驟9測試NAK極限值,如果超出該極限值,則在步驟10和步驟4中重新發(fā)送數(shù)據(jù)包。發(fā)送兩個OUT數(shù)據(jù)包
若發(fā)送的字節(jié)數(shù)介于65和128之間,則函數(shù)從步驟1處再次開始循環(huán)。如上所述,函數(shù)先寫滿第一個SNDFIFO,從步驟2開始立即進行傳輸直至步驟4。然后函數(shù)返回至步驟1,發(fā)現(xiàn)還有數(shù)據(jù)等待發(fā)送,于是在步驟2中再次寫滿SNDFIFO。由于這不是第一次傳輸,因此,在步驟4函數(shù)不能開始傳輸下一個數(shù)據(jù)包。再次返回至步驟1時,第一個SNDFIFO通過USB傳輸?shù)谝粋€數(shù)據(jù)包,與此同時,第二個數(shù)據(jù)包仍處于第二個SNDFIFO中,做好傳輸準備。需要注意的是,在步驟2中,函數(shù)實際保存了兩組BC/FB數(shù)據(jù)(字節(jié)計數(shù)和FIFO第一個字節(jié)),一組為當前正在傳輸?shù)腟NDFIFO,另一組為在第二個SNDFIFO中等待的即將傳輸?shù)臄?shù)據(jù)。若沒有數(shù)據(jù)需要發(fā)送(并且兩個SNDFIFO都裝滿了數(shù)據(jù)),則控制流程進入步驟5。函數(shù)在步驟5中等待傳輸?shù)谝粋€數(shù)據(jù)包,然后在步驟6判斷傳輸結(jié)果。如上所述,如果數(shù)據(jù)包為“NAK'd”,函數(shù)在第10步將未決的BC/FB數(shù)值重新載入FIFO和字節(jié)計數(shù)寄存器中,在步驟4重新發(fā)送數(shù)據(jù)包,流程分支返回至步驟5 (通過步驟1),等待傳輸完成。每產(chǎn)生一個NAK,按步驟5-6-9-10-4-5循環(huán)往復,直至接收到ACK或達到NAK極限值為止。
如果外設應答(ACK) OUT傳輸,函數(shù)在步驟7測試流程是否終止。如果沒有完成,則函數(shù)流程進入步驟8,發(fā)送正在第二個SNDFIFO中等待的下一個數(shù)據(jù)包。在步驟8中,函數(shù)執(zhí)行多個任務:如果步驟10中需要載入“未決的” BC/FC數(shù)值,將這些數(shù)值復制到“當前” BC/FC變量;通過加載當前字節(jié)數(shù),切換第二個SNDFIFO至USB;在步驟4中開始傳輸OUT數(shù)據(jù)。每接收到一個NAK信號,便如上所述循環(huán)操作。若在步驟7至步驟11過程中接收到ACK響應,則函數(shù)退出。
發(fā)送三個或更多個OUT數(shù)據(jù)包
在一個數(shù)據(jù)包正在發(fā)送,而另一個數(shù)據(jù)包將要開始發(fā)送(等待第二個SNDFIFO)之前,該函數(shù)基本上是按照先前發(fā)送兩個數(shù)據(jù)包的流程進行的。每次函數(shù)均在第4步開始啟動另一個數(shù)據(jù)包,在步驟1判斷是否還有數(shù)據(jù)需載入SNDFIFO中。必須在還有數(shù)據(jù)需要發(fā)送,同時程序流程中FIFO有效的情況下,才能進入步驟2。因為步驟1至步驟3中“寫SNDFIFO”循環(huán)的優(yōu)先級高于步驟5...4的“發(fā)送FIFO”循環(huán),因此數(shù)據(jù)通過USB傳輸時總是被寫入SNDFIFO,從而實現(xiàn)雙緩沖。性能
圖3. MAX3421E載入和發(fā)送USB數(shù)據(jù)包的示波器曲線
圖3為所示為用Send_OUT_Record()函數(shù)載入和發(fā)送64字節(jié)OUT數(shù)據(jù)包時疊加的示波器曲線。數(shù)據(jù)的大小為512個字節(jié),由8個64字節(jié)數(shù)據(jù)包組成。由圖中可以看出,與MAX3421E相連的外設不產(chǎn)生NAK信號,因此可對最大傳輸帶寬進行測量,示波器一次就可捕獲整個傳輸過程。
最上面的兩條曲線為SPI主機(SPI硬件具有ARM7)通過SPI端口向MAX3421E SNDFIFO載入64字節(jié)數(shù)據(jù)的曲線。每次寫SNDFIFO時,SS# (從機選擇)線路為低電平,SCK (串行時鐘)脈沖為65 x 8,每當載入命令字節(jié)時,向64字節(jié)數(shù)據(jù)提供8個時鐘脈沖。在該曲線開始之前,第一次SNDFIFO加載結(jié)束,因此,圖3給出了7次SNDFIFO載入時的情況。
第三條曲線是USB D+信號,表示64字節(jié)OUT數(shù)據(jù)包正通過USB從主機MAX3421E向外設傳輸。最下面一條曲線是脈沖,表示ARM7載入MAX3421E HXFR寄存器,開始傳輸。
需要注意的是:在USB數(shù)據(jù)包開始不久,ARM7開始載入第二個SNDFIFO,同時USB數(shù)據(jù)包在總線上進行傳輸。這一雙緩沖過程可大大改善傳輸帶寬。
帶寬測試
圖4. 圖3傳輸過程的CATC (LeCroy)和總線分析結(jié)果
如圖4所示,Send_OUT_Record()函數(shù)以6.76Mbps速率傳輸521字節(jié)的數(shù)據(jù)。作為參考,與PC相連的USB全速USB thumb drive采用UHCI USB控制器(USB的唯一附件) 以5.94Mbps速率傳輸512字節(jié)的數(shù)據(jù)。
方案實現(xiàn)中的注意事項
以下提供的代碼實例是采用Maxim USB函數(shù)庫進行編寫和測試的,Maxim USB函數(shù)庫的詳細說明請參見應用筆記3936,"Maxim USB庫"。該函數(shù)庫工具包含MAX3421E 和MAX3420E USB外設控制器。若要對固件進行測試的話,采用USB電纜將MAX3421E主機連接至MAX3420E外設,通過將標為"*1*"的語句注釋掉,使MAX3420E接受每個OUT傳輸(無NAK)。注: MAX3421E的未來版本將不會再涉及到FIFO的重新載入問題。只需重寫HXFR寄存器,MAX3421E就可重新啟動一個OUT數(shù)據(jù)包。這種改進不再需要Send_OUT_Record()函數(shù)。
Send_OUT_Record()函數(shù)實例
// ******************************************************************************* // Send an OUT record to end point 'ep'. // pBuf points to the byte buffer; TBC is total byte count. // NAKLimit is the number of NAKs to accept before returning. // // Returns HRSL code (0 for success, 4 for NAK limit exceeded, HRSL for problems) // ******************************************************************************* // BYTE Send_OUT_Record(BYTE ep, BYTE *pBuf, WORD TBC, WORD NAKLimit) { static WORD NAKct,rb; // Buf index, NAK counter, remaining bytes to send WORD bytes2send; // temp BYTE Available_Buffers; // Remaining buffers count (0-2) BYTE FI_FB; // Temporary FIFO first byte static BYTE CurrentBC; // Byte count for currently-sending FIFO static BYTE CurrentFB; // First FIFO byte for currently-sending FIFO static BYTE PendingBC; // Byte count for next 64 byte packet scheduled for sending static BYTE PendingFB; // First FIFO byte for next 64 byte packet scheduled for sending BYTE dum; BYTE Transfer_In_Progress,FirstPass; // flags // NAKct=0; Available_Buffers = 2; rb = TBC; // initial remaining bytes = total byte count FirstPass = 1; // do { while((rb!=0)&&(Available_Buffers!=0)) // WHILE there are more bytes to load and a buffer is available { // Pwreg(rEPIRQ,bmOUT1DAVIRQ);// *1* enable the 3420 for another OUT transfer FI_FB = *pBuf; // Save the first byte of the 64 byte packet bytes2send = (rb >= 64) ? 64: rb; // Lower of 64 bytes and remaining bytes rb -= bytes2send; // Adjust 'remaining bytes' Hwritebytes(rSNDFIFO,64,pBuf); pBuf += 64; // Advance the buffer pointer to the next 64-byte chunk Available_Buffers -= 1 // One fewer buffer is now available // if(Available_Buffers==1) // Only one has been loaded { CurrentBC = bytes2send; CurrentFB = FI_FB; } else // Available_Buffers must be 0, both loaded. { PendingBC = bytes2send; PendingFB = FI_FB; } // if(FirstPass) { FirstPass = 0; Hwreg(rSNDBC,CurrentBC); // Load the byte count L7_ON // Light 7 is used as scope pulse Hwreg(rHIRQ,bmHXFRDNIRQ); // Clear the IRQ Hwreg(rHXFR,(tokOUT | ep)); // Launch an OUT1 transfer L7_OFF } } // While there are bytes to load and there is space for them // do // While a transfer is in progress (not yet ACK'd) { // while((Hrreg(rHRSL) & 0x0F) == hrBUSY) ; // Hang here until current packet completes while((Hrreg(rHIRQ) & bmHXFRDNIRQ) != bmHXFRDNIRQ) ; dum = Hrreg(rHRSL) & 0x0F; // Get transfer result if (dum == hrNAK) { Transfer_In_Progress = 1; NAKct += 1; if (NAKct == NAKLimit) return(hrNAK); else { Hwreg(rSNDBC,0); // Flip FIFOs Hwreg(rSNDFIFO,CurrentFB); Hwreg(rSNDBC,CurrentBC); // Flip FIFOs back L7_ON // Scope pulse Hwreg(rHIRQ,bmHXFRDNIRQ); // Clear the IRQ Hwreg(rHXFR,(tokOUT | ep)); // Launch an OUT1 transfer L7_OFF } } else if (dum == hrACK) { Available_Buffers += 1; NAKct = 0; Transfer_In_Progress = 0; // Finished this transfer if (Available_Buffers != 2) // Still some data to send { CurrentBC = PendingBC; CurrentFB = PendingFB; Hwreg(rSNDBC,CurrentBC); L7_ON // Scope pulse Hwreg(rHIRQ,bmHXFRDNIRQ); // Clear the IRQ Hwreg(rHXFR,(tokOUT | ep)); // Launch an OUT1 transfer L7_OFF } } else return(dum); } while(Transfer_In_Progress); } while(Available_Buffers!=2); // Go until both buffers are available (have been sent) return(0); }
評論
查看更多