Send and Receive data to PC without UART (STM32 USB COM)

This tutorial will cover yet another feature of the USB in STM32 and Today we will see how can we communicate to the computer without using the UART. I will show you how can we use the STM32 USB to send and receive data from the computer, just like we did using the UART.

To do so, I will use the STM32F103C8 controller in the USB DEVICE mode, and the Communication Device class (Virtual Com Port). The setup for the same is shown below

CubeMX Setup

First of all I am selecting the USB in DEVICE only MODE, as shown above

In the USB DEVICE, select the class as Communication Device Class (Virtual Port Com) and leave everything to default

Finally the clock is set to maximum here. As you can see above, the USB clock is automatically adjusted to 48 MHz



HOW TO SEND DATA

The functions required to send or receive data are located in USB_DEVICE -> App -> usbd_cdc_if.c file.

The function CDC_Transmit_FS(uint8_t* Buf, uint16_t Len) can be used to transmit the data to the PC via the USB. The parameters are the Buf (Buffer to send) and the Len (length of the data)

#include "main.h"
#include "usb_device.h"

#include "usbd_cdc_if.h"
#include "string.h"

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t *data = "Hello World from USB CDC\n";
/* USER CODE END 0 */

int main(void)
{
  HAL_Init();
  
  SystemClock_Config();

  MX_GPIO_Init();
  MX_USB_DEVICE_Init();

  while (1)
  {
	  CDC_Transmit_FS(data, strlen(data));
	  HAL_Delay (1000);
  }
}

As you can see above, I am transmitting the data every 1 second.

When we run the code, the DEVICE gets detected in the computer as shown below

I have opened the COM 5 on the Hercules, and the data is being printed every second



HOW TO RECEIVE DATA

The function CDC_Transmit_FS is globally available, and hence I was able to use it in the main file. On the other hand, the function CDC_Receive_FS is a static function defined in USB_DEVICE -> App -> usbd_cdc_if.c file, and hence can not be used outside that file.

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);
  return (USBD_OK);
  /* USER CODE END 6 */
}

In order to make use of it, I am going to define a buffer in the main.c file as shown below

uint8_t buffer[64];

Next, I am going to externally define the same in the usbd_cdc_if.c file so that i can use the buffer in the receive function

/* USER CODE BEGIN PRIVATE_TYPES */
extern uint8_t buffer[64];
/* USER CODE END PRIVATE_TYPES */

and finally modify the CDC_Receive_FS function as shown below

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);
  
  memset (buffer, '\0', 64);  // clear the buffer
  uint8_t len = (uint8_t)*Len;
  memcpy(buffer, Buf, len);  // copy the data to the buffer
  memset(Buf, '\0', len);   // clear the Buf also
  
  return (USBD_OK);
  /* USER CODE END 6 */
}

In the above code,

  • clear the buffer first to remove any data from previous reception
  • Find the actual length of the received data
  • copy the data from the received Buf into our buffer
  • clear the Buf too ( I have realized that this saves the old data, if not cleared)


RESULT

The result of the above code is shown below

The controller was keep transmitting “Hello World from USB CDC”. When i sent the data (“123456789”) to the controller, it gets stored in the buffer.


The same happened here too. This time I transmitted a different data and it gets stored in the buffer.

We can later process this data in any means we want since it can be accessed from the main.c file itself

Check out the Video Below




Info

You can help with the development by DONATING Below.
To download the project, click the DOWNLOAD button.

Subscribe
Notify of

23 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
keyboard_arrow_up