Files
Arduino/Sketch_LowPower/Sketch_LowPower.ino
Jérôme Delacotte 7b30d6e298 first commit
2025-03-06 11:15:32 +01:00

373 lines
10 KiB
C++
Executable File

/*
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;
}
}