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
#endif
#ifdef ARDUINO_ARCH_ARC32
#ifdef __ARDUINO_ARC__
#include "include/arc32/defines.h"
#endif
@ -39,9 +39,12 @@ class ArduinoLowPowerClass {
void attachInterruptWakeup(uint32_t pin, voidFuncPtr callback, uint32_t mode);
#ifdef ARDUINO_ARCH_ARC32
void wakeFromSleepCallback(void);
#endif
#ifdef __ARDUINO_ARC__
void wakeFromSleepCallback(void);
void wakeFromDoze(void);
void detachInterruptWakeup(uint32_t pin);
uint32_t arc_restore_addr;
#endif
private:
#ifdef ARDUINO_ARCH_SAMD
@ -50,24 +53,27 @@ class ArduinoLowPowerClass {
#define RTC_ALARM_WAKEUP 0xFF
#endif
#ifdef ARDUINO_ARCH_ARC32
void wakeFromDoze();
void switchToHybridOscillator();
void switchToCrystalOscillator();
void attachWakeInterruptRTC(void (*userCallBack)());
#ifdef __ARDUINO_ARC__
void turnOffUSB();
void turnOnUSB();
void setRTCCMR(int milliseconds);
uint32_t readRTC_CCVR();
bool isSleeping = false;
uint32_t millisToRTCTicks(int milliseconds);
void enableRTCInterrupt();
void x86_C2Request();
void (*pmCB)();
#define RTC_ALARM_WAKEUP 0xFF
void turnOnUSB();
void switchToHybridOscillator();
void switchToCrystalOscillator();
void setRTCCMR(int seconds);
uint32_t readRTC_CCVR();
bool isSleeping = false;
volatile bool dozing = false;
uint32_t millisToRTCTicks(int milliseconds);
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
#endif
};
extern ArduinoLowPowerClass LowPower;

View File

@ -33,21 +33,23 @@
#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)
{
*(uint32_t*)RTC_CCR |= 0xFFFFFFFE;
uint32_t rtc_eoi = *(uint32_t*)RTC_EOI; //clear match interrupt
LowPower.wakeFromSleepCallback();
unsigned int flags = interrupt_lock();
LowPower.wakeFromDoze();
LowPower.wakeFromSleepCallback();
interrupt_unlock(flags);
}
void ArduinoLowPowerClass::idle()
{
turnOffUSB();
dozing = true;
//switch from external crystal oscillator to internal hybrid oscilator
switchToHybridOscillator();
@ -62,9 +64,9 @@ void ArduinoLowPowerClass::idle()
void ArduinoLowPowerClass::idle(uint32_t duration)
{
idle();
delayTicks(millisToRTCTicks(duration));
wakeFromDoze();
idle();
delayTicks(millisToRTCTicks(duration));
wakeFromDoze();
}
void ArduinoLowPowerClass::wakeFromDoze()
@ -72,158 +74,273 @@ void ArduinoLowPowerClass::wakeFromDoze()
//Powerup hybrid oscillator
uint32_t current_val = *(uint32_t*)OSC0_CFG1;
*(uint32_t*)OSC0_CFG1 = current_val & 0xFFFFFFFB;
//Set system clock to the Hybrid Oscillator
current_val = *(uint32_t*)CCU_SYS_CLK_CTL;
*(uint32_t*)CCU_SYS_CLK_CTL = current_val | 0x00000001;
//switch back to the external crystal oiscillator
void switchToCrystalOscillator();
//switch back to the external crystal oscillator
switchToCrystalOscillator();
turnOnUSB();
dozing = false;
}
void ArduinoLowPowerClass::sleep()
{
turnOffUSB();
//*(uint32_t*)SLP_CFG &= 0xFFFFFEFF;
x86_C2Request();
isSleeping = true;
//*(uint32_t*)CCU_LP_CLK_CTL = (*(uint32_t*)CCU_LP_CLK_CTL) | 0x00000002;
//uint32_t c2 = *(uint32_t*)P_LVL2;
*(uint32_t*)PM1C |= 0x00002000;
uint32_t creg_mst0_ctrl = 0;
creg_mst0_ctrl = __builtin_arc_lr(QM_SS_CREG_BASE);
/*
* Clock gate the sensor peripherals at CREG level.
* This clock gating is independent of the peripheral-specific clock
* gating provided in ss_clk.h .
*/
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)
{
setRTCCMR(duration);
enableRTCInterrupt();
sleep();
enableAONPTimerInterrrupt(duration);
sleep();
}
void ArduinoLowPowerClass::deepSleep()
{
turnOffUSB();
x86_C2Request();
isSleeping = true;
*(uint32_t*)CCU_LP_CLK_CTL |= 0x00000001;
//uint32_t c2 = *(uint32_t*)P_LVL2;
*(uint32_t*)PM1C |= 0x00002000;
sleep();
}
void ArduinoLowPowerClass::deepSleep(uint32_t duration)
{
setRTCCMR(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;
sleep(duration);
}
inline void ArduinoLowPowerClass::wakeFromSleepCallback(void)
{
if(pmCB != NULL)
pmCB();
}
void ArduinoLowPowerClass::attachWakeInterruptRTC(void (*userCallBack)())
{
pmCB = userCallBack;
if(pmCB != NULL)
pmCB();
}
//Privates
void ArduinoLowPowerClass::turnOffUSB()
{
*(uint32_t*)USB_PHY_CFG0 |= 0x00000001;
*(uint32_t*)USB_PHY_CFG0 |= 0x00000001;
}
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);
//*(uint32_t*)RTC_CMR = readRTC_CCVR() + milliseconds;
//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;
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()
{
return *(uint32_t*)RTC_CCVR;
return *RTC_CCVR;
}
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;
*(uint32_t*)RTC_CCR |= 0x00000001;
*(uint32_t*)RTC_CCR &= 0xFFFFFFFD;
setRTCCMR(seconds);
*(uint32_t*)RTC_MASK_INT &= 0xFFFFFEFE;
*(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_connect(IRQ_RTC_INTR , &PM_InterruptHandler);
interrupt_enable(IRQ_RTC_INTR);
volatile uint32_t read = *(uint32_t*)RTC_EOI;
}
void ArduinoLowPowerClass::x86_C2Request()
{
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
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) {
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) {
// check for external wakeup sources
// RTC library should call this API to enable the alarm subsystem
switch (pin) {
case RTC_ALARM_WAKEUP:
attachWakeInterruptRTC(callback);
break;
case RESET_BUTTON_WAKEUP:
gpio_cfg_data_t pin_cfg = {
.gpio_type = GPIO_INTERRUPT,
.int_type = LEVEL,
.int_polarity = ACTIVE_LOW,
.int_debounce = DEBOUNCE_OFF,
.int_ls_sync = LS_SYNC_OFF,
.gpio_cb = callback
};
soc_gpio_deconfig(SOC_GPIO_AON, 0);
/* set RESET GPIO button to be an input */
soc_gpio_set_config(SOC_GPIO_AON, 0, &pin_cfg);
SET_PIN_PULLUP(32*3 + 8, 1);
break;
}
return;
}
//pinMode(pin, INPUT_PULLUP);
//attachInterrupt(pin, callback, mode);
void ArduinoLowPowerClass::detachInterruptWakeup(uint32_t pin)
{
pmCB = NULL;
if( pin >= NUM_DIGITAL_PINS )
{
if(pin == INT_RTC)
{
interrupt_disable(IRQ_RTC_INTR);
}
else if (pin == INT_COMP)
{
interrupt_disable(IRQ_ALWAYS_ON_TMR);
}
else if (pin == AON_TIMER)
{
interrupt_disable(IRQ_COMPARATORS_INTR);
}
else
{
interrupt_disable(IRQ_ALWAYS_ON_GPIO);
}
}
}
ArduinoLowPowerClass LowPower;

View File

@ -1,3 +1,4 @@
#define OSC0_STAT 0xB0800004
#define OSC0_CFG1 0xB0800008
#define CCU_SS_PERIPH_CLK_GATE_CTL 0xB0800028
#define CCU_SYS_CLK_CTL 0xB0800038
@ -5,18 +6,43 @@
#define P_LVL2 0xB0800504
#define PM1C 0xB0800518
#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_PHY_CFG0 0xB0800800
#define RTC_CCVR 0xB0000400
#define RTC_CCVR (volatile int*)0xB0000400 // Current Counter Value Register
#define RTC_CMR 0xB0000404
#define RTC_CCR 0xB000040C
#define RTC_EOI 0xB0000418
#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 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_C1 1
#define SLEEP_HOST_C2 2
@ -26,6 +52,20 @@
#define SLEEP_SS_SS2 6
#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 */
#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 */
@ -37,4 +77,6 @@
#include <Arduino.h>
#include <stdint.h>
#include <interrupt.h>
#include <board.h>
#include <board.h>
#include "qm_sensor_regs.h"
#include "ss_power_states.h"