STM32F103C8T6(Blue Pill) 上移植 USB 虚拟串口(CDC)
方案 STM32CubeMX + HAL 库 + USB Device CDC 类
一、硬件准备
| 项目 | 说明 |
|---|---|
| MCU | STM32F103C8T6 |
| USB | PA11(DM)、PA12(DP) |
| 上拉电阻 | DP 需 1.5kΩ 上拉到 3.3V(多数板子已集成) |
| 晶振 | 8 MHz 外部晶振(必须) |
| Boot0 | 0(从 Flash 启动) |
二、CubeMX 关键配置
1、SYS
- Debug:
Serial Wire - Timebase Source:
SysTick
2、RCC
- HSE:
Crystal/Ceramic Resonator - LSE:Disable
3、USB
- USB Device(FS)
- Class:
Communication Device Class (CDC)
4、USB_DEVICE
- Class:
CDC - USBD_CDC_APP:
Enabled
5、Clock Configuration
HSE = 8 MHz
PLL Source = HSE
PLL Mul = x9
SYSCLK = 72 MHz
USB Prescaler = PLLCLK / 1.5 = 48 MHz
USB 时钟必须是 48 MHz,否则无法枚举
三、生成代码后必须修改的地方
1、打开 USB 中断
CubeMX 有时不会自动打开 USB 中断:
/* USER CODE BEGIN 2 */
HAL_NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
/* USER CODE END 2 */
2、CDC 接收回调函数(核心)
在 usbd_cdc_if.c 中找到:
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
/* USER CODE BEGIN 6 */
extern void USB_CDC_RxHandler(uint8_t *buf, uint32_t len);
USB_CDC_RxHandler(Buf, *Len);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
return (USBD_OK);
/* USER CODE END 6 */
}
3、自己封装一个接收处理函数
/* usb_cdc.c */
#include "usbd_cdc_if.h"
void USB_CDC_RxHandler(uint8_t *buf, uint32_t len)
{
if (len == 0) return;
// 示例:原样返回(回显)
CDC_Transmit_FS(buf, len);
}
4、发送函数封装
#include "usbd_cdc_if.h"
void USB_CDC_SendString(char *str)
{
CDC_Transmit_FS((uint8_t*)str, strlen(str));
}
四、main.c 初始化
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USB_DEVICE_Init(); // 初始化 USB CDC
USB_CDC_SendString("STM32F103 USB CDC Ready!\r\n");
while (1)
{
USB_CDC_SendString("Hello USB CDC\r\n");
HAL_Delay(1000);
}
}
参考代码 STM32F103C8T6 USB虚拟串口移植 www.youwenfan.com/contentcnu/69817.html
五、PC 端现象
| 项目 | 结果 |
|---|---|
| 设备管理 | 出现 COMx |
| 波特率 | 任意(忽略) |
| 串口工具 | 可正常收发 |
| 供电 | USB 供电即可 |
不需要 USB 转 TTL
六、常见问题与解决方法
1. 电脑识别不了设备
检查:
- 晶振是不是 8 MHz
- USB 时钟是不是 48 MHz
- PA11 / PA12 是否被复用
2. 枚举失败 / 未知设备
解决:
- 换 USB 线(很多线只能充电)
- 加 22Ω 串阻(PA11/PA12)
- 缩短 USB 线
3. 发送数据卡死
原因:CDC_Transmit_FS 未发送完成又调用
解决:
while (CDC_Transmit_FS(buf, len) != USBD_OK) {
HAL_Delay(1);
}
4. 接收数据不完整
原因:USB 包最大 64 字节
解决:自己拼包(环形缓冲)
七、进阶
USB + printf 重定向
int _write(int fd, char *ptr, int len)
{
CDC_Transmit_FS((uint8_t*)ptr, len);
return len;
}
USB + 串口共存
- USB 用于调试
- USART1 用于模块通信
USB + 上位机协议
- 自定义帧头 + CRC
- 用于 Bootloader / 参数配置
八、工程级建议
| 项目 | 建议 |
|---|---|
| 工程 | CubeMX + HAL |
| 调试 | 先保证 USB 枚举 |
| 协议 | 用长度 + 校验 |
| 速度 | 实测 800Kbps+ |
浙公网安备 33010602011771号