arduino/libraries/Adafruit_SleepyDog_Library/utility/WatchdogAVR.cpp
2018-11-03 20:21:33 +00:00

138 lines
4.0 KiB
C++

// Be careful to use a platform-specific conditional include to only make the
// code visible for the appropriate platform. Arduino will try to compile and
// link all .cpp files regardless of platform.
#if defined(ARDUINO_ARCH_AVR) || defined(__AVR__)
#include <avr/interrupt.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include "WatchdogAVR.h"
// Define watchdog timer interrupt.
ISR(WDT_vect)
{
// Nothing needs to be done, however interrupt handler must be defined to
// prevent a reset.
}
int WatchdogAVR::enable(int maxPeriodMS) {
// Pick the closest appropriate watchdog timer value.
int actualMS;
_setPeriod(maxPeriodMS, _wdto, actualMS);
// Enable the watchdog and return the actual countdown value.
wdt_enable(_wdto);
return actualMS;
}
void WatchdogAVR::reset() {
// Reset the watchdog.
wdt_reset();
}
void WatchdogAVR::disable() {
// Disable the watchdog and clear any saved watchdog timer value.
wdt_disable();
_wdto = -1;
}
int WatchdogAVR::sleep(int maxPeriodMS) {
// Pick the closest appropriate watchdog timer value.
int sleepWDTO, actualMS;
_setPeriod(maxPeriodMS, sleepWDTO, actualMS);
// Build watchdog prescaler register value before timing critical code.
uint8_t wdps = ((sleepWDTO & 0x08 ? 1 : 0) << WDP3) |
((sleepWDTO & 0x04 ? 1 : 0) << WDP2) |
((sleepWDTO & 0x02 ? 1 : 0) << WDP1) |
((sleepWDTO & 0x01 ? 1 : 0) << WDP0);
// The next section is timing critical so interrupts are disabled.
cli();
// First clear any previous watchdog reset.
MCUSR &= ~(1<<WDRF);
// Now change the watchdog prescaler and interrupt enable bit so the watchdog
// reset only triggers the interrupt (and wakes from deep sleep) and not a
// full device reset. This is a timing critical section of code that must
// happen in 4 cycles.
WDTCSR |= (1<<WDCE) | (1<<WDE); // Set WDCE and WDE to enable changes.
WDTCSR = wdps; // Set the prescaler bit values.
WDTCSR |= (1<<WDIE); // Enable only watchdog interrupts.
// Critical section finished, re-enable interrupts.
sei();
// Disable USB if it exists
#ifdef USBCON
USBCON |= _BV(FRZCLK); //freeze USB clock
PLLCSR &= ~_BV(PLLE); // turn off USB PLL
USBCON &= ~_BV(USBE); // disable USB
#endif
// Set full power-down sleep mode and go to sleep.
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_mode();
// Chip is now asleep!
// Once awakened by the watchdog execution resumes here. Start by disabling
// sleep.
sleep_disable();
// Check if the user had the watchdog enabled before sleep and re-enable it.
if (_wdto != -1) {
wdt_enable(_wdto);
}
// Return how many actual milliseconds were spent sleeping.
return actualMS;
}
void WatchdogAVR::_setPeriod(int maxMS, int &wdto, int &actualMS) {
// Note the order of these if statements from highest to lowest is
// important so that control flow cascades down to the right value based
// on its position in the range of discrete timeouts.
if ((maxMS >= 8000) || (maxMS == 0)) {
wdto = WDTO_8S;
actualMS = 8000;
}
else if (maxMS >= 4000) {
wdto = WDTO_4S;
actualMS = 4000;
}
else if (maxMS >= 2000) {
wdto = WDTO_2S;
actualMS = 2000;
}
else if (maxMS >= 1000) {
wdto = WDTO_1S;
actualMS = 1000;
}
else if (maxMS >= 500) {
wdto = WDTO_500MS;
actualMS = 500;
}
else if (maxMS >= 250) {
wdto = WDTO_250MS;
actualMS = 250;
}
else if (maxMS >= 120) {
wdto = WDTO_120MS;
actualMS = 120;
}
else if (maxMS >= 60) {
wdto = WDTO_60MS;
actualMS = 60;
}
else if (maxMS >= 30) {
wdto = WDTO_30MS;
actualMS = 30;
}
else {
wdto = WDTO_15MS;
actualMS = 15;
}
}
#endif