Normally, the AVR hardware clears the global interrupt flag (in SREG) before entering an interrupt. This means other interrupts are disabled inside the running handler until the handler finishes and exits.
The RETI instruction is the normal function epilogue for an interrupt handler, and thus re-enables interrupts (just like executing an SEI instruction).
For this reason, normally, interrupt handlers can not be nested.
For most interrupt handlers, this is the desired behavior. For some its even required in order to prevent infinitely recursive interrupts. Recursion could easily exceed the available stack space and crash the running program. In rare circumstances it might be desirable to re-enable the global interrupt flag as early as possible inside the interrupt handler, allowing recursive interrupt handling. Carefully.
This could be accomplished by inserting an sei() instruction right at the beginning of the interrupt handler, but this still leaves a few instructions inside the (compiler-generated) interrupt prologue to run with global interrupts disabled. A better method is to instruct the compiler to insert an SEI instruction at the very beginning of the interrupt handler by declaring the handler in the following way (XXX_vect is the name of a valid interrupt vector):
ISR(XXX_vect, ISR_NOBLOCK) { ... }
Compare the following three disassembly examples which demonstrate how this works:
Non-nested, normal method:
ISR(TIM0_OVF_vect) { //do nothing } PUSH R1 PUSH R0 IN R0, 0x3F PUSH R0 CLR R1 POP R0 OUT 0x3F, R0 POP R0 POP R1 RETI ; <<-- Interrupts re-enabled here!
Nested, SEI inserted after prologue:
ISR(TIM0_OVF_vect) { sei(); //do nothing } PUSH R1 PUSH R0 IN R0, 0x3F PUSH R0 CLR R1 SEI ; <<-- Global Interrupts re-enable here! POP R0 OUT 0x3F, R0 POP R0 POP R1 RETI
Nested, best method:
ISR(TIM0_OVF_vect, ISR_NOBLOCK) { //do nothing } SEI ; <<-- Global Interrupts Enabled here! PUSH R1 PUSH R0 IN R0, 0x3F PUSH R0 CLR R1 POP R0 OUT 0x3F, R0 POP R0 POP R1 RETI