AVR Low Power Example Project
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
- Microchip Studio
- ATmega328PB Xplained Mini
- Power Debugger Kit
- Two micro USB Cables
- Three female to male wires
- One male to male wire
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.
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.
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.
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.
Figure 1-1 Power Debugger and ATmega328PB Xplained Mini connection.
Hardware Configuration
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.
When the Device Selection window appears, enter "328P" into the search window and then select the Atmega328PB. Click OK.
A project will be created containing an empty while(1) loop in main().
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.
Under Tool, Select mEDBG and debugWIRE
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:
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.
ISR(TIMER0_OVF_vect){
count++;
if(count==20){
count=0;
// Toogle LED
PORTB=PORTB ^ 0x20;
}
}
The complete program is as follows,
#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();
}
Verify the Project Runs
Build the project and program the device by selecting Debug > Continue.
Ensure debugging for the project is terminated by selecting Debug > Disable debugWire then Close.
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.
Select Power and Start
Turn off the target Power Supply to the Xplained board and enable power measurement
Close the Device Programming
Verify that the average current consumption is about 2.6 mA
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.
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 <avr/power.h>
Add accompanying code to optimize_power_consumption().
PPR0 = 0xDF;
PPR1 = 0x3F;
#include <avr/wdt.h>
Add the following code in optimize_power_consumption() to turn off watchdog timer.
Cli();
Wdt_reset();
MCUSR&= ~(1<<WDRF);
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)
DDRB &= 0xE0;
DDRC &= 0xC9;
DDRD &= 0x03;
DDRE &= 0xF3;
PORTB |= ~(0xE0);
PORTC |= ~(0xC9);
PORTD |= ~(0x03);
PORTE |= ~(0xF3);
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.
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(SLEEP_MODE_PWR_DOWN);
Call the function sleep_mode() from main() to enter Sleep mode.
{
sleep_mode();
}
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:
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.
{
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();
}
}
Program the application and measure power consumption
Current consumption observed should be down to around 1.52 mA.
Set target power switch off: Tools > Device Programming > Tools setting
Current consumption should have been lowered to approximately 20.7 μA.
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.