
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