ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-12-26 16:02:46  阅读:249  来源: 互联网

标签:FS USB SETUP 19 USBFS FIFO uint32 len ep


目录

1. SETUP数据包的获取

2. OUT数据处理

3. IN 数据传输

4. 读FIFO

5. 写FIFO


1. SETUP数据包的获取

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

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

在接收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中断处理流程如下图:

在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传输结束中断。

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;
}

标签:FS,USB,SETUP,19,USBFS,FIFO,uint32,len,ep
来源: https://blog.csdn.net/pq113_6/article/details/122011584

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有