ATmega328 Power Reduction

Power Reduction

ATmega328 Power reduction

Power

The ATmega328 is already fairly efficient compared to older technologies, but what if you need to reduce the power to the minimum for a remote, solar-powered weather station? There are ways to reduce the overall power consumption of the ATmega328 considerably without much work. Here are some ways that I've found useful.

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 if you are running a solar panel in parallel with a lithium ion battery. The voltage is between 3.2V and 3.7V at the CPU, depending on battery charge.

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!
}
            
Arduino Board Logo

 

Arduino-Board is the go-to source for information on many available Arduino and Arduino-like boards, tutorials and projects.

Help and Support

Arduino-Board

Stay updated

Sign up if you would like to receive our once monthly newsletter.