Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for ATTINY84, ATTINY85, new function powerDownMoreTime and declare WEAK an ISR_RUTINE fucntion for WatchDog feature #47

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -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
}
196 changes: 133 additions & 63 deletions LowPower.cpp
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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__)
/*******************************************************************************
Expand Down
16 changes: 15 additions & 1 deletion LowPower.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ enum idle_t
IDLE_2
};

enum usi_t
{
USI_OFF,
USI_ON
};

class LowPowerClass
{
public:
Expand All @@ -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__)

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
### Low-Power
Lightweight low power library for Arduino.

Version: 1.60
Version: 1.70

Date: 01-04-2016

Expand All @@ -12,6 +12,8 @@ Devices Supported:
* ATMega2560
* ATMega256RFR2
* ATSAMD21G18A
* ATTINY85
* ATTINY84

####Notes:
External interrupt during standby on ATSAMD21G18A requires a patch to the <a href="https://github.com/arduino/ArduinoCore-samd">Arduino SAMD Core</a> in order for it to work. Fix is provided by this particular <a href="https://github.com/arduino/ArduinoCore-samd/pull/90">pull request</a>.
2 changes: 2 additions & 0 deletions keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
idle KEYWORD2
adcNoiseReduction KEYWORD2
powerDown KEYWORD2
powerDownMoreTime KEYWORD2
ISR_RUTINE KEYWORD2
powerSave KEYWORD2
powerStandby KEYWORD2
powerExtStandby KEYWORD2
Expand Down
Loading