7 Power Management and Sleep Modes

Use of the SLEEP instruction can allow an application to reduce its power consumption considerably. AVR devices can be put into different sleep modes. Refer to the device datasheet for details.

There are several macros provided in this header file to actually put the device to sleep. The simplest way is to set the desired sleep mode using set_sleep_mode() , and then call sleep_mode(). This macro automatically sets the sleep enable bit, goes to sleep, and clears the sleep enable bit.

Example:
#include <avr/sleep.h>
...
    set_sleep_mode(<mode>);
    sleep_mode();
Note: Unless your purpose is to completely lock the CPU (until a hardware reset), interrupts need to be enabled before going to sleep.
Often the ISR sets a software flag or variable that is being checked, and if set, handled in the main loop. If the sleep command is used in the main loop there is a potential for a race condition to occur. In the following code there is a race condition between sleep being issued an the flag being set in the ISR.
#include <avr/interrupt.h>
#include <avr/sleep.h>
...
volatile bool flag = false;
...
ISR(PCINT1_vect){
    flag = true;
}
int main(){
    ...
    while(1){
        if(flag){
            flag = false;
            ...
       }
       sleep_cpu();
       ...
    }
}            

The problem in this code comes from the fact that the ISR can happen at virtually any point in time. If the interrupt happens just after the if statement has been evaluated the device will go to sleep without doing what is required in the if statement. The actual consequence of this is dependent on the application. A way to avoid such race conditions is to disable global interrupts before checking the SW flag using the cli() command.

Example:
>#include <avr/interrupt.h>
#include <avr/sleep.h>
...
volatile bool flag = false;
...
ISR(PCINT1_vect){
    flag = true;
}
int main(){
    ...
    sleep_enable();
    while(1){
        cli();
        if(flag){
            flag = false;
            ...
            sei();
            sleep_cpu();
       }
       sei();
       ...
    }
} 

This sequence ensures an atomic test of flag with interrupts being disabled. If the condition is met, sleep mode will be prepared, and the SLEEP instruction will be scheduled immediately after a SEI instruction. As the instruction right after the SEI is guaranteed to be executed before an interrupt could trigger, it is sure the device will be put to sleep. If an interrupt is pending when global interrupts are disabled the device will then jump to the ISR and continue execution after the SEI and sleep instruction. The program flow will reach the if statement and not sit waiting for a new interrupt in sleep.

Note that some AVR datasheets recommend disabling sleep immediately after waking and enabling sleep immediately before the sleep command. This recommendation is to protect against entering sleep in case the programmer has created bad pointers. It is debatable what would be the best behavior for any given application if it starts executing code from the wrong part of flash. The best type of protection is likely to use the WDT to reset the device.

Some devices have the ability to disable the Brown Out Detector (BOD) before going to sleep. This will also reduce power while sleeping. If the specific AVR device has this ability then an additional macro is defined: sleep_bod_disable(). This macro generates inline assembly code that will correctly implement the timed sequence for disabling the BOD before sleeping. However, there is a limited number of cycles after the BOD has been disabled that the device can be put into sleep mode, otherwise the BOD will not truly be disabled. Recommended practice is to disable the BOD (sleep_bod_disable()), set the interrupts (sei()), and then put the device to sleep (sleep_cpu()), as follows:

cli();
if (some_condition){
    sleep_bod_disable();
    sei();
    sleep_cpu();
}
sei();