11
\$\begingroup\$

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);

}
\$\endgroup\$
2
  • \$\begingroup\$ 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. \$\endgroup\$
    – brhans
    Commented Mar 26, 2015 at 16:46
  • \$\begingroup\$ 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. \$\endgroup\$
    – Galaxy
    Commented Mar 27, 2015 at 9:06

6 Answers 6

10
\$\begingroup\$

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.

\$\endgroup\$
0
3
\$\begingroup\$

So I recently battled with this issue for a REALLY long time.

If your STM32CubeIDE generated code doesn't work from the get-go, then you really want to look into ensuring that your clock is set up properly.

Remember that the USB FS takes a 48 MHz clock, and for me it only worked in the PLL1. I used an STM32H7.

\$\endgroup\$
2
\$\begingroup\$

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.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ 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. \$\endgroup\$
    – Galaxy
    Commented Apr 7, 2015 at 9:03
1
\$\begingroup\$

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;
}
\$\endgroup\$
1
\$\begingroup\$

This problem is due to the USB host needs time to enum the CDC when the STM32 is plugged in. Once enum is started the function USBD_CDC_Init() in ubd_cdc.c will be called, which in turn calls CDC_Init_FS() in usbd_cdc_if.c. To check whether initialization is done, we could simply check hUsbDeviceFS.pClassData:

In usbd_cdc_if.c, add:

bool CDC_Class_Init_Ok(void)
{
  return hUsbDeviceFS.pClassData;
}

Then in main.c:

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USB_DEVICE_Init();
  while (!CDC_Class_Init_Ok());

  while (1) {
  }
}
\$\endgroup\$
0
\$\begingroup\$

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.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ 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 \$\endgroup\$
    – stiebrs
    Commented Oct 25, 2018 at 8:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.