应用笔记 4000

MAX3421E版本1和2主机OUT传输


摘要 : 该应用笔记对主机USB控制器MAX3421E版本1和2进行了讨论。MAX3421E采用双缓冲发送FIFO向USB外设发送数据。在该器件的版本1和2中,需着重考虑OUT传输的编程。该应用笔记详细说明了发送机制。并给出了单缓冲和双缓冲OUT传输的例程。

引言

MAX3421E是一款USB主机/外设控制器,也就是说,当作为主机工作时,采用双缓冲发送FIFO将数据发送至USB外设,这一对儿FIFO由两个寄存器控制:

R2:SNDFIFO,FIFO数据寄存器
R7:SNDBC,字节计数寄存器

微控制器对SNDFIFO寄存器R2进行重复写操作,向FIFO写入多达64字节的数据。然后,微控制器再对SNDBC寄存器进行写操作,完成以下3项任务:
  1. 指明MAX3421E SIE (串行接口引擎) FIFO中需要发送的字节数。
  2. 连接SNDFIFO和SNDBC寄存器至USB逻辑,以进行USB通信。
  3. 清除SNDBAVIRQ中断标志。如果第二个FIFO可以用于µC装载的话,那么SNDBAVIRQ将立即重新触发。
图1. SNDFIFO寄存器和SNDBC寄存器载入一对“ping-pong” FIFO和字节计数寄存器
图1. SNDFIFO寄存器和SNDBC寄存器载入一对“ping-pong” FIFO和字节计数寄存器

图1所示,第一个FIFO字节不是由物理FIFO产生,而是由用来调谐两个内部时钟使其同步的同步寄存器产生,其中一个时钟用于微控制器,另一个时钟用于USB逻辑。

单缓冲传输编程

对于那些带宽、无严格要求的简易传输而言,固件发送一个数据包相对比较简单。步骤如下:
  1. 将字节载入SNDFIFO。
  2. 将字节载入SNDBC寄存器。
  3. 向HXFR寄存器写入OUT PID和端点号,传输开始。
  4. 等待HXFRDNIRQ (主机发送完成中断请求)。
  5. 从HRSL寄存器中读取传输结果代码。
    1. 如果为ACK,表明传输已完成。
    2. 如果为NAK,表明将进入下一步。
  6. 再次装载第一个FIFO字节,重新开始传输:
    1. 写SNDBC = 0,在微控制器控制下返回一个虚拟值,以切换含有OUT数据的FIFO。
    2. 仅将第一个FIFO字节重新写入SNDFIFO寄存器,该字节进入图1中的SYNC寄存器。
    3. 再次将重新发送的数据包的字节数写入SNDBC寄存器,这样可切换FIFO返回至USB侧,重新进行USB通信。
    4. 进入第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. 发送一个数据包(1至64字节总有效载荷)。
  2. 发送两个数据包(65至128字节总有效载荷)。
  3. 发送三个或多个数据包(129或更多字节总有效载荷)。
请参考本应用笔记最后给出的Send_OUT_Record()函数例程,调用程序需向该函数传送4个参数:
  1. ep,为OUT数据包分配的终端号
  2. *pBUF,装满数据字节的缓冲器指针
  3. TBC发送总字节数(缓冲器中的字节数)
  4. 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);
}

相关型号
MAX3421E 免费样品
下一步
EE-Mail 订阅EE-Mail,接收关于您感兴趣的新文档的自动通知。
© , Maxim Integrated Products, Inc.
The content on this webpage is protected by copyright laws of the United States and of foreign countries. For requests to copy this content, contact us.
APP 4000:
应用笔记 4000,AN4000, AN 4000, APP4000, Appnote4000, Appnote 4000