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\$ 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\$ 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\$ 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\$ Commented Oct 25, 2018 at 8:59

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.