Pico Power

An active Atmega328 consumes over 10mA of current at around 5V. To reduce power consumption various techniques can be used. Using all the techniques below I was able to reduce power consumption to less than 0.001 mA (my multimeter reads 0.000mA).

Turn off ADC
Set ADEN bit to 1 in register ADCSRA:

ADCSRA &= ~(_BV(ADEN));

Turn off Analog Comparator
Set ACD bit to 1 in ACSR

ACSR &= ~(_BV(ACD));

Turn off the Watchdog Timer
Straightforward but first remember to unset the flags which are set on Reset.

#include <avr/wdt.h>

MCUSR = 0;
wdt_disable();

Turn off power to peripherals
Set bits in the PRR (Power Reduction Register)

PRR = 0b11001111;
Bit Label Purpose
 7  PRTWI  Shut down TWI by writing 1.
 6  PRTIM2  Shut down Timer/Counter2 in synch mode (AS2 = 0).
 5  PRTIM0  Shut down Timer/Counter0 by writing 1.
 4  Reserved (set to 0).
 3  PRTIM1  Shut down Timer/Counter1.
 2  PRSPI  Shut down SPI by writing 1.
 1  PRUSART0  Shut down USART by writing 1.
 0  PRADC  Shut down ADC. ADC must be disabled before shut down.

Sleep whenever possible
Setup the chip in a particular mode. Can be done in setup.

set_sleep_mode(SLEEP_MODE_PWR_DOWN);
Sleep Mode Meaning
 SLEEP_MODE_IDLE  Turn off CPU clock
 SLEEP_MODE_ADC  ADC Noise Reduction. Turn of I/O clock in addition.
 SLEEP_MODE_PWR_DOWN  Power Down. All clocks off. Cannot wake up from timer
 SLEEP_MODE_PWR_SAVE  Allows Timer2 to be running. Power Down + Timer.
#include <avr/sleep.h>
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Setup Power mode

cli();  // Disable interrupts
// Do whatever if needed to confirm we need to go to sleep
sleep_enable(); // Enable sleep (needed to prevent us going to sleep by accident)
sleep_bod_disable();  // Optionally disable the BOD
sei();  // Enable interrupts just before going to sleep
sleep_cpu();  // Go to sleep
sleep_disable(); // When woken up - disable sleep to be on the safe side

Disable BOD when sleeping
This should have been really simple but it’s unnecessarily complex (it seems to me) requiring first setting and then unsetting a bit. The below function may be used (as in example above).

#define sleep_bod_disable() \
{ \
uint8_t tempreg; \
__asm__ __volatile__("in %[tempreg], %[mcucr]" "\n\t" \
"ori %[tempreg], %[bods_bodse]" "\n\t" \
"out %[mcucr], %[tempreg]" "\n\t" \
"andi %[tempreg], %[not_bodse]" "\n\t" \
"out %[mcucr], %[tempreg]" \
: [tempreg] "=&d" (tempreg) \
: [mcucr] "I" _SFR_IO_ADDR(MCUCR), \
[bods_bodse] "i" (_BV(BODS) | _BV(BODSE)), \
[not_bodse] "i" (~_BV(BODSE))); \
}

Leave a Reply