1
0
mirror of https://github.com/arduino-libraries/ArduinoLowPower.git synced 2025-04-19 11:42:14 +03:00

Import latest code from @bigdinotech

This commit is contained in:
Martino Facchin 2016-12-06 17:10:13 +01:00
parent 7304fe0419
commit 3e4915e8c9
3 changed files with 285 additions and 120 deletions

View File

@ -7,7 +7,7 @@
#error The library is not compatible with AVR boards #error The library is not compatible with AVR boards
#endif #endif
#ifdef ARDUINO_ARCH_ARC32 #ifdef __ARDUINO_ARC__
#include "include/arc32/defines.h" #include "include/arc32/defines.h"
#endif #endif
@ -39,9 +39,12 @@ class ArduinoLowPowerClass {
void attachInterruptWakeup(uint32_t pin, voidFuncPtr callback, uint32_t mode); void attachInterruptWakeup(uint32_t pin, voidFuncPtr callback, uint32_t mode);
#ifdef ARDUINO_ARCH_ARC32 #ifdef __ARDUINO_ARC__
void wakeFromSleepCallback(void); void wakeFromSleepCallback(void);
#endif void wakeFromDoze(void);
void detachInterruptWakeup(uint32_t pin);
uint32_t arc_restore_addr;
#endif
private: private:
#ifdef ARDUINO_ARCH_SAMD #ifdef ARDUINO_ARCH_SAMD
@ -50,24 +53,27 @@ class ArduinoLowPowerClass {
#define RTC_ALARM_WAKEUP 0xFF #define RTC_ALARM_WAKEUP 0xFF
#endif #endif
#ifdef ARDUINO_ARCH_ARC32 #ifdef __ARDUINO_ARC__
void wakeFromDoze();
void switchToHybridOscillator();
void switchToCrystalOscillator();
void attachWakeInterruptRTC(void (*userCallBack)());
void turnOffUSB(); void turnOffUSB();
void turnOnUSB(); void turnOnUSB();
void setRTCCMR(int milliseconds); void switchToHybridOscillator();
uint32_t readRTC_CCVR(); void switchToCrystalOscillator();
bool isSleeping = false; void setRTCCMR(int seconds);
uint32_t millisToRTCTicks(int milliseconds); uint32_t readRTC_CCVR();
void enableRTCInterrupt(); bool isSleeping = false;
void x86_C2Request(); volatile bool dozing = false;
void (*pmCB)(); uint32_t millisToRTCTicks(int milliseconds);
#define RTC_ALARM_WAKEUP 0xFF void enableRTCInterrupt(int seconds);
void enableAONGPIOInterrupt(int aon_gpio, int mode);
void enableAONPTimerInterrrupt(int millis);
static void resetAONPTimer();
static void wakeFromRTC();
void x86_C2Request();
void x86_C2LPRequest();
void (*pmCB)();
#define RTC_ALARM_WAKEUP 0xFF
#define RESET_BUTTON_WAKEUP 0xFE #define RESET_BUTTON_WAKEUP 0xFE
#endif #endif
}; };
extern ArduinoLowPowerClass LowPower; extern ArduinoLowPowerClass LowPower;

View File

