Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
AVR microcontroller sleep demonstrations
Simple demonstrations of putting AVR microcontrollers to sleep in power-down mode,
which results in minimum current. Coded with Arduino IDE version 1.0.4 (and with
the Arduino-Tiny core for the ATtiny MCUs, http://code.google.com/p/arduino-tiny/)
For ATmega328P, ~0.1µA.
For ATtinyX5 revisions that implement software BOD disable, ~0.1µA,
for ATtinyX5 revisions that don't, ~20µA.
/*----------------------------------------------------------------------*
* Sleep demo for ATmega328P. *
* Wire a button from digital pin 2 (INT0) to ground. *
* Wire an LED with an appropriate dropping resistor from pin D13 to *
* ground. *
* Pushing the button wakes the MCU. *
* After waking, the MCU flashes the LED, then waits 10 seconds before *
* going back to sleep. *
* *
* Jack Christensen 07May2013 *
* *
* Tested with Arduino 1.0.5 and an Arduino Uno. *
* Test conditions for all results below: *
* 5V regulated power supply, fed to the Vin pin *
* 16MHz system clock *
* Fuse bytes (L/H/E): 0xFF / 0xDE / 0x05 *
* Optiboot bootloader *
* *
* Uno R1 *
* 38mA active, 26mA with MCU in power-down mode. *
* *
* Uno SMD edition *
* 42mA active, 31mA power-down. *
* *
* Adafruit Boarduino *
* Power select jumper set to "USB", USB (FTDI) not connected. *
* 15mA active, 3mA power-down. *
* *
* Adafruit Boarduino without power LED *
* 12mA active, 0.1µA power-down. *
* *
* Breadboarded ATmega328P-PU *
* 12mA active, 0.1µA power-down. *
* *
* This work is licensed under the Creative Commons Attribution- *
* ShareAlike 3.0 Unported License. To view a copy of this license, *
* visit http://creativecommons.org/licenses/by-sa/3.0/ or send a *
* letter to Creative Commons, 171 Second Street, Suite 300, *
* San Francisco, California, 94105, USA. *
*----------------------------------------------------------------------*/
#include <avr/sleep.h>
const int LED = 13; //LED on pin 13
const unsigned long KEEP_RUNNING = 10000; //milliseconds
void setup(void)
{
//to minimize power consumption while sleeping, output pins must not source
//or sink any current. input pins must have a defined level; a good way to
//ensure this is to enable the internal pullup resistors.
for (byte i=0; i<20; i++) { //make all pins inputs with pullups enabled
pinMode(i, INPUT_PULLUP);
}
pinMode(LED, OUTPUT); //make the led pin an output
digitalWrite(LED, LOW); //drive it low so it doesn't source current
}
void loop(void)
{
for (byte i=0; i<5; i++) { //flash the LED
digitalWrite(LED, HIGH);
delay(100);
digitalWrite(LED, LOW);
delay(100);
}
delay(KEEP_RUNNING); //opportunity to measure active supply current
digitalWrite(LED, HIGH); //one blink before sleeping
delay(100);
digitalWrite(LED, LOW);
goToSleep();
}
void goToSleep(void)
{
byte adcsra = ADCSRA; //save the ADC Control and Status Register A
ADCSRA = 0; //disable the ADC
EICRA = _BV(ISC01); //configure INT0 to trigger on falling edge
EIMSK = _BV(INT0); //enable INT0
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
cli(); //stop interrupts to ensure the BOD timed sequence executes as required
sleep_enable();
//disable brown-out detection while sleeping (20-25µA)
uint8_t mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE);
uint8_t mcucr2 = mcucr1 & ~_BV(BODSE);
MCUCR = mcucr1;
MCUCR = mcucr2;
//sleep_bod_disable(); //for AVR-GCC 4.3.3 and later, this is equivalent to the previous 4 lines of code
sei(); //ensure interrupts enabled so we can wake up again
sleep_cpu(); //go to sleep
sleep_disable(); //wake up here
ADCSRA = adcsra; //restore ADCSRA
}
//external interrupt 0 wakes the MCU
ISR(INT0_vect)
{
EIMSK = 0; //disable external interrupts (only need one to wake up)
}
/*----------------------------------------------------------------------*
* Sleep demo for ATtinyX4. *
* Wire a button from pin D2 (INT0, PB2, DIP pin 5) to ground. *
* Wire an LED with an appropriate dropping resistor from pin *
* D0 (PB0, DIP pin 2) to ground. *
* Pushing the button wakes the MCU. *
* After waking, the MCU flashes the LED, then waits 10 seconds before *
* going back to sleep. *
* *
* Jack Christensen 04Nov2013 *
* *
* Developed with Arduino 1.0.5. *
* Test conditions for all results below: *
* 5V or 3.3V regulated power supply *
* 8MHz system clock (internal RC oscillator) *
* Fuse bytes (L/H/E): 0xE2 / 0xD5 / 0xFF *
* Arduino-Tiny core, http://code.google.com/p/arduino-tiny/ *
* *
* ATtiny84A-PU *
* Vcc=5V: 4.9mA active, 0.1µA power-down. *
* Vcc=3.3V: 3.1mA active, 0.1µA power-down. *
* *
* This work is licensed under the Creative Commons Attribution- *
* ShareAlike 3.0 Unported License. To view a copy of this license, *
* visit http://creativecommons.org/licenses/by-sa/3.0/ or send a *
* letter to Creative Commons, 171 Second Street, Suite 300, *
* San Francisco, California, 94105, USA. *
*----------------------------------------------------------------------*/
#include <avr/sleep.h>
const int LED_PIN = 0;
const unsigned long KEEP_RUNNING = 10000; //milliseconds
void setup(void)
{
//to minimize power consumption while sleeping, output pins must not source
//or sink any current. input pins must have a defined level; a good way to
//ensure this is to enable the internal pullup resistors.
for (byte i=0; i<11; i++) { //make all pins inputs with pullups enabled
pinMode(i, INPUT_PULLUP);
}
pinMode(LED_PIN, OUTPUT); //make the led pin an output
digitalWrite(LED_PIN, LOW); //drive it low so it doesn't source current
}
void loop(void)
{
for (byte i=0; i<5; i++) { //wake up, flash the LED
digitalWrite(LED_PIN, HIGH);
delay(100);
digitalWrite(LED_PIN, LOW);
delay(100);
}
delay(KEEP_RUNNING); //opportunity to measure active supply current
digitalWrite(LED_PIN, HIGH); //blink LED once before sleeping
delay(100);
digitalWrite(LED_PIN, LOW);
goToSleep();
}
void goToSleep(void)
{
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
MCUCR &= ~(_BV(ISC01) | _BV(ISC00)); //INT0 on low level
GIMSK |= _BV(INT0); //enable INT0
byte adcsra = ADCSRA; //save ADCSRA
ADCSRA &= ~_BV(ADEN); //disable ADC
cli(); //stop interrupts to ensure the BOD timed sequence executes as required
byte mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE); //turn off the brown-out detector
byte mcucr2 = mcucr1 & ~_BV(BODSE);
MCUCR = mcucr1;
MCUCR = mcucr2;
sei(); //ensure interrupts enabled so we can wake up again
sleep_cpu(); //go to sleep
sleep_disable(); //wake up here
ADCSRA = adcsra; //restore ADCSRA
}
//external interrupt 0 wakes the MCU
ISR(INT0_vect)
{
GIMSK = 0; //disable external interrupts (only need one to wake up)
}
/*----------------------------------------------------------------------*
* Sleep demo for ATtinyX5. *
* Wire a button from pin D2 (INT0, PB2, DIP pin 7) to ground. *
* Wire an LED with an appropriate dropping resistor from pin *
* D4 (PB4, DIP pin 3) to ground. *
* Pushing the button wakes the MCU. *
* After waking, the MCU flashes the LED, then waits 10 seconds before *
* going back to sleep. *
* *
* Jack Christensen 07May2013 *
* *
* Developed with Arduino 1.0.4. *
* Test conditions for all results below: *
* 5V regulated power supply *
* 8MHz system clock (internal RC oscillator) *
* Fuse bytes (L/H/E): 0xE2 / 0xD5 / 0xFF *
* Arduino-Tiny core, http://code.google.com/p/arduino-tiny/ *
* *
* Note that only the ATtinyX5 devices below have BOD disable *
* functionality implemented. With Vcc=5V, the BOD will draw *
* 20-25µA, depending on temperature. *
* ATtiny25, revision E, and newer *
* ATtiny45, revision D, and newer *
* ATtiny85, revision C, and newer *
* *
* ATtiny45V-10PU, Rev. G *
* 7.4mA active, 0.1µA power-down. *
* *
* ATtiny85V-10PU, Rev. B *
* 7.1mA active, 21µA power-down. *
* *
* This work is licensed under the Creative Commons Attribution- *
* ShareAlike 3.0 Unported License. To view a copy of this license, *
* visit http://creativecommons.org/licenses/by-sa/3.0/ or send a *
* letter to Creative Commons, 171 Second Street, Suite 300, *
* San Francisco, California, 94105, USA. *
*----------------------------------------------------------------------*/
#include <avr/sleep.h>
#define LED 4 //LED on pin 4, PB4, DIP pin 3
#define KEEP_RUNNING 10000 //milliseconds
#define BODS 7 //BOD Sleep bit in MCUCR
#define BODSE 2 //BOD Sleep enable bit in MCUCR
void setup(void)
{
//to minimize power consumption while sleeping, output pins must not source
//or sink any current. input pins must have a defined level; a good way to
//ensure this is to enable the internal pullup resistors.
for (byte i=0; i<5; i++) { //make all pins inputs with pullups enabled
pinMode(i, INPUT);
digitalWrite(i, HIGH);
}
pinMode(LED, OUTPUT); //make the led pin an output
digitalWrite(LED, LOW); //drive it low so it doesn't source current
}
void loop(void)
{
goToSleep();
for (byte i=0; i<5; i++) { //wake up, flash the LED
digitalWrite(LED, HIGH);
delay(100);
digitalWrite(LED, LOW);
delay(100);
}
delay(KEEP_RUNNING); //opportunity to measure active supply current
}
void goToSleep(void)
{
byte adcsra, mcucr1, mcucr2;
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
MCUCR &= ~(_BV(ISC01) | _BV(ISC00)); //INT0 on low level
GIMSK |= _BV(INT0); //enable INT0
adcsra = ADCSRA; //save ADCSRA
ADCSRA &= ~_BV(ADEN); //disable ADC
cli(); //stop interrupts to ensure the BOD timed sequence executes as required
mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE); //turn off the brown-out detector
mcucr2 = mcucr1 & ~_BV(BODSE); //if the MCU does not have BOD disable capability,
MCUCR = mcucr1; // this code has no effect
MCUCR = mcucr2;
sei(); //ensure interrupts enabled so we can wake up again
sleep_cpu(); //go to sleep
sleep_disable(); //wake up here
ADCSRA = adcsra; //restore ADCSRA
}
//external interrupt 0 wakes the MCU
ISR(INT0_vect)
{
GIMSK = 0; //disable external interrupts (only need one to wake up)
}
@mariocaptain

