MAX3421E版本1和2主机OUT传输

来源:互联网 发布:知的草书写法 编辑:程序博客网 时间:2024/05/01 20:18

引言

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 sendWORD bytes2send;                // tempBYTE Available_Buffers;         // Remaining buffers count (0-2)BYTE FI_FB;                     // Temporary FIFO first bytestatic BYTE CurrentBC;          // Byte count for currently-sending FIFOstatic BYTE CurrentFB;          // First FIFO byte for currently-sending FIFOstatic BYTE PendingBC;          // Byte count for next 64 byte packet scheduled for sendingstatic BYTE PendingFB;          // First FIFO byte for next 64 byte packet scheduled for sendingBYTE dum;BYTE Transfer_In_Progress,FirstPass;    // flags//NAKct=0;Available_Buffers = 2;rb = TBC;                       // initial remaining bytes = total byte countFirstPass = 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


原创粉丝点击