first commit
This commit is contained in:
372
ATMEGA_SLEEP/ATMEGA_SLEEP.ino
Executable file
372
ATMEGA_SLEEP/ATMEGA_SLEEP.ino
Executable file
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
6-13-2011
|
||||
Low Power Testing
|
||||
Spark Fun Electronics 2011
|
||||
Nathan Seidle
|
||||
|
||||
This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license).
|
||||
|
||||
This code is written to control a 7 segment display with as little power as possible, waking up only
|
||||
when the external button is hit or when the 32kHz oscillator rolls over every 8 seconds.
|
||||
|
||||
Make sure to turn off Brown out detect (uses ~16uA).
|
||||
|
||||
6-23-2011: Down to 38uA!
|
||||
|
||||
6-29-2011: 17uA, not sure why.
|
||||
|
||||
7-2-2011: Currently (hah!) at 1.13uA
|
||||
|
||||
7-4-2011: Let's wake up every 8 seconds instead of every 1 to save even more power
|
||||
Since we don't display seconds, this should be fine
|
||||
Let's reduce the system clock to 8MHz/256 so that when servicing Timer2 int, we run even lower
|
||||
We are now at ~1.05uA on average
|
||||
|
||||
*/
|
||||
#include <avr/sleep.h> //Needed for sleep_mode
|
||||
#include <avr/power.h> //Needed for powering down perihperals such as the ADC/TWI and Timers
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
int show_the_time = FALSE;
|
||||
|
||||
//For testing
|
||||
long seconds = 2317;
|
||||
int minutes = 58;
|
||||
int hours = 12;
|
||||
|
||||
int digit1 = 11; //PWM, Display pin 1
|
||||
int digit2 = 10; //PWM, Display pin 2
|
||||
int digit3 = 9; //PWM, Display pin 6
|
||||
int digit4 = 6; //PWM, Display pin 8
|
||||
|
||||
int segA = A1; //Display pin 14
|
||||
int segB = 3; //Display pin 16
|
||||
int segC = 4; //Display pin 13
|
||||
int segD = 5; //Display pin 3
|
||||
int segE = A0; //Display pin 5
|
||||
int segF = 7; //Display pin 11
|
||||
int segG = 8; //Display pin 15
|
||||
|
||||
//The very important 32.686kHz interrupt handler
|
||||
SIGNAL(TIMER2_OVF_vect){
|
||||
//seconds++;
|
||||
seconds += 8; //We sleep for 8 seconds instead of 1 to save more power
|
||||
//We don't update minutes and hours here to save power
|
||||
//Instead, we update the minutes and hours when you hit the display button
|
||||
}
|
||||
|
||||
//The interrupt occurs when you push the button
|
||||
SIGNAL(INT0_vect){
|
||||
//When you hit the button, we will need to display the time
|
||||
if(show_the_time == FALSE) show_the_time = TRUE;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
//To reduce power, setup all pins as inputs with no pullups
|
||||
for(int x = 1 ; x < 18 ; x++){
|
||||
pinMode(x, INPUT);
|
||||
digitalWrite(x, LOW);
|
||||
}
|
||||
|
||||
pinMode(2, INPUT); //This is the main button, tied to INT0
|
||||
digitalWrite(2, HIGH); //Enable internal pull up on button
|
||||
|
||||
//These pins are used to control the display
|
||||
pinMode(segA, OUTPUT);
|
||||
pinMode(segB, OUTPUT);
|
||||
pinMode(segC, OUTPUT);
|
||||
pinMode(segD, OUTPUT);
|
||||
pinMode(segE, OUTPUT);
|
||||
pinMode(segF, OUTPUT);
|
||||
pinMode(segG, OUTPUT);
|
||||
|
||||
//These are PWM pins
|
||||
pinMode(digit1, OUTPUT);
|
||||
pinMode(digit2, OUTPUT);
|
||||
pinMode(digit3, OUTPUT);
|
||||
pinMode(digit4, OUTPUT);
|
||||
|
||||
//Power down various bits of hardware to lower power usage
|
||||
set_sleep_mode(SLEEP_MODE_PWR_SAVE);
|
||||
sleep_enable();
|
||||
|
||||
//Shut off ADC, TWI, SPI, Timer0, Timer1
|
||||
|
||||
ADCSRA &= ~(1<<ADEN); //Disable ADC
|
||||
ACSR = (1<<ACD); //Disable the analog comparator
|
||||
DIDR0 = 0x3F; //Disable digital input buffers on all ADC0-ADC5 pins
|
||||
DIDR1 = (1<<AIN1D)|(1<<AIN0D); //Disable digital input buffer on AIN1/0
|
||||
|
||||
power_twi_disable();
|
||||
power_spi_disable();
|
||||
power_usart0_disable();
|
||||
power_timer0_disable(); //Needed for delay_ms
|
||||
power_timer1_disable();
|
||||
//power_timer2_disable(); //Needed for asynchronous 32kHz operation
|
||||
|
||||
//Setup TIMER2
|
||||
TCCR2A = 0x00;
|
||||
//TCCR2B = (1<<CS22)|(1<<CS20); //Set CLK/128 or overflow interrupt every 1s
|
||||
TCCR2B = (1<<CS22)|(1<<CS21)|(1<<CS20); //Set CLK/1024 or overflow interrupt every 8s
|
||||
ASSR = (1<<AS2); //Enable asynchronous operation
|
||||
TIMSK2 = (1<<TOIE2); //Enable the timer 2 interrupt
|
||||
|
||||
//Setup external INT0 interrupt
|
||||
EICRA = (1<<ISC01); //Interrupt on falling edge
|
||||
EIMSK = (1<<INT0); //Enable INT0 interrupt
|
||||
|
||||
//System clock futzing
|
||||
//CLKPR = (1<<CLKPCE); //Enable clock writing
|
||||
//CLKPR = (1<<CLKPS3); //Divid the system clock by 256
|
||||
|
||||
//Serial.begin(9600);
|
||||
//Serial.println("32kHz Testing:");
|
||||
|
||||
sei(); //Enable global interrupts
|
||||
}
|
||||
|
||||
void loop() {
|
||||
sleep_mode(); //Stop everything and go to sleep. Wake up if the Timer2 buffer overflows or if you hit the button
|
||||
|
||||
if(show_the_time == TRUE) {
|
||||
|
||||
//Update the minutes and hours variables
|
||||
minutes += seconds / 60; //Example: seconds = 2317, minutes = 58 + 38 = 96
|
||||
seconds %= 60; //seconds = 37
|
||||
hours += minutes / 60; //12 + (96 / 60) = 13
|
||||
minutes %= 60; //minutes = 36
|
||||
|
||||
//Here is where we assume 12 hour display
|
||||
while(hours > 12)
|
||||
hours -= 12;
|
||||
//while(hours > 24) hours -= 24;
|
||||
|
||||
/*Serial.print(hours, DEC);
|
||||
Serial.print(":");
|
||||
Serial.print(minutes, DEC);
|
||||
Serial.print(":");
|
||||
Serial.println(seconds, DEC);*/
|
||||
|
||||
for(int x = 0 ; x < 500 ; x++)
|
||||
displayNumber(1234);
|
||||
|
||||
digitalWrite(A5, HIGH);
|
||||
fake_msdelay(100); //Small debounce
|
||||
digitalWrite(A5, LOW);
|
||||
|
||||
show_the_time = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
//This is a not-so-accurate delay routine
|
||||
//Calling fake_msdelay(100) will delay for about 100ms
|
||||
//Assumes 8MHz clock
|
||||
void fake_msdelay(int x){
|
||||
for( ; x > 0 ; x--)
|
||||
fake_usdelay(1000);
|
||||
}
|
||||
|
||||
//This is a not-so-accurate delay routine
|
||||
//Calling fake_usdelay(100) will delay for about 100us
|
||||
//Assumes 8MHz clock
|
||||
void fake_usdelay(int x){
|
||||
for( ; x > 0 ; x--) {
|
||||
__asm__("nop\n\t");
|
||||
__asm__("nop\n\t");
|
||||
__asm__("nop\n\t");
|
||||
__asm__("nop\n\t");
|
||||
__asm__("nop\n\t");
|
||||
__asm__("nop\n\t");
|
||||
__asm__("nop\n\t");
|
||||
}
|
||||
}
|
||||
|
||||
//Given a number, we display 10:22
|
||||
//After running through the 4 numbers, the display is left turned off
|
||||
void displayNumber(int toDisplay) {
|
||||
|
||||
#define DISPLAY_BRIGHTNESS 500
|
||||
//Display brightness
|
||||
//Each digit is on for a certain amount of microseconds
|
||||
//Then it is off until we have reached a total of 20ms for the function call
|
||||
//Let's assume each digit is on for 1000us
|
||||
//If each digit is on for 1ms, there are 4 digits, so the display is off for 16ms.
|
||||
//That's a ratio of 1ms to 16ms or 6.25% on time (PWM).
|
||||
//Let's define a variable called brightness that varies from:
|
||||
//5000 blindingly bright (15.7mA current draw per digit)
|
||||
//2000 shockingly bright (11.4mA current draw per digit)
|
||||
//1000 pretty bright (5.9mA)
|
||||
//500 normal (3mA)
|
||||
//200 dim but readable (1.4mA)
|
||||
//50 dim but readable (0.56mA)
|
||||
//5 dim but readable (0.31mA)
|
||||
//1 dim but readable in dark (0.28mA)
|
||||
|
||||
|
||||
#define DIGIT_ON HIGH
|
||||
#define DIGIT_OFF LOW
|
||||
|
||||
//long beginTime = millis();
|
||||
|
||||
for(int digit = 4 ; digit > 0 ; digit--) {
|
||||
|
||||
//Turn on a digit for a short amount of time
|
||||
switch(digit) {
|
||||
case 1:
|
||||
digitalWrite(digit1, DIGIT_ON);
|
||||
break;
|
||||
case 2:
|
||||
digitalWrite(digit2, DIGIT_ON);
|
||||
break;
|
||||
case 3:
|
||||
digitalWrite(digit3, DIGIT_ON);
|
||||
break;
|
||||
case 4:
|
||||
digitalWrite(digit4, DIGIT_ON);
|
||||
break;
|
||||
}
|
||||
|
||||
//Turn on the right segments for this digit
|
||||
lightNumber(toDisplay % 10);
|
||||
toDisplay /= 10;
|
||||
|
||||
//delayMicroseconds(DISPLAY_BRIGHTNESS); //Display this digit for a fraction of a second (between 1us and 5000us, 500 is pretty good)
|
||||
fake_usdelay(1500); //Display this digit for a fraction of a second (between 1us and 5000us, 500 is pretty good)
|
||||
|
||||
//Turn off all segments
|
||||
lightNumber(10);
|
||||
|
||||
//Turn off all digits
|
||||
digitalWrite(digit1, DIGIT_OFF);
|
||||
digitalWrite(digit2, DIGIT_OFF);
|
||||
digitalWrite(digit3, DIGIT_OFF);
|
||||
digitalWrite(digit4, DIGIT_OFF);
|
||||
}
|
||||
|
||||
//while( (millis() - beginTime) < 10) ; //Wait for 20ms to pass before we paint the display again
|
||||
fake_msdelay(1);
|
||||
}
|
||||
|
||||
//Given a number, turns on those segments
|
||||
//If number == 10, then turn off number
|
||||
void lightNumber(int numberToDisplay) {
|
||||
|
||||
#define SEGMENT_ON LOW
|
||||
#define SEGMENT_OFF HIGH
|
||||
|
||||
switch (numberToDisplay){
|
||||
|
||||
case 0:
|
||||
digitalWrite(segA, SEGMENT_ON);
|
||||
digitalWrite(segB, SEGMENT_ON);
|
||||
digitalWrite(segC, SEGMENT_ON);
|
||||
digitalWrite(segD, SEGMENT_ON);
|
||||
digitalWrite(segE, SEGMENT_ON);
|
||||
digitalWrite(segF, SEGMENT_ON);
|
||||
digitalWrite(segG, SEGMENT_OFF);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
digitalWrite(segA, SEGMENT_OFF);
|
||||
digitalWrite(segB, SEGMENT_ON);
|
||||
digitalWrite(segC, SEGMENT_ON);
|
||||
digitalWrite(segD, SEGMENT_OFF);
|
||||
digitalWrite(segE, SEGMENT_OFF);
|
||||
digitalWrite(segF, SEGMENT_OFF);
|
||||
digitalWrite(segG, SEGMENT_OFF);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
digitalWrite(segA, SEGMENT_ON);
|
||||
digitalWrite(segB, SEGMENT_ON);
|
||||
digitalWrite(segC, SEGMENT_OFF);
|
||||
digitalWrite(segD, SEGMENT_ON);
|
||||
digitalWrite(segE, SEGMENT_ON);
|
||||
digitalWrite(segF, SEGMENT_OFF);
|
||||
digitalWrite(segG, SEGMENT_ON);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
digitalWrite(segA, SEGMENT_ON);
|
||||
digitalWrite(segB, SEGMENT_ON);
|
||||
digitalWrite(segC, SEGMENT_ON);
|
||||
digitalWrite(segD, SEGMENT_ON);
|
||||
digitalWrite(segE, SEGMENT_OFF);
|
||||
digitalWrite(segF, SEGMENT_OFF);
|
||||
digitalWrite(segG, SEGMENT_ON);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
digitalWrite(segA, SEGMENT_OFF);
|
||||
digitalWrite(segB, SEGMENT_ON);
|
||||
digitalWrite(segC, SEGMENT_ON);
|
||||
digitalWrite(segD, SEGMENT_OFF);
|
||||
digitalWrite(segE, SEGMENT_OFF);
|
||||
digitalWrite(segF, SEGMENT_ON);
|
||||
digitalWrite(segG, SEGMENT_ON);
|
||||
break;
|
||||
|
||||
case 5:
|
||||
digitalWrite(segA, SEGMENT_ON);
|
||||
digitalWrite(segB, SEGMENT_OFF);
|
||||
digitalWrite(segC, SEGMENT_ON);
|
||||
digitalWrite(segD, SEGMENT_ON);
|
||||
digitalWrite(segE, SEGMENT_OFF);
|
||||
digitalWrite(segF, SEGMENT_ON);
|
||||
digitalWrite(segG, SEGMENT_ON);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
digitalWrite(segA, SEGMENT_ON);
|
||||
digitalWrite(segB, SEGMENT_OFF);
|
||||
digitalWrite(segC, SEGMENT_ON);
|
||||
digitalWrite(segD, SEGMENT_ON);
|
||||
digitalWrite(segE, SEGMENT_ON);
|
||||
digitalWrite(segF, SEGMENT_ON);
|
||||
digitalWrite(segG, SEGMENT_ON);
|
||||
break;
|
||||
|
||||
case 7:
|
||||
digitalWrite(segA, SEGMENT_ON);
|
||||
digitalWrite(segB, SEGMENT_ON);
|
||||
digitalWrite(segC, SEGMENT_ON);
|
||||
digitalWrite(segD, SEGMENT_OFF);
|
||||
digitalWrite(segE, SEGMENT_OFF);
|
||||
digitalWrite(segF, SEGMENT_OFF);
|
||||
digitalWrite(segG, SEGMENT_OFF);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
digitalWrite(segA, SEGMENT_ON);
|
||||
digitalWrite(segB, SEGMENT_ON);
|
||||
digitalWrite(segC, SEGMENT_ON);
|
||||
digitalWrite(segD, SEGMENT_ON);
|
||||
digitalWrite(segE, SEGMENT_ON);
|
||||
digitalWrite(segF, SEGMENT_ON);
|
||||
digitalWrite(segG, SEGMENT_ON);
|
||||
break;
|
||||
|
||||
case 9:
|
||||
digitalWrite(segA, SEGMENT_ON);
|
||||
digitalWrite(segB, SEGMENT_ON);
|
||||
digitalWrite(segC, SEGMENT_ON);
|
||||
digitalWrite(segD, SEGMENT_ON);
|
||||
digitalWrite(segE, SEGMENT_OFF);
|
||||
digitalWrite(segF, SEGMENT_ON);
|
||||
digitalWrite(segG, SEGMENT_ON);
|
||||
break;
|
||||
|
||||
case 10:
|
||||
digitalWrite(segA, SEGMENT_OFF);
|
||||
digitalWrite(segB, SEGMENT_OFF);
|
||||
digitalWrite(segC, SEGMENT_OFF);
|
||||
digitalWrite(segD, SEGMENT_OFF);
|
||||
digitalWrite(segE, SEGMENT_OFF);
|
||||
digitalWrite(segF, SEGMENT_OFF);
|
||||
digitalWrite(segG, SEGMENT_OFF);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user