1
votes

I need to send data from the PC to my STM32F3, so I decided to use a built-in USB in uC. But now I have a problem - I want to send to stm32 big amount of data at once - I mean something like 200-500 Bytes.

When I send from PC with minicom packets which have less than 64 chart - everything is fine - callback CDC_Receive_FS(uint8_t* Buf, uint32_t *Len) occurs once - it enables UsbRxFlag, just to inform the running program that there is data available.

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */
  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  if( (Buf[0] == 'A') & (Buf[1] == 'T') ){
      GPIOB->BSRR = (uint32_t)RX_Led_Pin;
      UsbRxFlag = 1;
  }

  return (USBD_OK);
  /* USER CODE END 6 */
}

But when I try to send more data (just long text from minicom ) to uC, something weird happens - sometimes uC doesn't react at all - sometimes it doesn't take into account some data.

How can I handle sending to STM32F3 more than 64Bytes over USB-CDC?

2
Aside: (Buf[0] == 'A') & (Buf[1] == 'T') is more commonly written as (Buf[0] == 'A') && (Buf[1] == 'T'), yet makes no real functional difference here. & vs. &&.chux - Reinstate Monica

2 Answers

4
votes

The maximum packet length for full-speed USB communication is 64 bytes. So the data will be transferred in chunks of 64 bytes and needs to be reassembled on the other end.

USB CDC is based on bulk transfer endpoints and implements a data stream (also known as pipe), not a message stream. It's basically a stream of bytes. So if you send 200 bytes, do not expect any indication of where the 200 bytes end. Such information is not transmitted.

Your code looks a bit suspicious:

  • You probably meant '&&' instead of '&' as pointed out by Reinstate Monica.
  • Unless you change buffers, USBD_CDC_SetRxBuffer only needs to be called once at initialization.
  • When CDC_Receive_FS is called, a data packet has already been received. Buf will point to the buffer you have specified with USBD_CDC_SetRxBuffer. Len provides the length of the packet. So the first thing you would do is process the received data. Once the data has been processed and the buffer can be reused again, you would call USBD_CDC_ReceivePacket to indicate that you are ready to receive the next packet. So move USBD_CDC_SetRxBuffer to another function (unless you want to use several buffers) and move USBD_CDC_ReceivePacket to the end of CDC_Receive_FS.

The incorrect order of the function calls could likely have led to the received data being overwritten while you are still processing it.

But the biggest issue is likely that you expect that the entire data is received in a single piece if you sent is as a single piece, or that it at least contains an indication of the end of the piece. That's not the case. You will have to implement this yourself.

If you are using a text protocol, you could buffer all incoming data until you detect a line feed. Then you know that you have a complete command and can execute it.

0
votes

The following is a general purpose implementation for reading an arbitrary number of bytes: https://github.com/philrawlings/bluepill-usb-cdc-test.

The full code is a little too long to post here, but this essentially modifies usb_cdc_if.c to create a circular buffer and exposes additional functions (CDC_GetRxBufferBytesAvailable_FS(), CDC_ReadRxBuffer_FS() and CDC_FlushRxBuffer_FS()) which can be consumed from main.c. The readme.md text shown on the main page describes all the code changes required.

As mentioned by @Codo, you will need to either add termination characters to your source data, or include a "length" value (which itself would be a fixed number of bytes) at the beginning to then indicate how many bytes are in the data payload.