STM32 USB CDC Virtual COM Port (VCP) Example

In this tutorial, we’ll explore How To use the STM32 USB CDC Virtual COM Port (VCP) to transmit & receive data from a PC without using the STM32 UART module. The examples provided in this tutorial will help you set up the STM32 USB CDC device properly and use it to send data to a PC, receive data of unknown length, and route the STM32 USB CDC to Printf() function to use it for printing data over USB.

Table of Contents

  1. STM32 USB CDC & VCP
  2. STM32 USB Virtual COM Port Example
  3. STM32 USB CDC Transmit Example
  4. STM32 USB CDC Read Example
  5. STM32 USB CDC Printf() Example
  6. STM32 USB CDC Troubleshooting
  7. Wrap Up


USB CDC (Communication Device Class) is a protocol specification for USB communication. There are many other USB classes that specify various protocols over the USB physical layer to enable communication of (data, audio, video devices, HID devices, mass storage, wireless controllers, and much more).

Virtual COM Port (VCP) is a Microsoft Windows interface to access different communication channels (e.g. Serial RS232, USB, etc) as if they were connected to a physical COM port. Therefore, the baud rate value in a serial terminal is meaningless for VCP.

❕ Note
Virtual COM Port (VCP)USB CDC (Communication Device Class)
is a Microsoft Windows interface to access different communication channels (e.g. Serial RS232, USB, etc)is a protocol specification for USB communication

In this tutorial, we’ll set up our STM32 board’s hardware USB to operate as a CDC device and connect it to a VCP on a PC to communicate data back and forth between a PC and an STM32 microcontroller over USB FS (Full-Speed). This is an alternative way of using the STM32 serial UART ports for communication with a PC.

STM32 USB Virtual COM Port Example

In this example project, we’ll configure the USB hardware on the Blue Pill’s STM32F103 target microcontroller to operate in the USB CDC class as a device.

Therefore, we’ll be able to connect our STM32 microcontroller to any PC and get it detected as a USB CDC device that communicates with the PC over a VCP (Virtual COM Port).

Step #1

Open STM32CubeMX, create a new project, and select the target microcontroller.

Step #2

Enable The USB FS (Device) Hardware Peripheral


Step #3

Enable The USB CDC (VCP) Middleware Software Stack


Step #4

Go to the RCC clock configuration page and enable the HSE external crystal oscillator input.

Go to the SYS configurations and enable SWD (Serial Wire Debug).

STM32 RCC External Clock Selection CubeMX

Step #5

Go to the clock configurations page, and select the HSE as a clock source, PLL output, and type in 72MHz for the desired output system frequency. Hit the “ Enter” key, and let the application solve for the required PLL dividers/multipliers to achieve the desired clock rate.

Step #6

Name & Generate The Project Initialization Code For CubeIDE or The IDE You’re Using.

STM32 USB CDC Transmit Example

After configuring & generating the USB CDC project initialization code, let’s now create a USB CDC Transmit example. We’ll send out a buffer (string message) to the PC over a virtual COM port every 100ms.

We’ll use the CDC_Transmit_FS(uint8_t* Buf, uint16_t Len) function that you can find at this location: USB_DEVICE/ App/ usbd_cdc_if.h

The 2 parameters of this function are: Buf which is a pointer to the buffer of data to be transmitted, and Len which is the length of the data buffer.

The Application Code For This Test Example

STM32 USB CDC Transmit Example Test Result

After flashing the code to the Blue Pill board, I’ve connected the USB port to my host PC which could easily recognize and enumerate the STM32 USB CDC device. In my case, it’s assigned to the COM11 port.


Therefore, I’ll open a serial terminal on the COM11 port with any baud rate (keep in mind that it doesn’t affect the speed or anything).


STM32 USB CDC Read Example

The next example is to receive data over USB CDC of unknown length. We’ll use the exact same project configurations as the previous example but some modifications should be done.

The CDC_Receive_FS() function is defined as a static function in the usbd_cdc_if.h file. This means we can’t use it externally outside of that file (like in main.c file, for example). And this is actually intuitive, as we can’t just trigger the USB_CDC_Receive operation manually. And here is why!

The USB CDC software stack handles the data reception for us, so we don’t need to “manually start” a data read operation. Instead, we’ll add a callback function that notifies our application whenever a USB CDC data packet is received. Inside the callback function, I’ll send the received data again over USB CDC as a loopback so we can debug the behavior of our system.

Here are the steps that you need to follow to implement an STM32 USB CDC Receive data (of unknown length):

Step #1

Add the following function declaration to the main.h file

Step #2

Add the following RxHandler function’s implementation to the main.c file

