AVR Low Power Example Project

Last modified by Microchip on 2023/11/09 09:02

Objective

This page illustrates several methods of configuring an 8-bit AVR® MCU to operate with low power consumption. With this exercise, you will:

  • Create a project and add simple code to blink an LED
  • Run the project and measure the power consumption
  • Make modifications to the code to lower power
  • Run the modified project and observe reduced power consumption

Reference Materials

ATmega328PB Xplained Board

The ATmega328PB Xplained Mini Evaluation Kit is a hardware platform for evaluating the Atmel ATmega328PB microcontroller. An external debugger is NOT needed to run these exercises. The ATmega328PB has a fully integrated embedded debugger on-board.

ATmega328PB Xplained Board

Power Debugger Kit

The Power Debugger is a CMSIS-DAP compatible debugger that works with Atmel Studio v7.0 or later. The power Debugger sends runtime power measurement and application debug data to the Data Visualizer.

Atmel Power Debugger

The Power Debugger has two independent current sensing channels for measuring and optimizing the power consumption of a design.
The ‘A’ channel is the recommend channel for accurately measuring low currents.
The ‘B’ channel is the recommended channel for measuring higher currents with lower resolution.

Back to Top

Hardware Setup

Connecting the Power Debugger to the ATmega328PB Xplained Board

The ‘A’ and ‘B’ channel current measurement ports on the Power Debugger board are depicted with ammeter symbols on the silkscreen. The voltage supply is connected to the input of the ammeter, and the load (target) is connected to the output. With the following steps, the power debugger measures the consumption on the AVR core.

Table 1-1 Power Debugger and ATmega328PB Xplained Mini connection.

Power Debugger and ATmega328PB Xplained Mini connection table

Figure 1-1 Power Debugger and ATmega328PB Xplained Mini connection.

Power Debugger and ATmega328PB Xplained Mini connection

Hardware Configuration

Power Debugger and ATmega328PB Xplained Mini connection

Back to Top

Measuring the Current in Active Mode

Project Creation

Open Atmel Studio 7
Select File > New > Project
Select GCC C Executable Project and give it the name "LowPower".
Choose a location to save the project on your computer.

Choose project location dialog

When the Device Selection window appears, enter "328P" into the search window and then select the Atmega328PB. Click OK.

Device selection window

A project will be created containing an empty while(1) loop in main().

#include <avr/io.h>

