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