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.
#include <avr/sleep.h> ... set_sleep_mode(<mode>); sleep_mode();
#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.
>#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();