MAX3421E版本1和2主機OUT傳輸

引言

MAX3421E是一款USB主機/外設控制器,也就是說,當作爲主機工作時,採用雙緩衝發送FIFO將數據發送至USB外設,這一對兒FIFO由兩個寄存器控制:

R2:NDFIFO,FIFO數據寄存器
R7:SNDBC,字節計數寄存器

微控制器對SNDFIFO寄存器R2進行重複寫操作,向FIFO寫入多達64字節的數據。然後,微控制器再對SNDBC寄存器進行寫操作,完成以下3項任務:

指明MAX3421E SIE (串行接口引擎) FIFO中需要發送的字節數。

連接SNDFIFO和SNDBC寄存器至USB邏輯,以進行USB通信。

清除SNDBAVIRQ中斷標誌。如果第二個FIFO可以用於µC裝載的話,那麼SNDBAVIRQ將立即重新觸發。

圖1. SNDFIFO寄存器和SNDBC寄存器載入一對“ping-pong”FIFO和字節計數寄存器
圖1. SNDFIFO寄存器和SNDBC寄存器載入一對“ping-pong”FIFO和字節計數寄存器

如圖1所示,第一個FIFO字節不是由物理FIFO產生,而是由用來調諧兩個內部時鐘使其同步的同步寄存器產生,其中一個時鐘用於微控制器,另一個時鐘用於USB邏輯。

單緩衝傳輸編程

對於那些帶寬、無嚴格要求的簡易傳輸而言,固件發送一個數據包相對比較簡單。步驟如下:

將字節載入SNDFIFO。

將字節載入SNDBC寄存器。

向HXFR寄存器寫入OUT PID和端點號,傳輸開始。

將字節載入SNDBC寄存器。

向HXFR寄存器寫入OUT PID和端點號,傳輸開始。

等待HXFRDNIRQ (主機發送完成中斷請求)。

從HRSL寄存器中讀取傳輸結果代碼。

如果爲ACK,表明傳輸已完成。

如果爲NAK,表明將進入下一步。

再次裝載第一個FIFO字節,重新開始傳輸:

寫SNDBC = 0,在微控制器控制下返回一個虛擬值,以切換含有OUT數據的FIFO。

僅將第一個FIFO字節重新寫入SNDFIFO寄存器,該字節進入圖1中的SYNC寄存器。

再次將重新發送的數據包的字節數寫入SNDBC寄存器,這樣可切換FIFO返回至USB側,重新進行USB通信。

進入第3步,重新啓動數據包。

由第4步可以看出該過程屬於單緩衝傳輸。由於需要等待完成第4步操作,故當第一個FIFO從MAX3421E SNDFIFO移向USB外設時微控制器無需載入第二個FIFO。

若採用MAX3421E版本1,還需要執行第6步,因此,每次重複USB OUT傳輸時,同步觸發器必須重新初始化。

雙緩衝傳輸編程

當由多個64字節數據包組成的長數據組從USB主機傳輸至USB外設時,利用雙緩衝可以提高性能。當SNDFIFO連接至USB發送一個數據包時,微控制器同時把下一個64字節數據包載入其他SNDFIFO中,從而改善了系統性能。然而,其程序流程要比單緩衝傳輸稍微複雜。某些情況下,需要觸發FIFO (如得到NAK信號時),重新裝載第一個字節並重寫字節計數寄存器,使其返回初始位置,這就要求程序隨時跟蹤數據的兩個緩衝器,圖2給出了雙緩衝傳輸的一種可能流程。本應用筆記最後給出了Send_OUT_Record()函數,實現圖2所示流程。

圖2. 雙緩衝OUT數據包傳輸流程
圖2. 雙緩衝OUT數據包傳輸流程

右側循環(步驟1至步驟4)將USB數據裝載到FIFO;而從第5步開始左側的循環則通過USB分配FIFO,並處理NAK重試。“First pass?”判斷(步驟3)確保在主控制器載入第一個SNDFIFO後,USB傳輸立即開始。設計該流程圖時使FIFO加載優先級高於發送優先級,因此,仍能保持雙緩衝性能。

該流程最好從以下三個方面考慮:

發送一個數據包(1至64字節總有效載荷)。

發送兩個數據包(65至128字節總有效載荷)。

發送三個或多個數據包(129或更多字節總有效載荷)。

請參考本應用筆記最後給出的Send_OUT_Record()函數例程,調用程序需向該函數傳送4個參數:

ep,爲OUT數據包分配的終端號

*pBUF,裝滿數據字節的緩衝器指針

TBC發送總字節數(緩衝器中的字節數)

NAKLimit,在停止和返回之前,從外設接收的連續NAK響應個數

該函數返回發送最後一個數據包時讀取的MAX3421E主機計算結果代碼寄存器(HRSL)的數值。如果傳輸成功,數值將爲“ACK”,如果超出NAK極限值,或因其它問題終止產生錯誤代碼,則數值爲“NAK”。

發送一個OUT數據包

當發送的字節數等於或小於64字節時,從步驟1開始循環。若步驟1的結果爲真,則SPI主機寫SNDFIFO。如果在第10步中需要用到BC和FB,也就是說,外設以“NAK”響應OUT傳輸時,函數保存字節數(BC)和SNDFIFO第一個字節(FB)。由於這是第一次數據傳輸,函數從第4步開始進行OUT傳輸。直至沒有字節需要發送時再返回步驟1,然後函數在第5步等待傳輸完成,並在第6步測試設備響應。在該點上如果得到ACK,步驟7檢測到沒有數據需要發送,則函數將在步驟11返回。然而,如果該點得到NAK,則在步驟9測試NAK極限值,如果超出該極限值,則在步驟10和步驟4中重新發送數據包。

