USB VCP虚拟串口通讯详细配置步骤(STM32H732)
一、软件版本二、CodeMX配置1、使能外部高速时钟。2、开启USB外设,选择USB Device功能,速度为默认全速USB设备12Mbps,使用默认配置。3、配置中间层使能CDC(VPC),参数依然全默认。4、时钟配置,选用外部HSE时钟源,将USB时钟配置为48MHz。5、修改Heap Size和Stack Size大小,太小导致串口识别不到或者识别错误出现黄色感叹号。6、生成Keil工程,编译运行代码。7、打开设备管理器,复位单片机,识别到虚拟串口。三、软件配置1、单片机向PC发送,在main.c中声明发送字符串和长度。2、PC向单片机发送,在usbd_cdc_if.c修改函数,实现接收回传。3、添加USB_printf打印函数,在usbd_cdc_if.c添加函数,方便直接输出字符串。四、速度测试一、软件版本
1、单片机型号:STM32H743IIT6;正点原子-阿波罗版
2、Keil-MDK:V5.32
3、CubeMX:6.7.0
4、HAL:STM32Cube FW_H7 V1.11.0
5、参考文章:
1.STM32 USB使用记录:使用CDC类虚拟串口(VCP)进行通讯
2.STM32CubeIDE 简单配置USB虚拟串口 并实现printf
3.STM32Cube配置USB虚拟串口发送与接收回传
4.STM32USB开发备忘之CDC_VCP实验
二、CodeMX配置
1、使能外部高速时钟。
2、开启USB外设,选择USB Device功能,速度为默认全速USB设备12Mbps,使用默认配置。
3、配置中间层使能CDC(VPC),参数依然全默认。
4、时钟配置,选用外部HSE时钟源,将USB时钟配置为48MHz。
5、修改Heap Size和Stack Size大小,太小导致串口识别不到或者识别错误出现黄色感叹号。
6、生成Keil工程,编译运行代码。
7、打开设备管理器,复位单片机,识别到虚拟串口。
三、软件配置
主要修改usbd_cdc_if.c和main.c,会使用这四个函数,就可以用单片机与PC进行USB基本通信:
CDC_Control_FS()
来自主机请求的回调函数;CDC控制命令处理,列举了主机有可能向设备发送的一些命令。没有具体的处理过程,需要用户自己编写。其中包括串口参数的设置,要做串口转USB通信的话需要修改这里。只是为了用USB与PC通信则不用管这里。每个命令具体的意思需要查询CDC类手册。
CDC_Receive_FS()
接收数据回调函数;Buf为接收缓存。这个缓存实际上就是CDC_Init_FS()中设置的UserRxBufferFS[]数组。这个全局数组的定义在usbd_cdc_if.c文件中。Len为接收到数据的长度。这个变量不是全局的,需要用户声明变量把这个传出去。
CDC_Transmit_FS()
用来发送数据;
CDC_TransmitCplt_FS()
发送完成回调函数;
1、单片机向PC发送,在main.c中声明发送字符串和长度。
#include "usbd_core.h"#include "usbd_cdc.h"#include "usbd_cdc_if.h"char USB_TxBuf[32];uint16_t len;/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){len=sprintf(USB_TxBuf,"Hello GJtech!\r\n");CDC_Transmit_FS((uint8_t*)USB_TxBuf,len);HAL_Delay(1000);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}
2、PC向单片机发送,在usbd_cdc_if.c修改函数,实现接收回传。
接收函数在usbd_cdc_if.h中并没有给出,而是在usbd_cdc_if.c中以static关键字给出的,这个函数其实是在USB接收时自动进入中断并调用的,可以直接在其中将接收缓冲区的内容做处理或转存。
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len){CDC_Transmit_FS(Buf,*Len);USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);USBD_CDC_ReceivePacket(&hUsbDeviceFS);return (USBD_OK);}
3、添加USB_printf打印函数,在usbd_cdc_if.c添加函数,方便直接输出字符串。
/* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */#include <stdarg.h>void usb_printf(const char *format, ...){va_list args;uint32_t length;va_start(args, format);length = vsnprintf((char *)UserTxBufferFS, APP_TX_DATA_SIZE, (char *)format, args);va_end(args);CDC_Transmit_FS(UserTxBufferFS, length);}/* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */
四、速度测试
在此正点原子单片机上,编写Python代码测试速度,可得到单片机发送数据速度为868.352K Byte/s(6.9M bps),接收速度为804.864K Byte/s(6.4M bps)。
因为相比硬件串口而言,USB虚拟的串口速度可以变得非常快,图形化的串口工具已经无法用来测速了,所以用python写了个脚本进行测试,测试脚本如下:
#!/usr/bin/env python3# -*- coding: utf-8 -*-from typing import Counterimport serialimport serial.tools.list_portsimport timeimport datetimeprint('正在搜索串口……')port_list = list(serial.ports())print('发现串口:')for i in range(0, len(port_list)):print(port_list[i])print('')port = input('请输入串口号,并按回车确认: ')print('')ser = serial.Serial(port, 22333, timeout=5)run = Truecount = 0starttime = int(round(time.time() * 1000))print(datetime.datetime.now().strftime('%H:%M:%S.%f') + ':开始测试单片机向上位机发送数据……')ser.write('S'.encode('utf-8'))while (run):ser.read(2048) # 接收来自单片机的数据count += 1currenttime = int(round(time.time() * 1000))run = False if (currenttime - starttime) >= 1000 else Trueser.write('E'.encode('utf-8'))print(datetime.datetime.now().strftime('%H:%M:%S.%f') + ':结束测试,速度约为 ' + str(count * 2048 / 1000) + 'K Byte/s' +'='+ str(count * 2048 / 1000*8) + 'K bps\n')sendbuf = bytes(2048)run = Truecount = 0starttime = int(round(time.time() * 1000))print(datetime.datetime.now().strftime('%H:%M:%S.%f') + ':开始测试单片机接收上位机的数据……')while (run):count += ser.write(sendbuf) # 向单片机发送数据currenttime = int(round(time.time() * 1000))run = False if (currenttime - starttime) >= 1000 else Trueprint(datetime.datetime.now().strftime('%H:%M:%S.%f') + ':结束测试,速度约为 ' + str(count / 1000) + 'K Byte/s' +'=' +str(count / 1000*8) + 'K bps\n')ser.close()exit()