micropython/cc3200/mods/pybsleep.c

718 lines
27 KiB
C

/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2015 Daniel Campora
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdint.h>
#include <string.h>
#include "py/mpstate.h"
#include MICROPY_HAL_H
#include "py/runtime.h"
#include "inc/hw_types.h"
#include "inc/hw_ints.h"
#include "inc/hw_nvic.h"
#include "inc/hw_common_reg.h"
#include "inc/hw_memmap.h"
#include "cc3200_asm.h"
#include "rom_map.h"
#include "interrupt.h"
#include "systick.h"
#include "prcm.h"
#include "spi.h"
#include "pin.h"
#include "pybsleep.h"
#include "mpirq.h"
#include "pybpin.h"
#include "simplelink.h"
#include "modnetwork.h"
#include "modwlan.h"
#include "osi.h"
#include "debug.h"
#include "mpexception.h"
#include "mperror.h"
#include "sleeprestore.h"
#include "serverstask.h"
#include "antenna.h"
#include "cryptohash.h"
#include "pybrtc.h"
/******************************************************************************
DECLARE PRIVATE CONSTANTS
******************************************************************************/
#define SPIFLASH_INSTR_READ_STATUS (0x05)
#define SPIFLASH_INSTR_DEEP_POWER_DOWN (0xB9)
#define SPIFLASH_STATUS_BUSY (0x01)
#define LPDS_UP_TIME (425) // 13 msec
#define LPDS_DOWN_TIME (98) // 3 msec
#define USER_OFFSET (131) // 4 smec
#define WAKEUP_TIME_LPDS (LPDS_UP_TIME + LPDS_DOWN_TIME + USER_OFFSET) // 20 msec
#define WAKEUP_TIME_HIB (32768) // 1 s
#define FORCED_TIMER_INTERRUPT_MS (PYB_RTC_MIN_ALARM_TIME_MS)
#define FAILED_SLEEP_DELAY_MS (FORCED_TIMER_INTERRUPT_MS * 2)
/******************************************************************************
DECLARE PRIVATE TYPES
******************************************************************************/
// storage memory for Cortex M4 registers
typedef struct {
uint32_t msp;
uint32_t psp;
uint32_t psr;
uint32_t primask;
uint32_t faultmask;
uint32_t basepri;
uint32_t control;
} arm_cm4_core_regs_t;
// storage memory for the NVIC registers
typedef struct {
uint32_t vector_table; // Vector Table Offset
uint32_t aux_ctrl; // Auxiliary control register
uint32_t int_ctrl_state; // Interrupt Control and State
uint32_t app_int; // Application Interrupt Reset control
uint32_t sys_ctrl; // System control
uint32_t config_ctrl; // Configuration control
uint32_t sys_pri_1; // System Handler Priority 1
uint32_t sys_pri_2; // System Handler Priority 2
uint32_t sys_pri_3; // System Handler Priority 3
uint32_t sys_hcrs; // System Handler control and state register
uint32_t systick_ctrl; // SysTick Control Status
uint32_t systick_reload; // SysTick Reload
uint32_t systick_calib; // SysTick Calibration
uint32_t int_en[6]; // Interrupt set enable
uint32_t int_priority[49]; // Interrupt priority
} nvic_reg_store_t;
typedef struct {
mp_obj_base_t base;
mp_obj_t obj;
WakeUpCB_t wakeup;
} pybsleep_obj_t;
typedef struct {
mp_obj_t gpio_lpds_wake_cb;
wlan_obj_t *wlan_obj;
pyb_rtc_obj_t *rtc_obj;
} pybsleep_data_t;
/******************************************************************************
DECLARE PRIVATE DATA
******************************************************************************/
STATIC const mp_obj_type_t pybsleep_type;
STATIC nvic_reg_store_t *nvic_reg_store;
STATIC pybsleep_data_t pybsleep_data = {NULL, NULL, NULL};
volatile arm_cm4_core_regs_t vault_arm_registers;
STATIC pybsleep_reset_cause_t pybsleep_reset_cause = PYB_SLP_PWRON_RESET;
STATIC pybsleep_wake_reason_t pybsleep_wake_reason = PYB_SLP_WAKED_PWRON;
/******************************************************************************
DECLARE PRIVATE FUNCTIONS
******************************************************************************/
STATIC pybsleep_obj_t *pybsleep_find (mp_obj_t obj);
STATIC void pybsleep_flash_powerdown (void);
STATIC NORETURN void pybsleep_suspend_enter (void);
void pybsleep_suspend_exit (void);
STATIC void pybsleep_obj_wakeup (void);
STATIC void PRCMInterruptHandler (void);
STATIC void pybsleep_iopark (bool hibernate);
STATIC bool setup_timer_lpds_wake (void);
STATIC bool setup_timer_hibernate_wake (void);
/******************************************************************************
DEFINE PUBLIC FUNCTIONS
******************************************************************************/
__attribute__ ((section (".boot")))
void pybsleep_pre_init (void) {
// allocate memory for nvic registers vault
ASSERT ((nvic_reg_store = mem_Malloc(sizeof(nvic_reg_store_t))) != NULL);
}
void pybsleep_init0 (void) {
// initialize the sleep objects list
mp_obj_list_init(&MP_STATE_PORT(pybsleep_obj_list), 0);
// register and enable the PRCM interrupt
osi_InterruptRegister(INT_PRCM, (P_OSI_INTR_ENTRY)PRCMInterruptHandler, INT_PRIORITY_LVL_1);
// disable all LPDS and hibernate wake up sources (WLAN is disabed/enabled before entering LDPS mode)
MAP_PRCMLPDSWakeupSourceDisable(PRCM_LPDS_GPIO);
MAP_PRCMLPDSWakeupSourceDisable(PRCM_LPDS_TIMER);
MAP_PRCMHibernateWakeupSourceDisable(PRCM_HIB_SLOW_CLK_CTR | PRCM_HIB_GPIO2 | PRCM_HIB_GPIO4 | PRCM_HIB_GPIO13 |
PRCM_HIB_GPIO17 | PRCM_HIB_GPIO11 | PRCM_HIB_GPIO24 | PRCM_HIB_GPIO26);
// check the reset casue (if it's soft reset, leave it as it is)
if (pybsleep_reset_cause != PYB_SLP_SOFT_RESET) {
switch (MAP_PRCMSysResetCauseGet()) {
case PRCM_POWER_ON:
pybsleep_reset_cause = PYB_SLP_PWRON_RESET;
break;
case PRCM_CORE_RESET:
case PRCM_MCU_RESET:
case PRCM_SOC_RESET:
pybsleep_reset_cause = PYB_SLP_HARD_RESET;
break;
case PRCM_WDT_RESET:
pybsleep_reset_cause = PYB_SLP_WDT_RESET;
break;
case PRCM_HIB_EXIT:
if (PRCMGetSpecialBit(PRCM_WDT_RESET_BIT)) {
pybsleep_reset_cause = PYB_SLP_WDT_RESET;
}
else {
pybsleep_reset_cause = PYB_SLP_HIB_RESET;
// set the correct wake reason
switch (MAP_PRCMHibernateWakeupCauseGet()) {
case PRCM_HIB_WAKEUP_CAUSE_SLOW_CLOCK:
pybsleep_wake_reason = PYB_SLP_WAKED_BY_RTC;
// TODO repeat the alarm
break;
case PRCM_HIB_WAKEUP_CAUSE_GPIO:
pybsleep_wake_reason = PYB_SLP_WAKED_BY_GPIO;
break;
default:
break;
}
}
break;
default:
break;
}
}
}
void pybsleep_signal_soft_reset (void) {
pybsleep_reset_cause = PYB_SLP_SOFT_RESET;
}
void pybsleep_add (const mp_obj_t obj, WakeUpCB_t wakeup) {
pybsleep_obj_t * sleep_obj = m_new_obj(pybsleep_obj_t);
sleep_obj->base.type = &pybsleep_type;
sleep_obj->obj = obj;
sleep_obj->wakeup = wakeup;
// remove it in case it was already registered
pybsleep_remove (obj);
mp_obj_list_append(&MP_STATE_PORT(pybsleep_obj_list), sleep_obj);
}
void pybsleep_remove (const mp_obj_t obj) {
pybsleep_obj_t *sleep_obj;
if ((sleep_obj = pybsleep_find(obj))) {
mp_obj_list_remove(&MP_STATE_PORT(pybsleep_obj_list), sleep_obj);
}
}
void pybsleep_set_gpio_lpds_callback (mp_obj_t cb_obj) {
pybsleep_data.gpio_lpds_wake_cb = cb_obj;
}
void pybsleep_set_wlan_obj (mp_obj_t wlan_obj) {
pybsleep_data.wlan_obj = (wlan_obj_t *)wlan_obj;
}
void pybsleep_set_rtc_obj (mp_obj_t rtc_obj) {
pybsleep_data.rtc_obj = (pyb_rtc_obj_t *)rtc_obj;
}
pybsleep_reset_cause_t pybsleep_get_reset_cause (void) {
return pybsleep_reset_cause;
}
/******************************************************************************
DEFINE PRIVATE FUNCTIONS
******************************************************************************/
STATIC pybsleep_obj_t *pybsleep_find (mp_obj_t obj) {
for (mp_uint_t i = 0; i < MP_STATE_PORT(pybsleep_obj_list).len; i++) {
// search for the object and then remove it
pybsleep_obj_t *sleep_obj = ((pybsleep_obj_t *)(MP_STATE_PORT(pybsleep_obj_list).items[i]));
if (sleep_obj->obj == obj) {
return sleep_obj;
}
}
return NULL;
}
STATIC void pybsleep_flash_powerdown (void) {
uint32_t status;
// Enable clock for SSPI module
MAP_PRCMPeripheralClkEnable(PRCM_SSPI, PRCM_RUN_MODE_CLK | PRCM_SLP_MODE_CLK);
// Reset SSPI at PRCM level and wait for reset to complete
MAP_PRCMPeripheralReset(PRCM_SSPI);
while(!MAP_PRCMPeripheralStatusGet(PRCM_SSPI));
// Reset SSPI at module level
MAP_SPIReset(SSPI_BASE);
// Configure SSPI module
MAP_SPIConfigSetExpClk (SSPI_BASE, PRCMPeripheralClockGet(PRCM_SSPI),
20000000, SPI_MODE_MASTER,SPI_SUB_MODE_0,
(SPI_SW_CTRL_CS |
SPI_4PIN_MODE |
SPI_TURBO_OFF |
SPI_CS_ACTIVELOW |
SPI_WL_8));
// Enable SSPI module
MAP_SPIEnable(SSPI_BASE);
// Enable chip select for the spi flash.
MAP_SPICSEnable(SSPI_BASE);
// Wait for the spi flash
do {
// Send the status register read instruction and read back a dummy byte.
MAP_SPIDataPut(SSPI_BASE, SPIFLASH_INSTR_READ_STATUS);
MAP_SPIDataGet(SSPI_BASE, &status);
// Write a dummy byte then read back the actual status.
MAP_SPIDataPut(SSPI_BASE, 0xFF);
MAP_SPIDataGet(SSPI_BASE, &status);
} while ((status & 0xFF) == SPIFLASH_STATUS_BUSY);
// Disable chip select for the spi flash.
MAP_SPICSDisable(SSPI_BASE);
// Start another CS enable sequence for Power down command.
MAP_SPICSEnable(SSPI_BASE);
// Send Deep Power Down command to spi flash
MAP_SPIDataPut(SSPI_BASE, SPIFLASH_INSTR_DEEP_POWER_DOWN);
// Disable chip select for the spi flash.
MAP_SPICSDisable(SSPI_BASE);
}
STATIC NORETURN void pybsleep_suspend_enter (void) {
// enable full RAM retention
MAP_PRCMSRAMRetentionEnable(PRCM_SRAM_COL_1 | PRCM_SRAM_COL_2 | PRCM_SRAM_COL_3 | PRCM_SRAM_COL_4, PRCM_SRAM_LPDS_RET);
// save the NVIC control registers
nvic_reg_store->vector_table = HWREG(NVIC_VTABLE);
nvic_reg_store->aux_ctrl = HWREG(NVIC_ACTLR);
nvic_reg_store->int_ctrl_state = HWREG(NVIC_INT_CTRL);
nvic_reg_store->app_int = HWREG(NVIC_APINT);
nvic_reg_store->sys_ctrl = HWREG(NVIC_SYS_CTRL);
nvic_reg_store->config_ctrl = HWREG(NVIC_CFG_CTRL);
nvic_reg_store->sys_pri_1 = HWREG(NVIC_SYS_PRI1);
nvic_reg_store->sys_pri_2 = HWREG(NVIC_SYS_PRI2);
nvic_reg_store->sys_pri_3 = HWREG(NVIC_SYS_PRI3);
nvic_reg_store->sys_hcrs = HWREG(NVIC_SYS_HND_CTRL);
// save the systick registers
nvic_reg_store->systick_ctrl = HWREG(NVIC_ST_CTRL);
nvic_reg_store->systick_reload = HWREG(NVIC_ST_RELOAD);
nvic_reg_store->systick_calib = HWREG(NVIC_ST_CAL);
// save the interrupt enable registers
uint32_t *base_reg_addr = (uint32_t *)NVIC_EN0;
for(int32_t i = 0; i < (sizeof(nvic_reg_store->int_en) / 4); i++) {
nvic_reg_store->int_en[i] = base_reg_addr[i];
}
// save the interrupt priority registers
base_reg_addr = (uint32_t *)NVIC_PRI0;
for(int32_t i = 0; i < (sizeof(nvic_reg_store->int_priority) / 4); i++) {
nvic_reg_store->int_priority[i] = base_reg_addr[i];
}
// switch off the heartbeat led (this makes sure it will blink as soon as we wake up)
mperror_heartbeat_switch_off();
// park the gpio pins
pybsleep_iopark(false);
// store the cpu registers
sleep_store();
// save the restore info and enter LPDS
MAP_PRCMLPDSRestoreInfoSet(vault_arm_registers.psp, (uint32_t)sleep_restore);
MAP_PRCMLPDSEnter();
// let the cpu fade away...
for ( ; ; );
}
void pybsleep_suspend_exit (void) {
// take the I2C semaphore
uint32_t reg = HWREG(COMMON_REG_BASE + COMMON_REG_O_I2C_Properties_Register);
reg = (reg & ~0x3) | 0x1;
HWREG(COMMON_REG_BASE + COMMON_REG_O_I2C_Properties_Register) = reg;
// take the GPIO semaphore
reg = HWREG(COMMON_REG_BASE + COMMON_REG_O_GPIO_properties_register);
reg = (reg & ~0x3FF) | 0x155;
HWREG(COMMON_REG_BASE + COMMON_REG_O_GPIO_properties_register) = reg;
// restore de NVIC control registers
HWREG(NVIC_VTABLE) = nvic_reg_store->vector_table;
HWREG(NVIC_ACTLR) = nvic_reg_store->aux_ctrl;
HWREG(NVIC_INT_CTRL) = nvic_reg_store->int_ctrl_state;
HWREG(NVIC_APINT) = nvic_reg_store->app_int;
HWREG(NVIC_SYS_CTRL) = nvic_reg_store->sys_ctrl;
HWREG(NVIC_CFG_CTRL) = nvic_reg_store->config_ctrl;
HWREG(NVIC_SYS_PRI1) = nvic_reg_store->sys_pri_1;
HWREG(NVIC_SYS_PRI2) = nvic_reg_store->sys_pri_2;
HWREG(NVIC_SYS_PRI3) = nvic_reg_store->sys_pri_3;
HWREG(NVIC_SYS_HND_CTRL) = nvic_reg_store->sys_hcrs;
// restore the systick register
HWREG(NVIC_ST_CTRL) = nvic_reg_store->systick_ctrl;
HWREG(NVIC_ST_RELOAD) = nvic_reg_store->systick_reload;
HWREG(NVIC_ST_CAL) = nvic_reg_store->systick_calib;
// restore the interrupt priority registers
uint32_t *base_reg_addr = (uint32_t *)NVIC_PRI0;
for (uint32_t i = 0; i < (sizeof(nvic_reg_store->int_priority) / 4); i++) {
base_reg_addr[i] = nvic_reg_store->int_priority[i];
}
// restore the interrupt enable registers
base_reg_addr = (uint32_t *)NVIC_EN0;
for(uint32_t i = 0; i < (sizeof(nvic_reg_store->int_en) / 4); i++) {
base_reg_addr[i] = nvic_reg_store->int_en[i];
}
HAL_INTRODUCE_SYNC_BARRIER();
// ungate the clock to the shared spi bus
MAP_PRCMPeripheralClkEnable(PRCM_SSPI, PRCM_RUN_MODE_CLK | PRCM_SLP_MODE_CLK);
#if MICROPY_HW_ANTENNA_DIVERSITY
// re-configure the antenna selection pins
antenna_init0();
#endif
// reinitialize simplelink's interface
sl_IfOpen (NULL, 0);
// restore the configuration of all active peripherals
pybsleep_obj_wakeup();
// reconfigure all the previously enabled interrupts
mp_irq_wake_all();
// we need to init the crypto hash engine again
CRYPTOHASH_Init();
// trigger a sw interrupt
MAP_IntPendSet(INT_PRCM);
// force an exception to go back to the point where suspend mode was entered
nlr_raise(mp_obj_new_exception(&mp_type_SystemExit));
}
STATIC void PRCMInterruptHandler (void) {
// reading the interrupt status automatically clears the interrupt
if (PRCM_INT_SLOW_CLK_CTR == MAP_PRCMIntStatus()) {
// reconfigure it again (if repeat is true)
pyb_rtc_repeat_alarm (pybsleep_data.rtc_obj);
pybsleep_data.rtc_obj->irq_flags = PYB_RTC_ALARM0;
// need to check if irq's are enabled from the user point of view
if (pybsleep_data.rtc_obj->irq_enabled && (pybsleep_data.rtc_obj->pwrmode & PYB_PWR_MODE_ACTIVE)) {
mp_irq_handler(pybsleep_data.rtc_obj->irq_obj);
}
pybsleep_data.rtc_obj->irq_flags = 0;
} else {
// interrupt has been triggered while waking up from LPDS
switch (MAP_PRCMLPDSWakeupCauseGet()) {
case PRCM_LPDS_HOST_IRQ:
pybsleep_data.wlan_obj->irq_flags = MODWLAN_WIFI_EVENT_ANY;
mp_irq_handler(pybsleep_data.wlan_obj->irq_obj);
pybsleep_wake_reason = PYB_SLP_WAKED_BY_WLAN;
pybsleep_data.wlan_obj->irq_flags = 0;
break;
case PRCM_LPDS_GPIO:
mp_irq_handler(pybsleep_data.gpio_lpds_wake_cb);
pybsleep_wake_reason = PYB_SLP_WAKED_BY_GPIO;
break;
case PRCM_LPDS_TIMER:
// reconfigure it again if repeat is true
pyb_rtc_repeat_alarm (pybsleep_data.rtc_obj);
pybsleep_data.rtc_obj->irq_flags = PYB_RTC_ALARM0;
// next one clears the wake cause flag
MAP_PRCMLPDSWakeupSourceDisable(PRCM_LPDS_TIMER);
mp_irq_handler(pybsleep_data.rtc_obj->irq_obj);
pybsleep_data.rtc_obj->irq_flags = 0;
pybsleep_wake_reason = PYB_SLP_WAKED_BY_RTC;
break;
default:
break;
}
}
}
STATIC void pybsleep_obj_wakeup (void) {
for (mp_uint_t i = 0; i < MP_STATE_PORT(pybsleep_obj_list).len; i++) {
pybsleep_obj_t *sleep_obj = ((pybsleep_obj_t *)MP_STATE_PORT(pybsleep_obj_list).items[i]);
sleep_obj->wakeup(sleep_obj->obj);
}
}
STATIC void pybsleep_iopark (bool hibernate) {
mp_map_t *named_map = mp_obj_dict_get_map((mp_obj_t)&pin_board_pins_locals_dict);
for (uint i = 0; i < named_map->used; i++) {
pin_obj_t * pin = (pin_obj_t *)named_map->table[i].value;
switch (pin->pin_num) {
#ifdef DEBUG
// skip the JTAG pins
case PIN_16:
case PIN_17:
case PIN_19:
case PIN_20:
break;
#endif
default:
// enable a weak pull-up if the pin is unused
if (!pin->used) {
MAP_PinConfigSet(pin->pin_num, pin->strength, PIN_TYPE_STD_PU);
}
if (hibernate) {
// make it an input
MAP_PinDirModeSet(pin->pin_num, PIN_DIR_MODE_IN);
}
break;
}
}
// park the sflash pins
HWREG(0x4402E0E8) &= ~(0x3 << 8);
HWREG(0x4402E0E8) |= (0x2 << 8);
HWREG(0x4402E0EC) &= ~(0x3 << 8);
HWREG(0x4402E0EC) |= (0x2 << 8);
HWREG(0x4402E0F0) &= ~(0x3 << 8);
HWREG(0x4402E0F0) |= (0x2 << 8);
HWREG(0x4402E0F4) &= ~(0x3 << 8);
HWREG(0x4402E0F4) |= (0x1 << 8);
// if the board has antenna diversity, only park the antenna
// selection pins when going into hibernation
#if MICROPY_HW_ANTENNA_DIVERSITY
if (hibernate) {
#endif
// park the antenna selection pins
// (tri-stated with pull down enabled)
HWREG(0x4402E108) = 0x00000E61;
HWREG(0x4402E10C) = 0x00000E61;
#if MICROPY_HW_ANTENNA_DIVERSITY
} else {
// park the antenna selection pins
// (tri-stated without changing the pull up/down resistors)
HWREG(0x4402E108) &= ~0x000000FF;
HWREG(0x4402E108) |= 0x00000C61;
HWREG(0x4402E10C) &= ~0x000000FF;
HWREG(0x4402E10C) |= 0x00000C61;
}
#endif
}
STATIC bool setup_timer_lpds_wake (void) {
uint64_t t_match, t_curr;
int64_t t_remaining;
// get the time remaining for the RTC timer to expire
t_match = MAP_PRCMSlowClkCtrMatchGet();
t_curr = MAP_PRCMSlowClkCtrGet();
// get the time remaining in terms of slow clocks
t_remaining = (t_match - t_curr);
if (t_remaining > WAKEUP_TIME_LPDS) {
// subtract the time it takes to wakeup from lpds
t_remaining -= WAKEUP_TIME_LPDS;
t_remaining = (t_remaining > 0xFFFFFFFF) ? 0xFFFFFFFF: t_remaining;
// setup the LPDS wake time
MAP_PRCMLPDSIntervalSet((uint32_t)t_remaining);
// enable the wake source
MAP_PRCMLPDSWakeupSourceEnable(PRCM_LPDS_TIMER);
return true;
}
// disable the timer as wake source
MAP_PRCMLPDSWakeupSourceDisable(PRCM_LPDS_TIMER);
uint32_t f_seconds;
uint16_t f_mseconds;
// setup a timer interrupt immediately
pyb_rtc_calc_future_time (FORCED_TIMER_INTERRUPT_MS, &f_seconds, &f_mseconds);
MAP_PRCMRTCMatchSet(f_seconds, f_mseconds);
// LPDS wake by timer was not possible, force an interrupt in active mode instead
MAP_PRCMIntEnable(PRCM_INT_SLOW_CLK_CTR);
return false;
}
STATIC bool setup_timer_hibernate_wake (void) {
uint64_t t_match, t_curr;
int64_t t_remaining;
// get the time remaining for the RTC timer to expire
t_match = MAP_PRCMSlowClkCtrMatchGet();
t_curr = MAP_PRCMSlowClkCtrGet();
// get the time remaining in terms of slow clocks
t_remaining = (t_match - t_curr);
if (t_remaining > WAKEUP_TIME_HIB) {
// subtract the time it takes for wakeup from hibernate
t_remaining -= WAKEUP_TIME_HIB;
// setup the LPDS wake time
MAP_PRCMHibernateIntervalSet((uint32_t)t_remaining);
// enable the wake source
MAP_PRCMHibernateWakeupSourceEnable(PRCM_HIB_SLOW_CLK_CTR);
return true;
}
// disable the timer as wake source
MAP_PRCMLPDSWakeupSourceDisable(PRCM_HIB_SLOW_CLK_CTR);
uint32_t f_seconds;
uint16_t f_mseconds;
// setup a timer interrupt immediately
pyb_rtc_calc_future_time (FORCED_TIMER_INTERRUPT_MS, &f_seconds, &f_mseconds);
MAP_PRCMRTCMatchSet(f_seconds, f_mseconds);
// LPDS wake by timer was not possible, force an interrupt in active mode instead
MAP_PRCMIntEnable(PRCM_INT_SLOW_CLK_CTR);
return false;
}
/******************************************************************************/
// Micro Python bindings; Sleep class
/// \function idle()
/// Gates the processor clock until an interrupt is triggered
STATIC mp_obj_t pyb_sleep_idle (mp_obj_t self_in) {
__WFI();
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_sleep_idle_obj, pyb_sleep_idle);
/// \function suspend(wlan)
/// Enters suspended mode. Wake up sources should have been enable prior to
/// calling this method.
STATIC mp_obj_t pyb_sleep_suspend (mp_obj_t self_in) {
nlr_buf_t nlr;
// check if we should enable timer wake-up
if (pybsleep_data.rtc_obj->irq_enabled && (pybsleep_data.rtc_obj->pwrmode & PYB_PWR_MODE_LPDS)) {
if (!setup_timer_lpds_wake()) {
// lpds entering is not possible, wait for the forced interrupt and return
HAL_Delay (FAILED_SLEEP_DELAY_MS);
return mp_const_none;
}
} else {
// disable the timer as wake source
MAP_PRCMLPDSWakeupSourceDisable(PRCM_LPDS_TIMER);
}
// do we need network wake-up?
if (pybsleep_data.wlan_obj->irq_enabled) {
MAP_PRCMLPDSWakeupSourceEnable (PRCM_LPDS_HOST_IRQ);
server_sleep_sockets();
} else {
MAP_PRCMLPDSWakeupSourceDisable (PRCM_LPDS_HOST_IRQ);
}
// entering and exiting suspended mode must be an atomic operation
// therefore interrupts need to be disabled
uint primsk = disable_irq();
if (nlr_push(&nlr) == 0) {
pybsleep_suspend_enter();
nlr_pop();
}
// an exception is always raised when exiting suspend mode
enable_irq(primsk);
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_sleep_suspend_obj, pyb_sleep_suspend);
/// \function hibernate()
/// Enters hibernate mode. Wake up sources should have been enable prior to
/// calling this method.
STATIC mp_obj_t pyb_sleep_hibernate (mp_obj_t self_in) {
// check if we should enable timer wake-up
if (pybsleep_data.rtc_obj->irq_enabled && (pybsleep_data.rtc_obj->pwrmode & PYB_PWR_MODE_HIBERNATE)) {
if (!setup_timer_hibernate_wake()) {
// hibernating is not possible, wait for the forced interrupt and return
HAL_Delay (FAILED_SLEEP_DELAY_MS);
return mp_const_none;
}
} else {
// disable the timer as hibernate wake source
MAP_PRCMLPDSWakeupSourceDisable(PRCM_HIB_SLOW_CLK_CTR);
}
wlan_stop(SL_STOP_TIMEOUT);
pybsleep_flash_powerdown();
// must be done just before entering hibernate mode
pybsleep_iopark(true);
MAP_PRCMHibernateEnter();
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_sleep_hibernate_obj, pyb_sleep_hibernate);
/// \function reset_cause()
/// Returns the last reset casue
STATIC mp_obj_t pyb_sleep_reset_cause (mp_obj_t self_in) {
return MP_OBJ_NEW_SMALL_INT(pybsleep_reset_cause);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_sleep_reset_cause_obj, pyb_sleep_reset_cause);
/// \function wake_reason()
/// Returns the wake up reson from ldps or hibernate
STATIC mp_obj_t pyb_sleep_wake_reason (mp_obj_t self_in) {
return MP_OBJ_NEW_SMALL_INT(pybsleep_wake_reason);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_sleep_wake_reason_obj, pyb_sleep_wake_reason);
STATIC const mp_map_elem_t pybsleep_locals_dict_table[] = {
// instance methods
{ MP_OBJ_NEW_QSTR(MP_QSTR_idle), (mp_obj_t)&pyb_sleep_idle_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_suspend), (mp_obj_t)&pyb_sleep_suspend_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_hibernate), (mp_obj_t)&pyb_sleep_hibernate_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_reset_cause), (mp_obj_t)&pyb_sleep_reset_cause_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_wake_reason), (mp_obj_t)&pyb_sleep_wake_reason_obj },
// class constants
{ MP_OBJ_NEW_QSTR(MP_QSTR_ACTIVE), MP_OBJ_NEW_SMALL_INT(PYB_PWR_MODE_ACTIVE) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_SUSPENDED), MP_OBJ_NEW_SMALL_INT(PYB_PWR_MODE_LPDS) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_HIBERNATING), MP_OBJ_NEW_SMALL_INT(PYB_PWR_MODE_HIBERNATE) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_POWER_ON), MP_OBJ_NEW_SMALL_INT(PYB_SLP_PWRON_RESET) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_HARD_RESET), MP_OBJ_NEW_SMALL_INT(PYB_SLP_HARD_RESET) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_WDT_RESET), MP_OBJ_NEW_SMALL_INT(PYB_SLP_WDT_RESET) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_HIB_RESET), MP_OBJ_NEW_SMALL_INT(PYB_SLP_HIB_RESET) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_SOFT_RESET), MP_OBJ_NEW_SMALL_INT(PYB_SLP_SOFT_RESET) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_WLAN_WAKE), MP_OBJ_NEW_SMALL_INT(PYB_SLP_WAKED_BY_WLAN) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_PIN_WAKE), MP_OBJ_NEW_SMALL_INT(PYB_SLP_WAKED_BY_GPIO) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_RTC_WAKE), MP_OBJ_NEW_SMALL_INT(PYB_SLP_WAKED_BY_RTC) },
};
STATIC MP_DEFINE_CONST_DICT(pybsleep_locals_dict, pybsleep_locals_dict_table);
STATIC const mp_obj_type_t pybsleep_type = {
{ &mp_type_type },
.name = MP_QSTR_Sleep,
.locals_dict = (mp_obj_t)&pybsleep_locals_dict,
};
const mp_obj_base_t pyb_sleep_obj = {&pybsleep_type};