Clock Interrupts, Part 2, and the ATmega4809

OK, I was going to give examples where reenabling interrupts was necessary for smooth, error-free operation. But then I hit a snag. It turns out that interrupts cannot be reenabled simply with sei() in the ATmega4809 in the Arduino Nano Every!

There is an important difference in operation compared to the other AVR microcontrollers used in Arduino boards. The others turn off the global interrupt enable (the bit in SREG) when the ISR is started and clear the bit with the RETI instruction at the end of the ISR. The ATmega4809 does not alter the global interrupt enable bit but instead has a read-only register, CPUINT.STATUS that indicates if an ISR is executing, and this blocks other interrupts as necessary. The RETI instruction clears the bit in this register to reenable interrupts. During execution of the ISR, this bit, and not the bit in SREG, keeps other interrupts from happening.

So I pondered the problem and posted this discovery, looking to see if someone else discovered this and found a solution. I did this on the Arduino site forum. https://forum.arduino.cc/t/you-cannot-enable-interrupts-within-an-isr/879593 A fellow, Bill Westfield linked the issue on AVRFreaks, got a reply with a workaround. The function:

__attribute(( naked, noinline )) static void isrIrqOn () { 
    asm("reti"); 
}

has no content but the RETI instruction. So calling this function clears the bit in the CPUINT.STATUS register. Code in the ISR that follows allows using sei() and cli(). So one only needs to put the following statements at the start of an ISR that wants to enable and disable interrupts:

cli(); // Disable globally first
isrIrqOn(); // enable interrupts by faking the return

Note that the order of these statements is important if you want to be sure interrupts are not enabled until the code does so with the call to sei.

This “trick” worked fine in the five example programs in my next book that needed this. So where was this time consuming code in an ISR? Drivers for the DHT11/DHT22 humidity and temperature sensor and using the write function in the RF24 library for an NRF24L01 transceiver.

The inexpensive DHT sensors only have a handshake with the microcontroller at the very start of the read-out sequence, then they send out a stream of bits that need to be fairly accurately timed and takes about 5ms. If interrupts are disabled during that time, several clock ticks will be missed and if the USART is being used to read there is a good possibility that characters will be lost as well. So the driver code reenables interrupts repeatedly for small periods of time, allowing ISRs to run as long as they are short.

The RF24 represents a different problem. While there is a non-blocking write function that uses an external interrupt as a callback on completion, frankly the ideal solution, I decided to try just reenabling interrupts while the blocking write function was executing (this can take several dozen milliseconds in the worst case). This ended up working fine.

So what if you have both devices? Well there is an issue if during the DHT read the timer interrupt interrupts itself and wants to do the RF24 write since the that recursed routine needs to return for the DHT read to continue. This means that extra code will be needed to prevent running the transceiver during a sensor read.