This comment has been minimized.

Copy link

commented Mar 26, 2015

Hi Jack,

I tried running your code on a perfboarded Atmega328P, but my best result was 138uA at POWER DOWN (during that 10 seconds power consumption is about 3mA).
There are just a few minor differences from your setup:

  • I used the internal oscillator at 8MHz
  • LED on pin digital 8 instead of 13
  • I used the USBAsp as the programmer so I did not use a bootloader
  • Fuses are: L:E2 H:DF E:06 (0x06 equivalent to 0xFE)

Could you advise where I have possibly gone wrong? I have been searching around but no clue. Are we supposed to also turn off the peripherals?

Thanks in advance.
Dave

@mariocaptain

This comment has been minimized.

Copy link

commented Mar 26, 2015

UPDATE:
I actually used yet another button on pin 3 (INT1) with the intention to use for my later experiment. I thought it did not have anything to do with it, but when I removed the button, the number came down to 71uA!
I think I those are all the differences though. Still, would you have any advice?

Dave

@mariocaptain

This comment has been minimized.

Copy link

commented Mar 26, 2015

UPDATE 2:
After double checking everything, and referring to the article Adventures in Low Power Land by SparkFun, I experiment with removing the following lines in your code:

for (byte i=0; i<20; i++) { //make all pins inputs with pullups enabled
pinMode(i, INPUT_PULLUP);
}

