3 changed files with 338 additions and 1 deletions
@ -0,0 +1,225 @@ |
|||||||
|
#if defined(ARDUINO_ARCH_SAMD) |
||||||
|
|
||||||
|
#include "ArduinoLowPower.h" |
||||||
|
|
||||||
|
static void configGCLK6() |
||||||
|
{ |
||||||
|
// enable EIC clock
|
||||||
|
GCLK->CLKCTRL.bit.CLKEN = 0; //disable GCLK module
|
||||||
|
while (GCLK->STATUS.bit.SYNCBUSY); |
||||||
|
|
||||||
|
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK6 | GCLK_CLKCTRL_ID( GCM_EIC )) ; //EIC clock switched on GCLK6
|
||||||
|
while (GCLK->STATUS.bit.SYNCBUSY); |
||||||
|
|
||||||
|
GCLK->GENCTRL.reg = (GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSCULP32K | GCLK_GENCTRL_ID(6)); //source for GCLK6 is OSCULP32K
|
||||||
|
while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY); |
||||||
|
|
||||||
|
GCLK->GENCTRL.bit.RUNSTDBY = 1; //GCLK6 run standby
|
||||||
|
while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY); |
||||||
|
|
||||||
|
/* Errata: Make sure that the Flash does not power all the way down
|
||||||
|
* when in sleep mode. */ |
||||||
|
|
||||||
|
NVMCTRL->CTRLB.bit.SLEEPPRM = NVMCTRL_CTRLB_SLEEPPRM_DISABLED_Val; |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoLowPowerClass::idle() { |
||||||
|
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk; |
||||||
|
PM->SLEEP.reg = 2; |
||||||
|
__DSB(); |
||||||
|
__WFI(); |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoLowPowerClass::idle(uint32_t millis) { |
||||||
|
setAlarmIn(millis); |
||||||
|
idle(); |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoLowPowerClass::sleep() { |
||||||
|
bool restoreUSBDevice = false; |
||||||
|
|
||||||
|
if (SERIAL_PORT_USBVIRTUAL) { |
||||||
|
USBDevice.standby(); |
||||||
|
} else { |
||||||
|
USBDevice.detach(); |
||||||
|
restoreUSBDevice = true; |
||||||
|
} |
||||||
|
|
||||||
|
// Ralf
|
||||||
|
//USBDevice.detach();
|
||||||
|
//restoreUSBDevice = true;
|
||||||
|
//----------
|
||||||
|
|
||||||
|
// Disable systick interrupt: See https://www.avrfreaks.net/forum/samd21-samd21e16b-sporadically-locks-and-does-not-wake-standby-sleep-mode
|
||||||
|
SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk; |
||||||
|
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; |
||||||
|
__DSB(); |
||||||
|
__WFI(); |
||||||
|
// Enable systick interrupt
|
||||||
|
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; |
||||||
|
if (restoreUSBDevice) { |
||||||
|
USBDevice.attach(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoLowPowerClass::sleep(uint32_t millis) { |
||||||
|
setAlarmIn(millis); |
||||||
|
sleep(); |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoLowPowerClass::deepSleep() { |
||||||
|
sleep(); |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoLowPowerClass::deepSleep(uint32_t millis) { |
||||||
|
sleep(millis); |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoLowPowerClass::setAlarmIn(uint32_t millis) { |
||||||
|
|
||||||
|
if (!rtc.isConfigured()) { |
||||||
|
attachInterruptWakeup(RTC_ALARM_WAKEUP, NULL, (irq_mode)0); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t now = rtc.getEpoch(); |
||||||
|
rtc.setAlarmEpoch(now + millis/1000); |
||||||
|
rtc.enableAlarm(rtc.MATCH_YYMMDDHHMMSS); |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoLowPowerClass::attachInterruptWakeup(uint32_t pin, voidFuncPtr callback, irq_mode mode) { |
||||||
|
|
||||||
|
if (pin > PINS_COUNT) { |
||||||
|
// check for external wakeup sources
|
||||||
|
// RTC library should call this API to enable the alarm subsystem
|
||||||
|
switch (pin) { |
||||||
|
case RTC_ALARM_WAKEUP: |
||||||
|
rtc.begin(false); |
||||||
|
rtc.attachInterrupt(callback); |
||||||
|
/*case UART_WAKEUP:*/ |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
EExt_Interrupts in = g_APinDescription[pin].ulExtInt; |
||||||
|
if (in == NOT_AN_INTERRUPT || in == EXTERNAL_INT_NMI) |
||||||
|
return; |
||||||
|
|
||||||
|
//pinMode(pin, INPUT_PULLUP);
|
||||||
|
attachInterrupt(pin, callback, mode); |
||||||
|
|
||||||
|
configGCLK6(); |
||||||
|
|
||||||
|
// Enable wakeup capability on pin in case being used during sleep
|
||||||
|
EIC->WAKEUP.reg |= (1 << in); |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoLowPowerClass::attachAdcInterrupt(uint32_t pin, voidFuncPtr callback, adc_interrupt mode, uint16_t lo, uint16_t hi) |
||||||
|
{ |
||||||
|
uint8_t winmode = 0; |
||||||
|
|
||||||
|
switch (mode) { |
||||||
|
case ADC_INT_BETWEEN: winmode = ADC_WINCTRL_WINMODE_MODE3; break; |
||||||
|
case ADC_INT_OUTSIDE: winmode = ADC_WINCTRL_WINMODE_MODE4; break; |
||||||
|
case ADC_INT_ABOVE_MIN: winmode = ADC_WINCTRL_WINMODE_MODE1; break; |
||||||
|
case ADC_INT_BELOW_MAX: winmode = ADC_WINCTRL_WINMODE_MODE2; break; |
||||||
|
default: return; |
||||||
|
} |
||||||
|
|
||||||
|
adc_cb = callback; |
||||||
|
|
||||||
|
configGCLK6(); |
||||||
|
|
||||||
|
// Configure ADC to use GCLK6 (OSCULP32K)
|
||||||
|
while (GCLK->STATUS.bit.SYNCBUSY) {} |
||||||
|
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_ADC |
||||||
|
| GCLK_CLKCTRL_GEN_GCLK6 |
||||||
|
| GCLK_CLKCTRL_CLKEN; |
||||||
|
while (GCLK->STATUS.bit.SYNCBUSY) {} |
||||||
|
|
||||||
|
// Set ADC prescaler as low as possible
|
||||||
|
ADC->CTRLB.bit.PRESCALER = ADC_CTRLB_PRESCALER_DIV4; |
||||||
|
while (ADC->STATUS.bit.SYNCBUSY) {} |
||||||
|
|
||||||
|
// Configure window mode
|
||||||
|
ADC->WINLT.reg = lo; |
||||||
|
ADC->WINUT.reg = hi; |
||||||
|
ADC->WINCTRL.reg = winmode; |
||||||
|
while (ADC->STATUS.bit.SYNCBUSY) {} |
||||||
|
|
||||||
|
// Enable window interrupt
|
||||||
|
ADC->INTENSET.bit.WINMON = 1; |
||||||
|
while (ADC->STATUS.bit.SYNCBUSY) {} |
||||||
|
|
||||||
|
// Enable ADC in standby mode
|
||||||
|
ADC->CTRLA.bit.RUNSTDBY = 1; |
||||||
|
while (ADC->STATUS.bit.SYNCBUSY) {} |
||||||
|
|
||||||
|
// Enable continuous conversions
|
||||||
|
ADC->CTRLB.bit.FREERUN = 1; |
||||||
|
while (ADC->STATUS.bit.SYNCBUSY) {} |
||||||
|
|
||||||
|
// Configure input mux
|
||||||
|
ADC->INPUTCTRL.bit.MUXPOS = g_APinDescription[pin].ulADCChannelNumber; |
||||||
|
while (ADC->STATUS.bit.SYNCBUSY) {} |
||||||
|
|
||||||
|
// Enable the ADC
|
||||||
|
ADC->CTRLA.bit.ENABLE = 1; |
||||||
|
while (ADC->STATUS.bit.SYNCBUSY) {} |
||||||
|
|
||||||
|
// Start continuous conversions
|
||||||
|
ADC->SWTRIG.bit.START = 1; |
||||||
|
while (ADC->STATUS.bit.SYNCBUSY) {} |
||||||
|
|
||||||
|
// Enable the ADC interrupt
|
||||||
|
NVIC_EnableIRQ(ADC_IRQn); |
||||||
|
} |
||||||
|
|
||||||
|
void ArduinoLowPowerClass::detachAdcInterrupt() |
||||||
|
{ |
||||||
|
// Disable the ADC interrupt
|
||||||
|
NVIC_DisableIRQ(ADC_IRQn); |
||||||
|
|
||||||
|
// Disable the ADC
|
||||||
|
ADC->CTRLA.bit.ENABLE = 0; |
||||||
|
while (ADC->STATUS.bit.SYNCBUSY) {} |
||||||
|
|
||||||
|
// Disable continuous conversions
|
||||||
|
ADC->CTRLB.bit.FREERUN = 0; |
||||||
|
while (ADC->STATUS.bit.SYNCBUSY) {} |
||||||
|
|
||||||
|
// Disable ADC in standby mode
|
||||||
|
ADC->CTRLA.bit.RUNSTDBY = 1; |
||||||
|
while (ADC->STATUS.bit.SYNCBUSY) {} |
||||||
|
|
||||||
|
// Disable window interrupt
|
||||||
|
ADC->INTENCLR.bit.WINMON = 1; |
||||||
|
while (ADC->STATUS.bit.SYNCBUSY) {} |
||||||
|
|
||||||
|
// Disable window mode
|
||||||
|
ADC->WINCTRL.reg = ADC_WINCTRL_WINMODE_DISABLE; |
||||||
|
while (ADC->STATUS.bit.SYNCBUSY) {} |
||||||
|
|
||||||
|
// Restore ADC prescaler
|
||||||
|
ADC->CTRLB.bit.PRESCALER = ADC_CTRLB_PRESCALER_DIV512_Val; |
||||||
|
while (ADC->STATUS.bit.SYNCBUSY) {} |
||||||
|
|
||||||
|
// Restore ADC clock
|
||||||
|
while (GCLK->STATUS.bit.SYNCBUSY) {} |
||||||
|
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_ADC |
||||||
|
| GCLK_CLKCTRL_GEN_GCLK0 |
||||||
|
| GCLK_CLKCTRL_CLKEN; |
||||||
|
while (GCLK->STATUS.bit.SYNCBUSY) {} |
||||||
|
|
||||||
|
adc_cb = nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
void ADC_Handler() |
||||||
|
{ |
||||||
|
// Clear the interrupt flag
|
||||||
|
ADC->INTFLAG.bit.WINMON = 1; |
||||||
|
LowPower.adc_cb(); |
||||||
|
} |
||||||
|
|
||||||
|
ArduinoLowPowerClass LowPower; |
||||||
|
|
||||||
|
#endif // ARDUINO_ARCH_SAMD
|
||||||
@ -0,0 +1,106 @@ |
|||||||
|
#ifndef _ARDUINO_LOW_POWER_H_ |
||||||
|
#define _ARDUINO_LOW_POWER_H_ |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_AVR |
||||||
|
#error The library is not compatible with AVR boards |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_SAMD |
||||||
|
#include "RTCZero.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
#if defined(ARDUINO_SAMD_TIAN) || defined(ARDUINO_NRF52_PRIMO) |
||||||
|
// add here any board with companion chip which can be woken up
|
||||||
|
#define BOARD_HAS_COMPANION_CHIP |
||||||
|
#endif |
||||||
|
|
||||||
|
#define RTC_ALARM_WAKEUP 0xFF |
||||||
|
|
||||||
|
#ifdef ARDUINO_API_VERSION |
||||||
|
using irq_mode = PinStatus; |
||||||
|
#else |
||||||
|
using irq_mode = uint32_t; |
||||||
|
#endif |
||||||
|
|
||||||
|
//typedef void (*voidFuncPtr)( void ) ;
|
||||||
|
typedef void (*onOffFuncPtr)( bool ) ; |
||||||
|
|
||||||
|
typedef enum{ |
||||||
|
OTHER_WAKEUP = 0, |
||||||
|
GPIO_WAKEUP = 1, |
||||||
|
NFC_WAKEUP = 2, |
||||||
|
ANALOG_COMPARATOR_WAKEUP = 3 |
||||||
|
} wakeup_reason; |
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_SAMD |
||||||
|
enum adc_interrupt |
||||||
|
{ |
||||||
|
ADC_INT_BETWEEN, |
||||||
|
ADC_INT_OUTSIDE, |
||||||
|
ADC_INT_ABOVE_MIN, |
||||||
|
ADC_INT_BELOW_MAX, |
||||||
|
}; |
||||||
|
#endif |
||||||
|
|
||||||
|
|
||||||
|
class ArduinoLowPowerClass { |
||||||
|
public: |
||||||
|
void idle(void); |
||||||
|
void idle(uint32_t millis); |
||||||
|
void idle(int millis) { |
||||||
|
idle((uint32_t)millis); |
||||||
|
} |
||||||
|
|
||||||
|
void sleep(void); |
||||||
|
void sleep(uint32_t millis); |
||||||
|
void sleep(int millis) { |
||||||
|
sleep((uint32_t)millis); |
||||||
|
} |
||||||
|
|
||||||
|
void deepSleep(void); |
||||||
|
void deepSleep(uint32_t millis); |
||||||
|
void deepSleep(int millis) { |
||||||
|
deepSleep((uint32_t)millis); |
||||||
|
} |
||||||
|
|
||||||
|
void attachInterruptWakeup(uint32_t pin, voidFuncPtr callback, irq_mode mode); |
||||||
|
|
||||||
|
#ifdef BOARD_HAS_COMPANION_CHIP |
||||||
|
void companionLowPowerCallback(onOffFuncPtr callback) { |
||||||
|
companionSleepCB = callback; |
||||||
|
} |
||||||
|
void companionSleep() { |
||||||
|
companionSleepCB(true); |
||||||
|
} |
||||||
|
void companionWakeup() { |
||||||
|
companionSleepCB(false); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_NRF52 |
||||||
|
void enableWakeupFrom(wakeup_reason peripheral, uint32_t pin = 0xFF, uint32_t event = 0xFF, uint32_t option = 0xFF); |
||||||
|
wakeup_reason wakeupReason(); |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_SAMD |
||||||
|
void attachAdcInterrupt(uint32_t pin, voidFuncPtr callback, adc_interrupt mode, uint16_t lo, uint16_t hi); |
||||||
|
void detachAdcInterrupt(); |
||||||
|
#endif |
||||||
|
|
||||||
|
private: |
||||||
|
void setAlarmIn(uint32_t millis); |
||||||
|
#ifdef ARDUINO_ARCH_SAMD |
||||||
|
RTCZero rtc; |
||||||
|
voidFuncPtr adc_cb; |
||||||
|
friend void ADC_Handler(); |
||||||
|
#endif |
||||||
|
#ifdef BOARD_HAS_COMPANION_CHIP |
||||||
|
void (*companionSleepCB)(bool); |
||||||
|
#endif |
||||||
|
}; |
||||||
|
|
||||||
|
extern ArduinoLowPowerClass LowPower; |
||||||
|
|
||||||
|
#endif |
||||||
Loading…
Reference in new issue