I have a 4MB serial flash - Adesto SPI, using SPI2 on the 32F417.
Code was generated / extracted from Cube ST libraries to implement a removable storage device via USB, in 2MB of this flash device. This works great. It uses interrupts.
However, other stuff is also accessing this serial flash. The 2MB file system is also accessed via FatFS and that also works, so files can be exchanged between embedded code and say a Windows computer via USB. And other parts are used as a "linear flash" for data logging etc.
I have started doing thorough tests on the data logging code and it runs for a bit and then spectacularly crashes. The stack dump in Cube shows a ton of total crap. Almost certainly the reason is that the USB interrupts are getting in at any time, and both SPI and the flash device itself are obviously not re-entrant
A lot of the serial flash access code is running in main() before USB interrupts are enabled, which is why this issue has only just now surfaced - a year after starting the project. But now I am running test code as an RTOS thread so it exposes the problem.
I am using mutexes to protect SPI and the flash device on the end of it, but this merely enables FatFS and data logging calls to be done in multiple RTOS threads. One cannot use a mutex to block an ISR.
The obvious hack is to block USB interrupts around the SPI2 functions. These functions are called both before and after USB interrupts are enabled, so the usual way is to save the interrupt enable status, disable ints, and restore the status upon exit.
The Q is where. The USB code, done by someone else a couple of years ago, is so vastly complex nobody understands it. But I found these snippets, in which just two lines are interesting:
HAL_StatusTypeDef HAL_PCD_Start(PCD_HandleTypeDef *hpcd)
{
#if defined (USB_OTG_FS) || defined (USB_OTG_HS)
USB_OTG_GlobalTypeDef *USBx = hpcd->Instance;
#endif /* defined (USB_OTG_FS) || defined (USB_OTG_HS) */
__HAL_LOCK(hpcd);
#if defined (USB_OTG_FS) || defined (USB_OTG_HS)
if ((hpcd->Init.battery_charging_enable == 1U) &&
(hpcd->Init.phy_itface != USB_OTG_ULPI_PHY))
{
/* Enable USB Transceiver */
USBx->GCCFG |= USB_OTG_GCCFG_PWRDWN;
}
#endif /* defined (USB_OTG_FS) || defined (USB_OTG_HS) */
(void)USB_DevConnect(hpcd->Instance);
__HAL_PCD_ENABLE(hpcd);
__HAL_UNLOCK(hpcd);
return HAL_OK;
}
/**
* @brief Stop the USB device.
* @param hpcd PCD handle
* @retval HAL status
*/
HAL_StatusTypeDef HAL_PCD_Stop(PCD_HandleTypeDef *hpcd)
{
__HAL_LOCK(hpcd);
__HAL_PCD_DISABLE(hpcd);
if (USB_StopDevice(hpcd->Instance) != HAL_OK)
{
__HAL_UNLOCK(hpcd);
return HAL_ERROR;
}
(void)USB_DevDisconnect(hpcd->Instance);
__HAL_UNLOCK(hpcd);
return HAL_OK;
}
__HAL_PCD_ENABLE(hpcd) ;
__HAL_PCD_DISABLE(hpcd) ;
and these are macros and it's obvious what they do:
USBx->GAHBCFG |= 1;
USBx->GAHBCFG &= ~1;
Looking in the RM, page 1274, I see this is toggling a global USB interrupt enable bit, but the description (int pending presentation to application) puzzles me. Is it the right one?
Stepping through the code to see what USBx should be, I see 0x50000000. It is also defined as USB_OTG_FS_PERIPH_BASE. So the way to obtain that bit 0 value is
uint32_t temp = USB_OTG_FS_PERIPH_BASE->GAHBCFG; // bit 0 holds current int enable status - wrong syntax though; this works
#define USB_CF *(volatile uint32_t*) (USB_OTG_FS_PERIPH_BASE+GAHBCFG)
uint32_t temp=USB_CF
Does this make sense?
Thank you very much for any pointers.
There are more cunning ways to do this, as always, e.g. getting the USB ISR to save a write or read request which is then processed by an RTOS thread, but this should work
It is not a high performance USB application.