You are on page 1of 3

ATmega devices and power-down mode

(Last updated 31 Jan 2010)

The firmware ritual for putting any of the ATmega devices into power-down or power-save mode isn't obvious
(at least, it wasn't to me). I've added this page describing the ritual as used in a couple of my low-power
projects, in the hopes that others will benefit.
A typical project
My datalogger project (see here) uses power-down mode to conserve battery life. I set up the logger on my
bench with the following conditions:
Bench suppy set to 3.0 VDC;
2 GB microSD card installed;
SparkFun boost-converter power supply BYPASSED (not in the circuit);
UART cable NOT connected (eliminates drain from MAX232 converter);
AVR programming pod NOT connected;
1.0 ohm resistor in series with postive lead from bench supply.
I measured the voltage across the 1.0 ohm resistor with power applied. With the datalogger in power-down
mode (indicated by the state of a debug port line on the MCU), the voltage drop was 0.6 mVDC, or 600 uA of
I repeated the above measurement, but with the SparkFun boost-converter power supply in the circuit. In this
case, the voltage drop was 1.0 mVDC, or 1.0 mA of current.
As an aside, I have built a small project that blinks a bunch of LEDs, then goes into power-down mode until a
button is pressed. That device has nothing active except the MCU. Using the above test bed but with a 0.1
ohm resistor in the positive rail, the voltage drop across the resistor is too low for me to measure with my
Fluke 75 meter. I know the value is in the microAmps, but I don't know how low.
A look at the code
Here is the code from my datalogger source that makes the above power-down sequence work. Note that I
found the core of this code somewhere on the Web, but have lost track of the original page.
static void hibernate(void)
volatile uint8_t mcutmp;
if (DCTimersRead(TIMER_USER) == 0) // if the serial port is not active...
while ((PINB & (1<<0)) == 0) ; // spin while PB0 is low
cli(); // quiet for just a moment
ShutOffADC(); // prepare ADC for sleep
PRR = (1<<PRTWI) | (1<<PRTIM0) | (1<<PRTIM1) | (1<<PRTIM2) | (1<<PRSPI) |
(1<<PRADC) | (1<<PRUSART0);
ATmega power-down
1 of 3 2/29/2012 8:00 AM
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // set the type of sleep mode to use
sleep_enable(); // enable sleep mode
sei(); // allow interrupts to end sleep

PCICR = (1<<PCIE0) | (1<<PCIE2); // enable interrupt on pin-change on
PB0 and PD0

PORT_DEBUG = PORT_DEBUG & ~(1<<BIT_DEBUG); // pull debug line low
mcutmp = MCUCR | ((1<<BODS) | (1<<BODSE));
MCUCR = mcutmp;
mcutmp = mcutmp & ~(1<<BODSE);
MCUCR = mcutmp;
sleep_cpu(); // nighty-night
sleep_disable(); // just woke up, disable sleep mode
for safety
PRR = PRR & ~((1<<PRTWI) | (1<<PRTIM2)); // bring up the systems we need now
PORT_DEBUG = PORT_DEBUG | (1<<BIT_DEBUG); // pull debug line high
Here is the code for shutting off the ADC, used above
* ShutOffADC shut down the ADC and prepare for power reduction
void ShutOffADC(void)
ACSR = (1<<ACD); // disable A/D comparator
ADCSRA = (0<<ADEN); // disable A/D converter
DIDR0 = 0x3f; // disable all A/D inputs (ADC0-ADC5)
DIDR1 = 0x03; // disable AIN0 and AIN1
Additional information
There are a few things worth noting in the hibernate() function above. Per the Atmel docs on the '48P family
of devices, power-down and power-save modes each conserve power, but power-down allows a very limited
number of signals for subsequent wake-up. Be sure to compare the requirements of the two low-power
modes with your design and choose the appropriate sleep mode.
Since my design can sleep with nothing running but the pin-change interrupt subsystem, I was able to use
power-down mode and wake on an interrupt from the real-time clock (tied to PB0). Just for kicks, I also
allow wake-up on UART traffic (PD0), so the user can just press a key to wake up the logger. Setting up the
pin-change interrupts is done (in part) by the write to PCICR. Refer to the Atmel docs for setting up the
other registers associated with pin-change interrupts.
The PRR register is used to shut off selected subsystems within the MCU. Prior to entering sleep mode, I shut
off unneeded subsystems by writing a 1 to each associated bit in PRR. Note that a subsystem that has been
shut off cannot be used as a source of a wake-up signal!
The sequence for actually putting the MCU to sleep involves the block of five lines ending with the call to
sleep_cpu(). These instructions must be executed in this order and there must be no additional instructions
ATmega power-down
2 of 3 2/29/2012 8:00 AM
inserted in this block! Following the execution of sleep_cpu(), the MCU will be put into the selected mode.
The next instruction in the sequence will NOT be executed until the MCU wakes up.
I hope the above details clarify the steps needed to develop low-power applications on the ATmega devices.
If you have any questions or comments, feel free to email me at the address on my home page.

ATmega power-down
3 of 3 2/29/2012 8:00 AM