Step #3

Go to the usbd_cdc_if.h file, and add the following two lines of code inside the CDC_Receive_FS() function.

At the end, it becomes as follows :

And that’s all what you need to do. Below is the full application code in the main.c file. Of course, you can do whatever you want with the received data instead of sending it back to the PC as I’ve done for the sake of testing.

The Application Code For This Test Example

STM32 USB CDC Receive Example Test Result

I’ve placed one breakpoint at line #28 as you can see in the test demo video below. Whenever a message is sent from the terminal to the STM32 board, the handler function is called and we can see that the received data inside the Buf array is exactly what we’ve sent from the PC.

After clicking the resume button, the STM21 executes the code starting from line #28 which sends the received data back to the PC and we can see that data on the serial terminal as well. So everything works as expected!

STM32 USB CDC Printf() Example

One last application before concluding this tutorial, we’ll route the Printf() function to the USB CDC transmit function. In this way, you can use the Printf() function in your project to send data over the USB CDC directly (just like a wrapper layer for the USB CDC).

The Application Code For This Test Example

STM32 USB CDC Prinf() Example Test Result


STM32 USB CDC Troubleshooting

Here are some common issues that you might encounter while working with the STM32 USB CDC in different projects and how you can mitigate those issues to achieve your system’s desired behavior.

1. Transmitting Big Chunks of Data

If you’re sending big chunks of data in your application, make sure that you give the transmit function enough time before attempting to transmit data again. You can implement this logic by monitoring the USB CDC transmit completion flag for example.

2. Excessive Memory Utilization

The USB software stack in the HAL libraries has a pretty large memory footprint. To put it in context, let’s consider the example provided in this tutorial. By checking the build analyzer, you can see that it’s taking up to 45% of the program memory space and 32% of the RAM memory. Which is quite a big space for what the application is doing.


This can be optimized by reducing the buffer sizes in the USB CDC header files as long as your application doesn’t require large data buffers. Alternatively, you might consider checking out external libraries like the tinyUSB for example.

Required Parts For STM32 Examples

All the example Code/LABs/Projects in this STM32 Series of Tutorials are done using the Dev boards & Electronic Parts Below:

QTY.Component NameAmazon.comAliExpresseBay
1STM32-F103 BluePill Board (ARM Cortex-M3 @ 72MHz)AmazonAliExpresseBay
1Nucleo-L432KC (ARM Cortex-M4 @ 80MHz)AmazonAliExpresseBay
1ST-Link V2 DebuggerAmazonAliExpresseBay
1LEDs KitAmazonAmazonAliExpresseBay
1Resistors KitAmazonAmazonAliExpresseBay
1Capacitors KitAmazonAmazonAliExpress & AliExpresseBay & eBay
1Jumper Wires PackAmazonAmazonAliExpress & AliExpresseBay & eBay
1Push ButtonsAmazonAmazonAliExpresseBay
1Micro USB CableAmazonAliExpresseBay

★ Check The Links Below For The Full Course Kit List & LAB Test Equipment Required For Debugging ★

Download Attachments

You can download all attachment files for this Article/Tutorial (project files, schematics, code, etc..) using the link below. Please consider supporting our work through the various support options listed in the link down below. Every small donation helps to keep this website up and running and ultimately supports the whole community.

Wrap Up

In conclusion, we’ve discussed how to use the STM32 USB CDC for transmitting & receiving data to/from a PC without using the STM32 UART peripheral. The Printf() function can also be routed to the USB CDC for directly printing messages over USB. The provided examples should be a good starting point for your next STM32 USB CDC project.

If you’re just getting started with STM32, you need to check out the STM32 Getting Started Tutorial here.

Follow this STM32 Series of Tutorials to learn more about STM32 Microcontrollers Programming.

Share This Page With Your Network!
Join Our +25,000 Newsletter Subscribers!

Stay Updated With All New Content Releases. You Also Get Occasional FREE Coupon Codes For Courses & Other Stuff!

Photo of author
Khaled Magdy
Embedded systems engineer with several years of experience in embedded software and hardware design. I work as an embedded SW engineer in the Automotive & e-Mobility industry. However, I still do Hardware design and SW development for DSP, Control Systems, Robotics, AI/ML, and other fields I'm passionate about.
I love reading, writing, creating projects, and teaching. A reader by day and a writer by night, it's my lifestyle. I believe that the combination of brilliant minds, bold ideas, and a complete disregard for what is possible, can and will change the world! I will be there when it happens, will you?

2 thoughts on “STM32 USB CDC Virtual COM Port (VCP) Example”

Leave a Comment