STM32CubeMX USB CDC VCP?

user505160 picture user505160 · Nov 5, 2015 · Viewed 39.4k times · Source

I've found large number of examples, but nothing on how to do it "properly" from STM32MXCube.

How do I create skeleton code from STM32CubeMX for USB CDC virtual COM port communications (if possible STM32F4 Discovery)?

Answer

Michel Sanches picture Michel Sanches · Nov 5, 2015

A STM32CubeMX project for Discovery F4 with CDC as USB device should work out of the box. Assuming you use an up-to-date STM32CubeMX and library:

  • Start STM32CubeMX
  • Select the board Discovery F4
  • Enable peripheral UBS_OTG_FS device only (leave over stuff uncheck)
  • Enable midlleware USB_Device Communication .. .aka CDC

In the clock tab check the clock source is HSE HCLK. It shall give 168 MHz HLCK and 48 MHz in the 48 MHz (USB). Check there is no red anywhere.

Save the project

Generate code (I used SW4STM32 toolchains)

Build (you may need to switch to internal CDT builder vs. GNU make).

Now add some code to send data over the COM port and voila it should work.

Actually, the tricky part is not try to make any "CDC" access until the host USB connects (no CDC setup yet)

Here is how I did it for quick emit test:

In file usbd_cdc_if.c

uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)
{
    uint8_t result = USBD_OK;

    /* USER CODE BEGIN 7 */
    if (hUsbDevice_0 == NULL)
        return -1;

    USBD_CDC_SetTxBuffer(hUsbDevice_0, Buf, Len);
    result = USBD_CDC_TransmitPacket(hUsbDevice_0);
    /* USER CODE END 7 */

    return result;
}

static int8_t CDC_DeInit_FS(void)
{
    /* USER CODE BEGIN 4 */
    hUsbDevice_0 = NULL;
    return (USBD_OK);
    /* USER CODE END 4 */
}

In file main.c

/* USER CODE BEGIN Includes */
#include "usbd_cdc_if.h"
/* USER CODE END Includes */
....

/* USER CODE BEGIN WHILE */
while (1)
{
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    uint8_t HiMsg[] = "hello\r\n";
    CDC_Transmit_FS(HiMsg, strlen(HiMsg));
    HAL_Delay(200);
}

As soon you plug the micro USB (CN5) CDC data will start to show on the host terminal.

That works. I can see "hello" on the terminal (you may need to install a driver, http://www.st.com/web/en/catalog/tools/PF257938).

For reception, it needs to be first armed, say, started by a first call to USBD_CDC_ReceivePacket() in a good place. For that it can be CDC_Init_FS.

Then you can handle data as it arrives in CDC_Receive_FS and rearming reception again from here.

That works for me.

static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
{
    /* USER CODE BEGIN 6 */
    USBD_CDC_ReceivePacket(hUsbDevice_0);
    return (USBD_OK);
    /* USER CODE END 6 */
}

static int8_t CDC_Init_FS(void)
{
    hUsbDevice_0 = &hUsbDeviceFS;

    /* USER CODE BEGIN 3 */
    /* Set Application Buffers */
    USBD_CDC_SetTxBuffer(hUsbDevice_0, UserTxBufferFS, 0);
    USBD_CDC_SetRxBuffer(hUsbDevice_0, UserRxBufferFS);
    USBD_CDC_ReceivePacket(hUsbDevice_0);

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