And it works!
My multimeter shows 0.000 mA (can't display anything below this). So I hope it is actually 0.1uA.

Thanks for the great code! And any further comment from you is greatly appreciated.

Cheers,
Dave

@JChristensen

This comment has been minimized.

Copy link
Owner Author

commented Sep 4, 2017

Late reply but without seeing all the code and the schematic, I can only speculate. Using the internal pullup resistors is definitely not an issue, and is actually recommended to ensure minimum current consumption (I do this all the time). Note that my code above calls pinMode() a second time on the LED pin, changing it to an output (and turning the pullup off). If the LED is wired from the pin to ground, and the pullup is enabled, then current will flow from the pin while the MCU sleeps.

@pittex

This comment has been minimized.

Copy link

commented Jan 26, 2018

Hello, thank you for your work.

I'm using sleep_ATtinyX5.ino and it works normally as described, but if the PB2 pin remains in the same state after the KEEP_RUNNING the program contained in the loop no longer runs.

Do you have any idea to help me?

Thanks

@Surgbc

This comment has been minimized.

Copy link

commented Feb 6, 2018

The problem is that your code and the pin connected to INT0 remaining low will take your MCU to sleep with external interrupts disabled so that it will not be able to wake up.
MCUCR &= ~(_BV(ISC01) | _BV(ISC00)); //INT0 on low level
GIMSK |= _BV(INT0); //enable INT0

GIMSK = 0;

sleep_cpu(); //go to sleep

Check the sequence of those three lines. As long as PB2 is low, the ISR is called after (//enable INT0), then ISR disables all interrupts ( GIMSK = 0 ) so that sleep_cpu() is called without interrupts enabled.

You can solve this by changing the interrupt is activated to either rising or falling edge or just any logical change, eg MCUCR &= _BV(ISC00);

@maufl

This comment has been minimized.

Copy link

commented Jul 8, 2018

Hey, I'm new to programming AVRs and found this very useful, thanks! I use the ATmega328P and the code snippet works. However, I hand to comment out the code in the interrupt service routine, otherwise it would always only wake once then had to be reset. Do you have an educated guess why?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.