4.5 Execution of the Sleep Instruction and Shared Variables Between ISR and Main
volatile uint8_t shared_flag;
ISR(perip_vector){
uint8_t i_flags = PERIP_INTFLAGS;
shared_flag = 0x01;
PERIP_INTFLAGS = i_flags;
}
main(){
while(1){
cli();
if(shared_flag == 0x01){
shared_flag &= ~0x01;
sei();
handle_event_etc();
}
sei();
sleep();
}
}
In this case, the shared_flag is protected from being altered by the ISR while it is being checked and cleared. However, if the ISR writes to the flag after the first sei(), inside the if statement, the sleep instruction will be executed without the shared_flag being checked again. As such, the events that need to happen in main() will not happen until the next time an interrupt occurs, waking the device from sleep.
main(){
while(1){
cli();
if(shared_flag == 0){
sei();
sleep();
}
sei();
//handle shared_flags and run application
}
}
cli();
shared_flag &= ~0x01;
sei();
This makes the read-modify-write an atomic action, that is, it cannot be interrupted by any ISRs. However, avoid this by using General Purpose Registers (GPR) as shared variables between ISRs. These registers allow the compiler to compile any single-bit modification to one line of assembly code that will execute in a single CPU cycle. In comparison, using a read-modify-write of a uint8_t variable will take three cycles, or five cycles, when taking the cli() and sei() instructions into account.