STM32F407电机控制板USB CDC虚拟串口收发
使用USB推荐使用外部时钟,对于USB_FS而言其总线时钟一般为48MHz;接口中启用USB,这里仅作为从设备使用(Device_Only);中间件中启用USB_DEVICE库,使用CDC类(Communication Device Class Virtual Port Com);开启外部高速时钟,如果使用高速外部时钟HSE,则需要单片机PH0-OSC_IN和PH0-OSC_OUT两个引脚接入一4
STM32F407IGT6电机控制板 USB CDC虚拟串口收发
1.STM32cubeMX工程配置
时钟配置
使用USB推荐使用外部时钟,对于USB_FS而言其总线时钟一般为48MHz;
启用USB接口
接口中启用USB,这里仅作为从设备使用(Device_Only);
使用USB设备库
中间件中启用USB_DEVICE库,使用CDC类(Communication Device Class Virtual Port Com);
1.时钟配置
开启外部高速时钟,如果使用高速外部时钟HSE,则需要单片机PH0-OSC_IN和PH0-OSC_OUT两个引脚接入一4MHz~16MHz的陶瓷晶振,笔者开发板的高速晶振为8MHZ。

SYS中Debug模式表示MCU使用的下载调试模式,一般烧录程序会使STLINK/DAP等,按照自己的模式选择即可,笔者这里开发板使用DAPLink下载器,因此选择Serial Wire即可

时钟树配置如下

2.启用USB接口
配置USB FS从机全速模式,最大速率12MBit/s

3.使用USB设备库
IP Audio Device Class(IP音频设备类):这个类别定义了一种用于在IP网络上传输音频数据的设备。它允许音频设备通过IP协议与计算机或其他设备进行通信,从而实现音频数据的传输和控制。
IP Communication Device Class(IP通信设备类):这个类别定义了一种用于在IP网络上进行通信的设备。它提供了一套标准接口和协议,使设备能够与计算机或其他设备进行数据交换、实时通信和控制。
IP Download Firmware Update Class(IP固件下载更新类):这个类别定义了一种用于通过IP网络下载和更新设备固件的设备。它提供了一套标准协议和接口,使设备能够通过IP协议接收、存储和安装固件更新。
IP Human Interface Device Class(IP人机界面设备类):这个类别定义了一种用于通过IP网络连接人机界面设备的设备。它允许用户通过IP协议与设备进行交互和控制,例如通过远程访问控制计算机的鼠标、键盘或触摸屏等输入设备。
IP Custom Human Interface Device Class(IP自定义人机界面设备类):这个类别定义了一种用于通过IP网络连接自定义人机界面设备的设备。它提供了灵活的接口和协议,使设备能够与计算机或其他设备进行自定义的人机界面交互和控制。
IP Mass Storage Device Class(IP大容量存储设备类):这个类别定义了一种用于通过IP网络连接大容量存储设备的设备。它允许用户通过IP协议访问、传输和管理存储设备上的文件和数据,类似于传统的USB大容量存储设备(如U盘或移动硬盘)的功能。
此处选择IP Communication Device Class(IP通信设备类),用于实时数据交互。

其他默认设置,生成代码。
2.Keil工程修改
上述配置生成的代码中,对于用户来说USB使用相关的代码都在 USB_DEVICE > App 中,这其中最重要的就是 usbd_cdc_if.c 文件,大多数时候我们只要改写这个文件就可以实现相关需求了,该文件主要结构与说明如下:

上面代码中最常处理的只有下面四个函数:CDC_Control_FS() 来自主机请求的回调函数CDC_Receive_FS() 接收数据回调函数;CDC_Transmit_FS() 用来发送数据;CDC_TransmitCplt_FS() 发送完成回调函数

在 CDC_Receive_FS 中添加了一行 CDC_Transmit_FS(Buf, *Len); 代码,实现了回环效果;
3.串口环回测试
不管上位机软件中波特率设置为多少都可以正常通讯,因为使用USB虚拟串口的时候真正数据传输用的是USB,串口本身参数这些已经无所谓了。

4.通信延迟测试
在windows平台使用C++构建串口一收一发的延时测试程序,调用微软提供的串口类。测试请修改L"\\.\COM31"串口为自己的串口号。
代码如下:
#include <windows.h>
#include <stdio.h>
#include <string.h>
#define TEST_DATA "test"
#define TEST_COUNT 100
long long get_time_in_us() {
LARGE_INTEGER frequency, counter;
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&counter);
return (counter.QuadPart * 1000000LL) / frequency.QuadPart;
}
int main() {
HANDLE hSerial = CreateFile(L"\\\\.\\COM31", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hSerial == INVALID_HANDLE_VALUE) {
fprintf(stderr, "打开串口失败\n");
return 1;
}
// 配置串口
DCB dcbSerialParams = { 0 };
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
if (!GetCommState(hSerial, &dcbSerialParams)) {
fprintf(stderr, "获取串口属性失败\n");
CloseHandle(hSerial);
return 1;
}
dcbSerialParams.BaudRate = CBR_115200;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;
if (!SetCommState(hSerial, &dcbSerialParams)) {
fprintf(stderr, "设置串口属性失败\n");
CloseHandle(hSerial);
return 1;
}
COMMTIMEOUTS timeouts = { 0 };
timeouts.ReadIntervalTimeout = 50;
timeouts.ReadTotalTimeoutConstant = 50;
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 50;
timeouts.WriteTotalTimeoutMultiplier = 10;
SetCommTimeouts(hSerial, &timeouts);
long long delays[TEST_COUNT] = { 0 };
long long total_delay = 0, min_delay = 99999999, max_delay = 0;
for (int i = 0; i < TEST_COUNT; i++) {
DWORD bytesWritten, bytesRead;
char buffer[16] = { 0 };
long long start_time = get_time_in_us();
WriteFile(hSerial, TEST_DATA, strlen(TEST_DATA), &bytesWritten, NULL); // 发送数据
ReadFile(hSerial, buffer, strlen(TEST_DATA), &bytesRead, NULL); // 接收数据
long long end_time = get_time_in_us();
if (bytesRead > 0 && strncmp(buffer, TEST_DATA, strlen(TEST_DATA)) == 0) {
long long delay = end_time - start_time;
delays[i] = delay;
total_delay += delay;
if (delay < min_delay) min_delay = delay;
if (delay > max_delay) max_delay = delay;
}
else {
printf("第 %d 次测试接收数据失败\n", i + 1);
}
}
CloseHandle(hSerial);
double avg_delay = total_delay / (double)TEST_COUNT;
printf("延时统计 (单位: us):\n");
printf("最大延时: %lld us\n", max_delay);
printf("最小延时: %lld us\n", min_delay);
printf("平均延时: %.2f us\n", avg_delay);
return 0;
}
测试100组收发数据,统计最大最小延时和平均延时:

平均延时在200us以下,可以初步满足5KHZ的实时控制周期需求,后续将使用C++和simulink混合编程的方式实现simulink C mex s-function与STM32低延时实时通信。
参考:
【1】STM32 USB使用记录:使用CDC类虚拟串口(VCP)进行通讯https://blog.csdn.net/Naisu_kun/article/details/118192032
【2】Visual C++串口编程全解https://blog.csdn.net/weixin_29476595/article/details/144328738
更多推荐



所有评论(0)