GD32450i-EVAL学习笔记 19 - USB FS 数据传输

目录

1. SETUP数据包的获取

2. OUT数据处理

3. IN 数据传输

4. 读FIFO

5. 写FIFO


1. SETUP数据包的获取

SETUP数据包的获取发生在接收数据FIFO非空中断

下图是接收数据FIFO非空中断的处理流程图:

GD32450i-EVAL学习笔记 19 - USB FS 数据传输

在接收SETUP数据包前必须先设置好USBFS_DOEP0LEN中的STPCNT的大小,控制端点每收到一个 SETUP 数据包后, STPCNT的值都会递减。

STPCNT的值必须设置为3.

而接收数据 FIFO 中需要分配一些额外空间,以便能够在控制端点上接收连续的最多三个 SETUP 数据包。每个SETUP需要8个字节的数据和4个字节的SETUP状态,3个SETUP就是36个字节,还需要4个字节的“建立阶段完成”状态,所以接收数据FIFO需要分配最少40个字节(10个字)。GD32F450例程中这里设置的是24字节,改成40字节没看出什么变化。

void usbInitCtrlEP(void)
{
    /* set OUT endpoint 0 receive length to 40 bytes, 1 packet and 3 setup packets */
    USBFS.Dev->DOEPx[0].LEN = ((uint32_t)40 << 0) | ((uint32_t)1 << 19) | ((uint32_t)3 << 29);
}

在数据类型为6的接收数据FIFO非空中断处理中,只需要把USB数据读入到一个结构即可

usbReadBuf(USB_EP0, (uint8_t *)(&gUsbReqInfo), sizeof(gUsbReqInfo));

接收数据FIFO非空中断处理完后,接着发生数据类型为3的接收数据FIFO非空中断,表示主机的OUT传输过程结束了。

void usbDevIntRxFIFO(void)
{
    __IO uint32_t value = 0;
    uint8_t ep = 0;
    uint8_t pid = 0;
    uint16_t len = 0;
    uint8_t status = 0;
    /* disable the Rx status queue non-empty interrupt */
    USBFS.Global->GINTEN &= ~((uint32_t)1 << 4); //bit4: receive FIFO non-empty interrupt enable 
    value = USBFS.Global->GRSTATP;
    ep = (uint8_t)(value & 0xF);                    //bit0-3: endpoint number
    len = (uint16_t)((value >> 4) & 0x7FF);         //bit4-14: byte count
    pid = (uint8_t)((value >> 15) & 0x3);           //bit15-16: data PID
    status = (uint8_t)((value >> 17) & 0xF);        //bit17-20: received packet status
    if(status == 6)
        USBINT_INFO(Printf(" ***************************\n"));
    USBINT_INFO(Printf("    RxFIFOS:%d\n", status));
    
    switch(status)
    {
        case 1: //global OUT NAK (triggers an interrupt)
            break;
        case 2: //OUT data packet received
            if(len > 0)
            {
                USBINT_INFO(Printf("    EP%d OUT Data\n", ep));
                if (ep == 0)
                {
                    usbData0OutProcess();
                }
                else
                {
                    void(*usbEpnIntUser[])(void) =
                    {
                        usbEp1OutUsr,
                        usbEp2OutUsr,
                        usbEp3OutUsr,
                    };
                    (*usbEpnIntUser[(ep - 1)])();
                }
            }
            else if (ep == 0)
            {
                usbStatus0OutProcess();
                if(gUsbDevState.ctrlState == USB_CTRL_IDLE)
                {
                    usbInitCtrlEP();
                }
            }
            break;
        case 3: //OUT transfer completed (triggers an interrupt)
            break;
        case 4: //SETUP transaction completed (triggers an interrupt)
            break;
        case 6: //SETUP data packet received
            if (ep == 0 && len == 8 && pid == 0)
            {
                if(gUsbDevState.ctrlState != USB_CTRL_IDLE)
                    USBINT_INFO(Printf("USB EP0 is not in IDLE:%d, %x, %d\n", gUsbDevState.ctrlState, 
                        gUsbReqInfo.bmRequestType, gUsbReqInfo.bRequest));
                usbReadBuf(USB_EP0, (uint8_t *)(&gUsbReqInfo), sizeof(gUsbReqInfo));
                gUsbDevState.ctrlState = USB_CTRL_SETUP;
                USBINT_INFO(Printf("    bmRequestType:%d\n", gUsbReqInfo.bmRequestType));
                USBINT_INFO(Printf("    bRequest:%d\n", gUsbReqInfo.bRequest));
                USBINT_INFO(Printf("    wIndex:%d\n", gUsbReqInfo.wIndex));
                USBINT_INFO(Printf("    wLength:%d\n", gUsbReqInfo.wLength));
                USBINT_INFO(Printf("    wValue:%x\n", gUsbReqInfo.wValue));
            }
            break;
        default:
            break;
    }
    /* enable the Rx status queue level interrupt */
    USBFS.Global->GINTEN |= ((uint32_t)1 << 4);
}

