Saturday, December 11, 2010

atmega*8 Hardware Sleep-Wake Toggle

While working on some holiday projects, I needed on off switches for some JeeNode based widgets. It took me a while to get it working well, and I learned some stuff while doing it. Here is the software and a explanation of the hardware setup. Enjoy.


/*
SleepWakeToggle

SUMMARY
Toggles an atmega*8 state from running to lowest power sleep
mode using a hardware interrupt, e.g. a switch. Suitable for
Arduino, JeeNode, etc.

WHY
Battery powered devices last double-plus-much-longer if you power
the chip down when not in use.

HOW IT WORKS
Interrupts are a way of having your program flow altered by simple
real world events (e.g. passage of time or activation of switches).

This program demonstrates using a single switch to accomplish two
things: putting the chip in sleep mode and then waking it up. When
a switch is activated, an interrupt is triggered and program
control is shifted to a function you specify (a callback). Two
hardware interrupts are available INT0 and INT1.

Boldly rebinding INT0/INT1 callbacks to the wake and sleep functions
in the callbacks themselves is bad magic, and doesn't seem to work
:-). So the sleep function is called from loop() after a callback
sets a flag. This way the callback which triggers sleep (here:
'toggle') is done before 'void sleep()' actually runs. This is good
because, the interrupt has to be rebound to 'wake' just before going
in to sleep_mode.

HARDWARE SETUP
Lowest power sleep mode, SLEEP_MODE_PWR_DOWN, can only respond to a
pin going from high to low. Hence, tie desired pin (2 or 3) to +
with a resistor (i.e. a "pull-up" resistor), and connect a momentary
switch to ground and the pin.

SOFTWARE SETUP
You will want to provide functions that do something interesting and
that cause the system to be in your desired state before actually
powering down. These are: "void importantThing()" and "void
prepareToSleep()" below. The state that is achieved after power up
or reset is selected by setting the global flag buttonPushed in the
setup function.

Hereby placed in the public domain, December 2010
by Kael Fischer <kael.fischer@gmail.com>

*/

// hardware interrupt stuff
#include <avr/sleep.h>
#define INT0 0
#define INT0_PIN 2
#define INT1 1
#define INT1_PIN 3

// application specific stuff
#define BLINK_PIN 13

// global flags
int buttonPushed;

void importantThing(){
// the 'work' the program does
digitalWrite(BLINK_PIN, HIGH);
delay(1000);
digitalWrite(BLINK_PIN, LOW);
delay(1000);
}

void prepareToSleep(){
// When a button is pushed
// do some prep before sleeping
// to get in the desired state
digitalWrite(BLINK_PIN, LOW);
}

void wake(){

// This is the callback that runs when the
// chip is waking up.
buttonPushed=0;

// Can set variables and check stuff. Cannot do fancy stuff.
// Program flow returns to line after 'sleep_mode()' in sleep.

// No code is required here.
}

void toggle(){
// set a flag telling system to sleep on next loop iteration.
// this is the normal running state callback triggered by the
// switch.
detachInterrupt(1); // this seems to be allowed in callback
if (buttonPushed ==0){ // just in case, ignore switch if already
buttonPushed=1; // doing it
}
}

void sleep(){
prepareToSleep(); //application specific

// YOU ARE GETTING SLEEPY.....
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
attachInterrupt(INT1,wake,LOW); // rebind interrupt
sleep_mode(); // sleeping

// program returns here, after 'wake' callback

// now back to loop
}

void setup() {
// On Off Toggle setup
pinMode(INT1_PIN, INPUT); // set switch pin to input
// to limit current
attachInterrupt(INT1,toggle,LOW);
buttonPushed=1; // set to 1 = sleep after reset
// 0 = run loop after reset

// app specific setup here
pinMode(BLINK_PIN, OUTPUT);
}

void loop() {
if (buttonPushed == 1) {
sleep();
} else {
// reset button events to sleep
attachInterrupt(INT1,toggle,LOW);
}
// do important things
importantThing();
}

No comments:

Post a Comment