發送兩個OUT數據包

若發送的字節數介於65和128之間,則函數從步驟1處再次開始循環。如上所述,函數先寫滿第一個SNDFIFO,從步驟2開始立即進行傳輸直至步驟4。然後函數返回至步驟1,發現還有數據等待發送,於是在步驟2中再次寫滿SNDFIFO。由於這不是第一次傳輸,因此,在步驟4函數不能開始傳輸下一個數據包。再次返回至步驟1時,第一個SNDFIFO通過USB傳輸第一個數據包,與此同時,第二個數據包仍處於第二個SNDFIFO中,做好傳輸準備。需要注意的是,在步驟2中,函數實際保存了兩組BC/FB數據(字節計數和FIFO第一個字節),一組爲當前正在傳輸的SNDFIFO,另一組爲在第二個SNDFIFO中等待的即將傳輸的數據。若沒有數據需要發送(並且兩個SNDFIFO都裝滿了數據),則控制流程進入步驟5。

函數在步驟5中等待傳輸第一個數據包,然後在步驟6判斷傳輸結果。如上所述,如果數據包爲“NAK'd”,函數在第10步將未決的BC/FB數值重新載入FIFO和字節計數寄存器中,在步驟4重新發送數據包,流程分支返回至步驟5 (通過步驟1),等待傳輸完成。每產生一個NAK,按步驟5-6-9-10-4-5循環往復,直至接收到ACK或達到NAK極限值爲止。

如果外設應答(ACK) OUT傳輸,函數在步驟7測試流程是否終止。如果沒有完成,則函數流程進入步驟8,發送正在第二個SNDFIFO中等待的下一個數據包。在步驟8中,函數執行多個任務:如果步驟10中需要載入“未決的” BC/FC數值,將這些數值複製到“當前” BC/FC變量;通過加載當前字節數,切換第二個SNDFIFO至USB;在步驟4中開始傳輸OUT數據。每接收到一個NAK信號,便如上所述循環操作。若在步驟7至步驟11過程中接收到ACK響應,則函數退出。

發送三個或更多個OUT數據包

在一個數據包正在發送,而另一個數據包將要開始發送(等待第二個SNDFIFO)之前,該函數基本上是按照先前發送兩個數據包的流程進行的。每次函數均在第4步開始啓動另一個數據包,在步驟1判斷是否還有數據需載入SNDFIFO中。必須在還有數據需要發送,同時程序流程中FIFO有效的情況下,才能進入步驟2。因爲步驟1至步驟3中“寫SNDFIFO”循環的優先級高於步驟5...4的“發送FIFO”循環,因此數據通過USB傳輸時總是被寫入SNDFIFO,從而實現雙緩衝。

性能

圖3. MAX3421E載入和發送USB數據包的示波器曲線
圖3. MAX3421E載入和發送USB數據包的示波器曲線

圖3爲所示爲用Send_OUT_Record()函數載入和發送64字節OUT數據包時疊加的示波器曲線。數據的大小爲512個字節,由8個64字節數據包組成。由圖中可以看出,與MAX3421E相連的外設不產生NAK信號,因此可對最大傳輸帶寬進行測量,示波器一次就可捕獲整個傳輸過程。

最上面的兩條曲線爲SPI主機(SPI硬件具有ARM7)通過SPI端口向MAX3421E SNDFIFO載入64字節數據的曲線。每次寫SNDFIFO時,SS# (從機選擇)線路爲低電平,SCK (串行時鐘)脈衝爲65 x 8,每當載入命令字節時,向64字節數據提供8個時鐘脈衝。在該曲線開始之前,第一次SNDFIFO加載結束,因此,圖3給出了7次SNDFIFO載入時的情況。

第三條曲線是USB D+信號,表示64字節OUT數據包正通過USB從主機MAX3421E向外設傳輸。最下面一條曲線是脈衝,表示ARM7載入MAX3421E HXFR寄存器,開始傳輸。

需要注意的是:在USB數據包開始不久,ARM7開始載入第二個SNDFIFO,同時USB數據包在總線上進行傳輸。這一雙緩衝過程可大大改善傳輸帶寬。

帶寬測試

圖4. 圖3傳輸過程的CATC (LeCroy)和總線分析結果
圖4. 圖3傳輸過程的CATC (LeCroy)和總線分析結果

如圖4所示,Send_OUT_Record()函數以6.76Mbps速率傳輸521字節的數據。作爲參考,與PC相連的USB全速USB thumb drive採用UHCI USB控制器(USB的唯一附件) 以5.94Mbps速率傳輸512字節的數據。


方案實現中的注意事項

以下提供的代碼實例是採用Maxim USB函數庫進行編寫和測試的,Maxim USB函數庫的詳細說明請參見應用筆記3936,"Maxim USB庫"。該函數庫工具包含MAX3421E 和MAX3420E USB外設控制器。若要對固件進行測試的話,採用USB電纜將MAX3421E主機連接至MAX3420E外設,通過將標爲"*1*"的語句註釋掉,使MAX3420E接受每個OUT傳輸(無NAK)。

注: MAX3421E的未來版本將不會再涉及到FIFO的重新載入問題。只需重寫HXFR寄存器,MAX3421E就可重新啓動一個OUT數據包。這種改進不再需要Send_OUT_Record()函數。

Send_OUT_Record()函數實例

// *******************************************************************************
// 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);
}

 

轉自:http://www.elecfans.com/tongxin/119/2009042453552.html


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章