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, this can be avoided by using General Purpose Input Output (GPIOR) registers as shared variables between ISRs. Registers GPIOR0 to GPIOR3 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.