By now almost all of my projects included some sort of communication with PC, mostly with external USB-UART bridge chip like MCP2200 or CP210x. Although this devices were mostly prototypes and/or single products, such approach added unwanted costs and since almost all uC that I used already have USB peripheral embedded, there is no excuse not to use it.
So, here it is, STM32 USB CDC  – communication with PC over USB, generated with STM32CubeMX on STM32L100 discovery board. 

STM32CubeMX UDB CDC middleware selection

To generate basic USB CDC device with CubeMX, follow this previous post, but change Middleware USB profile to CDC. Also, set endpoint size to 64 bytes. More about endpoints later.

Again, USB files contains “settings for CDC” in this files:

  1. USB device library/Middlewares
    • usbd_core.c (provides all USB device core functions)
    • usbd_ioreq.c, usbd_ctlreq.c (provides everything for endpoint controls and USB request)
    • usbd_cdc.c (provides everything to interact with USB host, descriptors, data IO …)
  2. Application files:
    • usbd_conf.c (provides low layer / HAL functions)
    • usbd_device.c (provides initializing function called from main())
    • usbd_desc.c (provides USB device descriptors)
    • usbd_cdc_if.c (provides application read/write functions)

More about file hierarchy can be found in STM32Cube USB device library user manual.

Files to modify CDC to our custom needs are:

  1. USB VID/PID in usbd_desc.c 
  2. USB configuration defines in usbd_conf.h
    This is set with CubeMX so don’t change this defines.
  3. USB struct handle in usbd_device.c 
  4. Descriptors in usbd_cdc.c
    You don’t really need to change anything for now, but there are many descriptor fields that can be modified.
    As it is stated at the top of the .c file, generated CDC contains 2 data endpoints (IN and OUT) and 1 control endpoint. Endpoints could be seen as buffers inside devices, that have a specific purpose. 

    Endpoint naming is host-based; meaning, on our STM32, IN endpoint actually means data direction from uC to PC (PC IN endpoint). usbd_cdc.h contains CDC packet/endpoint sizes

If we build&upload the code, Device Manager shows “USB Serial Device (COMx)” under “Ports (COM & LPT)”. More about drivers below.

To test our USB communication, we will do a simple echo device. Everything that comes into device over USB CDC (from PC) is echoed back to PC.

Receive data

There is a function (generated by CubeMX) at the beginning of peripherals initialization:


and in there:

USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS);

While “hUsbDeviceFS” is our struct of all USB device data, “USBD_Interface_fops_FS” is struct of callback pointers to functions (Init, DeInit, Control, Receive), in our case:

