1. 项目概述
最近拿到一块STM32C092RC开发板,迫不及待想试试它的USB虚拟串口功能。作为STM32C0系列的新成员,这款芯片在保持低成本优势的同时,提供了不错的通信外设支持。本文将详细记录如何通过USB虚拟串口实现开发板与PC的数据交互,包括环境搭建、代码实现和实际调试的全过程。
对于嵌入式开发者来说,串口通信是最基础也最常用的调试手段。传统的UART转USB方案需要额外芯片,而STM32C0内置的USB外设可以直接虚拟出串口设备,既节省成本又简化电路设计。这个功能在快速原型开发阶段特别实用,可以直接通过USB线实现日志输出和指令交互。
2. 开发环境准备
2.1 硬件配置清单
- STM32C092RC开发板(板载USB Type-C接口)
- USB Type-C数据线(支持数据传输)
- 安装了Windows 10/11的PC
- 可选:逻辑分析仪(用于信号抓取分析)
2.2 软件工具链
- STM32CubeIDE:1.13.2版本(官方集成开发环境)
- STM32CubeMX:6.9.0版本(外设配置工具)
- 串口调试助手:推荐使用Tera Term或Putty
- USB驱动:STM32 Virtual COM Port Driver
注意:使用前请确保已安装最新版STM32CubeProgrammer,部分USB功能需要其驱动支持。
3. USB虚拟串口实现详解
3.1 时钟树配置
STM32C0的USB外设需要精确的48MHz时钟。在CubeMX中按以下步骤配置:
- 启用HSI16内部时钟源
- 配置PLL将16MHz倍频到48MHz
- 选择PLL作为USB时钟源
- 系统时钟配置为48MHz
关键参数验证:
c复制// 生成的时钟配置代码片段
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1;
RCC_OscInitStruct.PLL.PLLN = 6;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
3.2 USB外设初始化
在CubeMX中配置USB为Device模式,选择CDC类(Communication Device Class):
- 启用USB外设
- 选择Device模式
- 添加CDC类
- 配置端点参数:
- 控制端点:默认8字节
- 数据端点:64字节(建议大小)
生成的USB描述符会自动包含以下关键信息:
- Vendor ID:0x0483(ST官方ID)
- Product ID:0x5740(CDC类标准PID)
- 字符串描述符:产品名称、制造商等
3.3 虚拟串口实现代码
STM32Cube库已经提供了CDC类的模板代码,主要需要关注以下几个回调函数:
c复制// 数据接收回调
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
// 将接收到的数据通过LED灯状态反映
if(Buf[0] == '1') {
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
CDC_Transmit_FS((uint8_t*)"LED ON\r\n", 8);
}
else if(Buf[0] == '0') {
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
CDC_Transmit_FS((uint8_t*)"LED OFF\r\n", 9);
}
return (USBD_OK);
}
// 发送函数封装
void USB_Printf(const char *format, ...)
{
char buffer[128];
va_list args;
va_start(args, format);
int length = vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
CDC_Transmit_FS((uint8_t*)buffer, length);
HAL_Delay(1); // 防止连续发送导致缓冲区溢出
}
4. PC端配置与通信测试
4.1 驱动安装与端口识别
- 开发板通过USB连接PC后,设备管理器会出现"其他设备"下的未知设备
- 右键选择"更新驱动程序",指向STM32提供的驱动文件
- 成功安装后会在"端口(COM和LPT)"下看到"STMicroelectronics Virtual COM Port"
常见问题:如果出现黄色感叹号,可能是驱动签名问题。Windows 10/11需要禁用驱动程序强制签名:
- 设置→更新和安全→恢复→高级启动→立即重新启动
- 疑难解答→高级选项→启动设置→重启
- 按7选择"禁用驱动程序强制签名"
4.2 串口调试工具配置
推荐使用Tera Term进行测试:
| 参数项 | 配置值 |
|---|---|
| 波特率 | 任意(虚拟串口忽略) |
| 数据位 | 8 |
| 停止位 | 1 |
| 校验位 | 无 |
| 流控 | 无 |
测试流程:
- 打开对应COM口
- 发送字符"1":开发板LED应点亮并返回"LED ON"
- 发送字符"0":LED应熄灭并返回"LED OFF"
5. 性能优化与高级应用
5.1 提高通信可靠性
实际测试中发现连续快速发送数据时可能出现丢包,可通过以下方式优化:
- 增加接收缓冲区:
c复制#define APP_RX_DATA_SIZE 512 // 原默认64
#define APP_TX_DATA_SIZE 512
- 实现流量控制:
c复制// 在CDC_Receive_FS回调中添加缓冲区检查
if(usbBufferFull) {
return USBD_BUSY;
}
- 使用DMA传输:
在CubeMX中为USB配置DMA通道,减轻CPU负担
5.2 多虚拟串口实现
STM32C0的USB外设支持多配置描述符,可以模拟多个串口:
- 修改USB描述符文件usbd_cdc.c
- 添加额外的通信端点对(Endpoint IN/OUT)
- 为每个虚拟串口创建独立的接收缓冲区
- 在CDC接口描述符中声明多个接口
6. 常见问题排查指南
6.1 设备无法识别
可能原因及解决方案:
| 现象 | 排查步骤 | 解决方法 |
|---|---|---|
| 设备管理器无反应 | 检查USB线是否支持数据传输 | 更换优质USB线 |
| 显示未知设备 | 确认驱动是否正确安装 | 手动指定驱动安装路径 |
| 频繁断开连接 | 检查VBUS供电是否稳定 | 在USB DP线加22Ω电阻 |
6.2 数据传输异常
典型问题处理:
-
数据截断:
- 检查APP_TX_DATA_SIZE定义
- 确保每次发送不超过缓冲区大小
- 添加发送间隔(实测最小1ms稳定)
-
乱码问题:
- 确认双方波特率设置一致(虽然是虚拟串口但需要匹配)
- 检查时钟配置,特别是USB 48MHz精度
- 用逻辑分析仪抓取USB DP/DM信号质量
-
长时间运行死机:
- 增加看门狗
- 检查USB中断优先级(建议设置为最高)
7. 实际应用案例扩展
7.1 固件升级(DFU)实现
利用USB CDC实现简单的固件更新:
- 在CubeMX中启用DFU模式
- 添加Bootloader代码
- 自定义通信协议:
- 接收"#BOOT#"进入DFU模式
- 分块传输固件数据
- 校验并写入Flash
关键代码片段:
c复制void Enter_DFU_Mode(void)
{
__disable_irq();
*(__IO uint32_t*)0x20003FF0 = 0xDEADBEEF; // 设置标志位
NVIC_SystemReset();
}
7.2 数据采集系统集成
将USB虚拟串口与ADC结合,实现实时数据上传:
- 配置ADC定时采样
- 创建环形缓冲区存储数据
- USB线程定时发送数据包
优化技巧:
- 使用二进制协议替代文本协议(节省带宽)
- 添加时间戳和校验和
- 实现简单的数据压缩算法
经过一周的实际使用,STM32C092RC的USB虚拟串口表现稳定,在115200波特率下连续工作72小时无异常。相比传统UART转USB方案,内置USB外设不仅节省了硬件成本,还简化了PCB布局。需要注意的是,在数据量大时建议启用DMA并合理设置缓冲区,可以显著提高通信可靠性。