-
-
Save JChristensen/5616922 to your computer and use it in GitHub Desktop.
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) | |
} |
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?
I have been having issues where the device does not wake up if noise is present during execution.
I have moved all of void loop to void sleep
void loop(void)
{
goToSleep();
}
void goToSleep(void)
{
delay(1000); //opportunity to measure active supply current
digitalWrite(LED, HIGH); //one blink before sleeping
delay(1000);
digitalWrite(LED, LOW);
byte adcsra = ADCSRA; //save the ADC Control and Status Register A
ADCSRA = 0; //disable the ADC
EICRA = _BV(ISC00); //configure INT0 to trigger on change
and changed interrupts at the end so it can trigger even if switched rapidly
//external interrupt 0 wakes the MCU ISR(INT0_vect) { EIMSK = _BV(INT0); }
I think it works now
@SixProjects That seems like a software solution for a hardware problem. Without knowing all the particulars, perhaps that's a reasonable approach but my first thought would be to address the noise.
Thank you for sharing.
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 thatsleep_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);