AnsweredAssumed Answered

USB Device Library: HAL_PCD_IRQHandler() porting issue to OS environment

Question asked by young.paul.004 on Dec 17, 2015
Latest reply on Dec 22, 2015 by Clive One
Hi Everyone,

I'm working on getting the ST middleware USB device stack to work on my development board but I'm having a hard time with the way how STM32Cube does things. I've already spent 3-4 days troubleshooting this issue and found no solution.

My problem is this piece of code (that's common to any USB device examples provided by ST):
02.  * @brief  This function handles USB-On-The-Go FS global interrupt request.
03.  * @param  None
04.  * @retval None
05.  */
06.#ifdef USE_USB_FS
07.void OTG_FS_IRQHandler(void)
09.void OTG_HS_IRQHandler(void)
12.  HAL_PCD_IRQHandler(&hpcd);

In all USB device examples provided by ST, HAL_PCB_IRQHandler() is executed from inside of the USB ISR to service the interrupt. That function will eventually bubble up and call my application code. The problem is my application code makes use of OS blocking function call (i.e SPI via DMA for reading/writing to/from uSD card) that cannot be executed from inside of an ISR. 

So what I did was moved HAL_PCD_IRQHandler() out of the ISR and into an OS task. The USB ISR would trigger an event, causing the task to exit its blocking state and execute HAL_PCD_IRQHandler() to service the interrupt. However, I noticed that if I don't clear the interrupt flags inside of the USB ISR, as soon as I exit it, it would be called again. To work around that, I disable the USB IRQ then exit out of the ISR like the following:
01.#ifdef USE_USB_FS
02.void OTG_FS_IRQHandler(void)
04.void OTG_HS_IRQHandler(void)
07.   uint8_t temp = 255;
09.   // trigger the USB interrupt event
10.   xQueueSendToBackFromISR(USBD_IntEvt, &temp, 0);
12.   // disable USB interrupt until request has been handled by OS task
13.   USB_DisableGlobalInt(hpcd.Instance);

The OS task looks like this:
01.static void USBD_IRQHandlerTask(void *pvParameters)
03.   uint8_t queueData;
05.   while(1)
06.   {
07.      // wait for USB interrupt event to occur
08.      xQueueReceive(USBD_IntEvt, &queueData, portMAX_DELAY);
10.      // service USB interrupt
11.      HAL_PCD_IRQHandler(&hpcd);
13.      // re-enable USB interrupt
14.      USB_EnableGlobalInt(hpcd.Instance);
15.   }

With the above modification, HAL_PCD_IRQHandler() is called from an OS task instead of the USB ISR. When it bubbles up and call my application code that calls some OS blocking functions, everything works out great. This modification works perfectly fine in Full Speed mode. But as soon as I switch to High Speed mode using an external USB PHY, it no longer work. Note that I did verify that it wasn't my hardware by running the High Speed mode example provided by ST, calling HAL_PCD_IRQHandler() from the ISR, and it worked. The only problem with that approach was, like I mentioned above, I couldn't make any OS blocking call since HAL_PCD_IRQHandler() was executed from an ISR.

Has anyone had this issue porting this piece of code to an OS environment? How did you deal with it? Why did it work for Full Speed and not High Speed (with the modification I did above)? I have a feeling that by disabling the USB IRQ before exiting the USB ISR is what cause the issue with High Speed mode (but I just cannot confirm it nor find a work-around). Any help is greatly appreciated. Thanks!!