GD32450i-EVAL学习笔记 19 - USB FS 数据传输
目录
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 数据传输相关推荐
- 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 设 ...
- Ext.Net学习笔记19:Ext.Net FormPanel 简单用法
Ext.Net学习笔记19:Ext.Net FormPanel 简单用法 FormPanel是一个常用的控件,Ext.Net中的FormPanel控件同样具有非常丰富的功能,在接下来的笔记中我们将一起 ...
- springmvc学习笔记(19)-RESTful支持
springmvc学习笔记(19)-RESTful支持 标签: springmvc springmvc学习笔记19-RESTful支持 概念 REST的样例 controller REST方法的前端控 ...
- 区块链学习笔记19——ETH难度调整
区块链学习笔记19--ETH难度调整 学习视频:北京大学肖臻老师<区块链技术与应用> 笔记参考:北京大学肖臻老师<区块链技术与应用>公开课系列笔记--目录导航页 前面学过,比特 ...
- VisionMaster 学习笔记(USB 孔定位)
海康VisionMaster 学习笔记(USB 孔定位) 这个例子是获得图像中 USB 孔的中心的坐标.图像如下: 首先,先加入一个'本地图像'模块,把我们的标准图像加载进去.具体如何加载图片可以参考 ...
- Python学习笔记19:列表 III
Python学习笔记19:列表 III 其实这篇笔记标题应该是列表扩展,从列表开始,将涵盖Python中的序列容器. 关于列表的基础知识,可以看我的前两篇文章: Python学习笔记1:列表. Pyt ...
- Linux 学习笔记19 信号
Linux 学习笔记19 信号 信号 信号概述 为什么要是使用信号--为了实现进程的有序退出 信号是进程运行过程中,由自身产生或者由进程外部发来的消息.信号是硬件中断的软件模拟(软中断) signal ...
- 影像组学视频学习笔记(19)-数据标准化、归一化极简概述、Li‘s have a solution and plan.
本笔记来源于B站Up主: 有Li 的影像组学系列教学视频 本节(19)主要介绍: 数据的标准化.归一化 为什么要进行标准化.归一化? 机器学习算法的要求 便于横向比较 # 标准化 (影像组学中最常用) ...
- TMS320F280049C 学习笔记19 可配置逻辑块 (CLB) 软件配置
文章目录 CLB tool 简介 CLB配置过程概述 软件安装 GNU Compiler Install the Simulation Viewer 使用CLB tool 导入空CLB工程 更新变量路 ...
最新文章
- 初学WPF之程序启动几种方式
- 直播 | AAAI 2022论文解读:三⻆分解一致性约束的端到端语音翻译
- 响应式建筑设计类dedecms模板
- Linux中expect命令实现交互,修改VNCPASSWD
- 判断QButtonGroup中哪个QRadioButton被选中
- 深度学习:波士顿房价预测
- CSS样式设置语法全解,样式优先级、值和单位、字体、文本、块级元素,行内元素,替换元素、非替换元素、display、float、position、table、li、光标、边距边框、轮廓、颜色背景
- h3cIP和TCP抓包分析实验
- Data Visualization [--0]
- 《Perl语言入门》读书笔记(一)Perl简介
- 求职软件测试工程师英文简历,软件测试工程师英文简历范文
- nuxt.js框架使用vue-waterfall-easy插件如何引入--语法引用
- 打印论文是单面还是双面?
- AI基础:卷积神经网络
- 励志短片:献给努力前行的你
- 用Python实现微信翻译机器人
- 解决Word2019使用卡顿问题
- scipy更新后imread,imresize函数被启用,替换方案
- U盘/光盘制作windows系统和linux系统
- COI 2020 Pastiri(贪心)