You've already forked ArduinoLowPower
mirror of
https://github.com/arduino-libraries/ArduinoLowPower.git
synced 2025-07-29 21:21:10 +03:00
Add support for ADC wakeup interrupt on SAMD21
This can be used to configure the ADC window interrupt on the SAMD21. It uses OSCULP32K via GCLK6 to clock the ADC while in sleep mode (the same as used for the EIC). Note that attachAdcInterrupt()/detachAdcInterrupt() should be called immediately before/after LowPower.sleep() otherwise analogRead() will not work as expected. There is also an example (AdcWakeup.ino) which is much like the ExternalWakeup example but uses the ADC interrupt instead.
This commit is contained in:
@ -28,6 +28,16 @@ typedef enum{
|
||||
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:
|
||||
@ -68,10 +78,17 @@ class ArduinoLowPowerClass {
|
||||
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);
|
||||
|
@ -3,6 +3,27 @@
|
||||
#include "ArduinoLowPower.h"
|
||||
#include "WInterrupts.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;
|
||||
@ -80,26 +101,117 @@ void ArduinoLowPowerClass::attachInterruptWakeup(uint32_t pin, voidFuncPtr callb
|
||||
//pinMode(pin, INPUT_PULLUP);
|
||||
attachInterrupt(pin, callback, mode);
|
||||
|
||||
// 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);
|
||||
configGCLK6();
|
||||
|
||||
// Enable wakeup capability on pin in case being used during sleep
|
||||
EIC->WAKEUP.reg |= (1 << in);
|
||||
}
|
||||
|
||||
/* Errata: Make sure that the Flash does not power all the way down
|
||||
* when in sleep mode. */
|
||||
void ArduinoLowPowerClass::attachAdcInterrupt(uint32_t pin, voidFuncPtr callback, adc_interrupt mode, uint16_t lo, uint16_t hi)
|
||||
{
|
||||
uint8_t winmode = 0;
|
||||
|
||||
NVMCTRL->CTRLB.bit.SLEEPPRM = NVMCTRL_CTRLB_SLEEPPRM_DISABLED_Val;
|
||||
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;
|
||||
|
Reference in New Issue
Block a user