USBD_CDC_ItfTypeDef USBD_Interface_fops_FS ={

Which means that “CDC_Receive_FS()” is our “packet received” callback and is called automatically from USB stack, containing pointer to buffer with received data and length of received data.
“CDC_Receive_FS()” can be found in “usbd_cdc_if.c”

Send data

In the same file we can also find “CDC_Transmit_FS()” (which further calls “USBD_CDC_SetTxBuffer()” and “USBD_CDC_TransmitPacket()” that actually sends data over endpoint, but check USB state before transmitting).

So, in order to echo back all data sent from host (PC), we just need to add this line to our “CDC_Receive_FS()”:

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len){
CDC_Transmit_FS(Buf, *Len); // ADD THIS LINE to echo back all incoming data
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
return (USBD_OK);

Note: This can work only if endpoint sizes are equal/large enough.

To test it, open serial terminal, send some data and see if it is received back.
Since this data is not send directly to UART like USB-UART converter, baud rate is actually not important, which is great, one less concern.

This is basically it. Everything on top of that is just a more fancy/controlled/reliable version of this. 

Here is a .zip of this example project in Keil.

Note: There is an official example in CubeMX firmware library under
“Projects\STM32 xxx \Applications\USB_Device\CDC_Standalone”,
which is USB to UART converter that can also set/get UART parameters (like baudrate, parity, …) over CDC control endpoint.

Note about the drivers

After upload, open any serial terminal (like Putty) and find your serial device in Device Manager under “Ports (COM & LPT)” named “USB Serial Device (COMx)”. That is, if windows can find a suitable driver for your device.

STM32 USB CDC drivers device recognition

Windows already provide standard (native) drivers for CDC devices  – “Usbser.sys”, but is automatically loaded only above Win 7.
In order to be able to communicate with STM32 USB CDC devices on Win != 10, you would need ST VCP drivers:

Note: VID/PID and drivers and OS device recognition. Even more about how does USB recognition work can be found here.


Now, since we have our basic USB communication without additional USB-UART/whatever converter chip, we can implement it in some protocol. Complex or simple like our SDP. Get the idea 🙂


  1. Pingback: STM32 And Custom USB HID Device? Yes Please! | Damogran Labs

  2. Thanks for sharing, good explanation and easy way to get started with USB.

  3. Hi,
    Thanks for sharing.
    I have a question related to STM32 USB CDC behaviour when compared to CP210x.
    In linux machine STM enumberated as ‘/dev/ttyACM*’ where as CP210x as ‘/dev/ttyUSB*’. I believe drivers used are different on linux kernel. Is there any way to match STM32’s USB driver to enumerate as ‘/dev/ttyUSB’ i.e. same as CP210x?

  4. Hi Domen,
    I use STM32F103RBT. What I understand is that I should generate the file ‘usbd_conf.h’ with Cube MX for STM32F103RBT to use with STM32 USB CDC lib.
    I don’t know how to use Cube MX. Could you help me?

  5. Hello, stupid question here.
    Is the STM32 can be possibly to b the host and able to connect to MCP2200?

  6. Hi there, i’m trying to implement the stm32 blue pill board as a slave on a PCB that gets controller by the user via USB, the problem is that when i plug in the board (USB (pc) to microUSB(uC)) it’s just a power supply, no com port is detected or anything, can u help?

    • Hello. If there is absolutely no reaction once device is connected to PC, I would assume one of the following issues is present:
      – you have a wrong cable (I once used some cheap power supply usb cables without data wires) or other hardware issue.
      – your device configuration (code) is invalid or CPU don’t even do initialization due some error
      – one of other 341532 possible issues 😀
      I would first try to search for any visible hardware issues, than I would program the most basic (default) USB example (CubeMX HID device) and try to get this working. Than, when you are sure that the board is OK, move on with CDC.

      p.s.: Do yourself a favor and while asking for help, describe as much details as you possibly can. What do you wish to accomplish, what have you already tried and what are the results of that, what have you changed in a given example, what did you discover when debugging code, … It is nearly impossible to help with no info.

  7. hi, i have one question.
    How can you combine USB CDC files which are created with CubeMX and other program written in sw4stm32 or Atollic TrueStudio with standard peripheral library?

    • I wouldn’t do that. Standard peripheral libraries is already outdated, and as far as I know, STM32 does not develop it anymore. Not the case with STM8, but for STM32, stick to either old StdPeripheral library or port to HAL/LowLayer.

      In case of CubeMX generated USB files, they are based on HAL, so it would be really hard to port this files to StdPeripheral lib. Maybe you could include required HAL libraries in the project, but this would take time – I wouldn’t go this way.

      You would probably re-write/port whole application to HAL/LL much faster than re-implement USB to use StdPeripheral lib. This would also alow you to regenerate project with CubeMX.

  8. If you find you can’t debug your USB:

    I’m working on a Blue Pill (stm32f103c) CDC, using the new StmCubeIDE. I couldn’t debug USB until I pulsed the D+ USB line (A12 on the Blue Pill) low with a 100 ohm resistor. This causes the link to re-register.

    Later I defined line A10 (portA pin 10) as open-drain, and pulse it low for 100ms. before the main program loop. I found some boards (Nucleo) have a transistor to do this.

  9. What’s happening if I can see it in the Device Manager but can’t open it?
    I tried adding your line to a STM32F303CTB’s CubeMX program; when I plug it in, it enumerates and shows up as a com port (Com8). It doesn’t show up in Teraterm, and if I try to open it, I get “Cannot be opened.”

    It shows up in WMI… but can’t be used.
    Also… is there a real need for CRC’s and such over USB, or is that adequately handled at a lower level (in the USB communication stack)?

  10. “When I plug it in, it enumerates and shows up as a com port (Com8). It doesn’t show up in Teraterm, and if I try to open it, I get “Cannot be opened.”

    Are you sure you have only one connection to this port (If you connect with some other software/scripts to this port, port might not be accessible, although it is listed). Another question is: is your STM responsive (can CPU handle USB stack and data, or is in your while (1) loop or some hardfault handler?

    Also… is there a real need for CRC’s and such over USB, or is that adequately handled at a lower level (in the USB communication stack)?

    The most honest answer I can give: I don’t know. But, first google look up lead me to a interesting forum topic:

    • That’s cool!
      I found the issue; evidently my “init code” (check ADC voltages, etc) was “too busy” and it wasn’t initializing correctly (too much of a gap between “plugging in” and “MX_USB_DEVICE_Init”, so the PC was getting confused).

      Pulling DM & DP low for 1sec before the MX_USB_DEVICE_Init() in main forced the computer to do an “unplug and replug” event and now it’s working well. Onwards!

      Now I’m really liking the USB… I’m not sure I’ll ever go back to normal serial:
      – Baud rate? Don’t care, but if I did, the STM32 can read what the PC wanted.
      – Which com port? Look at the USB info and find the “ThisPortIsMine” and I know which com port to open, no matter what the PC enumerates it as.
      – 2 Pins… and I get CD/RTS/CTS and everything else… and I can have multiple devices if I wanted to (so HID to send some control signals back and forth and…); no need to “decode and handle” line breaks (for a forced reset), etc.

  11. After doing a Reset in STM32F103, I verified a successful SW Reset. However, I lose the connection with the USB Host PC. I want to be able to reestablish the connection without removing/replacing the USB cables. Can you explain why and suggest any sollution?

  12. It resets the traffic (which disconnect windows), but it doesn’t do the “replug” event to cause the windows box to restart the connection. I fixed it by making the port GPIO and holding DM&DP “low” for 500ms before calling the MX_USB_DEVICE_Init() function.

    It means that every time you restart the device, it “unplugs and replugs”, but it does work. You need to let the device run without breakpoints for a couple of seconds after the USB_DEVICE_Init() so it will finalize the connection (you can put breakpoints in, but don’t stop the CPU for a little bit).

  13. Hi,

    Re:Unable to Transmit the Data (USB CDC ) to PC (STM32L476RG Eval Board).

    I am trying to transmit the data(USB CDC) over PC but I didn’t see any message printing on Serial Putty Console. Do I need to change any configuration settings. May I know , why transmission was not happening.. I am trying to dig into deep. so could you anyone try to help me out to overcome this Problem.I also followed your suggestion Loop back test. I tried but No Data Transmit was happened.

    • Hello Lakshmikanth. For anyone to help you, you would need to make a little effort and explain:
      – What does work? Where does your code stuck (did you try to debug it?)? Does it transmit anything or is ti completely dead?
      – Can you receive anything from some other board/device?
      – …
      If you need help, we are happy to help you – but we won’t guess what is going on or what you did or didn’t do.
      Learn how to ask for help and how to write an issue report.

      • Hi Domen,

        Thank you for your quick response.

        1) What are the steps I have been followed ?

        As per below link I followed the steps


        I generated code in CubeMx as per above link.
        I have added little bit code to Transmit the data to PC as mention below.
        – Enable LED pin (GPIO_SET)
        – Transmit The data (CDC_Transmit Function) CDC_Transmit_FS(UserTxBuffer,7);
        – Disable the LED pin(GPIO_RESET)

        2) What does work?

        Till Now I haven’t seen any Data Transmit(From Board STM32L476RG) to PC (Putty)

        3) Where does your code stuck (did you try to debug it?)?

        1) I tried to put in debug mode (In CubeMX enable SW in Debug options).
        2) I can able to see in the log ,it’s enter into CDC_Transmit Function and displayed as shown in below syntax.
        3) Debug_write(“Message Transmitted”,(uint16_t)20);
        i.e Message Trasmitted was displayed on Serial wire viewer on both STLINK Utiity/Atollic IDE (SWO output)
        4) I enabled USBD_DEBUG_LEVEL 3.Find the Below log for reference.

        [2.160] read(): Rx: $vFlashWrite:80051b0:\001\004\002\010\000\020\020\020\004\020\020\020\002\020\020\020\010ERROR: \000Invalid Device handle\000Invalid Class handle\000USBD SWO test1\000DEBUG : \000USBD SWO test2\000End\000\000\000\000\000\000\000\000\000\001\002\003\004\006\007\010\011\000\000\000\000\001\002\003\004\000\377\377\001\000@\015\003\000\377\032\006\000\0005\014\000@B\017\000\377\377\036\000\000\011=\000\000\022z\000\000}\004\377\000\0006n\001\000H\377\001\000l\377\002Message received\012\000CDC Receive\000Message Transmitted\000CDC Transmit\000Message:Buf\000\000\002\001\002CDC Config\000CDC Interface\000STMicroelectronics\000STM32 Virtual ComPort\000\000\000\000\377\001\000 \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000…
        [3.469] spawnCubeProgrammer(): 19:04:05:899 STLinkUSBDriver.dll loaded

        5) I have seen in the Log (Invalid Device/Class).. I noticed the it’s unable to find the Device/class.

        4) Does it transmit anything or is it completely dead?

        I could see any data transmission on PC (Putty)

        – Can you receive anything from some other board/device?
        I couldn’t receiving any data from board/device.

  14. Hi Domen,

    Thank you for response and giving some good instructions.
    Please find the below link for reference which has mention cleared.

    • Hi Domen,

      Actually, I tried to debug the USB CDC Communication.
      The Function CDC_Transmit_FS() => always return USBD_BUSY state.
      Need to Find the overcome of USBD_BUSY state.could you please help me out of this problem. I am looking forward for your reply

      • Well, I must respond in the same way as with first comment – if you need help, make an effort and describe what is going on in details, what you already tried, …
        I’m sorry, I didn’t notice your previous comment was sent to spam.

        I generated code in CubeMx as per above link.

        I can’t offer help for samples that are not part of this blog post. It is hard to advise for a specific problems that are not this project specific. I can only offer (if at all) generic answers, which you could probably google faster than you read this :).

        2) What does work?

        First, try to achieve that your board is registered by PC. Do you have correct drivers? In order to send/receive data, you must be sure your device is properly enumerated by PC OS.

        I tried to put in debug mode (In CubeMX enable SW in Debug options

        Logging is good. BUT, make sure your extensive logging does not cause delays – you should not block CPU to periodicaly handle USB stack.

        5) I have seen in the Log (Invalid Device/Class).. I noticed the it’s unable to find the Device/class.

        This is your homework. 🙂 Research what could go wrong when device needs to be enumerated by PC. You must see it in device manager, as it is displayed in this blog post.

  15. Hi Domen,

    Thank you for your prompt response.

    Hope you did’nt notice the Previous comment which was I mention that the Function CDC_Transmit_FS() => always return USBD_BUSY state.
    That why Data Transmission was not happening..

  16. Hi Domen,

    The issue is resolved after connecting with new USB daughter board along with USB Type A Cable. Actually I have seen below reference readme.txt of different controller Board from that I got to know.


    Thank you for your support.

  17. Hi Domen,
    In my project, I convert STM32 USB CDC to WINUSB Device with HAL library and now I want to use virtual com port too. But the problem is in a moment just an application running, so can I edit some points in descriptor or something to choose which application I wanna use?

Leave a Reply