目录

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 numberlen = (uint16_t)((value >> 4) & 0x7FF);         //bit4-14: byte countpid = (uint8_t)((value >> 15) & 0x3);           //bit15-16: data PIDstatus = (uint8_t)((value >> 17) & 0xF);        //bit17-20: received packet statusif(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 receivedif(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 receivedif (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 emptyepInIntF = 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 NAKif(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_SIZEEP1_IN_MEM_SIZE,#endif#ifdef EP2_IN_MEM_SIZEEP2_IN_MEM_SIZE, #endif#ifdef EP3_IN_MEM_SIZEEP3_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 0if (0 == port) {eplen &= ~(((uint32_t)0x7F << 0) | ((uint32_t)0x3 << 19));}elseeplen &= ~(((uint32_t)0x7FFF << 0) | ((uint32_t)0x3FF << 19) | ((uint32_t)0x03 << 29)); //bit0-18: transfer length, bit19-28: packet counteplen |= (uint32_t)1 << 19;#elseeplen = (uint32_t)1 << 19;#endifeplen |= 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;
}

GD32450i-EVAL学习笔记 19 - USB FS 数据传输相关推荐

  1. GD32450i-EVAL学习笔记 18 - USB FS

    目录 1. 定义USB FS的结构 2. IO初始化 3. 初始化频率 3.1 IRC48M 3.2 PLL48M 3.3 使能USB FS 4. USB中断初始化 4.1 设置中断优先级 4.2 设 ...

  2. Ext.Net学习笔记19:Ext.Net FormPanel 简单用法

    Ext.Net学习笔记19:Ext.Net FormPanel 简单用法 FormPanel是一个常用的控件,Ext.Net中的FormPanel控件同样具有非常丰富的功能,在接下来的笔记中我们将一起 ...

  3. springmvc学习笔记(19)-RESTful支持

    springmvc学习笔记(19)-RESTful支持 标签: springmvc springmvc学习笔记19-RESTful支持 概念 REST的样例 controller REST方法的前端控 ...

  4. 区块链学习笔记19——ETH难度调整

    区块链学习笔记19--ETH难度调整 学习视频:北京大学肖臻老师<区块链技术与应用> 笔记参考:北京大学肖臻老师<区块链技术与应用>公开课系列笔记--目录导航页 前面学过,比特 ...

  5. VisionMaster 学习笔记(USB 孔定位)

    海康VisionMaster 学习笔记(USB 孔定位) 这个例子是获得图像中 USB 孔的中心的坐标.图像如下: 首先,先加入一个'本地图像'模块,把我们的标准图像加载进去.具体如何加载图片可以参考 ...

  6. Python学习笔记19:列表 III

    Python学习笔记19:列表 III 其实这篇笔记标题应该是列表扩展,从列表开始,将涵盖Python中的序列容器. 关于列表的基础知识,可以看我的前两篇文章: Python学习笔记1:列表. Pyt ...

  7. Linux 学习笔记19 信号

    Linux 学习笔记19 信号 信号 信号概述 为什么要是使用信号--为了实现进程的有序退出 信号是进程运行过程中,由自身产生或者由进程外部发来的消息.信号是硬件中断的软件模拟(软中断) signal ...

  8. 影像组学视频学习笔记(19)-数据标准化、归一化极简概述、Li‘s have a solution and plan.

    本笔记来源于B站Up主: 有Li 的影像组学系列教学视频 本节(19)主要介绍: 数据的标准化.归一化 为什么要进行标准化.归一化? 机器学习算法的要求 便于横向比较 # 标准化 (影像组学中最常用) ...

  9. TMS320F280049C 学习笔记19 可配置逻辑块 (CLB) 软件配置

    文章目录 CLB tool 简介 CLB配置过程概述 软件安装 GNU Compiler Install the Simulation Viewer 使用CLB tool 导入空CLB工程 更新变量路 ...

最新文章

  1. 初学WPF之程序启动几种方式
  2. 直播 | AAAI 2022论文解读:三⻆分解一致性约束的端到端语音翻译
  3. 响应式建筑设计类dedecms模板
  4. Linux中expect命令实现交互,修改VNCPASSWD
  5. 判断QButtonGroup中哪个QRadioButton被选中
  6. 深度学习:波士顿房价预测
  7. CSS样式设置语法全解,样式优先级、值和单位、字体、文本、块级元素,行内元素,替换元素、非替换元素、display、float、position、table、li、光标、边距边框、轮廓、颜色背景
  8. h3cIP和TCP抓包分析实验
  9. Data Visualization [--0]
  10. 《Perl语言入门》读书笔记(一)Perl简介
  11. 求职软件测试工程师英文简历,软件测试工程师英文简历范文
  12. nuxt.js框架使用vue-waterfall-easy插件如何引入--语法引用
  13. 打印论文是单面还是双面?
  14. AI基础:卷积神经网络
  15. 励志短片:献给努力前行的你
  16. 用Python实现微信翻译机器人
  17. 解决Word2019使用卡顿问题
  18. scipy更新后imread,imresize函数被启用,替换方案
  19. U盘/光盘制作windows系统和linux系统
  20. COI 2020 Pastiri(贪心)

热门文章

  1. 计算机辅助设计还需要手绘吗,建筑设计师,还需要手绘吗?
  2. mp4怎么转换成mp3
  3. 魔百盒M401A_晶晨S905L3A_2+16G_安卓9_原厂卡刷固件包及详细教程
  4. 深入理解EMA和SMA
  5. Oracle之Oracle入门
  6. SEO笔记--代码优化(三)
  7. Redis Geo 实践
  8. t’触发器真值表和状态方程_利用真值表实现触发器的相互转换
  9. 学习记录:关于如何使用EndNote X9在word中插入文献
  10. 遍历和添加json对象的属性 和 遍历普通js对象的属性