[信息] 【实战经验】使用STM32CubeMX实现USB虚拟串口的环回测试功能

[复制链接]
 楼主| 香水城 发表于 2015-12-16 15:52 | 显示全部楼层 |阅读模式
本帖最后由 香水城 于 2017-8-16 14:46 编辑

使用STM32CubeMX实现USB虚拟串口的环回测试功能

前言
客户在STM32F401RET6中使用到了USB的虚拟串口功能。要求提供一个在STM32Cube中实现虚拟串口进行数据环回测试功能的范例程序。因为在我们目前所提供范例程序中,并没有适合客户需求的范例,所以我们在STM32CubeMX中创建一个范例程序给客户进行参考。
环境需求如下:
硬件环境:STM32F401C-Dicovery
电脑操作系统:window 7.x
集成开发环境:MDK、IAR
驱动:ST VCP Driver
STM32CubeMX:4.12.0
STM32CubeF4:1.10.0

STM32CubeMX中配置流程
实现步骤如下:
首先,打开STM32CubeMX软件,选择STM32F401C-Discovery板子。
1.PNG
在PinOut列中选择USB OTG FS的Device Only 选项
2.PNG
使能USB 中间件的虚拟串口功能
3.PNG
直接生成代码,这里我们选择MDK的IDE,工程名字叫VCP_Test
4.PNG
在生成的工程代码中的usbd_cdc_if.c文件中添加如下几行代码:
5.PNG
这个定义本来就有的,只是建议将定义的值修改为1024,这样效果更好。这个值的默认值是4,但是在实际的操作中发现,如果你上位机传输的数据大于4,且并不是4的整数倍的时候,会出现丢数据或者数据不返回的问题。其根本原因是因为虚拟串口的数据是以数据流的方式发送出来的,接收数据不知道每次接收到的数据大小是多少,所以使用了循环队列,但是一旦你的循环队列很小,很容易出现溢出的问题。比如你设置这个值为5,你发一个123456的数据给MCU,那么MCU就会返回123451这六个数据。最后的一个数据就是溢出了,所以只能被第一个数据所代替。
6.PNG
上边这部分代码是用来做串口参数设置的。下边的代码中会用到。
在CDC_Control_FS函数中的对应位置添加如下code
7.PNG
在CDC_Receive_FS函数中的对应位置添加如下代码
8.PNG
这是用来接收USB传输的数据,并缓存到UserTxBufferFS这个数组中。
在最后添加如下函数
9.PNG
10.PNG
这个函数实现了一个简单的数据队列操作,一旦有数据收到,就将该数据发送到PC端,实现数据的环回功能。
在main.c的主循环中,调用Main_loop这个函数即可。
11.PNG
最后修改一下堆栈的大小:
在MDK中修改堆栈:
12.PNG
IAR中配置堆栈大小
13.PNG
编译后直接下载到MCU中即可以运行查看结果。
注意:硬件需要一根Micro USB线来连接板子的USB端口和PC机。
          PC端需要已经安装好ST提供VCP驱动程序。
14.PNG
15.PNG

问题总结:
STM32CubeMX会使STM32全系列中的软件移植工作变得更方便。在一些以前看起来比较复杂的程序,在Cube中可以很简的完成。

对应的代码:VCP_Test_STM32F401C_Discovery
更多实战经验请看:【ST MCU实战经验汇总贴】

mmuuss586 发表于 2015-12-16 17:19 | 显示全部楼层

支持下;
huangqi412 发表于 2015-12-16 17:37 | 显示全部楼层
大客户真幸福,这也可以。
beyond696 发表于 2015-12-16 20:16 | 显示全部楼层
没这样搞过,看似不错!
jiaxinhui 发表于 2015-12-17 12:50 | 显示全部楼层
这个应用笔记好,必须得顶一下!                                                                                                                                                                                         
handleMessage 发表于 2015-12-17 15:06 来自手机 | 显示全部楼层
原来虚拟串口的数据是以数据流的方式发送出来的
z755924843 发表于 2016-3-7 16:06 | 显示全部楼层
香主你好,我按照的你的步骤修改了代码,运行在stm32F407discovery开发板,开发板只接收到一次上位机发送数据之后,就不运行了。想问一下,是不是401和407不一样啊? (我上位机使用的串口调试助手)
z755924843 发表于 2016-3-7 16:37 | 显示全部楼层
不好意思香主 找到问题了 原来是我大意少写了一个+号,单步调试的时候才发现
lxnshiwo813 发表于 2016-4-19 14:42 | 显示全部楼层
请问这个如何设置每隔10ms自动发送一次呢
jasoji 发表于 2017-7-18 14:37 | 显示全部楼层
本帖最后由 jasoji 于 2017-7-18 14:38 编辑

CDC_Receive_FS这样写,在大量数据环回还是有点问题的。
改成这样就没问题↓
  1. static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
  2. {
  3.     /* USER CODE BEGIN 6 */
  4.     USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  5.     USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  6.     if(UserTxBufPtrIn+*Len<=APP_TX_DATA_SIZE){  //UserTxBufferFS未满,续上len长度数据
  7.         memcpy((uint8_t*)(UserTxBufferFS+UserTxBufPtrIn),Buf,*Len);
  8.         UserTxBufPtrIn+=*Len;
  9.     } else {  //满了,写完后面回前面继续写
  10.         uint16_t lenBack, lenFront;
  11.         lenBack = APP_RX_DATA_SIZE-UserTxBufPtrIn;//后面剩多少
  12.         lenFront = *Len-lenBack;//前面继续写多少
  13.         memcpy((uint8_t*)(UserTxBufferFS+UserTxBufPtrIn), Buf, lenBack);
  14.         memcpy((uint8_t*)(UserTxBufferFS), (uint8_t*)(Buf+lenBack), lenFront);
  15.         UserTxBufPtrIn=lenFront;
  16.     }
  17.     return (USBD_OK);
  18.     /* USER CODE END 6 */
  19. }


然后main_loop那里改这个
  1. if(UserTxBufPtrOut != UserTxBufPtrIn) {
  2.     if(UserTxBufPtrOut<UserTxBufPtrIn)
  3.     {
  4.         uint16_t len;
  5.         len = UserTxBufPtrIn-UserTxBufPtrOut;
  6.         CDC_Transmit_FS((uint8_t*)(UserTxBufferFS+UserTxBufPtrOut), len);
  7.         UserTxBufPtrOut+=len;
  8.     } else {
  9.         uint16_t lenBack, lenFront;
  10.         lenBack = APP_RX_DATA_len-UserTxBufPtrOut;
  11.         lenFront = UserTxBufPtrIn;
  12.         CDC_Transmit_FS((uint8_t*)(UserTxBufferFS+UserTxBufPtrOut), lenBack);
  13.         CDC_Transmit_FS((uint8_t*)(UserTxBufferFS), lenFront);
  14.         UserTxBufPtrOut=UserTxBufPtrIn;
  15.     }
  16. }
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:意法半导体(中国)投资有限公司
简介:STM32技术专家

596

主题

17195

帖子

291

粉丝
快速回复 在线客服 返回列表 返回顶部
认证:意法半导体(中国)投资有限公司
简介:STM32技术专家

596

主题

17195

帖子

291

粉丝
快速回复 在线客服 返回列表 返回顶部