@ -33,21 +33,23 @@
#include "ArduinoLowPower.h" #include "ArduinoLowPower.h"
#ifdef ARDUINO_ARCH_ARC32 #ifdef __ARDUINO_ARC__
#include "WInterrupts.h" uint32_t arc_restore_addr;
uint32_t cpu_context[33];
static void PM_InterruptHandler(void) static void PM_InterruptHandler(void)
{ {
*(uint32_t*)RTC_CCR |= 0xFFFFFFFE; unsigned int flags = interrupt_lock();
uint32_t rtc_eoi = *(uint32_t*)RTC_EOI; //clear match interrupt LowPower.wakeFromDoze();
LowPower.wakeFromSleepCallback(); LowPower.wakeFromSleepCallback();
interrupt_unlock(flags);
} }
void ArduinoLowPowerClass::idle() void ArduinoLowPowerClass::idle()
{ {
turnOffUSB(); turnOffUSB();
dozing = true;
//switch from external crystal oscillator to internal hybrid oscilator //switch from external crystal oscillator to internal hybrid oscilator
switchToHybridOscillator(); switchToHybridOscillator();
@ -62,9 +64,9 @@ void ArduinoLowPowerClass::idle()
void ArduinoLowPowerClass::idle(uint32_t duration) void ArduinoLowPowerClass::idle(uint32_t duration)
{ {
idle(); idle();
delayTicks(millisToRTCTicks(duration)); delayTicks(millisToRTCTicks(duration));
wakeFromDoze(); wakeFromDoze();
} }
void ArduinoLowPowerClass::wakeFromDoze() void ArduinoLowPowerClass::wakeFromDoze()
@ -72,158 +74,273 @@ void ArduinoLowPowerClass::wakeFromDoze()
//Powerup hybrid oscillator //Powerup hybrid oscillator
uint32_t current_val = *(uint32_t*)OSC0_CFG1; uint32_t current_val = *(uint32_t*)OSC0_CFG1;
*(uint32_t*)OSC0_CFG1 = current_val & 0xFFFFFFFB; *(uint32_t*)OSC0_CFG1 = current_val & 0xFFFFFFFB;
//Set system clock to the Hybrid Oscillator //Set system clock to the Hybrid Oscillator
current_val = *(uint32_t*)CCU_SYS_CLK_CTL; current_val = *(uint32_t*)CCU_SYS_CLK_CTL;
*(uint32_t*)CCU_SYS_CLK_CTL = current_val | 0x00000001; *(uint32_t*)CCU_SYS_CLK_CTL = current_val | 0x00000001;
//switch back to the external crystal oiscillator //switch back to the external crystal oscillator
void switchToCrystalOscillator(); switchToCrystalOscillator();
turnOnUSB(); turnOnUSB();
dozing = false;
} }
void ArduinoLowPowerClass::sleep() void ArduinoLowPowerClass::sleep()
{ {
turnOffUSB(); uint32_t creg_mst0_ctrl = 0;
//*(uint32_t*)SLP_CFG &= 0xFFFFFEFF; creg_mst0_ctrl = __builtin_arc_lr(QM_SS_CREG_BASE);
x86_C2Request(); /*
isSleeping = true; * Clock gate the sensor peripherals at CREG level.
//*(uint32_t*)CCU_LP_CLK_CTL = (*(uint32_t*)CCU_LP_CLK_CTL) | 0x00000002; * This clock gating is independent of the peripheral-specific clock
//uint32_t c2 = *(uint32_t*)P_LVL2; * gating provided in ss_clk.h .
*(uint32_t*)PM1C |= 0x00002000; */
creg_mst0_ctrl |= (QM_SS_IO_CREG_MST0_CTRL_ADC_CLK_GATE |
QM_SS_IO_CREG_MST0_CTRL_I2C1_CLK_GATE |
QM_SS_IO_CREG_MST0_CTRL_I2C0_CLK_GATE |
QM_SS_IO_CREG_MST0_CTRL_SPI1_CLK_GATE |
QM_SS_IO_CREG_MST0_CTRL_SPI0_CLK_GATE);
__builtin_arc_sr(creg_mst0_ctrl, QM_SS_CREG_BASE);
x86_C2LPRequest();
idle();
__asm__ __volatile__(
"sleep %0"
:
: "i"(QM_SS_SLEEP_MODE_CORE_OFF));
creg_mst0_ctrl &= ~(QM_SS_IO_CREG_MST0_CTRL_ADC_CLK_GATE |
QM_SS_IO_CREG_MST0_CTRL_I2C1_CLK_GATE |
QM_SS_IO_CREG_MST0_CTRL_I2C0_CLK_GATE |
QM_SS_IO_CREG_MST0_CTRL_SPI1_CLK_GATE |
QM_SS_IO_CREG_MST0_CTRL_SPI0_CLK_GATE);
__builtin_arc_sr(creg_mst0_ctrl, QM_SS_CREG_BASE);
} }
void ArduinoLowPowerClass::sleep(uint32_t duration) void ArduinoLowPowerClass::sleep(uint32_t duration)
{ {
setRTCCMR(duration); enableAONPTimerInterrrupt(duration);
enableRTCInterrupt(); sleep();
sleep();
} }
void ArduinoLowPowerClass::deepSleep() void ArduinoLowPowerClass::deepSleep()
{ {
turnOffUSB(); sleep();
x86_C2Request();
isSleeping = true;
*(uint32_t*)CCU_LP_CLK_CTL |= 0x00000001;
//uint32_t c2 = *(uint32_t*)P_LVL2;
*(uint32_t*)PM1C |= 0x00002000;
} }
void ArduinoLowPowerClass::deepSleep(uint32_t duration) void ArduinoLowPowerClass::deepSleep(uint32_t duration)
{ {
setRTCCMR(duration); sleep(duration);
enableRTCInterrupt();
deepSleep();
}
void ArduinoLowPowerClass::switchToHybridOscillator()
{
//read trim value from OTP
uint32_t trimMask = *(uint16_t*)OSCTRIM_ADDR << 20;
*(uint32_t*)OSC0_CFG1 = 0x00000002 | trimMask; //switch to internal oscillator using trim value from OTP
}
void ArduinoLowPowerClass::switchToCrystalOscillator()
{
*(uint32_t*)OSC0_CFG1 = 0x00070009;
} }
inline void ArduinoLowPowerClass::wakeFromSleepCallback(void) inline void ArduinoLowPowerClass::wakeFromSleepCallback(void)
{ {
if(pmCB != NULL) if(pmCB != NULL)
pmCB(); pmCB();
}
void ArduinoLowPowerClass::attachWakeInterruptRTC(void (*userCallBack)())
{
pmCB = userCallBack;
} }
//Privates //Privates
void ArduinoLowPowerClass::turnOffUSB() void ArduinoLowPowerClass::turnOffUSB()
{ {
*(uint32_t*)USB_PHY_CFG0 |= 0x00000001; *(uint32_t*)USB_PHY_CFG0 |= 0x00000001;
} }
void ArduinoLowPowerClass::turnOnUSB() void ArduinoLowPowerClass::turnOnUSB()
{ {
*(uint32_t*)USB_PHY_CFG0 &= 0xFFFFFFFE; *(uint32_t*)USB_PHY_CFG0 &= 0xFFFFFFFE;
} }
void ArduinoLowPowerClass::setRTCCMR(int milliseconds) void ArduinoLowPowerClass::switchToHybridOscillator()
{ {
*(uint32_t*)RTC_CMR = readRTC_CCVR() + millisToRTCTicks(milliseconds); //read trim value from OTP
//*(uint32_t*)RTC_CMR = readRTC_CCVR() + milliseconds; uint32_t trimMask = *(uint16_t*)OSCTRIM_ADDR << 20;
*(uint32_t*)OSC0_CFG1 = 0x00000002 | trimMask; //switch to internal oscillator using trim value from OTP
}
void ArduinoLowPowerClass::switchToCrystalOscillator()
{
*(uint32_t*)OSC0_CFG1 = 0x00070009;
while(!(*(uint32_t*)OSC0_STAT & 0x00000002)); //wait till crystal oscillator is stable
}
void ArduinoLowPowerClass::setRTCCMR(int seconds)
{
*(uint32_t*)RTC_CMR = readRTC_CCVR() + seconds;
} }
uint32_t ArduinoLowPowerClass::readRTC_CCVR() uint32_t ArduinoLowPowerClass::readRTC_CCVR()
{ {
return *(uint32_t*)RTC_CCVR; return *RTC_CCVR;
} }
uint32_t ArduinoLowPowerClass::millisToRTCTicks(int milliseconds) uint32_t ArduinoLowPowerClass::millisToRTCTicks(int milliseconds)
{ {
return (uint32_t)((double)milliseconds*32.768); return (uint32_t)((double)milliseconds*32.768);
} }
void ArduinoLowPowerClass::enableRTCInterrupt() void ArduinoLowPowerClass::enableRTCInterrupt(int seconds)
{ {
*(uint32_t*)RTC_MASK_INT &= 0xFFFFFFFF; setRTCCMR(seconds);
*(uint32_t*)RTC_CCR |= 0x00000001; *(uint32_t*)RTC_MASK_INT &= 0xFFFFFEFE;
*(uint32_t*)RTC_CCR &= 0xFFFFFFFD; *(uint32_t*)RTC_CCR |= 0x00000001;
*(uint32_t*)RTC_CCR &= 0xFFFFFFFD;
volatile uint32_t read = *(uint32_t*)RTC_EOI;
pmCB = &wakeFromRTC;
interrupt_disable(IRQ_RTC_INTR);
interrupt_connect(IRQ_RTC_INTR , &PM_InterruptHandler);
delayTicks(6400); //2ms
interrupt_enable(IRQ_RTC_INTR);
}
void ArduinoLowPowerClass::enableAONPTimerInterrrupt(int millis)
{
pmCB = resetAONPTimer;
*(uint32_t*)AONPT_CFG = millisToRTCTicks(millis);
*(uint32_t*)AONPT_CTRL |= 0x00000003;
*(uint32_t*)AON_TIMER_MASK_INT &= 0xFFFFFEFE;
interrupt_disable(IRQ_ALWAYS_ON_TMR);
interrupt_connect(IRQ_ALWAYS_ON_TMR , &PM_InterruptHandler);
delayTicks(6400); //2ms
interrupt_enable(IRQ_ALWAYS_ON_TMR);
}
void ArduinoLowPowerClass::resetAONPTimer()
{
*(uint32_t*)AONPT_CFG = 0;
*(uint32_t*)AONPT_CTRL |= 0x00000001;
delayTicks(6400);
//trick the HOST into waking from AONPTimer
*(uint32_t*)AONPT_CFG = 10;
*(uint32_t*)AONPT_CTRL |= 0x00000003;
*(uint32_t*)AON_TIMER_MASK_INT &= 0xFFFFFFFE;
interrupt_enable(IRQ_ALWAYS_ON_TMR);
}
void ArduinoLowPowerClass::enableAONGPIOInterrupt(int aon_gpio, int mode)
{
switch(mode)
{
case CHANGE: //not supported just do the same as FALLING
*(uint32_t*)AON_GPIO_INTTYPE_LEVEL |= 1 << aon_gpio;
*(uint32_t*)AON_GPIO_INT_POL &= ~(1 << aon_gpio);
break;
case RISING:
*(uint32_t*)AON_GPIO_INTTYPE_LEVEL |= 1 << aon_gpio;
*(uint32_t*)AON_GPIO_INT_POL |= 1 << aon_gpio;
break;
case FALLING:
*(uint32_t*)AON_GPIO_INTTYPE_LEVEL |= 1 << aon_gpio;
*(uint32_t*)AON_GPIO_INT_POL &= ~(1 << aon_gpio);
break;
case HIGH:
*(uint32_t*)AON_GPIO_INTTYPE_LEVEL &= ~(1 << aon_gpio);
*(uint32_t*)AON_GPIO_INT_POL |= 1 << aon_gpio;
break;
case LOW:
*(uint32_t*)AON_GPIO_INTTYPE_LEVEL &= ~(1 << aon_gpio);
*(uint32_t*)AON_GPIO_INT_POL &= ~(1 << aon_gpio);
break;
default:
*(uint32_t*)AON_GPIO_INTTYPE_LEVEL &= ~(1 << aon_gpio);
*(uint32_t*)AON_GPIO_INT_POL &= ~(1 << aon_gpio);
break;
};
*(uint32_t*)AON_GPIO_SWPORTA_DDR &= ~(1 << aon_gpio);
*(uint32_t*)AON_GPIO_INTMASK &= ~(1 << aon_gpio);
*(uint32_t*)AON_GPIO_INTEN |= 1 << aon_gpio;
*(uint32_t*)AON_GPIO_MASK_INT &= 0xFFFFFEFE;
interrupt_disable(IRQ_ALWAYS_ON_GPIO);
interrupt_connect(IRQ_ALWAYS_ON_GPIO , &PM_InterruptHandler);
interrupt_enable(IRQ_ALWAYS_ON_GPIO);
}
void ArduinoLowPowerClass::wakeFromRTC()
{
*(uint32_t*)RTC_MASK_INT |= 0x00000101;
interrupt_disable(IRQ_RTC_INTR); interrupt_disable(IRQ_RTC_INTR);
interrupt_connect(IRQ_RTC_INTR , &PM_InterruptHandler); volatile uint32_t read = *(uint32_t*)RTC_EOI;
interrupt_enable(IRQ_RTC_INTR);
} }
void ArduinoLowPowerClass::x86_C2Request() void ArduinoLowPowerClass::x86_C2Request()
{ {
switchToHybridOscillator(); switchToHybridOscillator();
//set the CCU_C2_LP_EN bit
*(uint32_t*)CCU_LP_CLK_CTL = (*(uint32_t*)CCU_LP_CLK_CTL) | 0x00000002;
//request for the x86 core go into C2 sleep //request for the x86 core go into C2 sleep
volatile uint32_t c2 = *(volatile uint32_t*)P_LVL2; volatile uint32_t c2 = *(volatile uint32_t*)P_LVL2;
}
void ArduinoLowPowerClass::x86_C2LPRequest()
{
switchToHybridOscillator();
//request for the x86 core go into C2 sleep
*(uint32_t*)CCU_LP_CLK_CTL |= 0x00000002;
volatile uint32_t c2lp = *(volatile uint32_t*)P_LVL2;
} }
void ArduinoLowPowerClass::attachInterruptWakeup(uint32_t pin, voidFuncPtr callback, uint32_t mode) { void ArduinoLowPowerClass::attachInterruptWakeup(uint32_t pin, voidFuncPtr callback, uint32_t mode) {
extern uint32_t sizeof_g_APinDescription; if( pin >= NUM_DIGITAL_PINS )
{
pmCB = callback;
switch (pin)
{
case AON_GPIO0:
enableAONGPIOInterrupt(0, mode);
break;
case AON_GPIO1:
enableAONGPIOInterrupt(1, mode);
break;
case AON_GPIO2:
enableAONGPIOInterrupt(2, mode);
break;
case AON_GPIO3:
enableAONGPIOInterrupt(3, mode);
break;
case INT_BMI160:
enableAONGPIOInterrupt(4, mode);
break;
case INT_BLE:
enableAONGPIOInterrupt(5, mode);
break;
default:
break;
};
}
}
if (pin > sizeof_g_APinDescription) { void ArduinoLowPowerClass::detachInterruptWakeup(uint32_t pin)
// check for external wakeup sources {
// RTC library should call this API to enable the alarm subsystem pmCB = NULL;
switch (pin) { if( pin >= NUM_DIGITAL_PINS )
case RTC_ALARM_WAKEUP: {
attachWakeInterruptRTC(callback); if(pin == INT_RTC)
break; {
case RESET_BUTTON_WAKEUP: interrupt_disable(IRQ_RTC_INTR);
gpio_cfg_data_t pin_cfg = { }
.gpio_type = GPIO_INTERRUPT, else if (pin == INT_COMP)
.int_type = LEVEL, {
.int_polarity = ACTIVE_LOW, interrupt_disable(IRQ_ALWAYS_ON_TMR);
.int_debounce = DEBOUNCE_OFF, }
.int_ls_sync = LS_SYNC_OFF, else if (pin == AON_TIMER)
.gpio_cb = callback {
}; interrupt_disable(IRQ_COMPARATORS_INTR);
soc_gpio_deconfig(SOC_GPIO_AON, 0); }
/* set RESET GPIO button to be an input */ else
soc_gpio_set_config(SOC_GPIO_AON, 0, &pin_cfg); {
SET_PIN_PULLUP(32*3 + 8, 1); interrupt_disable(IRQ_ALWAYS_ON_GPIO);
break; }
} }
return;
}
//pinMode(pin, INPUT_PULLUP);
//attachInterrupt(pin, callback, mode);
} }
ArduinoLowPowerClass LowPower; ArduinoLowPowerClass LowPower;

