ATmega328p Power Consumption

The power consumed by an ATmega328 depends a lot on what you are doing with it. Just sitting there in a default state, it can use 16mA @ 5V while running at 16MHz. It is possible to reduce that to under 100uA just by manipulating a few picoPower features in the ATmega328P and other devices. I'll use the ATmega328P because I'm going to give specific numbers for some things and they will be different on different ATmega's.

Power Reduction

Vcc Voltage

This is an obvious one. If you lower the voltage you lower the power. At 1MHz the ATmega328 draws 6.75mA @ 5V, but only 0.92mA @ 3.3V. There are possible drawbacks, of course. Like finding 3.3V things to hook to the ATmega. The upside is that it will run on a single lithium ion cell. There is typically a series diode dropping the voltage from 3.7 down to 3.2 or so, at least if you are running a solar recharged lithium ion.

Clock Frequency

More low hanging fruit. If you lower the frequency you lower the power. At 8MHz the ATmega328 draws 11.68mA @ 5V, but at 1MHz it draws 6.75mA @ 5V. The drawback here is that you may not have enough CPU cycles to do the job.

Clock and Vcc

FrequencymA @ 5VmA @ 3.3V
1MHz6.750.92
8MHz11.684.16
16MHz (LP)16.327.40
16MHz (FS)17.527.94
20MHz (LP)19.718.86
20MHz (FS)21.129.46

(LP) Indicates the Low Power Crystal Oscillator
(FS) Indicates the Full Swing Crystal Oscillator

Clock Source

This one is not obvious at all. The internal oscillator takes the least amount of current to run, followed by an external crystal, followed by an external oscillator. The external oscillator not only takes more currrent in the AVR - it takes it's own currrent externally to power it.

Peripherals

This is harder to get at. You need to turn off the clocks to those peripherals you aren't using. Here is a list of peripherals and the power they used in this 8MHz test.

PeripheralmA @ 3.3V
ADC0.23
USART0.08
SPI0.15
Timer00.09
Timer10.12
Timer20.12
TWI0.18

Each one doesn't use much, but together they total up to right at 1mA. Not much unless you are trying to get under 400uA. They can each be turned off in the Power Reduction Register (PRR) by writing a one to the bit position:

            
                PRR |= (1<<PRTWI) | (1<<PRSPI) | (1<<PRUSART0);
            

Writing a 1 to the these three bits turns off clocks to all of the serial interfaces. Turning power back on is done by writing a 0 to the desired bits. It is a little backwards, but the register disables the peripherals. Because the clocks are abruptly stopped, the state of the peripheral is preserved, but the ADC will need to restart with a long conversion.

Sleep

This is the final step in saving power. It is drastic because it shuts down the CPU and hopes that a peripheral will wake it back up when there is something to do. In this example code, we shut down the CPU and wake it again when the INT0 interrupt fires (the INT0 pin goes low). The average current draw is 380µA @ 5V, which is pretty low. At 3.3V the current drops to 72uA, but if we wake up and write to an SD card, for instance, the current draw will jump to 13mA for 100mS, then 50mA for 50mS, before it drops back down to the 72µA level again.

Note that these power saving tips won't work directly with Arduino, because Counter/Timer0 is running 1mS interrupts, and each of those interrupts wakes the CPU. If you disable the Timer0 interrupts, the code will work as expected, but delays and the millis() function won't work right.

#include <avr/io.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>

#include <stdio.h>
#include <stdlib.h>

int main()
{
    PRR = 0xff;
    
    // Set up a pin for an LED.
    DDRB = 0x01;
    
    // Enable the interrupt as a falling edge.
    EICRA = (1<<ISC01);
    EIMSK = (1<<INT0);
    
    while (1)
    {
        // Go to sleep until interrupted.
        set_sleep_mode(SLEEP_MODE_PWR_DOWN);
        sleep_enable();
    
        // Must enable or can't wake!
        sei();
        sleep_cpu();
    
        // Zzz...
    
        sleep_disable();
        
        // Do stuff like toggle a bit.
        PINB |= 0x01;
    }
}

ISR(INT0_vect)
{
    // Empty. We don't care - we just want it to wake the CPU!
}
            
Notes:

picoPower ATmega328 vs ATmega328P