2. OUT数据处理

随后发生数据类型为4的接收数据FIFO非空中断,表示主机的SETUP阶段完成。类型3和类型4的中断都不需要处理任何事务。类型4的FIFO非空中断发生的同时会产生一个OUT中断。

OUT中断处理流程如下图:

GD32450i-EVAL学习笔记 19 - USB FS 数据传输

在SETUP阶段完成的OUT中断中,程序根据SETUP数据包(8字节)解码

void usbDevEpOutInt(void)
{
    uint8_t epOutInt = 0;
    uint8_t ep = 0;
    epOutInt = (USBFS.Dev->DAEPINT >> 16) & 0xF;
    while(epOutInt > 0)
    {
        if ((epOutInt & 0x01) > 0)
        {
            uint32_t epOutIntF = USBFS.Dev->DOEPx[ep].INTF & USBFS.Dev->DOEPINTEN;
            USBINT_INFO(Printf("EP%d Out:", ep));
            if((epOutIntF & (uint32_t)1 << 0) > 0) //bit0: transfer finished
            {
                USBFS.Dev->DOEPx[ep].INTF = (uint32_t)1 << 0;//clear transfer finished flag.
                USBINT_INFO(Printf("Transfer Finished "));
            }
            if((epOutIntF & (uint32_t)1 << 3) > 0) //bit3: SETUP phase finished
            {
                USBINT_INFO(Printf("Setup Finish "));
                USBFS.Dev->DOEPx[ep].INTF = (uint32_t)1 << 3;
                gUsbDevState.ctrlState = USB_CTRL_SETUP;
                usbSetup0Process();
                if(gUsbDevState.state == USB_STATE_ADDRESS)
                {
                    Printf("Address:%d\n", gUsbDevInfo.address);
                    usbSetAddress(gUsbDevInfo.address);
                    gUsbDevInfo.address = 0;
                    gUsbDevState.state = USB_STATE_ADDRESSED;
                }
                usbData0InProcess();
                usbStatus0InProcess();
                USBINT_INFO(Printf("    ###SETUP State:%d\n", gUsbDevState.ctrlState));
            }
            USBINT_INFO(Printf("\n"));
        }
        epOutInt >>= 1;
        ep++;
        if(ep > USBFS.maxEPNum)
        {
            Printf("EP Out Fail\n");
            break;
        }
    }
}

3. IN 数据传输

一般USB通信第一笔数据是设备描述符,在SETUP解码时下一步是IN数据,即将设备描述符发给主机。

USB IN中断分2种情况,一个是端点FIFO空中断和IN传输结束中断。

GD32450i-EVAL学习笔记 19 - USB FS 数据传输

void usbDevEpInInt(void)
{
    uint16_t epInInt = 0;
    uint8_t ep = 0;
    epInInt = (USBFS.Dev->DAEPINT >> 0) & 0xF;
    while(epInInt > 0)
    {
        if ((epInInt & 0x01) > 0)
        {
            uint32_t intEn = USBFS.Dev->DIEPINTEN;
            uint32_t fifoEmptyIntEn = USBFS.Dev->DIEPFEINTEN;
            uint32_t epInIntF;
            intEn |= ((fifoEmptyIntEn >> ep) & 0x1U) << 7;  //bit7: transmit FIFO empty
            epInIntF = USBFS.Dev->DIEPx->INTF & intEn;
            USBINT_INFO(Printf("EP%d In:", ep));
             
            if((epInIntF & ((uint32_t)1 << 0)) > 0) //transfer finished
            {
                USBFS.Dev->DIEPx->INTF = ((uint32_t)1 << 0);
                USBINT_INFO(Printf("Finish %d ", gUsbDevState.ctrlState));
                USBFS.Dev->DIEPx[ep].CTL |= ((uint32_t)1 << 27);   //Set NAK
                if(ep == 0)
                {
                    usbData0InProcess();
                    usbStatus0InProcess();
                    if(gUsbDevState.ctrlState >= USB_CTRL_STATUS_OUT) //Last Data
                    {
                        usbInitCtrlEP();
                    }
                }
            }
            
            if((epInIntF & ((uint32_t)1 << 7)) > 0) //transmit FIFO empty
            {
                void(*usbEpnIntUser[])(void) =
                {
                    usbEp1InUsr,
                    usbEp2InUsr,
                    usbEp3InUsr,
                }; 
                if(ep > 0)
                    (*usbEpnIntUser[(ep - 1)])();
                USBFS.Dev->DIEPFEINTEN &= ~((uint32_t)1 << ep);
                USBFS.Dev->DIEPx->INTF = ((uint32_t)1 << 7);
            }
            USBFS.Dev->DIEPx[ep].INTF = 0xFFFF;
            USBINT_INFO(Printf("\n"));
        }
        epInInt >>= 1;
        ep++;
    }
}

