Hints for using the CDC USB Serial

A project log for Notes on Using SystemWorkbench with STM32 BluePill

I finally succumb to IDE madness... for awhile.

Al WilliamsAl Williams 04/11/2017 at 03:053 Comments

When you add the USB CDC serial to your project, you don't get much help about what to do next. The CDC_Transmit_FS call is pretty obvious. Give it a message and a length and it sends it. Fair enough.

Receive, though is via a callback named CDC_Receive_FS. You don't call this. The framework calls it for you.

Here's my version:

static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)


uint32_t len=*Len;
if (hUsbDeviceFS.dev_state != USBD_STATE_CONFIGURED)
   return USBD_FAIL;

if (((Buf == NULL) || (Len == NULL)) || (*Len <= 0))
   return USBD_FAIL;

/* Get data */
uint8_t result = USBD_OK;
        result = USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
    while(result != USBD_OK);

       result = USBD_CDC_ReceivePacket(&hUsbDeviceFS);
    while(result != USBD_OK);

// add data to FIFO
    while (len--)
       if (FIFO_INCR(RX_FIFO.head)==RX_FIFO.tail)
             return USBD_FAIL;  // overrun
   return (USBD_OK);

  /* USER CODE END 6 */ 


This in in one of the headers:

#define  FIFO_SIZE 32  // must be 2^N

#define FIFO_INCR(x) (((x)+1)&((FIFO_SIZE)-1))

/* Structure of FIFO*/

typedef struct FIFO

uint32_t head;
uint32_t tail;
    uint8_t data[FIFO_SIZE];

extern FIFO RX_FIFO;

Then you need a way to read it out, too

/* Create FIFO*/
FIFO RX_FIFO = {.head=0, .tail=0};

// returns bytes read (could be zero)
// would be easy to make it end early on a stop char (e.g., \r or \n)
uint8_t VCP_read(uint8_t* Buf, uint32_t Len)
  uint32_t count=0;
/* Check inputs */
  if ((Buf == NULL) || (Len == 0))
  return 0;

while (Len--)
if (RX_FIFO.head==RX_FIFO.tail) return count;

return count;


volium wrote 02/22/2022 at 19:45 point

@Al Williams, I stumbled upon your code while trying to get CDC working in my own project.

I compared it to this "similar" implementation (; and while I do like that you added extra checks in the code, after trying both I realized yours will eventually cause a HardFault.

The issue has to do with this line in "CDC_Receive_FS":[RX_FIFO.head]=*Buf++;

Basically, you are incrementing the incoming "Buf" pointer, and the caller function is unaware, so every time a byte is processed, the base address of the buffer gets pushed up by 1. Depending on the size of the buffers used when calling "USBD_CDC_SetRxBuffer" in "CDC_Init_FS", the HardFault will happen sooner or later, since random RAM will start getting accessed and operated on by the the rest of the code.

The fix is to either use an auxiliary pointer to increment and dereference when getting the actual data, or to use index notation on the "Buf" pointer itself.

Another issue I found during testing is that "CDC_Receive_FS" may actually get called with *Len == 0, which your code treats as an error:

"if (((Buf == NULL) || (Len == NULL)) || (*Len <= 0))"

Removing the check for *Len<= 0 fixes that problem.

Overall, thanks for the post, it was super useful!

  Are you sure? yes | no

Al Williams wrote 06/29/2022 at 13:50 point

Yeah that buf should be indexed. As for the Len of zero problem I haven't noticed that but could be. Thanks!

  Are you sure? yes | no

arya.nava wrote 03/26/2020 at 14:07 point

!دمت گرم 

  Are you sure? yes | no