打印
[信息]

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

[复制链接]
6654|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 香水城 于 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板子。

在PinOut列中选择USB OTG FS的Device Only 选项

使能USB 中间件的虚拟串口功能

直接生成代码,这里我们选择MDK的IDE,工程名字叫VCP_Test

在生成的工程代码中的usbd_cdc_if.c文件中添加如下几行代码:

这个定义本来就有的,只是建议将定义的值修改为1024,这样效果更好。这个值的默认值是4,但是在实际的操作中发现,如果你上位机传输的数据大于4,且并不是4的整数倍的时候,会出现丢数据或者数据不返回的问题。其根本原因是因为虚拟串口的数据是以数据流的方式发送出来的,接收数据不知道每次接收到的数据大小是多少,所以使用了循环队列,但是一旦你的循环队列很小,很容易出现溢出的问题。比如你设置这个值为5,你发一个123456的数据给MCU,那么MCU就会返回123451这六个数据。最后的一个数据就是溢出了,所以只能被第一个数据所代替。

上边这部分代码是用来做串口参数设置的。下边的代码中会用到。
在CDC_Control_FS函数中的对应位置添加如下code

在CDC_Receive_FS函数中的对应位置添加如下代码

这是用来接收USB传输的数据,并缓存到UserTxBufferFS这个数组中。
在最后添加如下函数


这个函数实现了一个简单的数据队列操作,一旦有数据收到,就将该数据发送到PC端,实现数据的环回功能。
在main.c的主循环中,调用Main_loop这个函数即可。

最后修改一下堆栈的大小:
在MDK中修改堆栈:

IAR中配置堆栈大小

编译后直接下载到MCU中即可以运行查看结果。
注意:硬件需要一根Micro USB线来连接板子的USB端口和PC机。
          PC端需要已经安装好ST提供VCP驱动程序。



问题总结:
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 | 只看该作者
没这样搞过,看似不错!

使用特权

评论回复
5
jiaxinhui| | 2015-12-17 12:50 | 只看该作者
这个应用笔记好,必须得顶一下!                                                                                                                                                                                         

使用特权

评论回复
6
handleMessage| | 2015-12-17 15:06 | 只看该作者
原来虚拟串口的数据是以数据流的方式发送出来的

使用特权

评论回复
7
z755924843| | 2016-3-7 16:06 | 只看该作者
香主你好,我按照的你的步骤修改了代码,运行在stm32F407discovery开发板,开发板只接收到一次上位机发送数据之后,就不运行了。想问一下,是不是401和407不一样啊? (我上位机使用的串口调试助手)

使用特权

评论回复
8
z755924843| | 2016-3-7 16:37 | 只看该作者
不好意思香主 找到问题了 原来是我大意少写了一个+号,单步调试的时候才发现

使用特权

评论回复
9
lxnshiwo813| | 2016-4-19 14:42 | 只看该作者
请问这个如何设置每隔10ms自动发送一次呢

使用特权

评论回复
10
jasoji| | 2017-7-18 14:37 | 只看该作者
本帖最后由 jasoji 于 2017-7-18 14:38 编辑

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


然后main_loop那里改这个
if(UserTxBufPtrOut != UserTxBufPtrIn) {
    if(UserTxBufPtrOut<UserTxBufPtrIn)
    {
        uint16_t len;
        len = UserTxBufPtrIn-UserTxBufPtrOut;
        CDC_Transmit_FS((uint8_t*)(UserTxBufferFS+UserTxBufPtrOut), len);
        UserTxBufPtrOut+=len;
    } else {
        uint16_t lenBack, lenFront;
        lenBack = APP_RX_DATA_len-UserTxBufPtrOut;
        lenFront = UserTxBufPtrIn;
        CDC_Transmit_FS((uint8_t*)(UserTxBufferFS+UserTxBufPtrOut), lenBack);
        CDC_Transmit_FS((uint8_t*)(UserTxBufferFS), lenFront);
        UserTxBufPtrOut=UserTxBufPtrIn;
    }
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

596

主题

17055

帖子

283

粉丝