原理讲解
USB CDC(Communication Device Class)是USB标准中专门定义通信设备的类,其下分ACM(Abstract Control Model)、ECM等子类。STM32虚拟串口通常采用ACM子类,它包含两个数据端点(Bulk OUT/IN)和一条可选的通知端点(Interrupt IN)。数据端点负责传输用户数据,通知端点传输线路状态、编码格式等控制信息。
与物理串口的区别: 物理UART是异步串行,有波特率、停止位等参数;USB CDC将这些参数抽象为请求,实际传输是USB包格式,因此PC端虚拟串口的波特率设置不会影响实际传输速率,仅用于兼容应用层。
STM32的USB外设(如USB_OTG_FS、USB_FS Device)支持全速设备模式,使用内部48MHz时钟,配合DP上拉电阻实现连接检测。CubeMX自动生成初始化代码和CDC类驱动,开发者只需专注收发处理。
配置步骤
以下是基于STM32CubeMX(以F401RE为例)的配置流程:
- 创建工程:选择芯片,配置时钟树,确保USB外设时钟为48MHz(例如使用HSE+PLL,或内部48MHz时钟)。
-
使能USB设备:在
Connectivity中找到USB_OTG_FS,选择Device_Only模式;或使用USB_Device(某些系列)。 -
添加USB类:在
Middleware中点击USB_DEVICE,将Class for FS IP改为Communication Device Class (VCP),生成代码时自动添加usbd_cdc_if等文件。 -
确认端点配置(可在
USB_DEVICE配置面板查看):- CDC_NOTIFICATION_EP:通常为端点1,Interrupt IN,最大包长8字节。
- CDC_DATA_OUT_EP / CDC_DATA_IN_EP:通常为端点2,Bulk,最大包长64字节。
-
生成代码:勾选
Generate peripheral initialization as a pair of .c/.h files,生成后工程已包含标准CDC回调接口。
代码实现
核心文件是usbd_cdc_if.c,其中提供两个关键接口:
-
CDC_Transmit_FS(uint8_t *Buf, uint16_t Len):通过CDC发送数据到PC,返回USBD_OK或USBD_BUSY。 -
CDC_Receive_FS(uint8_t *Buf, uint32_t *Len):接收PC发来的数据(回调函数),需在函数末尾重新调用USBD_CDC_ReceivePacket以开启下一次接收。
完整echo示例(将PC发送的数据原样返回):
/* usbd_cdc_if.c */
#include "usbd_cdc_if.h"
static int8_t CDC_Init_FS(void) {
/* 初始化时启动接收 */
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
return USBD_OK;
}
static int8_t CDC_Receive_FS(uint8_t *Buf, uint32_t *Len) {
/* 将接收到的数据原样发送回PC */
uint8_t data[64];
uint16_t size = *Len;
memcpy(data, Buf, size);
CDC_Transmit_FS(data, size);
/* 重新开启下一次接收 */
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, Buf);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
return USBD_OK;
}
主动发送数据(例如在main循环中发送定长数据):
/* main.c */
#include "usbd_cdc_if.h"
extern USBD_HandleTypeDef hUsbDeviceFS;
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USB_DEVICE_Init(); /* 初始化USB CDC */
uint8_t msg[] = "Hello USB CDC\r\n";
while (1) {
CDC_Transmit_FS(msg, sizeof(msg)-1);
HAL_Delay(1000);
}
}
重要提示:CDC_Transmit_FS在数据发送完成前会返回USBD_BUSY,不可重复调用同一缓冲区,否则数据会被覆盖。建议使用无冲突的静态缓冲区或等待上一次发送完成。
注意事项
-
接收回调的“重臂”机制:每次
CDC_Receive_FS被调用后,必须重新调用USBD_CDC_ReceivePacket,否则后续数据无法接收。 -
端点缓冲区大小:全速CDC数据端点最大包长64字节,
UserRxBufferFS和UserTxBufferFS默认为2048字节,但单次收发不能超过端点包长,否则需自行分包。 -
中断安全性:尽量不在USB中断回调中做耗时操作;若使用
CDC_Transmit_FS发送,优先在裸机循环或RTOS任务中执行,避免长时间占用USB中断。 - 驱动安装:Windows系统首次插入时自动安装CDC驱动,有串口标志;Linux/macOS无需驱动。若识别异常,检查DP上拉电阻和USB差分线布线。
- 波特率无效:PC端任意波特率设置都不会影响USB传输,实际吞吐率由USB总线决定。
- 多实例问题:若需多个虚拟串口,可增加CDC接口数量和端点,但普通MCU资源有限,需谨慎设计。
通过以上步骤,你已能在STM32上实现稳定可靠的USB虚拟串口。CDC不仅节省了UART端口和电平转换芯片,还支持即插即用,非常适合批量下载、日志输出、上位机交互等场景。