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
|
||||
} 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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user