引言
MAX3421E是一款USB主機(jī)/外設(shè)控制器,也就是說,當(dāng)作為主機(jī)工作時(shí),采用雙緩沖發(fā)送FIFO將數(shù)據(jù)發(fā)送至USB外設(shè),這一對(duì)兒FIFO由兩個(gè)寄存器控制:R2:NDFIFO,F(xiàn)IFO數(shù)據(jù)寄存器
R7:SNDBC,字節(jié)計(jì)數(shù)寄存器
微控制器對(duì)SNDFIFO寄存器R2進(jìn)行重復(fù)寫操作,向FIFO寫入多達(dá)64字節(jié)的數(shù)據(jù)。然后,微控制器再對(duì)SNDBC寄存器進(jìn)行寫操作,完成以下3項(xiàng)任務(wù):
- 指明MAX3421E SIE (串行接口引擎) FIFO中需要發(fā)送的字節(jié)數(shù)。
- 連接SNDFIFO和SNDBC寄存器至USB邏輯,以進(jìn)行USB通信。
- 清除SNDBAVIRQ中斷標(biāo)志。如果第二個(gè)FIFO可以用于μC裝載的話,那么SNDBAVIRQ將立即重新觸發(fā)。
圖1. SNDFIFO寄存器和SNDBC寄存器載入一對(duì)“ping-pong”FIFO和字節(jié)計(jì)數(shù)寄存器
如圖1所示,第一個(gè)FIFO字節(jié)不是由物理FIFO產(chǎn)生,而是由用來調(diào)諧兩個(gè)內(nèi)部時(shí)鐘使其同步的同步寄存器產(chǎn)生,其中一個(gè)時(shí)鐘用于微控制器,另一個(gè)時(shí)鐘用于USB邏輯。
單緩沖傳輸編程
對(duì)于那些帶寬、無嚴(yán)格要求的簡易傳輸而言,固件發(fā)送一個(gè)數(shù)據(jù)包相對(duì)比較簡單。步驟如下:- 將字節(jié)載入SNDFIFO。
- 將字節(jié)載入SNDBC寄存器。
- 向HXFR寄存器寫入OUT PID和端點(diǎn)號(hào),傳輸開始。
- 等待HXFRDNIRQ (主機(jī)發(fā)送完成中斷請(qǐng)求)。
- 從HRSL寄存器中讀取傳輸結(jié)果代碼。
- 如果為ACK,表明傳輸已完成。
- 如果為NAK,表明將進(jìn)入下一步。
- 再次裝載第一個(gè)FIFO字節(jié),重新開始傳輸:
- 寫SNDBC = 0,在微控制器控制下返回一個(gè)虛擬值,以切換含有OUT數(shù)據(jù)的FIFO。
- 僅將第一個(gè)FIFO字節(jié)重新寫入SNDFIFO寄存器,該字節(jié)進(jìn)入圖1中的SYNC寄存器。
- 再次將重新發(fā)送的數(shù)據(jù)包的字節(jié)數(shù)寫入SNDBC寄存器,這樣可切換FIFO返回至USB側(cè),重新進(jìn)行USB通信。
- 進(jìn)入第3步,重新啟動(dòng)數(shù)據(jù)包。
若采用MAX3421E版本1,還需要執(zhí)行第6步,因此,每次重復(fù)USB OUT傳輸時(shí),同步觸發(fā)器必須重新初始化。
雙緩沖傳輸編程
當(dāng)由多個(gè)64字節(jié)數(shù)據(jù)包組成的長數(shù)據(jù)組從USB主機(jī)傳輸至USB外設(shè)時(shí),利用雙緩沖可以提高性能。當(dāng)SNDFIFO連接至USB發(fā)送一個(gè)數(shù)據(jù)包時(shí),微控制器同時(shí)把下一個(gè)64字節(jié)數(shù)據(jù)包載入其他SNDFIFO中,從而改善了系統(tǒng)性能。然而,其程序流程要比單緩沖傳輸稍微復(fù)雜。某些情況下,需要觸發(fā)FIFO (如得到NAK信號(hào)時(shí)),重新裝載第一個(gè)字節(jié)并重寫字節(jié)計(jì)數(shù)寄存器,使其返回初始位置,這就要求程序隨時(shí)跟蹤數(shù)據(jù)的兩個(gè)緩沖器,圖2給出了雙緩沖傳輸?shù)囊环N可能流程。本應(yīng)用筆記最后給出了Send_OUT_Record()函數(shù),實(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)確保在主控制器載入第一個(gè)SNDFIFO后,USB傳輸立即開始。設(shè)計(jì)該流程圖時(shí)使FIFO加載優(yōu)先級(jí)高于發(fā)送優(yōu)先級(jí),因此,仍能保持雙緩沖性能。
該流程最好從以下三個(gè)方面考慮:
- 發(fā)送一個(gè)數(shù)據(jù)包(1至64字節(jié)總有效載荷)。
- 發(fā)送兩個(gè)數(shù)據(jù)包(65至128字節(jié)總有效載荷)。
- 發(fā)送三個(gè)或多個(gè)數(shù)據(jù)包(129或更多字節(jié)總有效載荷)。
- ep,為OUT數(shù)據(jù)包分配的終端號(hào)
- *pBUF,裝滿數(shù)據(jù)字節(jié)的緩沖器指針
- TBC發(fā)送總字節(jié)數(shù)(緩沖器中的字節(jié)數(shù))
- NAKLimit,在停止和返回之前,從外設(shè)接收的連續(xù)NAK響應(yīng)個(gè)數(shù)
發(fā)送一個(gè)OUT數(shù)據(jù)包
當(dāng)發(fā)送的字節(jié)數(shù)等于或小于64字節(jié)時(shí),從步驟1開始循環(huán)。若步驟1的結(jié)果為真,則SPI主機(jī)寫SNDFIFO。如果在第10步中需要用到BC和FB,也就是說,外設(shè)以“NAK”響應(yīng)OUT傳輸時(shí),函數(shù)保存字節(jié)數(shù)(BC)和SNDFIFO第一個(gè)字節(jié)(FB)。由于這是第一次數(shù)據(jù)傳輸,函數(shù)從第4步開始進(jìn)行OUT傳輸。直至沒有字節(jié)需要發(fā)送時(shí)再返回步驟1,然后函數(shù)在第5步等待傳輸完成,并在第6步測(cè)試設(shè)備響應(yīng)。在該點(diǎn)上如果得到ACK,步驟7檢測(cè)到?jīng)]有數(shù)據(jù)需要發(fā)送,則函數(shù)將在步驟11返回。然而,如果該點(diǎn)得到NAK,則在步驟9測(cè)試NAK極限值,如果超出該極限值,則在步驟10和步驟4中重新發(fā)送數(shù)據(jù)包。發(fā)送兩個(gè)OUT數(shù)據(jù)包
若發(fā)送的字節(jié)數(shù)介于65和128之間,則函數(shù)從步驟1處再次開始循環(huán)。如上所述,函數(shù)先寫滿第一個(gè)SNDFIFO,從步驟2開始立即進(jìn)行傳輸直至步驟4。然后函數(shù)返回至步驟1,發(fā)現(xiàn)還有數(shù)據(jù)等待發(fā)送,于是在步驟2中再次寫滿SNDFIFO。由于這不是第一次傳輸,因此,在步驟4函數(shù)不能開始傳輸下一個(gè)數(shù)據(jù)包。再次返回至步驟1時(shí),第一個(gè)SNDFIFO通過USB傳輸?shù)谝粋€(gè)數(shù)據(jù)包,與此同時(shí),第二個(gè)數(shù)據(jù)包仍處于第二個(gè)SNDFIFO中,做好傳輸準(zhǔn)備。需要注意的是,在步驟2中,函數(shù)實(shí)際保存了兩組BC/FB數(shù)據(jù)(字節(jié)計(jì)數(shù)和FIFO第一個(gè)字節(jié)),一組為當(dāng)前正在傳輸?shù)腟NDFIFO,另一組為在第二個(gè)SNDFIFO中等待的即將傳輸?shù)臄?shù)據(jù)。若沒有數(shù)據(jù)需要發(fā)送(并且兩個(gè)SNDFIFO都裝滿了數(shù)據(jù)),則控制流程進(jìn)入步驟5。函數(shù)在步驟5中等待傳輸?shù)谝粋€(gè)數(shù)據(jù)包,然后在步驟6判斷傳輸結(jié)果。如上所述,如果數(shù)據(jù)包為“NAK'd”,函數(shù)在第10步將未決的BC/FB數(shù)值重新載入FIFO和字節(jié)計(jì)數(shù)寄存器中,在步驟4重新發(fā)送數(shù)據(jù)包,流程分支返回至步驟5 (通過步驟1),等待傳輸完成。每產(chǎn)生一個(gè)NAK,按步驟5-6-9-10-4-5循環(huán)往復(fù),直至接收到ACK或達(dá)到NAK極限值為止。
如果外設(shè)應(yīng)答(ACK) OUT傳輸,函數(shù)在步驟7測(cè)試流程是否終止。如果沒有完成,則函數(shù)流程進(jìn)入步驟8,發(fā)送正在第二個(gè)SNDFIFO中等待的下一個(gè)數(shù)據(jù)包。在步驟8中,函數(shù)執(zhí)行多個(gè)任務(wù):如果步驟10中需要載入“未決的” BC/FC數(shù)值,將這些數(shù)值復(fù)制到“當(dāng)前” BC/FC變量;通過加載當(dāng)前字節(jié)數(shù),切換第二個(gè)SNDFIFO至USB;在步驟4中開始傳輸OUT數(shù)據(jù)。每接收到一個(gè)NAK信號(hào),便如上所述循環(huán)操作。若在步驟7至步驟11過程中接收到ACK響應(yīng),則函數(shù)退出。
發(fā)送三個(gè)或更多個(gè)OUT數(shù)據(jù)包
在一個(gè)數(shù)據(jù)包正在發(fā)送,而另一個(gè)數(shù)據(jù)包將要開始發(fā)送(等待第二個(gè)SNDFIFO)之前,該函數(shù)基本上是按照先前發(fā)送兩個(gè)數(shù)據(jù)包的流程進(jìn)行的。每次函數(shù)均在第4步開始啟動(dòng)另一個(gè)數(shù)據(jù)包,在步驟1判斷是否還有數(shù)據(jù)需載入SNDFIFO中。必須在還有數(shù)據(jù)需要發(fā)送,同時(shí)程序流程中FIFO有效的情況下,才能進(jìn)入步驟2。因?yàn)椴襟E1至步驟3中“寫SNDFIFO”循環(huán)的優(yōu)先級(jí)高于步驟5...4的“發(fā)送FIFO”循環(huán),因此數(shù)據(jù)通過USB傳輸時(shí)總是被寫入SNDFIFO,從而實(shí)現(xiàn)雙緩沖。性能
圖3. MAX3421E載入和發(fā)送USB數(shù)據(jù)包的示波器曲線
圖3為所示為用Send_OUT_Record()函數(shù)載入和發(fā)送64字節(jié)OUT數(shù)據(jù)包時(shí)疊加的示波器曲線。數(shù)據(jù)的大小為512個(gè)字節(jié),由8個(gè)64字節(jié)數(shù)據(jù)包組成。由圖中可以看出,與MAX3421E相連的外設(shè)不產(chǎn)生NAK信號(hào),因此可對(duì)最大傳輸帶寬進(jìn)行測(cè)量,示波器一次就可捕獲整個(gè)傳輸過程。
最上面的兩條曲線為SPI主機(jī)(SPI硬件具有ARM7)通過SPI端口向MAX3421E SNDFIFO載入64字節(jié)數(shù)據(jù)的曲線。每次寫SNDFIFO時(shí),SS# (從機(jī)選擇)線路為低電平,SCK (串行時(shí)鐘)脈沖為65 x 8,每當(dāng)載入命令字節(jié)時(shí),向64字節(jié)數(shù)據(jù)提供8個(gè)時(shí)鐘脈沖。在該曲線開始之前,第一次SNDFIFO加載結(jié)束,因此,圖3給出了7次SNDFIFO載入時(shí)的情況。
第三條曲線是USB D+信號(hào),表示64字節(jié)OUT數(shù)據(jù)包正通過USB從主機(jī)MAX3421E向外設(shè)傳輸。最下面一條曲線是脈沖,表示ARM7載入MAX3421E HXFR寄存器,開始傳輸。
需要注意的是:在USB數(shù)據(jù)包開始不久,ARM7開始載入第二個(gè)SNDFIFO,同時(shí)USB數(shù)據(jù)包在總線上進(jìn)行傳輸。這一雙緩沖過程可大大改善傳輸帶寬。
帶寬測(cè)試
圖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ù)。
方案實(shí)現(xiàn)中的注意事項(xiàng)
以下提供的代碼實(shí)例是采用Maxim USB函數(shù)庫進(jìn)行編寫和測(cè)試的,Maxim USB函數(shù)庫的詳細(xì)說明請(qǐng)參見應(yīng)用筆記3936,"Maxim USB庫"。該函數(shù)庫工具包含MAX3421E 和MAX3420E USB外設(shè)控制器。若要對(duì)固件進(jìn)行測(cè)試的話,采用USB電纜將MAX3421E主機(jī)連接至MAX3420E外設(shè),通過將標(biāo)為"*1*"的語句注釋掉,使MAX3420E接受每個(gè)OUT傳輸(無NAK)。注: MAX3421E的未來版本將不會(huì)再涉及到FIFO的重新載入問題。只需重寫HXFR寄存器,MAX3421E就可重新啟動(dòng)一個(gè)OUT數(shù)據(jù)包。這種改進(jìn)不再需要Send_OUT_Record()函數(shù)。
Send_OUT_Record()函數(shù)實(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); }
評(píng)論
查看更多