# STM32 USB VCP bug

I have been working on a project for the past two weeks now and debugging this one issue has taken up this entire week. Wondering if anyone can help, I will try to be as explicit and clear as possible.

I am trying to implement a USB Virtual Comm Port on a MicroController based on the STM32F302K8 (Cortex M4). I have used STM32CubMX to generate the code needed to set up a USB Full Speed Device implementing a CDC class. My device shows up in both Windows (Device Manager) and Linux. I am able to implement a simple echo function based on the example code but when I now try to use the function USBD_CDC_SetTxBuffer to send data to the PC, this sets off a Hard Fault Handler. I have narrowed this down to the fact that the UsbDeviceFS.pClass (which is needed by USBD_CDC_SetTxBuffer) field is never initialized because USBD_CDC_Init() is never called in the initialization of the USB Device.

I have implemented fixes to several bugs (including changing the heap size, fixing the transmission flag in USBD_CDC_TransmitPacket, and changing the size of CDC_DATA_HS_MAX_PACKET_SIZE to 256 from 512) in the example code as documented on the ST forum but still getting the same errror.

My device setup code is

* USB Device Core handle declaration */
USBD_HandleTypeDef hUsbDeviceFS;

/* init function */
void MX_USB_DEVICE_Init(void)
{
/* Init Device Library,Add Supported Class and Start the library*/
USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS);

USBD_RegisterClass(&hUsbDeviceFS, &USBD_CDC);

USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS);

USBD_Start(&hUsbDeviceFS);

}

• Its been a while since I last worked with USB on an STM, but I think that USBD_CDC_Init() tries to do a malloc. The problem was that there isn't enough space on the heap in the default setup and you need to increase it. – brhans Mar 26 '15 at 16:46
• Hi, I've increased the heap size to 0x600 and nothing's happening. Which function calls malloc because when I put a breakpoint on it, it appears it is never called. – Galaxy Mar 27 '15 at 9:06

To answer my own question, the problem is that my code didn't wait for the USB to finish initialization and immediately began sending data. Inserting an active wait on a boolean or adding a delay (as pointed out by @ramez) solves the problem.

UPDATE This bug has been fixed in subsequent USB CDC driver versions from ST. There is now a HAL_Delay in the setup. Caveat is that if for any reason Sys_Tick does not work/is deactivated/not yet initialised, your code will hang.

• Yes, you should post this as a separate question. Keep in this answer only the information relevant for the original question. – m.Alin Apr 9 '15 at 17:55

I used CubeMX for generating code for STM32F4 discovery. I used it as virtual COM port as you. I did not use USBD_CDC_SetTxBuffer() function directly. In usbd_cdc_if.c file there is a function named CDC_Transmit_FS(). There was a bug in the generated code, the function took a buffer as parameter and it did nothing with it. The corrected function code is following:

uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)
{
uint8_t result = USBD_OK;
memcpy(UserTxBufferFS, Buf, sizeof(char) * Len);
USBD_CDC_SetTxBuffer(hUsbDevice_0, UserTxBufferFS, Len);
result = USBD_CDC_TransmitPacket(hUsbDevice_0);
return result;
}


Actually I had to add the memcpy to the code. After this correction I could send data from the microcotroller to the PC with this transmit function. For example:

int main(void)
{
HAL_Init();

SystemClock_Config();

MX_GPIO_Init();
MX_USB_DEVICE_Init();
configureGPIOs();

uint8_t Buf[] = "Test";

HAL_Delay(1000);

while (1)
{
CDC_Transmit_FS(Buf, 4);
HAL_Delay(1000);
}
}


The initalization in MX_USB_DEVICE_Init() is the same at me as yours.

• Thank you ramez. I have found the problem, I had to test if the virtual comm port had finished initialization, I used a boolean in CDC_Init_FS which the main loop waited to be true before calling CDC_Transmit_FS. I think the HAL_DELAY in your code achieves the same effect. Thank you for the help. – Galaxy Apr 7 '15 at 9:03

First, check if hUsbDevice_0 is null (missing element in your solution):

    if (hUsbDevice_0 == NULL)
return USBD_FAIL;


This will prevent from hanging your uC and does not need busy waiting in delays.

You can place it somewhere in CDC_Transmit_FS:

USBD_StatusTypeDef CDC_Transmit_FS(uint8_t* Buf, uint16_t Len) {

if (hUsbDevice_0 == NULL)
return USBD_FAIL;

USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) hUsbDevice_0->pClassData;

if (hcdc->TxState != 0)
return USBD_BUSY;

uint8_t result = USBD_OK;

USBD_CDC_SetTxBuffer(hUsbDevice_0, Buf, Len);
result = USBD_CDC_TransmitPacket(hUsbDevice_0);

return result;
}


I had the same problem but it turned out the only thing I need to do was re-plug the USB connection to the computer. Most time you flash the code and reset the microcontroller but on the PC side the enumeration is not updated. USBD_CDC_Init is called when the host starts to probe your device and that's why pClassData is NULL.

• You can force re-enumeration in software as well. Second dumbest way after re-plugging is disabling/enabling your port in device manager, if you don't have a custom driver handling this in a more fancy way – stiebrs Oct 25 '18 at 8:59