View File

@ -1,3 +1,4 @@
#define OSC0_STAT 0xB0800004
#define OSC0_CFG1 0xB0800008 #define OSC0_CFG1 0xB0800008
#define CCU_SS_PERIPH_CLK_GATE_CTL 0xB0800028 #define CCU_SS_PERIPH_CLK_GATE_CTL 0xB0800028
#define CCU_SYS_CLK_CTL 0xB0800038 #define CCU_SYS_CLK_CTL 0xB0800038
@ -5,18 +6,43 @@
#define P_LVL2 0xB0800504 #define P_LVL2 0xB0800504
#define PM1C 0xB0800518 #define PM1C 0xB0800518
#define SLP_CFG 0xB0800550 #define SLP_CFG 0xB0800550
#define SS_STS 0xB0800604
#define AONC_CNT 0xB0800700
#define AONC_CFG 0xB0800704
#define AONPT_CNT 0xB0800708
#define AONPT_STAT 0xB080070C
#define AONPT_CTRL 0xB0800710
#define AONPT_CFG 0xB0800714
#define USB_PLL_CFG0 0xB0800014 #define USB_PLL_CFG0 0xB0800014
#define USB_PHY_CFG0 0xB0800800 #define USB_PHY_CFG0 0xB0800800
#define RTC_CCVR 0xB0000400 #define RTC_CCVR (volatile int*)0xB0000400 // Current Counter Value Register
#define RTC_CMR 0xB0000404 #define RTC_CMR 0xB0000404
#define RTC_CCR 0xB000040C #define RTC_CCR 0xB000040C
#define RTC_EOI 0xB0000418 #define RTC_EOI 0xB0000418
#define RTC_MASK_INT 0xB0800478 #define RTC_MASK_INT 0xB0800478
#define AON_TIMER_MASK_INT 0xB08004C8
#define AON_GPIO_MASK_INT 0xB08004D4
#define AON_GPIO_SWPORTA_DR 0xB0800B00
#define AON_GPIO_SWPORTA_DDR 0xB0800B04
#define AON_GPIO_SWPORTA_CTL 0xB0800B08
#define AON_GPIO_INTEN 0xB0800B30
#define AON_GPIO_INTMASK 0xB0800B34
#define AON_GPIO_INTTYPE_LEVEL 0xB0800B38
#define AON_GPIO_INT_POL 0xB0800B3C
#define AON_GPIO_DEBOUNCE 0xB0888B48
#define AON_GPIO_PORTA_EOI 0xB0800B4C
#define OSCTRIM_ADDR 0xffffe1f8 #define OSCTRIM_ADDR 0xffffe1f8
#define QM_SS_SLEEP_MODE_CORE_OFF (0x0)
#define QM_SS_SLEEP_MODE_CORE_OFF_TIMER_OFF (0x20)
#define QM_SS_SLEEP_MODE_CORE_TIMERS_RTC_OFF (0x60)
#define SLEEP_HOST_C0 0 #define SLEEP_HOST_C0 0
#define SLEEP_HOST_C1 1 #define SLEEP_HOST_C1 1
#define SLEEP_HOST_C2 2 #define SLEEP_HOST_C2 2
@ -26,6 +52,20 @@
#define SLEEP_SS_SS2 6 #define SLEEP_SS_SS2 6
#define SLEEP_LPSS 7 #define SLEEP_LPSS 7
enum wakeSource{
AON_GPIO0 = 100,
AON_GPIO1 = 101,
AON_GPIO2 = 102,
AON_GPIO3 = 103,
AON_GPIO4 = 104,
AON_GPIO5 = 105,
INT_BMI160 = 104,
INT_BLE = 105,
INT_RTC = 106,
AON_TIMER = 107,
INT_COMP = 108,
};
/* Pin Muxing */ /* Pin Muxing */
#define PULLUP_BASE QRK_PMUX_PULLUP_0 #define PULLUP_BASE QRK_PMUX_PULLUP_0
/* Read current pull-up reg, Zero pin bit, OR new mode into these bits, write reg - thereby preserving other pin mode settings */ /* Read current pull-up reg, Zero pin bit, OR new mode into these bits, write reg - thereby preserving other pin mode settings */
@ -37,4 +77,6 @@
#include <Arduino.h> #include <Arduino.h>
#include <stdint.h> #include <stdint.h>
#include <interrupt.h> #include <interrupt.h>
#include <board.h> #include <board.h>
#include "qm_sensor_regs.h"
#include "ss_power_states.h"