int main(void)
{
   /* Replace with your application code */
   while (1)
    {
    }

Select View > Solution Explorer from the menu bar.

In the Solution Explorer, right click the project and select Properties.

Solution Explorer Window

Under Tool, Select mEDBG and debugWIRE

debutWIRE selection

Back to Top


Add Code to the Project

Add the following two statements in main() to call TMR0_init and set an I/O pin as an output:

​​​​​

TMR0_init();

DDRB |= 1<<DDRB5;   // Direction of pin PB5 set as Output

Add the Timer 0 initialization routine

/********************************************************************************
                                TMR 0 Initialization
********************************************************************************/

void TMR0_init( void ){

   // enable timer overflow interrupt for both Timer0 and Timer1
   TIMSK0=(1<<TOIE0)
   // set timer0 counter initial value to 0
   TCNT0=0x00;
   // start timer0 with /1024 prescaler
   TCCR0B = (1<<CS02) | (1<<CS00);

   // enable interrupts
   sei()

}

Provide the TMR0 interrupt service routine to the project, and make the LED blink at about 1 Hz.

uint8_t    count;
ISR(TIMER0_OVF_vect){

   count++;
   if(count==20){
       count=0;
       // Toogle LED
       PORTB=PORTB ^ 0x20;
    }
}

The complete program is as follows,

#include <avr/io.h>
#include "avr/interrupt.h"

   uint8_t    count;
/* Timer 0 Interrupt */
ISR(TIMER0_OVF_vect){

   count++;
   if(count==20){
       count=0;
       // Toogle LED
       PORTB=PORTB ^ 0x20;
    }
}

int main(void)
{
   TMR0_init();

   DDRB |= 1<<DDRB5;   // Direction of pin PB5 set as Output
   /* Replace with your application code */
   while (1)
    {
    }
}

/********************************************************************************
                                TMR 0 Initialization
********************************************************************************/

void TMR0_init( void ){

   // enable timer overflow interrupt for both Timer0 and Timer1
   TIMSK0=(1 <<TOIE0) ;

   // set timer0 counter initial value to 0
   TCNT0=0x00;

   // start timer0 with /1024 prescaler
   TCCR0B = (1 <<CS02) | (1<<CS00);

   // enable interrupts
   sei()

}

Back to Top


Verify the Project Runs

Build the project and program the device by selecting Debug > Continue.

The LED will blink if the project is working correctly

Ensure debugging for the project is terminated by selecting Debug > Disable debugWire then Close.

Back to Top


Measure the Power Consumption in Active Mode

In Atmel Studio 7, open the menu Tools > Data Visualizer

Power Debugger Data Gateway should be selected by default in the DGI Control Panel, select it if not. Click on Connect.

Power Debugger Data Gateway should be selected by default in the DGI Control Panel

Select Power and Start

Select Power and Start

Power Monitor

Turn off the target Power Supply to the Xplained board and enable power measurement

Enable Power Dialog

Close the Device Programming
Verify that the average current consumption is about 2.6 mA

Back to Top


Reducing the Power Consumption

There are several methods you can use to reduce the power consumption of an AVR® microcontroller. This example reduces power by:

  • Turning off unused peripherals
  • Stopping leakage current on the digital I/O pins
  • Using Sleep mode. Power consumption optimization techniques are implemented in the function Optimize_power_consumption(), which is called from main().
Disable digital input buffers and Analog comparator

For analog input pins, the digital input buffer should be disabled.

The digital input buffer measures the voltage on the pin and represents the value as a logical one or zero. An analog signal level close to VCC/2 on an input pin can cause significant additional current consumption. If the pin is used as analog pin there is no need to know if the analog signal’s digital value would be a one or zero; these digital pins should be disabled.

Back to Top


Turn off unused peripherals

Disable the peripherals not used in the application. The Power Reduction Register (PRR0) and (PRR1) can stop the clock to individual peripherals to reduce power consumption. Resources used by the peripheral remain occupied when the clock is stopped. In most instances, peripherals should be disabled before the clock is halted.

Include the <power.h> file in the main program
#include <avr/power.h>

Add accompanying code to optimize_power_consumption().

/* Power shutdown to unused peripherals*/
PPR0 = 0xDF;
PPR1 = 0x3F;
Add and #include for <wdt.h> to main.c.
#include <avr/wdt.h>

Add the following code in optimize_power_consumption() to turn off watchdog timer.

/* Disable interrupts*/
Cli();
Wdt_reset();
MCUSR&= ~(1<<WDRF);

Back to Top


Apply pull-up resistors

Unused and unconnected pins consume power. The unneeded power load of floating pins can be avoided by using the AVR's internal pull-up resistors on the unused pins. Port pins can be set to input pull-ups by setting the DDxn bit to 0 and PORTxn bit to 1. (where x is PORT B,C,D,E and n is 0 to 7)

/* Unused pins set as input pull up*/
    
DDRB  &= 0xE0;
    
DDRC &= 0xC9;
    
DDRD &= 0x03;
    
DDRE &= 0xF3;

    
PORTB |= ~(0xE0);
    
PORTC |= ~(0xC9);
    
PORTD |= ~(0x03);
    
PORTE |= ~(0xF3);

Back to Top


Use Sleep function

Sleep mode allows the application to save power by shutting down unused modules. The AVR provides various sleep modes allowing the user to tailor the power consumption to the application’s requirements.

Include <avr/sleep.h> header file from AVR library at the top of file.

Set the desired sleep mode in the optimize_power_consumption () function by configuring the microcontroller to use the sleep mode SLEEP_MODE_PWR_DOWN for minimum power consumption.

/* Set sleep mode */
set_sleep_mode(SLEEP_MODE_PWR_DOWN);

Call the function sleep_mode() from main() to enter Sleep mode.

while (1)
{
sleep_mode();
}

Back to Top


Using Pin Change Interrupt

To wake up the microcontroller from SLEEP_MODE_PWR_DOWN the Pin Change Interrupt is used. The ATmega328PB Xplained Mini board's switch (SW0) is connected to PB7. Pin PB7 is the source for the pin change interrupt. Make the following additions to main():

#include avr/interrupt.h

Configuring bit PCINT7 and PCICR register:

PCMSK0 |= (1<<PCINT7); //Enable Pin Change Interrupt 7
  PCICR |= (1<<PCIE0); //Enable the interrupt enable bit for PCINT
  sei();

Add the interrupt service routine:

In order to enable the ISR routines, the vector name for the PCINT7 is ISR (PCINT0_vect), defined in device header file.

ISR (PCINT0_vect)
{
    if (!(PINB & (1<<PINB7))) // if PINB7 is low (Switch pressed)
    {
       PORTB |= (1 <<PORTB5); // Turn ON LED
    }
   else
    {
       PORTB &= ~(1<<PORTB5); // Turn OFF LED
    }
}        }

