Showing results for 
Search instead for 
Did you mean: 

STM32F4 High Speed USB with LIBUSB

Associate II

Hey everyone,

I have been using the STM32F4 for quite some time now and have used the STM32F429 Eval1 board for developing a firmware that uses the external USB3300 PHY over ULPI to achieve high speed USB transfers in device mode with the CDC (virtual com port) profile.

While this works to some extent, the aim is to improve the bandwidth of the data transfers from device to the host to approximately 160Mbit/second. Since CDC emulates the old, but reliable, serial port I don't think it is meant to do these kinds of speedy transfers.

If anyone has been able to get these kinds of transfer speeds out of an STM32F4 high speed USB CDC device, I would love to hear how that would be achievable since my tests show that this interface chokes after about 14KB of transferred data.

When looking around I stumbled upon a video from STM with the topic: STM32 USB training - 09.3 USB CDC libusb device lab. In which they adjust a standard device CDC firmware for full-speed USB that enumerates as a virtual com port to a libusb device which would benefit from higher transfer speeds.

Following the adjustments from the video, the project compiles and can be flashed to the STM32F4. Both a windows 10 and ubuntu 22.04 machine find a new USB device and after installing the libusb drivers they are recognized but with some errors on both Windows (This device cannot start. (Code 10) STATUS_DEVICE_DATA_ERROR) and Ubuntu (usb 1-1: can't set config #1, error -32).

Does anyone have any idea what I would be doing wrong or is there a better resource for figuring out how to adjust the FW to work with a high speed libusb interface?

Or does anyone have experience using other, better USB libraries that work with STM32F4 and its high speed interface that would be able to get the 160Mbit/s bandwidth?

Any help, pointers or tips would be helpful!

In order to provide some more information, I adjusted the following in a standard high speed USB device with CDC profile:

  • In usbd_desc.c
#define USBD_VID     1155
#define USBD_LANGID_STRING     1033
#define USBD_PID_HS     0xC1B0
#define USBD_PRODUCT_STRING_HS     "LIBUSB interface"
#define USBD_INTERFACE_STRING_HS     "CDC Interface"
/** USB standard device descriptor. */
  0x12,                       /*bLength */
  USB_DESC_TYPE_DEVICE,       /*bDescriptorType*/
  0x01,                       /*bcdUSB */ /* changed to USB version 2.01
                                             in order to support LPM L1 suspend
                                             resume test of USBCV3.0*/
  0x00,                       /*bcdUSB */
#endif /* (USBD_LPM_ENABLED == 1) */
  0x00,                       /*bDeviceClass*/
  0x00,                       /*bDeviceSubClass*/
  0x00,                       /*bDeviceProtocol*/
  USB_MAX_EP0_SIZE,           /*bMaxPacketSize*/
  LOBYTE(USBD_VID),           /*idVendor*/
  HIBYTE(USBD_VID),           /*idVendor*/
  LOBYTE(USBD_PID_HS),        /*idProduct*/
  HIBYTE(USBD_PID_HS),        /*idProduct*/
  0x00,                       /*bcdDevice rel. 2.00*/
  USBD_IDX_MFC_STR,           /*Index of manufacturer  string*/
  USBD_IDX_PRODUCT_STR,       /*Index of product string*/
  USBD_IDX_SERIAL_STR,        /*Index of serial number string*/
  USBD_MAX_NUM_CONFIGURATION  /*bNumConfigurations*/

  • In usbd_cdc.h
/* CDC Endpoints parameters: you can fine tune these values depending on the needed baudrates and performance. */
#define CDC_DATA_HS_MAX_PACKET_SIZE                 512U  /* Endpoint IN & OUT Packet size */
#define CDC_DATA_FS_MAX_PACKET_SIZE                 64U  /* Endpoint IN & OUT Packet size */
#define CDC_CMD_PACKET_SIZE                         8U  /* Control Endpoint Packet size */
#define USB_CDC_CONFIG_DESC_SIZ                     32U
#define CDC_REQ_MAX_DATA_SIZE                       0x7U

  • In usbd_cdc.c, I removed all references to the CMD endpoint but the file is too long to be inserted here but I attached the file to this post.

Best regards!

Bob S

Do you have code that handles the CDC_SET_LINE_CODING and CDC_GET_LINE_CODING commands?

On Windows you can run Wireshark with the USBPCap module to see what Windows is seeing or not seeing. Or get an external USB analyzer (yeah, not helpful in the immediate term).

Associate II

"Do you have code that handles the CDC_SET_LINE_CODING and CDC_GET_LINE_CODING commands?"

Sort of, as far as I can see the usbd_cdc_if.c file contains a routine, as can be seen below here, that mentions these defines but doesn't really do anything with them.

When I use the non-modified ST HS USB Middleware, the routine gets called when the device gets connected, the virtual com port opens or closes. Would this affect the data throughput rate? Since, as far as I know changing the baudrate on the host side doesn't really seem to affect anything.

  * @brief  Manage the CDC class requests
  * @param  cmd: Command code
  * @param  pbuf: Buffer containing command data (request parameters)
  * @param  length: Number of data to be sent (in bytes)
  * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
static int8_t CDC_Control_HS(uint8_t cmd, uint8_t* pbuf, uint16_t length)
  /* USER CODE BEGIN 10 */
  /* Line Coding Structure                                                       */
  /* Offset | Field       | Size | Value  | Description                          */
  /* 0      | dwDTERate   |   4  | Number |Data terminal rate, in bits per second*/
  /* 4      | bCharFormat |   1  | Number | Stop bits                            */
  /*                                        0 - 1 Stop bit                       */
  /*                                        1 - 1.5 Stop bits                    */
  /*                                        2 - 2 Stop bits                      */
  /* 5      | bParityType |  1   | Number | Parity                               */
  /*                                        0 - None                             */
  /*                                        1 - Odd                              */
  /*                                        2 - Even                             */
  /*                                        3 - Mark                             */
  /*                                        4 - Space                            */
  /* 6      | bDataBits  |   1   | Number Data bits (5, 6, 7, 8 or 16).          */
  return (USBD_OK);
  /* USER CODE END 10 */

"On Windows you can run Wireshark with the USBPCap module to see what Windows is seeing or not seeing."

Good idea, never considered using wireshark for capturing USB data.

Yeah - you don't have code to handle the get/set line encoding requests. Search this forum for "CDC_GET_LINE_CODING" and you should find examples of code for this. This missing code may not be the cause of THIS problem, but it does cause problems with at least some Windows versions. Can't speak to Linux.

Chief II

This answers your questions:

This shows how to start with TinyUSB:

Contrary to ST's broken bloatware, TinyUSB is developed by people, who understand what they are doing...

Associate II

@Bob S​ I tried modified my CDC_Control_HS routine based on a post in the Knowledge base section form this forum. This unfortunately did not change anything on the data throughput.

@Piranha​ Funny you mentioned, after trying some different configurations I opted for implementing the TinyUSB library. After some descriptor issues I got a TinyUSB CDC configuration working but the maximum throughput so far is 4.5MB/s. I hope this has something to do with my configuration somewhere. Have you ever used the TinyUSB library in combination with the STM32F4 HS interface and if so, what kind of throughput did you get out of it?

Adding the get/set line coding support only attempts to addresses the "why doesn't WIndows/Linux connect" issue. It will have NO impact on throughput - unless you are really taking data to/from a physical UART. The USB transfers are always "as fast as one end can write and the other end can read".

I'm using TinyUSB for an MSC device on L4 series. Though in my case the speed doesn't matter, therefore I haven't invested time into that aspect. By the way on L4 I've noticed that compiling with -Os optimization produces not only the smallest executable, but it also turns out to be the fastest one. Probably because more instructions fit in the ART cache.

Turns out the DWC2 driver doesn't use DMA. :| One option is to return to ST's "miracle", which seems to support DMA, but for serious purposes in the long term using that code is basically like torturing yourself. Another option is to implement DMA support for TinyUSB driver and make a pull request. Everyone would thank you! 😉 If your goals are serious, I would try that.