Чуть правленные исходники v3.11 1.11: изменен делитель тактирования spi + частота кварца для мастер станции

This commit is contained in:
2025-10-03 09:48:30 +03:00
parent df18fedc7f
commit 3f886d0f98
94 changed files with 19758 additions and 0 deletions
@@ -0,0 +1,4 @@
#include "Adafruit_SleepyDog.h"
// Global instance of the main class for sketches to use.
WatchdogType Watchdog;
@@ -0,0 +1,28 @@
#ifndef ADAFRUIT_SLEEPYDOG_H
#define ADAFRUIT_SLEEPYDOG_H
// Platform-specific code goes below. Each #ifdef should check for the presence
// of their platform and pull in the appropriate watchdog implementation type,
// then typedef it to WatchdogType so the .cpp file can create a global instance.
#if defined(ARDUINO_ARCH_AVR) || defined(__AVR__)
#include "utility/WatchdogAVR.h"
typedef WatchdogAVR WatchdogType;
#elif defined(ARDUINO_ARCH_SAMD)
// Arduino Zero / ATSAMD series CPU watchdog support.
#include "utility/WatchdogSAMD.h"
typedef WatchdogSAMD WatchdogType;
#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
// Teensy 3.x watchdog support.
#include "utility/WatchdogKinetisK.h"
typedef WatchdogKinetisKseries WatchdogType;
#elif defined(__MKL26Z64__)
// Teensy LC watchdog support.
#include "utility/WatchdogKinetisL.h"
typedef WatchdogKinetisLseries WatchdogType;
#else
#error Unsupported platform for the Adafruit Watchdog library!
#endif
extern WatchdogType Watchdog;
#endif
@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Adafruit Industries
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,13 @@
# Adafruit SleepyDog Arduino Library
Arduino library to use the watchdog timer for system reset and low power sleep.
Currently supports the following hardware:
* Arduino Uno or other ATmega328P-based boards.
* Arduino Mega or other ATmega2560- or 1280-based boards.
* Arduino Zero, Adafruit Feather M0 (ATSAMD21).
* Arduino Leonardo or other 32u4-based boards (e.g. Adafruit Feather) WITH CAVEAT: USB Serial connection is clobbered on sleep; if sketch does not require Serial comms, this is not a concern. The example sketches all print to Serial and appear frozen, but the logic does otherwise continue to run. You can restore the USB serial connection after waking up using `USBDevice.attach();` and then reconnect to USB serial from the host machine.
* Partial support for Teensy 3.X and LC (watchdog, no sleep).
Adafruit Trinket and other boards using ATtiny MCUs are NOT supported.
@@ -0,0 +1,63 @@
// Adafruit Watchdog Library Basic Usage Example
//
// Simple example of how to use the watchdog library.
//
// Author: Tony DiCola
#include <Adafruit_SleepyDog.h>
void setup() {
Serial.begin(115200);
while(!Serial); // wait for Arduino Serial Monitor (native USB boards)
Serial.println("Adafruit Watchdog Library Demo!");
Serial.println();
// First a normal example of using the watchdog timer.
// Enable the watchdog by calling Watchdog.enable() as below.
// This will turn on the watchdog timer with a ~4 second timeout
// before reseting the Arduino. The estimated actual milliseconds
// before reset (in milliseconds) is returned.
// Make sure to reset the watchdog before the countdown expires or
// the Arduino will reset!
int countdownMS = Watchdog.enable(4000);
Serial.print("Enabled the watchdog with max countdown of ");
Serial.print(countdownMS, DEC);
Serial.println(" milliseconds!");
Serial.println();
// Now loop a few times and periodically reset the watchdog.
Serial.println("Looping ten times while resetting the watchdog...");
for(int i = 1; i <= 10; ++i) {
Serial.print("Loop #"); Serial.println(i, DEC);
delay(1000);
// Reset watchdog with every loop to make sure the sketch keeps running.
// If you comment out this call watch what happens in about 4 iterations!
Watchdog.reset();
}
Serial.println();
// Disable the watchdog entirely by calling Watchdog.disable();
Watchdog.disable();
// Finally demonstrate the watchdog resetting by enabling it for a shorter
// period of time and waiting a long time without a reset. Notice you can
// pass a _maximum_ countdown time (in milliseconds) to the enable call.
// The library will try to use that value as the countdown, but it might
// pick a smaller value if the hardware doesn't support it. The actual
// countdown value will be returned so you can see what it is.
countdownMS = Watchdog.enable(4000);
Serial.print("Get ready, the watchdog will reset in ");
Serial.print(countdownMS, DEC);
Serial.println(" milliseconds!");
Serial.println();
delay(countdownMS+1000);
// Execution will never get here because the watchdog resets the Arduino!
}
void loop() {
// We'll never actually get to the loop because the watchdog will reset in
// the setup function.
Serial.println("You shouldn't see this message.");
delay(1000);
}
@@ -0,0 +1,57 @@
// Adafruit Watchdog Library Sleep Example
//
// Simple example of how to do low power sleep with the watchdog timer.
//
// Author: Tony DiCola
#include <Adafruit_SleepyDog.h>
void setup() {
// For boards with "native" USB support (e.g. not using an FTDI chip or
// similar serial bridge), Serial connection may be lost on sleep/wake,
// and you might not see the "I'm awake" messages. Use the onboard LED
// as an alternate indicator -- the code turns it on when awake, off
// before going to sleep.
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH); // Show we're awake
Serial.begin(115200);
while(!Serial); // wait for Arduino Serial Monitor (native USB boards)
Serial.println("Adafruit Watchdog Library Sleep Demo!");
Serial.println();
}
void loop() {
Serial.println("Going to sleep in one second...");
delay(1000);
// To enter low power sleep mode call Watchdog.sleep() like below
// and the watchdog will allow low power sleep for as long as possible.
// The actual amount of time spent in sleep will be returned (in
// milliseconds).
digitalWrite(LED_BUILTIN, LOW); // Show we're asleep
int sleepMS = Watchdog.sleep();
// Alternatively you can provide a millisecond value to specify
// how long you'd like the chip to sleep, but the hardware only
// supports a limited range of values so the actual sleep time might
// be smaller. The time spent in sleep will be returned (in
// milliseconds).
// int sleepMS = Watchdog.sleep(1000); // Sleep for up to 1 second.
// Code resumes here on wake.
digitalWrite(LED_BUILTIN, HIGH); // Show we're awake again
// Try to reattach USB connection on "native USB" boards (connection is
// lost on sleep). Host will also need to reattach to the Serial monitor.
// Seems not entirely reliable, hence the LED indicator fallback.
#ifdef USBCON
USBDevice.attach();
#endif
Serial.print("I'm awake now! I slept for ");
Serial.print(sleepMS, DEC);
Serial.println(" milliseconds.");
Serial.println();
}
@@ -0,0 +1,9 @@
name=Adafruit SleepyDog Library
version=1.1.2
author=Adafruit
maintainer=Adafruit <info@adafruit.com>
sentence=Arduino library to use the watchdog timer for system reset and low power sleep.
paragraph=Arduino library to use the watchdog timer for system reset and low power sleep.
category=Other
url=https://github.com/adafruit/Adafruit_SleepyDog
architectures=*
@@ -0,0 +1,125 @@
// 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 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
@@ -0,0 +1,47 @@
#ifndef WATCHDOGAVR_H
#define WATCHDOGAVR_H
class WatchdogAVR {
public:
WatchdogAVR():
_wdto(-1)
{}
// Enable the watchdog timer to reset the machine after a period of time
// without any calls to reset(). The passed in period (in milliseconds) is
// just a suggestion and a lower value might be picked if the hardware does
// not support the exact desired value.
//
// The actual period (in milliseconds) before a watchdog timer reset is
// returned.
int enable(int maxPeriodMS = 0);
// Reset or 'kick' the watchdog timer to prevent a reset of the device.
void reset();
// Completely disable the watchdog timer.
void disable();
// Enter the lowest power sleep mode (using the watchdog timer) for the
// desired period of time. The passed in period (in milliseconds) is
// just a suggestion and a lower value might be picked if the hardware does
// not support the exact desired value
//
// The actual period (in milliseconds) that the hardware was asleep will be
// returned.
int sleep(int maxPeriodMS = 0);
private:
// Pick the closest (but not higher) watchdog timer value from the provided
// maximum period. Sets wdto to the chosen period value suitable for
// passing to wdt_enable(), and actualMS to the chosen period value in
// milliseconds. A max value of 0 will pick the longest value possible.
void _setPeriod(int maxMS, int &wdto, int &actualMS);
// Keep the last selected watchdog timer period so that the watchdog can be
// re-enabled at that rate after sleep. A value of -1 means no watchdog
// timer was enabled.
int _wdto;
};
#endif
@@ -0,0 +1,104 @@
// 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(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
#include <kinetis.h>
#include "WatchdogKinetisK.h"
static void one_bus_cycle(void) __attribute__((always_inline));
static void watchdog_config(int cfg, int val);
// Enable the watchdog timer to reset the machine after a period of time
// without any calls to reset(). The passed in period (in milliseconds) is
// just a suggestion and a lower value might be picked if the hardware does
// not support the exact desired value.
//
// The actual period (in milliseconds) before a watchdog timer reset is
// returned.
int WatchdogKinetisKseries::enable(int maxPeriodMS)
{
if (maxPeriodMS < 4) {
maxPeriodMS = 8000; // default is 8 seconds
}
if (setting != maxPeriodMS) {
watchdog_config(WDOG_STCTRLH_WDOGEN, maxPeriodMS);
setting = maxPeriodMS;
}
return maxPeriodMS;
}
// Reset or 'kick' the watchdog timer to prevent a reset of the device.
void WatchdogKinetisKseries::reset()
{
__disable_irq();
WDOG_REFRESH = 0xA602;
WDOG_REFRESH = 0xB480;
__enable_irq();
}
// Completely disable the watchdog timer.
void WatchdogKinetisKseries::disable()
{
if (setting > 0) {
watchdog_config(0, 4);
setting = 0;
}
}
// Enter the lowest power sleep mode for the desired period of time. The
// passed in period (in milliseconds) is just a suggestion and a lower value
// might be picked if the hardware does not support the exact desired value
//
// The actual period (in milliseconds) that the hardware was asleep will be
// returned.
int WatchdogKinetisKseries::sleep(int maxPeriodMS)
{
if (maxPeriodMS <= 0) return 0;
// TODO....
return 0;
}
static void watchdog_config(int cfg, int val)
{
__disable_irq();
WDOG_UNLOCK = WDOG_UNLOCK_SEQ1;
WDOG_UNLOCK = WDOG_UNLOCK_SEQ2;
one_bus_cycle();
WDOG_STCTRLH = cfg | WDOG_STCTRLH_ALLOWUPDATE;
WDOG_TOVALH = val >> 16;
WDOG_TOVALL = val;
WDOG_PRESC = 0;
__enable_irq();
for (int i=0; i < 256; i++) {
one_bus_cycle();
}
}
static void one_bus_cycle(void)
{
__asm__ volatile ("nop");
#if (F_CPU / F_BUS) > 1
__asm__ volatile ("nop");
#endif
#if (F_CPU / F_BUS) > 2
__asm__ volatile ("nop");
#endif
#if (F_CPU / F_BUS) > 3
__asm__ volatile ("nop");
#endif
#if (F_CPU / F_BUS) > 4
__asm__ volatile ("nop");
#endif
#if (F_CPU / F_BUS) > 5
__asm__ volatile ("nop");
#endif
#if (F_CPU / F_BUS) > 6
__asm__ volatile ("nop");
#endif
#if (F_CPU / F_BUS) > 7
__asm__ volatile ("nop");
#endif
}
#endif
@@ -0,0 +1,38 @@
#ifndef WATCHDOGKINETISK_H
#define WATCHDOGKINETISK_H
class WatchdogKinetisKseries {
public:
WatchdogKinetisKseries(): setting(0) {}
// Enable the watchdog timer to reset the machine after a period of time
// without any calls to reset(). The passed in period (in milliseconds) is
// just a suggestion and a lower value might be picked if the hardware does
// not support the exact desired value.
//
// The actual period (in milliseconds) before a watchdog timer reset is
// returned.
int enable(int maxPeriodMS = 0);
// Reset or 'kick' the watchdog timer to prevent a reset of the device.
void reset();
// Completely disable the watchdog timer.
void disable();
// Enter the lowest power sleep mode (using the watchdog timer) for the
// desired period of time. The passed in period (in milliseconds) is
// just a suggestion and a lower value might be picked if the hardware does
// not support the exact desired value
//
// The actual period (in milliseconds) that the hardware was asleep will be
// returned.
//
// NOTE: This is currently not implemented on the SAMD21!
int sleep(int maxPeriodMS = 0);
private:
int setting;
};
#endif
@@ -0,0 +1,74 @@
// 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(__MKL26Z64__)
#include <kinetis.h>
#include "WatchdogKinetisL.h"
// Normally the watchdog is disabled at startup. This removes the startup
// code. The watchdog will be active with 1024 ms timeout. Hopefully the
// user will configure the watchdog and begin resetting it before it causes
// a reboot. There is no way to start up without the watchdog and then
// enable it later...
extern "C" void startup_early_hook(void) {}
// Enable the watchdog timer to reset the machine after a period of time
// without any calls to reset(). The passed in period (in milliseconds) is
// just a suggestion and a lower value might be picked if the hardware does
// not support the exact desired value.
//
// The actual period (in milliseconds) before a watchdog timer reset is
// returned.
int WatchdogKinetisLseries::enable(int maxPeriodMS)
{
// The watchdog can only be programmed once. Then it's forever
// locked to this setting (until the chip reboots).
if (maxPeriodMS <= 0 || maxPeriodMS > 256) {
SIM_COPC = 12;
} else if (maxPeriodMS > 32) {
SIM_COPC = 8;
} else {
SIM_COPC = 4;
}
// Read the actual setting.
int val = SIM_COPC & 12;
if (val == 12) return 1024;
if (val == 8) return 256;
return 32;
}
// Reset or 'kick' the watchdog timer to prevent a reset of the device.
void WatchdogKinetisLseries::reset()
{
__disable_irq();
SIM_SRVCOP = 0x55;
SIM_SRVCOP = 0xAA;
__enable_irq();
}
// Completely disable the watchdog timer.
void WatchdogKinetisLseries::disable()
{
// no can do....
// The watchdog timer in this chip is write-once.
// The chip boots up with the watchdog at 1024 ms.
// You only get to configure it once. Then it
// remains locked to that setting, until a reboot.
}
// Enter the lowest power sleep mode for the desired period of time. The
// passed in period (in milliseconds) is just a suggestion and a lower value
// might be picked if the hardware does not support the exact desired value
//
// The actual period (in milliseconds) that the hardware was asleep will be
// returned.
int WatchdogKinetisLseries::sleep(int maxPeriodMS)
{
if (maxPeriodMS <= 0) return 0;
// TODO....
return 0;
}
#endif
@@ -0,0 +1,35 @@
#ifndef WATCHDOGKINETISL_H
#define WATCHDOGKINETISL_H
class WatchdogKinetisLseries {
public:
WatchdogKinetisLseries() {}
// Enable the watchdog timer to reset the machine after a period of time
// without any calls to reset(). The passed in period (in milliseconds) is
// just a suggestion and a lower value might be picked if the hardware does
// not support the exact desired value.
//
// The actual period (in milliseconds) before a watchdog timer reset is
// returned.
int enable(int maxPeriodMS = 0);
// Reset or 'kick' the watchdog timer to prevent a reset of the device.
void reset();
// Completely disable the watchdog timer.
void disable();
// Enter the lowest power sleep mode (using the watchdog timer) for the
// desired period of time. The passed in period (in milliseconds) is
// just a suggestion and a lower value might be picked if the hardware does
// not support the exact desired value
//
// The actual period (in milliseconds) that the hardware was asleep will be
// returned.
//
// NOTE: This is currently not implemented on the SAMD21!
int sleep(int maxPeriodMS = 0);
};
#endif
@@ -0,0 +1,240 @@
// Requires Adafruit_ASFcore library!
// 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_SAMD)
#include <sam.h>
#include "WatchdogSAMD.h"
int WatchdogSAMD::enable(int maxPeriodMS, bool isForSleep) {
// Enable the watchdog with a period up to the specified max period in
// milliseconds.
// Review the watchdog section from the SAMD21 datasheet section 17:
// http://www.atmel.com/images/atmel-42181-sam-d21_datasheet.pdf
int cycles;
uint8_t bits;
if(!_initialized) _initialize_wdt();
#if defined(__SAMD51__)
WDT->CTRLA.reg = 0; // Disable watchdog for config
while(WDT->SYNCBUSY.reg);
#else
WDT->CTRL.reg = 0; // Disable watchdog for config
while(WDT->STATUS.bit.SYNCBUSY);
#endif
// You'll see some occasional conversion here compensating between
// milliseconds (1000 Hz) and WDT clock cycles (~1024 Hz). The low-
// power oscillator used by the WDT ostensibly runs at 32,768 Hz with
// a 1:32 prescale, thus 1024 Hz, though probably not super precise.
if((maxPeriodMS >= 16000) || !maxPeriodMS) {
cycles = 16384;
bits = 0xB;
} else {
cycles = (maxPeriodMS * 1024L + 500) / 1000; // ms -> WDT cycles
if(cycles >= 8192) {
cycles = 8192;
bits = 0xA;
} else if(cycles >= 4096) {
cycles = 4096;
bits = 0x9;
} else if(cycles >= 2048) {
cycles = 2048;
bits = 0x8;
} else if(cycles >= 1024) {
cycles = 1024;
bits = 0x7;
} else if(cycles >= 512) {
cycles = 512;
bits = 0x6;
} else if(cycles >= 256) {
cycles = 256;
bits = 0x5;
} else if(cycles >= 128) {
cycles = 128;
bits = 0x4;
} else if(cycles >= 64) {
cycles = 64;
bits = 0x3;
} else if(cycles >= 32) {
cycles = 32;
bits = 0x2;
} else if(cycles >= 16) {
cycles = 16;
bits = 0x1;
} else {
cycles = 8;
bits = 0x0;
}
}
// Watchdog timer on SAMD is a slightly different animal than on AVR.
// On AVR, the WTD timeout is configured in one register and then an
// interrupt can optionally be enabled to handle the timeout in code
// (as in waking from sleep) vs resetting the chip. Easy.
// On SAMD, when the WDT fires, that's it, the chip's getting reset.
// Instead, it has an "early warning interrupt" with a different set
// interval prior to the reset. For equivalent behavior to the AVR
// library, this requires a slightly different configuration depending
// whether we're coming from the sleep() function (which needs the
// interrupt), or just enable() (no interrupt, we want the chip reset
// unless the WDT is cleared first). In the sleep case, 'windowed'
// mode is used in order to allow access to the longest available
// sleep interval (about 16 sec); the WDT 'period' (when a reset
// occurs) follows this and is always just set to the max, since the
// interrupt will trigger first. In the enable case, windowed mode
// is not used, the WDT period is set and that's that.
// The 'isForSleep' argument determines which behavior is used;
// this isn't present in the AVR code, just here. It defaults to
// 'false' so existing Arduino code works as normal, while the sleep()
// function (later in this file) explicitly passes 'true' to get the
// alternate behavior.
#if defined(__SAMD51__)
if(isForSleep) {
WDT->INTENSET.bit.EW = 1; // Enable early warning interrupt
WDT->CONFIG.bit.PER = 0xB; // Period = max
WDT->CONFIG.bit.WINDOW = bits; // Set time of interrupt
WDT->CTRLA.bit.WEN = 1; // Enable window mode
while(WDT->SYNCBUSY.reg); // Sync CTRL write
} else {
WDT->INTENCLR.bit.EW = 1; // Disable early warning interrupt
WDT->CONFIG.bit.PER = bits; // Set period for chip reset
WDT->CTRLA.bit.WEN = 0; // Disable window mode
while(WDT->SYNCBUSY.reg); // Sync CTRL write
}
reset(); // Clear watchdog interval
WDT->CTRLA.bit.ENABLE = 1; // Start watchdog now!
while(WDT->SYNCBUSY.reg);
#else
if(isForSleep) {
WDT->INTENSET.bit.EW = 1; // Enable early warning interrupt
WDT->CONFIG.bit.PER = 0xB; // Period = max
WDT->CONFIG.bit.WINDOW = bits; // Set time of interrupt
WDT->CTRL.bit.WEN = 1; // Enable window mode
while(WDT->STATUS.bit.SYNCBUSY); // Sync CTRL write
} else {
WDT->INTENCLR.bit.EW = 1; // Disable early warning interrupt
WDT->CONFIG.bit.PER = bits; // Set period for chip reset
WDT->CTRL.bit.WEN = 0; // Disable window mode
while(WDT->STATUS.bit.SYNCBUSY); // Sync CTRL write
}
reset(); // Clear watchdog interval
WDT->CTRL.bit.ENABLE = 1; // Start watchdog now!
while(WDT->STATUS.bit.SYNCBUSY);
#endif
return (cycles * 1000L + 512) / 1024; // WDT cycles -> ms
}
void WatchdogSAMD::reset() {
// Write the watchdog clear key value (0xA5) to the watchdog
// clear register to clear the watchdog timer and reset it.
WDT->CLEAR.reg = WDT_CLEAR_CLEAR_KEY;
#if defined(__SAMD51__)
while(WDT->SYNCBUSY.reg);
#else
while(WDT->STATUS.bit.SYNCBUSY);
#endif
}
void WatchdogSAMD::disable() {
#if defined(__SAMD51__)
WDT->CTRLA.bit.ENABLE = 0;
while(WDT->SYNCBUSY.reg);
#else
WDT->CTRL.bit.ENABLE = 0;
while(WDT->STATUS.bit.SYNCBUSY);
#endif
}
void WDT_Handler(void) {
// ISR for watchdog early warning, DO NOT RENAME!
#if defined(__SAMD51__)
WDT->CTRLA.bit.ENABLE = 0; // Disable watchdog
while(WDT->SYNCBUSY.reg);
#else
WDT->CTRL.bit.ENABLE = 0; // Disable watchdog
while(WDT->STATUS.bit.SYNCBUSY); // Sync CTRL write
#endif
WDT->INTFLAG.bit.EW = 1; // Clear interrupt flag
}
int WatchdogSAMD::sleep(int maxPeriodMS) {
int actualPeriodMS = enable(maxPeriodMS, true); // true = for sleep
// Enable standby sleep mode (deepest sleep) and activate.
// Insights from Atmel ASF library.
#if (SAMD20 || SAMD21)
// Don't fully power down flash when in sleep
NVMCTRL->CTRLB.bit.SLEEPPRM = NVMCTRL_CTRLB_SLEEPPRM_DISABLED_Val;
#endif
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
__DSB(); // Data sync to ensure outgoing memory accesses complete
__WFI(); // Wait for interrupt (places device in sleep mode)
// Code resumes here on wake (WDT early warning interrupt).
// Bug: the return value assumes the WDT has run its course;
// incorrect if the device woke due to an external interrupt.
// Without an external RTC there's no way to provide a correct
// sleep period in the latter case...but at the very least,
// might indicate said condition occurred by returning 0 instead
// (assuming we can pin down which interrupt caused the wake).
return actualPeriodMS;
}
void WatchdogSAMD::_initialize_wdt() {
// One-time initialization of watchdog timer.
// Insights from rickrlh and rbrucemtl in Arduino forum!
#if defined(__SAMD51__)
// SAMD51 WDT uses OSCULP32k as input clock now
// section: 20.5.3
OSC32KCTRL->OSCULP32K.reg =
OSC32KCTRL_OSCULP32K_EN1K | OSC32KCTRL_OSCULP32K_EN32K;
// Enable WDT early-warning interrupt
NVIC_DisableIRQ(WDT_IRQn);
NVIC_ClearPendingIRQ(WDT_IRQn);
NVIC_SetPriority(WDT_IRQn, 0); // Top priority
NVIC_EnableIRQ(WDT_IRQn);
while(WDT->SYNCBUSY.reg);
#else
// Generic clock generator 2, divisor = 32 (2^(DIV+1))
GCLK->GENDIV.reg = GCLK_GENDIV_ID(2) | GCLK_GENDIV_DIV(4);
// Enable clock generator 2 using low-power 32KHz oscillator.
// With /32 divisor above, this yields 1024Hz(ish) clock.
GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(2) |
GCLK_GENCTRL_GENEN |
GCLK_GENCTRL_SRC_OSCULP32K |
GCLK_GENCTRL_DIVSEL;
while(GCLK->STATUS.bit.SYNCBUSY);
// WDT clock = clock gen 2
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_WDT |
GCLK_CLKCTRL_CLKEN |
GCLK_CLKCTRL_GEN_GCLK2;
// Enable WDT early-warning interrupt
NVIC_DisableIRQ(WDT_IRQn);
NVIC_ClearPendingIRQ(WDT_IRQn);
NVIC_SetPriority(WDT_IRQn, 0); // Top priority
NVIC_EnableIRQ(WDT_IRQn);
#endif
_initialized = true;
}
#endif // defined(ARDUINO_ARCH_SAMD)
@@ -0,0 +1,43 @@
#ifndef WATCHDOGSAMD_H
#define WATCHDOGSAMD_H
class WatchdogSAMD {
public:
WatchdogSAMD():
_initialized(false)
{}
// Enable the watchdog timer to reset the machine after a period of time
// without any calls to reset(). The passed in period (in milliseconds)
// is just a suggestion and a lower value might be picked if the hardware
// does not support the exact desired value.
// User code should NOT set the 'isForSleep' argument either way --
// it's used internally by the library, but your sketch should leave this
// out when calling enable(), just let the default have its way.
//
// The actual period (in milliseconds) before a watchdog timer reset is
// returned.
int enable(int maxPeriodMS = 0, bool isForSleep = false);
// Reset or 'kick' the watchdog timer to prevent a reset of the device.
void reset();
// Completely disable the watchdog timer.
void disable();
// Enter the lowest power sleep mode (using the watchdog timer) for the
// desired period of time. The passed in period (in milliseconds) is
// just a suggestion and a lower value might be picked if the hardware
// does not support the exact desired value
//
// The actual period (in milliseconds) that the hardware was asleep will be
// returned.
int sleep(int maxPeriodMS = 0);
private:
void _initialize_wdt();
bool _initialized;
};
#endif