I am working on RF communication in STM32 using the NRF24L01+ module via SPI. This module raises an interrupt on a GPIO pin whenever a new message is received so I handle the interrupt to read and buffer the received messages:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == NRF24L01_IRQ_Pin) {
// Lots of code to read the message and clear the IRQ via SPI
}
}
(This is probably not a good idea in general since IRQ should be short-lived code but what is an alternative here? RF is kind of time sensitive and we need to buffer the message to make room for new ones to arrive.)
Sometimes I am getting HardFault in my main loop's invocation of HAL_SPI_TransmitReceive
which also access the module via SPI
while (1)
{
// Clear the NRF24L01+ status
CSN_LOW();
HAL_SPI_TransmitReceive(&hspi1, &command, &status, 1, SPI_TIMEOUT);
CSN_HIGH();
// Long code to process buffered messages and send responses (this requires SPI).
HAL_Delay(10);
}
If I understand things correctly, if the HAL_SPI_TransmitReceive
in my main code gets interrupted in a certain way (e.g. right during it is clocking in the data bytes) and another HAL_SPI_TransmitReceive
is invoked in the interrupt handler (clocking in new bytes), things could go wrong given the hspi handle is kind of global.
What is the right method to access SPI in both the main loop and the interrupt handler?
The question you are asking isn't really about interrupt safety but re-entrancy.
Some of the HAL functions might be safe to call in interrupt context, but they certainly aren't re-entrant. In other words, you can't call them in an interrupt while they might already be running in another context.
You either need to move all of your SPI accesses into the context of interrupts at the same priority, or else move them all to main context.
The simplest solution is to disable the interrupt and just poll the GPIO pin from main context. Whether this works or not depends on what else main context has to do.
If you have multiple things that you need to do in main context then you might consider using a pre-emptive RTOS and use some kind of synchronization mechanism between the interrupt and a task, such as giving a semaphore in the interrupt and waiting for it in the task.