My original situation, using an NRF24L01.
Loop as a task:
read output queue to see if data waiting to be sent. If queue has data, set to transmit mode, write it to data registers and send to addressed node on network (just write), then reset to read mode for chip.
next:
If data is available (read registers in chip or obey interrupt), then read chip data, then place that on main system queue for handling. Cycle and look at hardware again. Note that this is a polling solution without interrupts.
This works, but time delays in loops (equal level task without a delay seems not to promote task switching…) make this long.
Things needed to know: If chip is not unique on SPI interface, it is protected by a semaphore (in this case, it is not) at the SPI bus level. So we don’t have that semaphore. However, the chip is protected AT THE CHIP LEVEL from other tasks writing/reading.
The system is intended to have task driven messages through the NRF24L01 at various times. Other nodes in the mesh network can forward messages to this node at any time.
Handling messages in the mesh network is a task, and seems not to be the problem.
Problem is this. taking this to an interrupt based system works (as far as the hardware is concerned). I can cause an interrupt when data is available, and then execute a routine to put the data on the main system queue. Fine so far. except…
Remember that the chip is protected by a semaphore?
I understand (and use) the “fromISR” routines. No problem there, except that I’ll have to write routines that instead of:
// ****************************************************************************************************************************************************
// ************************************************************* SINGLE REGISTER READ *****************************************************************
// ****************************************************************************************************************************************************
// ONE BYTE READ, RETURNS REGISTER VALUE (NOT STATUS)
uint8_t RF24::read_register(uint8_t reg)
{
uint32_t status;
HAL_StatusTypeDef result;
// uint8_t stat;
uint8_t dresult[2];
#ifdef _FREERTOS
xSemaphoreTake(semaphore,portMAX_DELAY);
#endif
#ifdef _AZURE
tx_mutex_get(&semaphore, TX_WAIT_FOREVER);
#endif
reg = R_REGISTER | (reg & 0x1F);
result = hal_spi[interface]->receive(1,®,1,&dresult[0],0,NRF_SPI_PRESCALE,NRF_CS_GPIO_Port,NRF_CS_Pin);
#ifdef _FREERTOS
xSemaphoreGive(semaphore);
#endif
#ifdef _AZURE
tx_mutex_put(&semaphore);
#endif
// status = get_status();
return dresult[0];
}
The semaphore here is on the RF24 class and protects the chip. Unseen (and not included) is a semaphore that protects the interface if the interface is shared. In this case, it is not. SO thought of, but not needed
My code for the interrrupt routine is:
void NRF_NODE_RECEIVE_IRQ (void)
{
BaseType_t xHigherPriorityTaskWoken = false;
uint8_t buffer[64];
uint8_t data_available;
// ******************************************************************************************************
// ************************* check if incoming **********************************************************
// ******************************************************************************************************
data_available = rf24.available();
rf24.record_registers();
// check incoming data through NRF24L01
if ((data_available < 0x80) && (data_available != 0))
{
// data is available, read and process
// received_packet = (union SYSTEM_PACKET_type*) &buffer;
rf24.read(buffer, 32);
rf24.write_register(NRF_STATUS, 0x70); // all IRQ flags cleared
xQueueSendToBackFromISR(system_queue,(const void*)buffer,&xHigherPriorityTaskWoken );
// per instructions on queue
if (xHigherPriorityTaskWoken)
{
taskYIELD ();
}
} // end of receive check
else
{
// bad interrupt for some reason
// only interrupt is RX data, so reset all
rf24.write_register(NRF_STATUS, 0x70); // all IRQ flags cleared
// no need to delay yet
}
}
The problem is that the register reads and chip reads use semaphores to protect the chip. I can rewrite them so that the semaphores use the “fromISR” tag which makes them work.
RIght, so the routine would work, but I have another conceptual problem with all of this.
Interrupts are aperiodic, and as far as the programming is concerned, may happen at any time.
So after all this, my questions are:
- Assuming that the system IS in the middle of talking to the chip (meaning that the semaphore for the chip is taken), what happens in an interrupt? If I use the same semaphore that protects the chip, then the chip is already protected, and the system is frozen, and the system is in a deadlock.
- This happens regardless of whether or not I use “from ISR” or not, of course
- Since the system is frozen when in an interrupt, (seeing #1), there is no way to “unfreeze” the system, but I could always “give” the semaphore back (just occurred to me), do the interrupt, “take” the semaphore, and return to the original routine (talking to the chip). My impression is that this leaves the chip in an unknown (and highly disturbed) state. I’m not seeing this as a good idea.
Has anyone come across this before (likely) and what was the preferred solution, other than figuring a way to somehow delay the actual interrupt.
Even if I throw away the semaphores (which does not seem to be a good idea), I still have a conceptual problem with interlocking programmed (and random) chip accesses with interrupts(random) chip accesses.
OR should I put the IRQ event on a queue and handle it within the context of the rest of the tasks?
Did this make sense?
Suggestions are welcome.