这里有2个地方需要注意,端点1-3的IN中断处理需要放在空中断中,因为它们不会产生传输结束中断,这里不知道是不是哪里设置的问题。但是端点0的又不能放在这里,需要放在传输结束后才能发下一笔数据。

在传输结束中断中,对于GD32F450,需要将该端点设置为NAK,否则多笔数据时会只有第一笔数据才能收到,而对于STM32F407则没有问题。同样,也不知道哪里的设置的问题。

主要是整个软件框架和官方例程差别比较大,不知道哪里设置有问题,目前基本的功能是正常的,不确认现在的方式是否合法。这2个地方差不多卡了几个月才试出来,网上这块的资料太少。

4. 读FIFO

RX FIFO就一个,所有端点共用。每次读入一个字(32位)

uint16_t usbReadBuf(uint8_t port, uint8_t* buf, uint16_t len)
{
    uint32_t wLen = (len + 3) / 4;
    uint16_t count = 0;

    while (wLen-- > 0) 
    {
        *(__packed uint32_t *)buf = *(USBFS.FIFO[0]);
        buf += 4;
        count += 4;
    }
    if(count - len < 4)
        count = len;
    return count;
}

5. 写FIFO

每个端点分配了一个FIFO空间。端点0写的方式和其他端点稍微有点不同。为了统一风格,所有端点都采用1个包传输,即端点的CTRL寄存器的位19-20一直设置为1.

uint16_t usbWriteBuf(uint8_t port, uint8_t* buf, uint16_t len)
{
    uint32_t wLen = (len + 3) / 4;
    uint16_t count = 0;
    __IO uint32_t epctl;
    __IO uint32_t eplen;
    uint8_t epInSize[] = 
    {
        EP0_IN_MEM_SIZE,
        #ifdef EP1_IN_MEM_SIZE
        EP1_IN_MEM_SIZE,
        #endif
        #ifdef EP2_IN_MEM_SIZE
        EP2_IN_MEM_SIZE, 
        #endif
        #ifdef EP3_IN_MEM_SIZE
        EP3_IN_MEM_SIZE,
        #endif
    };
    
    if(port >= USBFS.maxEPNum)
        return 0;

    //USBFS.Dev->DIEPx[port].CTL |= (uint32_t)1 << 30;
    epctl = USBFS.Dev->DIEPx[port].CTL;
    eplen = USBFS.Dev->DIEPx[port].LEN;
    
    len = (len > epInSize[port]) ? epInSize[port] : len;
    #if 0
    if (0 == port) 
    {
        eplen &= ~(((uint32_t)0x7F << 0) | ((uint32_t)0x3 << 19));
    }
    else
        eplen &= ~(((uint32_t)0x7FFF << 0) | ((uint32_t)0x3FF << 19) | ((uint32_t)0x03 << 29)); //bit0-18: transfer length, bit19-28: packet count
    eplen |= (uint32_t)1 << 19;
    #else
    eplen = (uint32_t)1 << 19;
    #endif
    eplen |= len;

    /* enable the endpoint and clear the NAK */
    epctl |= ((uint32_t)1 << 26) | ((uint32_t)1 << 31);
    USBFS.Dev->DIEPx[port].LEN = eplen;
    USBFS.Dev->DIEPx[port].CTL = epctl;

    wLen = (len + 3) / 4;
    while (wLen-- > 0) 
    {
        *(USBFS.FIFO[port]) = *(__packed uint32_t *)buf;
        buf += 4;
        count += 4;
    }
    //if(len > 0)
        USBFS.Dev->DIEPFEINTEN |= (uint32_t)1 << port;
    if(count > len)
    {
        count = len;
    }
    return count;
}

上一篇:大数据之-Hadoop3.x_Yarn_FIFO调度器---大数据之hadoop3.x工作笔记0144


下一篇:Day05-变量问题