Summary
In this thread I show various power-saving techniques for the Atmega328P processor.
They include sleep modes, use of power-reduction registers, and other techniques. Applying all of them can result in a current draw as low as approximately 100 nano-amps (100 nA), well below the self-discharge rate of most batteries.
Proof from the datasheet for the Atmega328P (page 405 of my copy):
That is 100 nA at at 25°C running at 3v.
These techniques would be useful for battery-powered devices where the full power of the processor was only required intermittently, for example a TV remote, calculator, doorbell, or some sort of environmental monitor where you only needed to check things from time to time.
Introduction
I am going to explore various power-saving options for running your project, presumably from battery power. Tests will show which options have any effect at all, and which ones have the most effect.
These examples are specifically for the Atmega328P processor, but the techniques are pretty general.
Summary of methods
Use as many of the techniques listed here as are practical in your application. They are described in further detail below.
- Run the processor at a lower frequency
- Run the processor at a lower voltage
- Turn off unneeded internal modules in software (eg. SPI, I2C, Serial, ADC)
- Turn off brownout detection
- Turn off the Analog-to-Digital converter (ADC)
- Turn off the watchdog timer
- Put the processor to sleep
- Don't use inefficient voltage regulators - if possible run directly from batteries
- Don't use power-hungry displays (eg. indicator LEDs, backlit LCDs)
- Arrange to wake the processor from sleep only when needed
- Turn off (with a MOSFET) external devices (eg. SD cards, temperature sensors) until needed
Baseline - Arduino Uno
As a preliminary baseline test, we'll put this sketch onto an Arduino Uno Rev 3 board:
Sketch A
void setup () {}
void loop () {}
Clearly it doesn't do much. :)
Running from a 9V battery through the "power in" plug, it draws about 50 mA.
Running on 5V through the +5V pin, it draws about 49 mA.
(Note: around 68 mA on a Mega 2560 board)
Now we'll try putting it to sleep:
Sketch B
#include <avr/sleep.h>
void setup ()
{
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_cpu ();
} // end of setup
void loop () { }
Now the Uno draws 34.5 mA. A saving, but not a lot.
(Note: around 24 mA on a Mega 2560 board)
Atmega328 processor on its own
Using a "bare bones" board saves quite a bit of power.
Bare-bones board.
Sketch A above only draws 15.15 mA, a saving of 34.85 mA just by not using the development board. This would be accounted for by the fact that the development board has on it:
- Voltage regulators (for +5V and +3.3V)
- A USB interface chip (for the USB port)
- A "power" LED
Sketch B above only draws 360 µA (0.360 mA) which is a LOT less. Still, you would expect about 31 mA less than the figure above for the Uno, so that sounds about right.
Also, with low power consumption I can put the multimeter on the "microamps" range which is more sensitive.
The initial outcome is that, to save power, forget about using a development board. Further savings (like reducing clock speed) would be overshadowed by the huge overhead of the voltage regulator and USB interface chip.
So from now on I'll experiment with the "bare bones" board. However it can still be programmed using the normal Arduino IDE (Integrated Development Environment). In my case I am using the USBtinyISP programming gadget, which you can get from here for around $US 22.
http://www.ladyada.net/make/usbtinyisp/
To program the board I changed a line in the Arduino preferences.txt file, as follows:
The "bare bones" board I am playing with is from "Evil Mad Science" for around $US 13 for the board, processor chip, a ZIF (zero insertion force) socket, a 6-pin programming header, and a couple of other things:
http://evilmadscience.com/productsmenu/tinykitlist/230
The "Evil Mad Scientist" board. This particular one does not have the ZIF socket.
Different sleep modes
Let's substitute the various sleep modes in this line:
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
Sleep modes and power consumption:
- SLEEP_MODE_IDLE: 15 mA
- SLEEP_MODE_ADC: 6.5 mA
- SLEEP_MODE_PWR_SAVE: 1.62 mA
- SLEEP_MODE_EXT_STANDBY: 1.62 mA
- SLEEP_MODE_STANDBY : 0.84 mA
- SLEEP_MODE_PWR_DOWN : 0.36 mA
Power-save mode lets you keep Timer 2 running (providing clocked from an external source).
Stand-by mode is similar to power-down mode, except that the oscillator is kept running. This lets it wake up faster.
Note: In IDLE mode, the clocks are running. This means that (unless you disable it) the normal Timer 0 clock used to count millis() will be running, and thus you will wake up approximately every millisecond.
Brown-out disable
Another power saving can be made by disabling the brown-out detection. To detect low voltages the processor must generate a voltage source for comparison purposes. You can change the "extended fuse" (efuse) by using AVRdude, like this:
avrdude -c usbtiny -p m328p -U efuse:w:0x07:m
In SLEEP_MODE_PWR_DOWN mode, with brown-out disabled, the power went down from 360 µA to 335 µA, a saving of 25 µA.
Another way of turning off brown-out detection is to temporarily disable it like this:
Sketch C
#include <avr/sleep.h>
void setup ()
{
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
noInterrupts (); // timed sequence follows
sleep_enable();
// turn off brown-out enable in software
MCUCR = bit (BODS) | bit (BODSE); // turn on brown-out enable select
MCUCR = bit (BODS); // this must be done within 4 clock cycles of above
interrupts (); // guarantees next instruction executed
sleep_cpu (); // sleep within 3 clock cycles of above
} // end of setup
void loop () { }
Note that this is a timed sequence. You must do sleep_cpu() directly after the bit manipulation or the brown-out disable is cancelled.
Turn off ADC (analog to digital conversion)
Next thing we can do turn off the ADC subsystem by adding this line:
// disable ADC
ADCSRA = 0;
With that there the power consumption drops a large amount, down from 335 µA to 0.355 µA! (that is, 355 nA)
Configuring pins as inputs/outputs
Let's experiment with configuring the pins in various ways ...
Sketch D
#include <avr/sleep.h>
void setup ()
{
for (byte i = 0; i <= A5; i++)
{
pinMode (i, OUTPUT); // changed as per below
digitalWrite (i, LOW); // ditto
}
// disable ADC
ADCSRA = 0;
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
noInterrupts (); // timed sequence follows
sleep_enable();
// turn off brown-out enable in software
MCUCR = bit (BODS) | bit (BODSE);
MCUCR = bit (BODS);
interrupts (); // guarantees next instruction executed
sleep_cpu (); // sleep within 3 clock cycles of above
} // end of setup
void loop () { }
Testing in SLEEP_MODE_PWR_DOWN:
- All pins as outputs, and LOW: 0.35 µA (same as before).
- All pins as outputs, and HIGH: 1.86 µA.
- All pins as inputs, and LOW (in other words, internal pull-ups disabled): 0.35 µA (same as before).
- All pins as inputs, and HIGH (in other words, internal pull-ups enabled): 1.25 µA.
Note: This was tested with nothing connected to the pins. Obviously if you have an LED or something like that on an output pin, you will draw more current.
Power Reduction Register (PRR)
The next thing to experiment with is the Power Reduction Register (PRR). This lets you "turn off" various things inside the processor.
The various bits in this register turn off internal devices, as follows:
- Bit 7 - PRTWI: Power Reduction TWI
- Bit 6 - PRTIM2: Power Reduction Timer/Counter2
- Bit 5 - PRTIM0: Power Reduction Timer/Counter0
- Bit 4 - Res: Reserved bit
- Bit 3 - PRTIM1: Power Reduction Timer/Counter1
- Bit 2 - PRSPI: Power Reduction Serial Peripheral Interface
- Bit 1 - PRUSART0: Power Reduction USART0
- Bit 0 - PRADC: Power Reduction ADC
Tip:
The macros power_all_disable() and power_all_enable() modify the PRR register as appropriate for different processors.
Sketch E
#include <avr/sleep.h>
#include <avr/power.h>
void setup ()
{
// disable ADC
ADCSRA = 0;
// turn off various modules
power_all_disable ();
set_sleep_mode (SLEEP_MODE_IDLE);
noInterrupts (); // timed sequence follows
sleep_enable();
// turn off brown-out enable in software
MCUCR = bit (BODS) | bit (BODSE);
MCUCR = bit (BODS);
interrupts (); // guarantees next instruction executed
sleep_cpu (); // sleep within 3 clock cycles of above
} // end of setup
void loop () { }
Important note! You must use the PRR after setting ADCSRA to zero, otherwise the ADC is "frozen" in an active state.
Sketch F
#include <avr/power.h>
void setup ()
{
power_all_disable(); // turn off all modules
} // end of setup
void loop () { }
Sketch G
#include <avr/power.h>
void setup ()
{
ADCSRA = 0; // disable ADC
power_all_disable (); // turn off all modules
} // end of setup
void loop () { }
According to the datasheet the PRR only applies in active (non-sleep) and idle modes. In other modes, those modules are already turned off.
Sleep modes and power consumption with PRR = 0xFF:
- SLEEP_MODE_IDLE (Sketch E): 7.4 mA (was 15 mA)
- Not sleeping (Sketch F): 14 mA (was 16.1 mA)
- Not sleeping, no ADC (Sketch G): 13.6 mA (was 16.1 mA)
Turning on or off selected modules
Various macros (they vary by processor) let you power on or off individual modules:
Enabling:
- power_adc_enable(); // ADC converter
- power_spi_enable(); // SPI
- power_usart0_enable(); // Serial (USART)
- power_timer0_enable(); // Timer 0
- power_timer1_enable(); // Timer 1
- power_timer2_enable(); // Timer 2
- power_twi_enable(); // TWI (I2C)
Disabling:
- power_adc_disable(); // ADC converter
- power_spi_disable(); // SPI
- power_usart0_disable();// Serial (USART)
- power_timer0_disable();// Timer 0
- power_timer1_disable();// Timer 1
- power_timer2_disable();// Timer 2
- power_twi_disable(); // TWI (I2C)
Using the internal clock
By changing the fuse bits the processor can run on its 8 MHz internal clock. Running Sketch A above, with lfuse set to 0xE2, the board consumed 11.05 mA (compared to 15.15 mA using the crystal).
You can change the clock like this:
avrdude -c usbtiny -p m328p -U lfuse:w:0xE2:m
Another way of changing the clock speed is to enable the "divide clock by 8" fuse bit. So this gives you various other options, like a 16 MHz crystal divided by 8 giving 2 MHz, or the internal 8 MHz oscillator, divided by 8, giving 1 Mhz.
You can also run the processor on a low-power 128 KHz internal clock. Running Sketch A above, with lfuse set to 0xE3, the board consumed 6 mA (compared to 16.1 mA using the crystal).
Note that using the slower clock doesn't really help in sleep mode, because the clock is stopped anyway.
Warning: Once the clock is set to 128 KHz, you will have trouble programming the board. You need to add -B250 to the AVRdude command line. For example, to put the clock back to using the crystal:
avrdude -c usbtiny -p m328p -U lfuse:w:0xFF:m -B250
There is a fuse calculator for boards here:
http://www.engbedded.com/fusecalc
Another warning: Pay close attention to the fuse bit settings. If you get them wrong you can "brick" your processor. It can be recovered with a high-voltage programmer like the AVR Dragon, if you have one handy. In particular you don't want to turn off SPIEN (Enable Serial programming and Data Downloading). Nor do you want to turn on RSTDISBL (External reset disable).
Summary of clock speeds and current
lfuse Speed Current
0xFF 16 MHz 15.15 mA
0xE2 8 MHz 11.05 mA
0x7F 2 MHz 7.21 mA
0x62 1 MHz 6.77 mA
0xE3 128 KHz 6.00 mA
Another point is that running from a slower clock means, that if you sleep and wake, you are awake for longer (potentially 16 times as long if you cut a 16 MHz processor down to 1 MHz). So if you consume more power when awake, and are awake for much longer, then that can outweigh the savings from running at slower speeds.
Waking from sleep with a timer
Well, this sleeping as all very well, but a processor that stays asleep isn't particularly useful. Although, I have seen an example of that: the TV Begone remote control. What that does is send out some codes (to turn TVs off) and then goes to sleep permanently. The "activate" button on the gadget is the reset button. When you press that it does its stuff again.
Meanwhile this sketch below shows how you can use the watchdog timer to sleep for 8 seconds (the maximum you can set up a watchdog for) and then flash the LED 10 times, and go back to sleep. Whilst asleep it uses about 6.54 µA of current, so presumably the watchdog timer has a bit of an overhead (like, 6.2 µA).
Sketch H
#include <avr/sleep.h>
#include <avr/wdt.h>
const byte LED = 9;
void flash ()
{
pinMode (LED, OUTPUT);
for (byte i = 0; i < 10; i++)
{
digitalWrite (LED, HIGH);
delay (50);
digitalWrite (LED, LOW);
delay (50);
}
pinMode (LED, INPUT);
} // end of flash
// watchdog interrupt
ISR (WDT_vect)
{
wdt_disable(); // disable watchdog
} // end of WDT_vect
void setup () { }
void loop ()
{
flash ();
// disable ADC
ADCSRA = 0;
// clear various "reset" flags
MCUSR = 0;
// allow changes, disable reset
WDTCSR = bit (WDCE) | bit (WDE);
// set interrupt mode and an interval
WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0); // set WDIE, and 8 seconds delay
wdt_reset(); // pat the dog
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
noInterrupts (); // timed sequence follows
sleep_enable();
// turn off brown-out enable in software
MCUCR = bit (BODS) | bit (BODSE);
MCUCR = bit (BODS);
interrupts (); // guarantees next instruction executed
sleep_cpu ();
// cancel sleep as a precaution
sleep_disable();
} // end of loop
This slightly different example shows how you can flash an LED once a second (approximately) and then go to sleep for the rest of the time:
Sketch I
#include <avr/sleep.h>
#include <avr/wdt.h>
const byte LED = 9;
// watchdog interrupt
ISR (WDT_vect)
{
wdt_disable(); // disable watchdog
} // end of WDT_vect
void setup () { }
void loop ()
{
pinMode (LED, OUTPUT);
digitalWrite (LED, HIGH);
delay (50);
digitalWrite (LED, LOW);
pinMode (LED, INPUT);
// disable ADC
ADCSRA = 0;
// clear various "reset" flags
MCUSR = 0;
// allow changes, disable reset
WDTCSR = bit (WDCE) | bit (WDE);
// set interrupt mode and an interval
WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP1); // set WDIE, and 1 second delay
wdt_reset(); // pat the dog
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
noInterrupts (); // timed sequence follows
sleep_enable();
// turn off brown-out enable in software
MCUCR = bit (BODS) | bit (BODSE);
MCUCR = bit (BODS);
interrupts (); // guarantees next instruction executed
sleep_cpu ();
// cancel sleep as a precaution
sleep_disable();
} // end of loop
To save you looking up the various combinations of wake-up time here is the table from the datasheet for the Atmega328:
Basically you set some of the bits named WDP0 through to WDP3 as per the table, to get different times. Note that the smallest is 16 mS and the longest is 8 S.
You could arrange longer sleep times by having a "counting" loop and sleep/wake/sleep/wake for x times.
Note that the watchdog timer is independent of the clock speed of the processor, so an 8-second watchdog timer is still that, regardless of what speed you are clocking the processor at.
Warning about wake-up times
Various fuse settings make a big difference to the wake-up time. Some wake-up times are quite long (eg. 65 mS) because they are designed to allow the crystal clock to settle. If you are trying to save power, taking 65 mS to wake up, add 1 to a counter, and go back to sleep, is a lot. You would want to look at a "wake-up" fuse setting that is appropriate for the type of clock source you are using.
Note that if you disable brown-out detection in software rather than by changing the fuse settings then it takes around 60 µs longer to wake up, as mentioned in the datasheet:
Quote:
If BOD is disabled in software, the BOD function is turned off immediately after entering the sleep mode. Upon wake-up from sleep, BOD is automatically enabled again.
...
When the BOD has been disabled, the wake-up time from sleep mode will be approximately 60 μs to ensure that the BOD is working correctly before the MCU continues executing code.
For a fast wake-up (which means you get on with what you want to do more quickly) disable brown-out detection by altering the fuses. That's assuming you want to disable brown-out detection.
Waking from sleep with a signal
Another way of waking from sleep is to detect a logic level change on an interrupt pin (D2 or D3 on the Arduino). These are processor pins 4 and 5 on the actual Atmega328 chip.
Any interrupt will wake the processor, as this sketch demonstrates. When asleep it uses 0.15 µA of current.
Sketch J
#include <avr/sleep.h>
const byte LED = 9;
void wake ()
{
// cancel sleep as a precaution
sleep_disable();
// precautionary while we do other stuff
detachInterrupt (0);
} // end of wake
void setup ()
{
digitalWrite (2, HIGH); // enable pull-up
} // end of setup
void loop ()
{
pinMode (LED, OUTPUT);
digitalWrite (LED, HIGH);
delay (50);
digitalWrite (LED, LOW);
delay (50);
pinMode (LED, INPUT);
// disable ADC
ADCSRA = 0;
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
sleep_enable();
// Do not interrupt before we go to sleep, or the
// ISR will detach interrupts and we won't wake.
noInterrupts ();
// will be called when pin D2 goes low
attachInterrupt (0, wake, FALLING);
EIFR = bit (INTF0); // clear flag for interrupt 0
// turn off brown-out enable in software
// BODS must be set to one and BODSE must be set to zero within four clock cycles
MCUCR = bit (BODS) | bit (BODSE);
// The BODS bit is automatically cleared after three clock cycles
MCUCR = bit (BODS);
// We are guaranteed that the sleep_cpu call will be done
// as the processor executes the next instruction after
// interrupts are turned on.
interrupts (); // one cycle
sleep_cpu (); // one cycle
} // end of loop
You could combine both methods. That is, have a watchdog, and a logic level change interrupt.
Other power-saving techniques
- If you must use a voltage regulator, try to find one with a low quiescent current. For example the LM7805 itself consumes around 5 mA, so there isn't much use putting a lot of effort into saving the last microamp if your voltage regulator consumes 5 mA non-stop. For example, if you use 3 x 1.5V batteries, you should get around 4.5V which would be enough to power a processor without needing a voltage regulator.
- Go easy on "power" LEDs. LEDs can use quite a bit of current (say, 10 mA). If possible just flash one briefly rather than have it on all the time, or use a low-power LED. Experimentation shows that flashing an LED for 5 mS to 10 mS is quite noticeable. So, one 10 mS flash per second is only an overhead of one percent.
- If you have an external peripheral (eg. a clock chip) you could "power" it from a digital pin, and then turn it off when not required. For example, the DS1307 clock chip only uses 1.5 mA when active, so that could be powered by a digital pin.
- For higher power devices you could use a MOSFET transistor to turn the current on/off to it as required.
- Run the processor on a lower voltage (eg. 3.3V). For example Sketch H above only uses 4.6 µA when asleep, running on 3.3V, compared to 6.4 µA at 5V.
- Sleep the processor as much as possible, even if that means sleeping every second. For example I have a GPS clock that sleeps every second, waking just long enough to "tick" the hand around. Every day or so it wakes up for longer, powers up a GPS module, and resynchronizes its internal time with the GPS time.
Summary of power savings
Figures are the amount saved by each method, in milliamps:
Bypassing the "power plug" 8.4
Using a "bare bones" board 30.5
Power-down sleep mode 15.7
Use internal clock at 128 KHz 10.1
Use Power Reduction Register (PRR) 7.6
Use internal clock at 8 MHz 5.4
In sleep mode, saving in microamps:
Turn off ADC converter 334
Disable brown-out detection 25
Set all pins to input and low 2
Power budgeting
You can work out how long your project will last on a battery by doing a "power budget". That is, work out the average amount of power consumed. For example, if you use 5 mA for 1% of the time that would be an average of 0.05 mA (assuming that for the remaining 99% of the time you used a tiny amount like 0.35 µA).
Some typical battery capacities might be, in mA hours:
CR1212 18
CR1620 68
CR2032 210
NiMH AAA 900
Alkaline AAA 1250
NiMH AA 2400
Alkaline AA 2890
Li-Ion * 4400
* Li-Ion batteries come in a wide variety of sizes, this is just an example.
So for example, running from AAA batteries, drawing an average of 0.05 mA, the device could run for 1250 / 0.05 = 25000 hours (1041 days, or 33 months).
Also batteries have a self-discharge rate, so you would need to factor that in. The battery may self-discharge faster than your circuit discharges it! See the post further down for a discussion about self-discharge rates.
Also see this page about interrupts:
http://gammon.com.au/interrupts
[EDIT]
Confirmed low-current readings with Dave Jones' uCurrent device:
Available for $AUD 49.95 (at present) from:
http://www.eevblog.com/shop/
Warning! It tends to sell out very quickly. |