Fix #99: move spi ISR operations into helper thread. SPI from ISR is bad!

This commit is contained in:
geeksville
2020-04-18 14:22:24 -07:00
parent f9a805e3d4
commit db766f18ed
7 changed files with 61 additions and 28 deletions

View File

@@ -34,16 +34,12 @@ bool RH_RF95::init()
if (!RHSPIDriver::init())
return false;
// Determine the interrupt number that corresponds to the interruptPin
int interruptNumber = digitalPinToInterrupt(_interruptPin);
if (interruptNumber == NOT_AN_INTERRUPT)
return false;
#ifdef RH_ATTACHINTERRUPT_TAKES_PIN_NUMBER
interruptNumber = _interruptPin;
#endif
// Tell the low level SPI interface we will use SPI within this interrupt
spiUsingInterrupt(interruptNumber);
// spiUsingInterrupt(interruptNumber);
// No way to check the device type :-(
@@ -114,6 +110,17 @@ bool RH_RF95::init()
return false; // Too many devices, not enough interrupt vectors
}
_deviceForInterrupt[_myInterruptIndex] = this;
return enableInterrupt();
}
bool RH_RF95::enableInterrupt()
{
// Determine the interrupt number that corresponds to the interruptPin
int interruptNumber = digitalPinToInterrupt(_interruptPin);
if (interruptNumber == NOT_AN_INTERRUPT)
return false;
if (_myInterruptIndex == 0)
attachInterrupt(interruptNumber, isr0, ONHIGH);
else if (_myInterruptIndex == 1)
@@ -126,6 +133,12 @@ bool RH_RF95::init()
return true;
}
void RH_INTERRUPT_ATTR RH_RF95::disableInterrupt()
{
int interruptNumber = digitalPinToInterrupt(_interruptPin);
detachInterrupt(interruptNumber);
}
void RH_RF95::prepareDeepSleep()
{
// Determine the interrupt number that corresponds to the interruptPin
@@ -143,6 +156,13 @@ bool RH_RF95::isReceiving()
RH_RF95_MODEM_STATUS_HEADER_INFO_VALID)) != 0;
}
void RH_INTERRUPT_ATTR RH_RF95::handleInterruptLevel0()
{
disableInterrupt(); // Disable our interrupt until our helper thread can run (because the IRQ will remain asserted until we
// talk to it via SPI)
pendingInterrupt = true;
}
// C++ level interrupt handler for this instance
// LORA is unusual in that it has several interrupt lines, and not a single, combined one.
// On MiniWirelessLoRa, only one of the several interrupt lines (DI0) from the RFM95 is usefuly
@@ -222,6 +242,16 @@ void RH_RF95::handleInterrupt()
_cad = irq_flags & RH_RF95_CAD_DETECTED;
setModeIdle();
}
enableInterrupt(); // Let ISR run again
}
void RH_RF95::loop()
{
while (pendingInterrupt) {
pendingInterrupt = false; // If the flag was set, it is _guaranteed_ the ISR won't be running, because it masked itself
handleInterrupt();
}
}
// These are low level functions that call the interrupt handler for the correct
@@ -230,17 +260,17 @@ void RH_RF95::handleInterrupt()
void RH_INTERRUPT_ATTR RH_RF95::isr0()
{
if (_deviceForInterrupt[0])
_deviceForInterrupt[0]->handleInterrupt();
_deviceForInterrupt[0]->handleInterruptLevel0();
}
void RH_INTERRUPT_ATTR RH_RF95::isr1()
{
if (_deviceForInterrupt[1])
_deviceForInterrupt[1]->handleInterrupt();
_deviceForInterrupt[1]->handleInterruptLevel0();
}
void RH_INTERRUPT_ATTR RH_RF95::isr2()
{
if (_deviceForInterrupt[2])
_deviceForInterrupt[2]->handleInterrupt();
_deviceForInterrupt[2]->handleInterruptLevel0();
}
// Check whether the latest received message is complete and uncorrupted