Completed Code

After making the previous changes the complete code should like the following:

/*
 * lowpower2.c
 */


//#include <avr/io.h>

#include <avr/io.h>
#include "avr/interrupt.h"
#include "avr/wdt.h"
#include "avr/sleep.h"

#include <avr/power.h>
#include <avr/interrupt.h>
//#include <util/delay.h>

void    optimize_power_consumption(void){

   /* Disable digital input buffer on ADC pins */
   DIDR0 = (1 << ADC5D) | (1 << ADC4D) | (1 << ADC3D) | (1 << ADC2D) | (1 << ADC1D) | (1 << ADC0D);
   DIDR0 |= 0xC0 ;   /*ADC7D and ADC6D are undefined in header file so set bits this way*/

   /* Disable digital input buffer on Analog comparator pins */
   DIDR1 |= (1 << AIN1D) | (1 << AIN0D);
   /* Disable Analog Comparator */
   ACSR |= (1 << ACD);

   /*Power shutdown to unused peripherals*/
   PRR0 = 0xDF;
   PRR1 = 0x3F;
   /*Unused pins set as input pull up*/
   DDRB &= 0xE0;
   DDRC &= 0xC9;
   DDRD &= 0x03;
   DDRE &=0xF3;

   PORTB |=~(0xE0);
   PORTC |=~(0xC9);
   PORTD |=~(0x03);
   PORTE |=~(0xf3);

   /*Watchdog Timer OFF*/

   /* Disable interrupts */
   cli();
   /* Reset watchdog timer */
   wdt_reset();
   /* Clear WDRF in MCUSR */
   MCUSR &= ~(1<<WDRF);
   /* Turn off WDT */
   WDTCSR = 0x00;

   /* Set sleep mode */
   set_sleep_mode(SLEEP_MODE_PWR_DOWN);

}

/********************************************************************************
                                TMR 0 Initialization
********************************************************************************/

void TMR0_init( void ){

   // enable timer overflow interrupt for both Timer0 and Timer1
   TIMSK0=(1<<TOIE0) ;

   // set timer0 counter initial value to 0
   TCNT0=0x00;

   // start timer0 with /1024 prescaler
   TCCR0B = (1<<CS02) | (1<<CS00);

   // enable interrupts
   sei()

}  
    uint8_t    count;
/* Timer 0 Interrupt */
ISR(TIMER0_OVF_vect){

   count++;
   if(count==20){
       count=0;
       // Toogle LED
       PORTB=PORTB ^ 0x20;
    }
}

ISR (PCINT0_vect)
{
    if (!(PINB & (1<<PINB7))) // if PINB7 is low (Switch pressed)
    {
       PORTB |= (1<<PORTB5); // Turn ON LED
    }
   else
    {
       PORTB &= ~(1<<PORTB5); // Turn OFF LED
    }
}

int main(void)
{
   TMR0_init();

   DDRB |= 1<<DDRB5;   // Direction of pin PB5 set as Output
   DDRB &= ~(1<<DDB7); //Set PORTB7 as input

   optimize_power_consumption();

   PCMSK0 |= (1<<PCINT7); //Enable Pin Change Interrupt 7
   PCICR |= (1<<PCIE0); //Enable the interrupt enable bit for PCINT
   sei();

   //set_sleep_mode(SLEEP_MODE_PWR_DOWN);
   //sleep_enable();
   //sleep_cpu();

   /* Replace with your application code */
   while (1)
    {
       sleep_mode();
    }
}

Back to Top


Program the application and measure power consumption
Program the code by selecting Debug > Continue.
Wait until the application is programmed and the message at the bottom of the window appears as Running.
Exit the debugger by selecting Debug > Disable debugWire then Close.
Open the DataVisualizer window and check the power consumption as shown.

DataVisualizer window

Current consumption observed should be down to around 1.52 mA.

Set target power switch off: Tools > Device Programming > Tools setting

DataVisualizer window

Current consumption should have been lowered to approximately 20.7 μA.

Note that the Timer does not wake the device from Sleep mode. The application is set to exit sleep mode when the Pin Change Interrupt occurs.

Back to Top

Conclusions

This example demonstrated several different techniques to lower the power consumption of an AVR application. The power consumption can be significantly reduced by:

  • Intelligent design
  • Using sleep modes
  • Switching off unused peripherals

This example used the Atmel Studio 7 Data Visualizer and the Power Debugger Board to measure the power.

Back to Top