## 原理讲解 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为例)的配置流程: 1. **创建工程**:选择芯片,配置时钟树,确保USB外设时钟为48MHz(例如使用HSE+PLL,或内部48MHz时钟)。 2. **使能USB设备**:在`Connectivity`中找到`USB_OTG_FS`,选择`Device_Only`模式;或使用`USB_Device`(某些系列)。 3. **添加USB类**:在`Middleware`中点击`USB_DEVICE`,将`Class for FS IP`改为`Communication Device Class (VCP)`,生成代码时自动添加`usbd_cdc_if`等文件。 4. **确认端点配置**(可在`USB_DEVICE`配置面板查看): - CDC_NOTIFICATION_EP:通常为端点1,Interrupt IN,最大包长8字节。 - CDC_DATA_OUT_EP / CDC_DATA_IN_EP:通常为端点2,Bulk,最大包长64字节。 5. **生成代码**:勾选`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发送的数据原样返回): ```c /* 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循环中发送定长数据): ```c /* 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端口和电平转换芯片,还支持即插即用,非常适合批量下载、日志输出、上位机交互等场景。