Arduino AtMega328p low power consumption

For one of my projects, I want to have a really low power consumption device to be able to use a battery for many month. For this I implemented a low power solution as described here. I’ll try to simplify it a little bit and document it a little more …

Let’s start – what do we need ?

We need to have a AtMega328P plus a couple of wires to get the following circuit. This schema use a 16Mhz Quartz but it will be removed once the system setup.

Arduino low power minimal circuit

Arduino low power minimal circuit

 

 

 

 

 

 

 

 

 

 

 

 

Then, we need to use an ISP programming board like this one (USBASP v2). Another solution is to use another arduino as an ISP (see this article).

USB-ISP Arduino programming board

USB-ISP Arduino programming board

 

 

 

 

 

 

 

 

Let’s program the arduino bootloader

The first step is to use avrdude to program the bootloader. We are going to choose ATmegaBOOT_168_pro_8MHz.hex which is a bootloader using the internal oscillator instead of the external one. Arduino will now work at 8Mhz and do not need external Quartz.

To program the bootload I used the following command :

./avrdude -C ../etc/avrdude.conf -c usbasp -p atmega328p \
-U flash:w:../../../arduino/avr/bootloaders/atmega/ATmegaBOOT_168_pro_8MHz.hex

launched from /Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin on mac os X

Let’s update the fuses

To change the oscillator mode, we must also update arduino fuses.They are some kind of hardware configuration registers. There are 3 of them : low fuse, high fuse and extended fuse. What we are going to change is the low fuse to change the clock source to be internal (8Mhz internal Osc). For this the last 4 bits must be 0010. We will also change the startup timing to “slowly rising power” to delay startup after reset to 65ms.

The low fuse value will be, according to this, 0xE2 ; the command to be used is the following one :

sudo ./avrdude -C ../etc/avrdude.conf -c usbasp -p atmega328p -U lfuse:w:0xE2:m

You can verify the result with this command :

#sudo ./avrdude -C ../etc/avrdude.conf -c usbasp -p atmega328p -U lfuse:r:/tmp/fuz.out:i
#cat /tmp/fuz.out
:01000000E21D
:00000001FF

Now, the 16Mhz Quartz can be removed.

Let’s have a first test

Now, i will connect a LED to the pin #15 corresponding to GPIO 9 usable as a PWM to test it with the Fade example.

So in the arduino environment, open and compile Fade example (in basic folder). To be adapted to the device we are building, choose Arduino pro or mini as card and AtMega328, 3v3 8Mhz as processor.

In my case, as i’m using the ISP programmer, and it seams it is now not really well detected on Mac OsX maverick I have to use the command line for the programming :

sudo ./avrdude -C ../etc/avrdude.conf -c usbasp -p atmega328p \
-U flash:w:/var/folders/pz/tbk987c56x3791b90cqz65n80000gn/T/build8914543323292743780.tmp/Fade.cpp.hex

The path is subject to differ on you system. It can be obtained by configuring the arduino verbose output (Arduino >> preferences – print detailed during compile)

Now the led should start blinking !

At this point the power consumption of the circuit is about 6mA at 3.3V. It is interesting to notice that if you have a 5V supply you will be at 20mA for the same thing. Don’t forget to switch back to 5V to program the device, otherwise you’ll get programming error.

Let’s play with sleep mode

There are many sleep mode available in the avr/arduino according to the documentation :

the AtMega328p different sleep mode

the AtMega328p different sleep mode

What is important is being able to come back from this sleep mode to execute some peace of code. This table is indicating the different events waking up the arduino.

According to some website that have been done the low power exercises and many measures, like this excellent one. The power consumption in the different mode can achieve the following results :

  • 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

The best mode for power saving is SLEEP_MODE_PWR_DOWN according to these measures. This mode can be resumed from watchdog timer.

We can use a function like this one to switch to powerdown mode :

ISR (WDT_vect) {
   wdt_disable(); 
}

void startSleeping() {
    // clear various "reset" flags
    MCUSR = 0;     
    // allow changes, disable reset, enable Watchdog interrupt
    WDTCSR = bit (WDCE) | bit (WDE);
    // set interval (see datasheet p55)
    WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP1);    // 128K cycles = approximativly 1 second
    wdt_reset();  // start watchdog timer
    set_sleep_mode (SLEEP_MODE_PWR_DOWN); // prepare for powerdown  
    sleep_enable();  
    sleep_cpu ();   // power down !
}

void loop() {
   startSleeping();
   // things to be done once a second
   ...
   // ------------------------------
}

Based on this code, what I get from my current meter is a consumption 0.29mA at 5V and 0.19mA at 3.3V

You must take into consideration that wakeup time is about 65ms.

With a such consumption, the battery life estimation for a standard AAA battery is about 6 month.

Let’s deactivate most of the internal components

Now, we can deactivate components we are not using, especially during the sleep period :

void startSleeping() {
    // clear various "reset" flags
    MCUSR = 0;     
    // allow changes, disable reset, enable Watchdog interrupt
    WDTCSR = bit (WDCE) | bit (WDE);
    // set interval (see datasheet p55)
    WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP1);    // 128K cycles = approximativly 1 second
    wdt_reset();  // start watchdog timer
    set_sleep_mode (SLEEP_MODE_PWR_DOWN); // prepare for powerdown  
    sleep_enable(); 

    // turn off brown-out enable in software
    MCUCR = bit (BODS) | bit (BODSE);
    MCUCR = bit (BODS); 
    
    previousADCSRA = ADCSRA;    
    ADCSRA &= ~(1<<ADEN); //Disable ADC
    ACSR = (1<<ACD); //Disable the analog comparator
    DIDR0 = 0x3F; //Disable digital input buffers on all ADC0-ADC5 pins
    DIDR1 = (1<<AIN1D)|(1<<AIN0D); //Disable digital input buffer on AIN1/0

    power_twi_disable();
    power_spi_disable();
    power_usart0_disable(); //Needed for serial.print
    power_timer0_disable(); //Needed for delay and millis()
    power_timer1_disable();
    power_timer2_disable(); //Needed for asynchronous 32kHz operation

    sleep_cpu ();   // power down !
}

The current consumption in a such mode is about 26uA at 5V and 21uA at 3.3V. The battery life can be expected around 3 years which is good enough for my project.

Let’s reactivate everything on wake-up

To reactivate internal components on wake up, we just have to call the appropriate functions

void wakeup() {
    power_twi_enable();
    power_spi_enable();
    power_usart0_enable();
    power_timer0_enable();
    power_timer1_enable();
    power_timer2_enable();
    power_adc_enable();
    ADCSRA = previousADCSRA;
    // BOD is automatically restarted at wakeup
}

void loop() {

  startSleeping();
  sleep_disable();
  wakeup();

  // Do what you need to do ... 
}

 

 

 

 

 

4 thoughts on “Arduino AtMega328p low power consumption

  1. Have you allowed a project to run since you wrote this? Does it appear to be still running? I’m curious to see if the theoretical life can actually be observed.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.