Setting up STM32 USB CDC (Communications Device Class) allows your STM32 microcontroller to communicate with a PC over USB, appearing as a Virtual COM Port (VCP). No USB-to-UART bridge chip is needed — the STM32’s built-in USB peripheral handles it all. This is ideal for logging data, configuration interfaces, and firmware update without extra hardware. This tutorial covers complete USB CDC Virtual COM Port setup on STM32 using STM32CubeMX and HAL library.
Table of Contents
- What is USB CDC Virtual COM Port?
- Supported STM32 Devices
- STM32CubeMX USB CDC Setup
- Transmitting Data to PC
- Receiving Data from PC
- Redirecting printf to USB CDC
- Troubleshooting and Tips
- Frequently Asked Questions
What is USB CDC Virtual COM Port?
USB CDC (Communications Device Class) is a USB standard that makes a USB device appear as a serial COM port to the operating system. When you plug in a STM32 with USB CDC firmware, Windows/Linux/macOS installs a driver that creates a virtual COM port (COMx on Windows, /dev/ttyACMx on Linux).
Benefits over UART + USB-Serial bridge:
- No extra hardware: Eliminates CH340G, CP2102, or FTDI USB-serial converter
- Higher speed: USB 1.1 full-speed supports 12 Mbps vs UART’s typical 115200 bps
- Cost savings: Remove USB-serial bridge IC from PCB design
- Device power: Can power STM32 from USB bus (up to 500mA from USB 2.0)
Supported STM32 Devices
USB CDC requires STM32 with built-in USB Full-Speed peripheral. Key supported series:
- STM32F1xx: F103 series (very common in India, used in Blue Pill boards)
- STM32F4xx: F407, F429, F446 — all support USB FS (and some have USB HS)
- STM32F2xx, F3xx, L4xx, G0xx: Most variants with USB peripheral
- STM32 Blue Pill (F103C8T6): Very popular in India, has USB D+ (PA12) and D- (PA11) pins
Check your STM32 reference manual for “USB full-speed” in the feature list. STM32 without USB (like some F0 variants) cannot implement CDC.
STM32CubeMX USB CDC Setup
- Open your .ioc file in STM32CubeIDE
- Go to Connectivity → USB → Enable USB peripheral (Device FS)
- Go to Middleware → USB Device → Select Class: Communication Device Class (CDC)
- Configure USB clock (must be exactly 48 MHz):
- STM32F4: In Clock Configuration, set USB/SDIO clock to 48 MHz from PLL
- STM32F1: HSE or PLL must produce 48 MHz for USB clock
- Generate Code
CubeMX generates USB_DEVICE/App/usbd_cdc_if.c — this is where you implement TX and RX.
Transmitting Data to PC
/* usbd_cdc_if.c is generated by CubeMX */
/* To send data, use CDC_Transmit_FS() */
#include "usbd_cdc_if.h"
#include <stdio.h>
#include <string.h>
/* Send a simple string */
void USB_SendString(const char* str) {
/* Wait until USB is ready */
uint32_t timeout = 1000;
while (CDC_Transmit_FS((uint8_t*)str, strlen(str)) == USBD_BUSY && timeout--) {
HAL_Delay(1);
}
}
/* Send formatted data (like printf to USB) */
char txBuf[256];
void USB_Printf(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
int len = vsnprintf(txBuf, sizeof(txBuf), fmt, args);
va_end(args);
if (len > 0) {
uint32_t timeout = 1000;
while (CDC_Transmit_FS((uint8_t*)txBuf, len) == USBD_BUSY && timeout--) {
HAL_Delay(1);
}
}
}
/* Usage in main.c */
int main(void) {
/* ... init code ... */
float temperature = 28.5;
int count = 0;
while (1) {
USB_Printf("Count: %d, Temp: %.1f°C
", count++, temperature);
HAL_Delay(1000);
}
}
Receiving Data from PC
/* In usbd_cdc_if.c, modify CDC_Receive_FS callback */
/* This function is called when data arrives from PC */
#define RX_BUFFER_SIZE 64
uint8_t usbRxBuffer[RX_BUFFER_SIZE];
uint16_t usbRxLen = 0;
volatile uint8_t usbRxReady = 0;
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
/* Copy received data */
usbRxLen = *Len;
memcpy(usbRxBuffer, Buf, usbRxLen);
usbRxReady = 1;
/* Re-arm reception */
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
return (USBD_OK);
}
/* In main.c, check for received data */
while (1) {
if (usbRxReady) {
usbRxReady = 0;
usbRxBuffer[usbRxLen] = ''; /* Null-terminate */
/* Process received command */
if (strncmp((char*)usbRxBuffer, "LED_ON", 6) == 0) {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
USB_SendString("LED turned ON
");
}
else if (strncmp((char*)usbRxBuffer, "LED_OFF", 7) == 0) {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
USB_SendString("LED turned OFF
");
}
}
}
Redirecting printf to USB CDC
/* Add to usbd_cdc_if.c or a separate file */
/* This redirects all printf() calls to USB CDC */
#include <stdio.h>
#include "usbd_cdc_if.h"
int _write(int file, char *ptr, int len) {
/* Called by printf/puts for stdout */
uint32_t timeout = 5000;
/* Wait for USB ready */
while (CDC_Transmit_FS((uint8_t*)ptr, len) == USBD_BUSY) {
if (--timeout == 0) return -1;
HAL_Delay(1);
}
return len;
}
/* In project settings (Keil/GCC): enable --specs=nosys.specs or link with syscalls.c */
/* Then in main.c you can use printf normally: */
printf("Temperature: %.2f°C
", temp);
printf("ADC Value: %d
", adcValue);
Troubleshooting and Tips
- COM port not appearing: Verify 48 MHz USB clock. In STM32CubeIDE, check Clock Configuration — USB clock must show exactly 48 MHz
- STM32 Blue Pill (F103): Many Blue Pill clones have wrong 10kΩ pull-up on BOOT0 — check that PA12 (USB D+) has a 1.5kΩ pull-up to 3.3V for USB enumeration
- Transmit BUSY error: Call CDC_Transmit_FS from main loop only, not from ISR. USB stack is not ISR-safe.
- Linux permissions: Add user to dialout group:
sudo usermod -a -G dialout $USERthen logout/login - India Windows 10/11: Windows automatically installs CDC driver if it’s a standard CDC device. No driver download needed.
- Disconnect detection: Check USB plug event:
USBD_CDC_HandleTypeDef->TxStateto detect if PC is connected
Frequently Asked Questions
Does STM32 USB CDC work on Windows 11 without additional drivers?
Yes — Windows 10 and 11 include built-in USB CDC driver (usbser.sys). Your STM32 CDC device should enumerate automatically as a COM port without any driver installation, provided the firmware is correctly configured as a standard CDC device.
What is the maximum data rate with STM32 USB CDC?
USB Full Speed (12 Mbps raw) with CDC overhead achieves approximately 1–2 Mbps effective throughput. The CDC_Transmit_FS function can send up to 64 bytes per USB packet. For maximum throughput, batch data into large chunks rather than sending many small packets.
Can I use USB CDC on STM32 Blue Pill (F103C8T6)?
Yes — Blue Pill has USB D+ on PA12 and D- on PA11. The main issue with clone Blue Pills is a wrong pull-up resistor value on PA12 (should be 1.5kΩ to 3.3V for USB enumeration, but some clones have 10kΩ or wrong placement). Check or replace this resistor if USB doesn’t enumerate.
How do I identify which COM port is my STM32 on Windows?
Open Device Manager (Win+X → Device Manager) → Ports (COM & LPT). Your STM32 will appear as “STMicroelectronics Virtual COM Port” or similar. The COMxx number is what you use in your terminal (PuTTY, CoolTerm, Arduino Serial Monitor).
Is it safe to draw power from USB for STM32 projects?
USB 2.0 provides 500mA at 5V. STM32F103 at 72 MHz draws ~50–100mA. With peripherals, stay under 400mA total to be safe. For projects exceeding this, use a separate power supply and connect only USB D+/D- to the STM32 for communication.
Add comment