STM32 USB CDC

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. 

    Note:
    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:

MX_USB_DEVICE_Init();

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 ={
 CDC_Init_FS,
 CDC_DeInit_FS,
 CDC_Control_FS,
 CDC_Receive_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){
/* USER CODE BEGIN 6 */
CDC_Transmit_FS(Buf, *Len); // ADD THIS LINE to echo back all incoming data
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
 
return (USBD_OK);
/* USER CODE END 6 */
}

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: http://www.st.com/en/development-tools/stsw-stm32102.html

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


Upgrade

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 ๐Ÿ™‚

12 Comments

  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. Leon ล kapin

    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.

Leave a Reply