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