diff --git a/Examples/powerDownMoreTime_LongWatchDog/powerDownMoreTime_LongWatchDog.ino b/Examples/powerDownMoreTime_LongWatchDog/powerDownMoreTime_LongWatchDog.ino new file mode 100644 index 0000000..611dc88 --- /dev/null +++ b/Examples/powerDownMoreTime_LongWatchDog/powerDownMoreTime_LongWatchDog.ino @@ -0,0 +1,90 @@ +//Choose one of these see the samples +#define TEST_LOWPOWER +//#define TEST_LONGWATCHDOG +//#define TEST_LOWPOWER_AND_LONGWATCHDOG + +#include "LowPower.h" + +#ifndef TEST_LOWPOWER + //This is the code if you want to use the AUTOLONGRESET feature, only define AUTOLONGRESETcounterMAX. You could put a watchdog feature for minutes, even hours. + #include "avr/wdt.h" + #define AUTOLONGRESETcounterMAX 3 //Number of times of ISR(WDT_vect) to autoreset the board. I will autoreset the board after 8 secondes x AUTOLONGRESETcounterMAX + volatile boolean autoLongResetCounterEnabled = false; + volatile int autoLongResetCounter; + void ISR_RUTINE() { //This function is declared as WEAK in LowPower library, so you can override if you want. + if (autoLongResetCounterEnabled) { + autoLongResetCounter += 1; + if (autoLongResetCounter < AUTOLONGRESETcounterMAX - 1) { //-1 is because of the next lines (see: WDTCSR = 0b00001000 | 0b100001;) I will wait again 8 seconds + wdt_reset(); // Reset timer, still in interrupt mode + } else { + wdt_enabled(WDTO_8S); + MCUSR = 0; + WDTCSR |= 0b00011000; //WDCE y WDE = 1 --> config mode + WDTCSR = 0b00001000 | 0b100001; //clear WDIE (interrupt mode disabled), set WDE (reset mode enabled) and set interval to 8 seconds + //We could take out "-1" from "if" and sleep here for 64 ms, but I prefer to repeat the 8s loop again, finally I get what I want. + } + } else { + // It is only for sleep (autoLongResetCounterEnabled = false), so I copy source code from LowPower.h + // WDIE & WDIF is cleared in hardware upon entering this ISR + wdt_disable(); + } + } + + void wdt_long_enable() { + autoLongResetCounter = 0; + autoLongResetCounterEnabled = true; + cli(); //disabled ints + MCUSR = 0; //clear reset status + WDTCSR |= 0b00011000; //WDCE y WDE = 1 --> config mode + WDTCSR = 0b01000000 | 0b100001; //set WDIE (interrupt mode enabled), clear WDE (reset mode disabled) and set interval to 8 seconds + sei(); //enable ints + } + + void wdt_long_disable() { + autoLongResetCounterEnabled = false; + wdt_disable(); + } +#endif + +void setup() { +#ifndef TEST_LOWPOWER + wdt_long_disable(); +#endif + + pinMode(13, OUTPUT); + digitalWrite(13, LOW); + + Serial.begin(115200); + Serial.println("LowPower ISR weak TEST - FROM SETUP"); + delay(100); +#ifndef TEST_LOWPOWER + wdt_long_enable(); //You have to enabled it when you want. +#endif +} + +void loop() { +#ifdef TEST_LONGWATCHDOG + //In real sketch you have to remove the comment in the next line + //wdt_long_enable(); //Each loop, or each time you want, you have to reenable it to "reset" the timer, + // otherwise, after AUTOLONGRESETcounterMAX x 8 seconds, your arduino will autoreset itself. + delay(1000); + Serial.print(millis()/1000); + Serial.println(" seconds"); //After AUTOLONGRESETcounterMAX x 8 seconds you will see again the "LowPower ISR weak TEST - FROM SETUP" that is printed in SETUP, + //That is meant that the IC has autoreseted itself. +#endif +#ifdef TEST_LOWPOWER + LowPower.powerDownMoreTime(10, ADC_OFF, BOD_OFF); //Working perfectly without defining a new ISR_RUTINE + digitalWrite(13, HIGH); + delay(500); + digitalWrite(13, LOW); +#endif +#ifdef TEST_LOWPOWER_AND_LONGWATCHDOG + wdt_long_disable(); //Disable WatchDog before sleeping. + LowPower.powerDownMoreTime(10, ADC_OFF, BOD_OFF); //Working perfectly defining my new special ISR_RUTINE that can give me a LONG WATCHDOG AUTORESET feature + wdt_long_enable(); //Enabe it again + digitalWrite(13, HIGH); + delay(500); + digitalWrite(13, LOW); + delay(100000); //With this delay, it will autoreset +#endif +} diff --git a/LowPower.cpp b/LowPower.cpp index 5543bd6..448942b 100644 --- a/LowPower.cpp +++ b/LowPower.cpp @@ -1,7 +1,7 @@ /******************************************************************************* * LowPower Library -* Version: 1.60 -* Date: 01-04-2016 +* Version: 1.70 +* Date: 11-20-2017 * Author: Lim Phang Moh * Company: Rocket Scream Electronics * Website: www.rocketscream.com @@ -13,6 +13,9 @@ * * Revision Description * ======== =========== +* 1.70 Added support for ATTINY85, ATTINY84 and declare ISR_RUTINE weak for +* your own ISR_RUTINE in your sketch +* Add powerDownMoreTime function to sleep more than 8 seconds. * 1.60 Added support for ATmega256RFR2. Contributed by Rodmg. * 1.50 Fixed compiler optimization (Arduino IDE 1.6.x branch) on BOD enable * function that causes the function to be over optimized. @@ -625,6 +628,49 @@ void LowPowerClass::idle(period_t period, adc_t adc, timer5_t timer5, #endif +#if defined (__AVR_ATtiny85__) || defined (__AVR_ATtiny84__) +void LowPowerClass::idle(period_t period, adc_t adc, timer1_t timer1, timer0_t timer0, usi_t usi) +{ + // Temporary clock source variable + unsigned char clockSource = 0; + + if (adc == ADC_OFF) + { + ADCSRA &= ~(1 << ADEN); + power_adc_disable(); + } + + if (timer1 == TIMER1_OFF) power_timer1_disable(); + if (timer0 == TIMER0_OFF) power_timer0_disable(); + if (usi == USI_OFF) power_usi_disable(); + + if (period != SLEEP_FOREVER) + { + wdt_enable(period); + #if defined (__AVR_ATtiny85__) || defined (__AVR_ATtiny84__) + WDTCR |= (1 << WDIE); + #else + WDTCSR |= (1 << WDIE); + #endif + } + + lowPowerBodOn(SLEEP_MODE_IDLE); + + if (adc == ADC_OFF) + { + power_adc_enable(); + ADCSRA |= (1 << ADEN); + } + + if (timer1 == TIMER1_OFF) power_timer1_enable(); + if (timer0 == TIMER0_OFF) power_timer0_enable(); + if (usi == USI_OFF) power_usi_enable(); +} + +#endif + +#if !((defined __AVR_ATtiny85__) || (defined __AVR_ATtiny84__)) + /******************************************************************************* * Name: adcNoiseReduction * Description: Putting microcontroller into ADC noise reduction state. This is @@ -700,63 +746,6 @@ void LowPowerClass::adcNoiseReduction(period_t period, adc_t adc, #endif } -/******************************************************************************* -* Name: powerDown -* Description: Putting microcontroller into power down state. This is -* the lowest current consumption state. Use this together with -* external pin interrupt to wake up through external event -* triggering (example: RTC clockout pin, SD card detect pin). -* -* Argument Description -* ========= =========== -* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake -* up resource: -* (a) SLEEP_15MS - 15 ms sleep -* (b) SLEEP_30MS - 30 ms sleep -* (c) SLEEP_60MS - 60 ms sleep -* (d) SLEEP_120MS - 120 ms sleep -* (e) SLEEP_250MS - 250 ms sleep -* (f) SLEEP_500MS - 500 ms sleep -* (g) SLEEP_1S - 1 s sleep -* (h) SLEEP_2S - 2 s sleep -* (i) SLEEP_4S - 4 s sleep -* (j) SLEEP_8S - 8 s sleep -* (k) SLEEP_FOREVER - Sleep without waking up through WDT -* -* 2. adc ADC module disable control. Turning off the ADC module is -* basically removing the purpose of this low power mode. -* (a) ADC_OFF - Turn off ADC module -* (b) ADC_ON - Leave ADC module in its default state -* -* 3. bod Brown Out Detector (BOD) module disable control: -* (a) BOD_OFF - Turn off BOD module -* (b) BOD_ON - Leave BOD module in its default state -* -*******************************************************************************/ -void LowPowerClass::powerDown(period_t period, adc_t adc, bod_t bod) -{ - if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN); - - if (period != SLEEP_FOREVER) - { - wdt_enable(period); - WDTCSR |= (1 << WDIE); - } - if (bod == BOD_OFF) - { - #if defined __AVR_ATmega328P__ - lowPowerBodOff(SLEEP_MODE_PWR_DOWN); - #else - lowPowerBodOn(SLEEP_MODE_PWR_DOWN); - #endif - } - else - { - lowPowerBodOn(SLEEP_MODE_PWR_DOWN); - } - - if (adc == ADC_OFF) ADCSRA |= (1 << ADEN); -} /******************************************************************************* * Name: powerSave @@ -996,20 +985,101 @@ void LowPowerClass::powerExtStandby(period_t period, adc_t adc, bod_t bod, } #endif } +#endif + + +/******************************************************************************* +* Name: powerDown +* Description: Putting microcontroller into power down state. This is +* the lowest current consumption state. Use this together with +* external pin interrupt to wake up through external event +* triggering (example: RTC clockout pin, SD card detect pin). +* +* Argument Description +* ========= =========== +* 1. period Duration of low power mode. Use SLEEP_FOREVER to use other wake +* up resource: +* (a) SLEEP_15MS - 15 ms sleep +* (b) SLEEP_30MS - 30 ms sleep +* (c) SLEEP_60MS - 60 ms sleep +* (d) SLEEP_120MS - 120 ms sleep +* (e) SLEEP_250MS - 250 ms sleep +* (f) SLEEP_500MS - 500 ms sleep +* (g) SLEEP_1S - 1 s sleep +* (h) SLEEP_2S - 2 s sleep +* (i) SLEEP_4S - 4 s sleep +* (j) SLEEP_8S - 8 s sleep +* (k) SLEEP_FOREVER - Sleep without waking up through WDT +* +* 2. adc ADC module disable control. Turning off the ADC module is +* basically removing the purpose of this low power mode. +* (a) ADC_OFF - Turn off ADC module +* (b) ADC_ON - Leave ADC module in its default state +* +* 3. bod Brown Out Detector (BOD) module disable control: +* (a) BOD_OFF - Turn off BOD module +* (b) BOD_ON - Leave BOD module in its default state +* +*******************************************************************************/ +void LowPowerClass::powerDown(period_t period, adc_t adc, bod_t bod) +{ + if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN); + + if (period != SLEEP_FOREVER) + { + wdt_enable(period); + #if defined (__AVR_ATtiny85__) || defined (__AVR_ATtiny84__) + WDTCR |= (1 << WDIE); + #else + WDTCSR |= (1 << WDIE); + #endif + } + if (bod == BOD_OFF) + { + #if defined __AVR_ATmega328P__ + lowPowerBodOff(SLEEP_MODE_PWR_DOWN); + #else + lowPowerBodOn(SLEEP_MODE_PWR_DOWN); + #endif + } + else + { + lowPowerBodOn(SLEEP_MODE_PWR_DOWN); + } + + if (adc == ADC_OFF) ADCSRA |= (1 << ADEN); +} + + +void LowPowerClass::powerDownMoreTime(unsigned long seconds, adc_t adc, bod_t bod) +{ + while (seconds >= 8) {powerDown(SLEEP_8S, adc, bod); seconds -=8; } + if (seconds >= 4) {powerDown(SLEEP_4S, adc, bod); seconds -=4; } + if (seconds >= 2) {powerDown(SLEEP_2S, adc, bod); seconds -=2; } + if (seconds >= 1) {powerDown(SLEEP_1S, adc, bod); seconds -=1; } +} + + /******************************************************************************* * Name: ISR (WDT_vect) * Description: Watchdog Timer interrupt service routine. This routine is * required to allow automatic WDIF and WDIE bit clearance in -* hardware. +* hardware. You could override ISR_RUTINE in your sketch * *******************************************************************************/ -ISR (WDT_vect) -{ +__attribute__((weak)) +void ISR_RUTINE() { // WDIE & WDIF is cleared in hardware upon entering this ISR - wdt_disable(); + wdt_disable(); +} +ISR (WDT_vect) +{ + ISR_RUTINE(); } + + #elif defined (__arm__) #if defined (__SAMD21G18A__) /******************************************************************************* diff --git a/LowPower.h b/LowPower.h index 82f6f44..8df0225 100644 --- a/LowPower.h +++ b/LowPower.h @@ -115,6 +115,12 @@ enum idle_t IDLE_2 }; +enum usi_t +{ + USI_OFF, + USI_ON +}; + class LowPowerClass { public: @@ -140,14 +146,22 @@ class LowPowerClass void idle(period_t period, adc_t adc, timer4_t timer4, timer3_t timer3, timer1_t timer1, timer0_t timer0, spi_t spi, usart1_t usart1, twi_t twi, usb_t usb); + #elif (defined __AVR_ATtiny85__) || (defined __AVR_ATtiny84__) + void idle(period_t period, adc_t adc, timer1_t timer1, timer0_t timer0, usi_t usi); #else #error "Please ensure chosen MCU is either 168, 328P, 32U4, 2560 or 256RFR2." #endif + + #if !((defined __AVR_ATtiny85__) || (defined __AVR_ATtiny84__)) void adcNoiseReduction(period_t period, adc_t adc, timer2_t timer2) __attribute__((optimize("-O1"))); - void powerDown(period_t period, adc_t adc, bod_t bod) __attribute__((optimize("-O1"))); void powerSave(period_t period, adc_t adc, bod_t bod, timer2_t timer2) __attribute__((optimize("-O1"))); void powerStandby(period_t period, adc_t adc, bod_t bod) __attribute__((optimize("-O1"))); void powerExtStandby(period_t period, adc_t adc, bod_t bod, timer2_t timer2) __attribute__((optimize("-O1"))); + #endif + + void powerDown(period_t period, adc_t adc, bod_t bod) __attribute__((optimize("-O1"))); + void powerDownMoreTime(unsigned long seconds, adc_t adc, bod_t bod) __attribute__((optimize("-O1"))); + #elif defined (__arm__) diff --git a/README.md b/README.md index c241cad..c37ec12 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ### Low-Power Lightweight low power library for Arduino. -Version: 1.60 +Version: 1.70 Date: 01-04-2016 @@ -12,6 +12,8 @@ Devices Supported: * ATMega2560 * ATMega256RFR2 * ATSAMD21G18A +* ATTINY85 +* ATTINY84 ####Notes: External interrupt during standby on ATSAMD21G18A requires a patch to the Arduino SAMD Core in order for it to work. Fix is provided by this particular pull request. diff --git a/keywords.txt b/keywords.txt index c01a5b8..07134f0 100644 --- a/keywords.txt +++ b/keywords.txt @@ -13,6 +13,8 @@ idle KEYWORD2 adcNoiseReduction KEYWORD2 powerDown KEYWORD2 +powerDownMoreTime KEYWORD2 +ISR_RUTINE KEYWORD2 powerSave KEYWORD2 powerStandby KEYWORD2 powerExtStandby KEYWORD2 diff --git a/library.properties b/library.properties index 1ba34b5..f62a484 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Low-Power -version=1.6 +version=1.7 author=Rocket Scream Electronics maintainer=Rocket Scream Electronics sentence=Lightweight power management library