完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
问题 : 某客户工程师在其产品的设计中,使用了 STM32F205VET6。据其工程师讲述:他使用 ST 的 USB 固件库中的 VCP 例程来实现虚拟串口的功能,但是他发现虚拟串口一次输出的数据(从串口到上位机)如果超过 2Kbytes 就会造成数据丢失,只输出尾部的 2Kbytes。客户工程师检查代码发现 USB 的 FIFO 大小由宏定义 APP_RX_DATA_SIZE 决定,而 APP_RX_DATA_SIZE 的大小刚好为 2Kbytes。所以他认为此 FIFO设计太小而造成的,于是他将 FIFO 的大小改成 5Kbyte,不过修改后并不能解决问题。 调研 : 1. 打开“STM32F105/7, STM32F2 and STM32F4 USB on-the-go Host and device library (UM1021)”库里边的 VCP 例程,位于“...STM32_USB-Host- Device_Lib_V2.1.0ProjectUSB_Device_ExamplesVCP”中。对其进行测试,并没有出现所说的问题,APP_RX_DATA_SIZE 的大小仍然为 2Kbytes,不管传输的数据是 2Kbytes 还是 5Kbytes 甚至是25Kbytes,完全没有问题。 2. 了解客户程序 UART 所设置的波特率,为 115200,与原 VCP 例程一致。USB 采用的是 Full Speed,全速 USB 总线的帧周期为 1ms。 3. 在 u***d_conf.h 中可以看到 APP_RX_DATA_SIZE 的定义在这里,为 2048,它定义了 APP_Rx_Buffer的大小。APP_Rx_Buffer 其实是一个循环缓冲区APP_Rx_ptr_in 指明了其数据进来的位置,当USART 接收到数据时,将数据存储于 APP_Rx_ptr_in 指定的位置;APP_Rx_ptr_out 指明其数据取出的位置,当 USB 到 FIFO 中取出数据时,起始地址由 APP_Rx_ptr_out 决定。 4. 打开 VCP 项目,观察其程序通信部分。当 USART 每接收到字节时,进入 EVAL_COM_IRQHandler 函数,调用 VCP_Tx(0,0)函数,将收到的字节存储于 APP_Rx_Buffer[APP_Rx_ptr_in]中,在APP_Rx_Buffer 中的位置由 APP_Rx_ptr_in 指定。在 VCP_Tx 函数中, if(APP_Rx_ptr_in == APP_RX_DATA_SIZE) { APP_Rx_ptr_in = 0; } 可以看到当 APP_Rx_ptr_in 达到 APP_RX_DATA_SIZE 时,将其置 0,也就是在循环缓冲区中绕了一圈回到缓冲区起始地址。 5. 再来看 APP_Rx_Buffer 是如何被 USB 取走,并送到上位机的。在 u***d_cdc_core.c 中,我们在u***d_cdc_SOF 函数中看到: if (FrameCount++ == CDC_IN_FRAME_INTERVAL) { FrameCount = 0; Handle_USBAsynchXfer(pdev); } 可以看到,USB 每 CDC_IN_FRAME_INTERVAL 个帧调用一次 Handle_USBAsynchXfer 到 APP_Rx_Buffer中去取数据。CDC_IN_FRAME_INTERVAL 同样定义在 u***d_conf.h 中,全速的时候,其值为 5。在定义的这边,我们可以看到: APP_RX_DATA_SIZE*8/MAX_BAUDARATE*1000 should be > CDC_IN_FRAME_INTERVAL 其目的是在于告诉我们 APP_RX_DATA_SIZE、MAX_BAUDARATE 和 CDC_IN_FRAME_INTERVAL 的关系,以保证 APP_Rx_Buffer 是够用的。 6. 接着看 Handle_USBAsynchXfer 函数,同样,我们可以看到: if (APP_Rx_ptr_out == APP_RX_DATA_SIZE) { APP_Rx_ptr_out = 0; } 也就是当 APP_Rx_ptr_out 达到 APP_RX_DATA_SIZE 时,将其置 0,也就是在循环缓冲区中绕了一圈回到缓冲区起始地址。 if (APP_Rx_prt_out == APP_Rx_ptr_in) { USB_Tx_State = 0; return; } 当 APP_Rx_prt_out 赶上 APP_Rx_ptr_in 时,证明 Buffer 里边的数据已经发送完毕,返回。再往下看: if (APP_Rx_ptr_out > APP_Rx_ptr_in) /* rollback */ { APP_Rx_length = APP_RX_DATA_SIZE - APP_Rx_ptr_out; //① } else { APP_Rx_length = APP_Rx_ptr_in - APP_Rx_ptr_out; //② } 第① 种情况为 APP_Rx_ptr_out 比 APP_Rx_ptr_in 大,也就是说 APP_Rx_ptr_in 已经绕了一圈,而APP_Rx_ptr_out 还没有绕一圈,比如下面情况: 这种情况下:APP_Rx_length 设置为 APP_Rx_ptr_out 当前圈里还剩下数据长度; 第② 种情况为 APP_Rx_ptr_in 比 APP_Rx_ptr_out 大,也就是 APP_Rx_ptr_in 和 APP_Rx_ptr_out 处于同一圈,于是数据情况比如下面情况: 这种情况下:APP_Rx_length 设置为 APP_Rx_ptr_in 减去 APP_Rx_ptr_out,也就是当前所有数据长度。 程序的后面就是对 USB 包的设置,然后发送数据。 7. 结合 EVAL_COM_IRQHandler 和 Handle_USBAsynchXfer 函数对 APP_Rx_Buffer 的处理,我们可以发现,有一种情况在程序中是没有做处理的:当 APP_Rx_ptr_in 绕了一圈回来,并追上 APP_Rx_ptr_out 时,APP_Rx_Buffer 的数据已满,这个时候,若 USART 继续接收到数据,APP_Rx_ptr_in 指针继续增长,就会造成数据溢出,新来的数据冲掉还没被 USB 取走的旧数据。但是,需要注意的一点是,此 USB库的 VCP 例程是实时的,也就是 USART 收进来,USB 会取走送到上位机。而 USB 将数据送往上位机的速率是肯定大于 USART 接收数据的速率的。也就是说,这个例程可以保证“当 APP_Rx_ptr_in 绕了一圈回来,并追上 APP_Rx_ptr_out 时,APP_Rx_Buffer 的数据已满,这个时候,若 USART 继续接收到数据,APP_Rx_ptr_in 指针继续增长,就会造成数据溢出,新来的数据冲掉还没被 USB 取走的旧数据。”这种情况不会发生! 之前所分析的: APP_RX_DATA_SIZE*8/MAX_BAUDARATE*1000 should be > CDC_IN_FRAME_INTERVAL 这个要求,正是保证 APP_Rx_Buffer 安全的重要条件。 8. 至此,我们怀疑客户并不是照搬 VCP 例程,而是对其做了修改。拜访客户,了解到确实如此。客户由于其应用需要,将 VCP 程序拆成两部分,先是用 USART 把所有的数据接收进来,放到 RAM 中,然后再将数据送到 APP_Rx_Buffer,由 USB 将数据送往上位机。这样,问题就来了,由于客户 USART先从外部接收到并保存于 RAM 的数据大于 5Kbytes,而 CPU 将 RAM 中的数据搬往 APP_Rx_Buffer 的速率远大于 USB 将 APP_Rx_Buffer 送往上位机的速率。这样就造成了 APP_Rx_ptr_in 绕了一圈回来追上 APP_Rx_ptr_out 并造成溢出的情况,甚至可能是 APP_Rx_ptr_in 绕了几圈,而APP_Rx_ptr_out 却还未开始动,因为 USB 每 CDC_IN_FRAME_INTERVAL*1ms 才送一次数据。 9. 建议客户修改其程序,在将数据从 RAM 中搬往 APP_Rx_Buffer 的时候,不能采用原 VCP 例程中USART 一样的操作方式,也就是存一个字节到 APP_Rx_Buffer,APP_Rx_ptr_in 指针加 1,并绕圈。 新的程序需要在此基础上判断,当 APP_Rx_ptr_in 指针加 1 后,若等于 APP_Rx_ptr_out,置 “APP_Rx_Buffer 满”标志位,并停止将 RAM 中的数据搬往 APP_Rx_Buffer。等 USB 从 APP_Rx_Buffer 中取走数据,再清“APP_Rx_Buffer 满”标志位,允许将 RAM 中的数据继续搬往APP_Rx_Buffer。 10. 客户修改程序,问题解决。 结论 : 修改 VCP 例程时,没有对 APP_Rx_Buffer 的操作有足够的了解,造成在其特定应用中产生了数据溢出问题。 处理 : 在将数据存入 APP_Rx_Buffer 时,对 APP_Rx_ptr_in 指针在循环缓冲区中绕一圈回来后,是否会追上APP_Rx_ptr_out 指针进行监控,以避免数据溢出。 建议 : 在对例程进行修改程序以供自己的应用来使用的时候,我们不仅需要把接口看清楚,更需要把程序的流程看明白。 评分
|
||
相关推荐
|
||
蔡老师你好,看了您的这篇分析帖子收获很多!同时我也有再用F407的 这个VCP例程序,来做UBS-CAN通信,即当数据通过USB 虚拟串口接受到APP_Rx_Buff之后,采用CAN发送的,请您帮我分析一下我的程序代码,现在的问题是 CAN无法发送,我自己找不出问题所在。可提供咨询学习费用。
|
|
|
|
|
|
飞凌嵌入式ElfBoard EL 1板卡-i2c与从设备通讯编程示例之i2c-tools工具使用
652 浏览 0 评论
stc15f2k60s2利用串口传输字模存储到eeprom并进行点阵显示
696 浏览 1 评论
1264 浏览 0 评论
588 浏览 0 评论
嵌入式学习-飞凌嵌入式ElfBoard ELF 1板卡-串口通讯编程示例之串口编写程序
1190 浏览 0 评论
【youyeetoo X1 windows 开发板体验】少儿AI智能STEAM积木平台
11597 浏览 31 评论
浏览过的版块 |
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-2 20:08 , Processed in 0.547986 second(s), Total 42, Slave 33 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号