mirror of
https://github.com/arduino-libraries/ArduinoLowPower.git
synced 2025-04-19 11:42:14 +03:00
Merge pull request #25 from Lincoln-Agritech/master
Add support for ADC wakeup interrupt on SAMD21
This commit is contained in:
commit
6fd308af4a
61
examples/AdcWakeup/AdcWakeup.ino
Normal file
61
examples/AdcWakeup/AdcWakeup.ino
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
AdcWakeup
|
||||||
|
|
||||||
|
This sketch demonstrates the usage of the ADC to wakeup a chip in sleep mode.
|
||||||
|
Sleep modes allow a significant drop in the power usage of a board while it does nothing waiting for an event to happen. Battery powered application can take advantage of these modes to enhance battery life significantly.
|
||||||
|
|
||||||
|
In this sketch, changing the voltage on pin A0 will wake up the board. You can test this by connecting a potentiometer between VCC, A0, and GND.
|
||||||
|
Please note that, if the processor is sleeping, a new sketch can't be uploaded. To overcome this, manually reset the board (usually with a single or double tap to the RESET button)
|
||||||
|
|
||||||
|
This example code is in the public domain.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ArduinoLowPower.h"
|
||||||
|
|
||||||
|
// Blink sequence number
|
||||||
|
// Declare it volatile since it's incremented inside an interrupt
|
||||||
|
volatile int repetitions = 1;
|
||||||
|
|
||||||
|
// Pin used to trigger a wakeup
|
||||||
|
const int pin = A0;
|
||||||
|
// How sensitive to be to changes in voltage
|
||||||
|
const int margin = 10;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
pinMode(LED_BUILTIN, OUTPUT);
|
||||||
|
pinMode(pin, INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
for (int i = 0; i < repetitions; i++) {
|
||||||
|
digitalWrite(LED_BUILTIN, HIGH);
|
||||||
|
delay(500);
|
||||||
|
digitalWrite(LED_BUILTIN, LOW);
|
||||||
|
delay(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the voltage at the ADC pin
|
||||||
|
int value = analogRead(pin);
|
||||||
|
|
||||||
|
// Define a window around that value
|
||||||
|
uint16_t lo = max(value - margin, 0);
|
||||||
|
uint16_t hi = min(value + margin, UINT16_MAX);
|
||||||
|
|
||||||
|
// Attach an ADC interrupt on pin A0, calling repetitionsIncrease when the voltage is outside the given range.
|
||||||
|
// This should be called immediately before LowPower.sleep() because it reconfigures the ADC internally.
|
||||||
|
LowPower.attachAdcInterrupt(pin, repetitionsIncrease, ADC_INT_OUTSIDE, lo, hi);
|
||||||
|
|
||||||
|
// Triggers an infinite sleep (the device will be woken up only by the registered wakeup sources)
|
||||||
|
// The power consumption of the chip will drop consistently
|
||||||
|
LowPower.sleep();
|
||||||
|
|
||||||
|
// Detach the ADC interrupt. This should be called immediately after LowPower.sleep() because it restores the ADC configuration after waking up.
|
||||||
|
LowPower.detachAdcInterrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void repetitionsIncrease() {
|
||||||
|
// This function will be called once on device wakeup
|
||||||
|
// You can do some little operations here (like changing variables which will be used in the loop)
|
||||||
|
// Remember to avoid calling delay() and long running functions since this functions executes in interrupt context
|
||||||
|
repetitions ++;
|
||||||
|
}
|
@ -28,6 +28,16 @@ typedef enum{
|
|||||||
ANALOG_COMPARATOR_WAKEUP = 3
|
ANALOG_COMPARATOR_WAKEUP = 3
|
||||||
} wakeup_reason;
|
} 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 {
|
class ArduinoLowPowerClass {
|
||||||
public:
|
public:
|
||||||
@ -68,10 +78,17 @@ class ArduinoLowPowerClass {
|
|||||||
wakeup_reason wakeupReason();
|
wakeup_reason wakeupReason();
|
||||||
#endif
|
#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:
|
private:
|
||||||
void setAlarmIn(uint32_t millis);
|
void setAlarmIn(uint32_t millis);
|
||||||
#ifdef ARDUINO_ARCH_SAMD
|
#ifdef ARDUINO_ARCH_SAMD
|
||||||
RTCZero rtc;
|
RTCZero rtc;
|
||||||
|
voidFuncPtr adc_cb;
|
||||||
|
friend void ADC_Handler();
|
||||||
#endif
|
#endif
|
||||||
#ifdef BOARD_HAS_COMPANION_CHIP
|
#ifdef BOARD_HAS_COMPANION_CHIP
|
||||||
void (*companionSleepCB)(bool);
|
void (*companionSleepCB)(bool);
|
||||||
|
@ -3,6 +3,27 @@
|
|||||||
#include "ArduinoLowPower.h"
|
#include "ArduinoLowPower.h"
|
||||||
#include "WInterrupts.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() {
|
void ArduinoLowPowerClass::idle() {
|
||||||
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
|
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
|
||||||
PM->SLEEP.reg = 2;
|
PM->SLEEP.reg = 2;
|
||||||
@ -80,26 +101,117 @@ void ArduinoLowPowerClass::attachInterruptWakeup(uint32_t pin, voidFuncPtr callb
|
|||||||
//pinMode(pin, INPUT_PULLUP);
|
//pinMode(pin, INPUT_PULLUP);
|
||||||
attachInterrupt(pin, callback, mode);
|
attachInterrupt(pin, callback, mode);
|
||||||
|
|
||||||
// enable EIC clock
|
configGCLK6();
|
||||||
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);
|
|
||||||
|
|
||||||
// Enable wakeup capability on pin in case being used during sleep
|
// Enable wakeup capability on pin in case being used during sleep
|
||||||
EIC->WAKEUP.reg |= (1 << in);
|
EIC->WAKEUP.reg |= (1 << in);
|
||||||
|
}
|
||||||
|
|
||||||
/* Errata: Make sure that the Flash does not power all the way down
|
void ArduinoLowPowerClass::attachAdcInterrupt(uint32_t pin, voidFuncPtr callback, adc_interrupt mode, uint16_t lo, uint16_t hi)
|
||||||
* when in sleep mode. */
|
{
|
||||||
|
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;
|
ArduinoLowPowerClass LowPower;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user