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+
posted @ 2026-05-13 16:42  晃悠人生  阅读(138)  评论(0)    收藏  举报