From: Nathael Pajani Date: Sun, 30 Jan 2022 03:26:59 +0000 (+0100) Subject: Adding deep sleep support Improving sleep state support X-Git-Url: http://git.techno-innov.fr/?a=commitdiff_plain;h=c514612df9fe1c3c5fded71a746dca335bebefcd;p=soft%2Flpc82x%2Fcore Adding deep sleep support Improving sleep state support --- diff --git a/core/pmu.c b/core/pmu.c index 6bafb94..b3a6a3e 100644 --- a/core/pmu.c +++ b/core/pmu.c @@ -21,19 +21,34 @@ * *************************************************************************** */ +/* PMU unit */ #include "lib/stdint.h" #include "core/lpc_regs.h" #include "core/lpc_core.h" #include "core/system.h" +#include "core/watchdog.h" #include "core/pmu.h" #include "lib/errno.h" - -/* PMU unit */ +/* Read (read_gp_retain()) or Write (save_gp_retain()) to the general purpose + * retain registers. + * There are four 32 bits general purpose retain registers on the LPC82x uC. + * Data written to these registers is retained even across uC resets or + * reprogrammations so long as the uC remains powered. + * These functions read (or write) "nb" contiguous "values" from (or to) "offset" + * general purpose registers. + * - "values" is a user provided buffer for the data read from (or to be written to) + * the general purpose registers + * - "nb" is the buffer size. + * - "offset" is the first (starting at 0) general purpose register to be read + * from (or written to) + * "offset + nb" must be <= 4 + * The data is always placed or found in the first word of "values". + */ int save_gp_retain(uint32_t* values, uint8_t nb, uint8_t offset) { struct lpc_pm_unit* pmu = LPC_PMU; @@ -68,11 +83,14 @@ int read_gp_retain(uint32_t* values, uint8_t nb, uint8_t offset) * - do not enter "deep" sleep or power-down mode * - if "wake_sources_is_all_intrs" is set to 1, also wake up from unconfigured * interrupt sources + * This function does not turn off systick if it is activated, so the system will + * wake up on next tick. * This function returns after the next interrupt or event. */ void enter_sleep(int wake_sources_is_all_intrs) { struct syst_ctrl_block_regs* scb = LPC_SCB; + struct lpc_pm_unit* pmu = LPC_PMU; /* Configure SCR for sleep mode */ if (wake_sources_is_all_intrs == 1) { @@ -80,28 +98,103 @@ void enter_sleep(int wake_sources_is_all_intrs) } else { scb->scr = 0; } - wfe(); + pmu->power_ctrl = LPC_PM_DEFAULT; + wfi(); } +/* Enter permanent sleep state + * - Return to sleep mode after interrupt or event + * - do not enter "deep" sleep or power-down mode + * - if "wake_sources_is_all_intrs" is set to 1, also wake up from unconfigured + * interrupt sources + * This function does not turn off systick if it is activated. + * This function does not returns after the next interrupt or event, unless a call to + * exit_sleep() happened in an interrupt handler. + */ +void enter_continuous_sleep(int wake_sources_is_all_intrs) +{ + struct syst_ctrl_block_regs* scb = LPC_SCB; + struct lpc_pm_unit* pmu = LPC_PMU; + + /* Configure SCR for continuous sleep mode */ + if (wake_sources_is_all_intrs == 1) { + /* Wakeup from any interrupt, even those not enabled */ + scb->scr = SCB_SCR_SEVONPEND | SCB_SCR_SLEEPONEXIT; + } else { + scb->scr = SCB_SCR_SLEEPONEXIT; + } + pmu->power_ctrl = LPC_PM_DEFAULT; + wfi(); +} + +/* Exit permanent sleep state + * Can be called from an interrupt handler to get back to thread mode at the end of + * the handler execution. + */ +void exit_sleep(void) +{ + struct syst_ctrl_block_regs* scb = LPC_SCB; + scb->scr &= ~SCB_SCR_SLEEPONEXIT; +} /* Enter deep sleep. * NOTE : entering deep sleep implies a lot of side effects. I'll try to list them all here * so this can be done right. - * + * - The PLL is stopped, system must switch back to IRC before entering deep sleep + * - The IRC is running, but it's output is disabled. + * - Memory retains it's state + * - Flash stays ON + * - The peripherals will be stopped + * - Systick will be stopped + * - If using the Self Wake-Up timer as wake-up source then it must be turned ON before + * calling the function, and it MUST use either the 10KHZ clock or the external pin clock. */ -void enter_deep_sleep(void) +void enter_deep_sleep(uint32_t wakeup_freq_sel, uint32_t self_wakeup) { struct lpc_sys_config* sys_config = LPC_SYS_CONFIG; + struct syst_ctrl_block_regs* scb = LPC_SCB; + struct lpc_pm_unit* pmu = LPC_PMU; + uint32_t tmp = ~(0); /* Ask for the same clock status when waking up */ sys_config->powerdown_wake_cfg = sys_config->powerdown_run_cfg; - /* Set deep_sleep config */ + + /* Set deep_sleep config (Watchdog and BrownOut detection running ?) */ if (system_brown_out_detection_enabled()) { - sys_config->powerdown_sleep_cfg = LPC_DEEP_SLEEP_CFG_NOWDTLOCK_BOD_ON; - } else { - sys_config->powerdown_sleep_cfg = LPC_DEEP_SLEEP_CFG_NOWDTLOCK_BOD_OFF; + tmp &= ~(LPC_POWER_DOWN_BOD); + } + if (watchdog_get_timer_val()) { + tmp &= ~(LPC_POWER_DOWN_WDT_OSC); } + sys_config->powerdown_sleep_cfg = tmp; + + /* Use Self Wake-Up timer to wake-up ? */ + if (self_wakeup != 0) { + self_wakeup_timer_start(self_wakeup); + } + + /* Turn off PLL and move to IRC */ + clock_config(FREQ_SEL_IRC); + /* Enter deep sleep */ - /* FIXME */ + pmu->power_ctrl = LPC_PM_DEEP_SLEEP; + scb->scr = SCB_SCR_SLEEPDEEP; + + wfi(); + + pmu->power_ctrl = LPC_PM_DEFAULT; + scb->scr = 0; + + /* Go back to PLL */ + clock_config(wakeup_freq_sel); +} + + + +/* Prevent deep power down entry */ +void prevent_deep_power_down(void) +{ + struct lpc_pm_unit* pmu = LPC_PMU; + pmu->power_ctrl |= LPC_PM_NO_DPD; } diff --git a/include/core/pmu.h b/include/core/pmu.h index d27f8fe..bea8966 100644 --- a/include/core/pmu.h +++ b/include/core/pmu.h @@ -39,23 +39,65 @@ * - do not enter "deep" sleep or power-down mode * - if "wake_sources_is_all_intrs" is set to 1, also wake up from unconfigured * interrupt sources + * This function does not turn off systick if it is activated, so the system will + * wake up on next tick. * This function returns after the next interrupt or event. */ void enter_sleep(int wake_sources_is_all_intrs); +/* Enter permanent sleep state + * - Return to sleep mode after interrupt or event + * - do not enter "deep" sleep or power-down mode + * - if "wake_sources_is_all_intrs" is set to 1, also wake up from unconfigured + * interrupt sources + * This function does not turn off systick if it is activated. + * This function does not returns after the next interrupt or event, unless a call to + * exit_sleep() happened in an interrupt handler. + */ +void enter_continuous_sleep(int wake_sources_is_all_intrs); + +/* Exit permanent sleep state + * Can be called from an interrupt handler to get back to thread mode at the end of + * the handler execution. + */ +void exit_sleep(void); /* Enter deep sleep. * NOTE : entering deep sleep implies a lot of side effects. I'll try to list them all here - * so this can be done right. - * - * Note : see remark about RTC and deep sleep in section 5.3.3 of UM10441 + * so this can be done right. + * - The PLL is stopped, system must switch back to IRC before entering deep sleep + * - The IRC is running, but it's output is disabled. + * - Memory retains it's state + * - Flash stays ON + * - The peripherals will be stopped + * - Systick will be stopped + * - If using the Self Wake-Up timer as wake-up source then it must be turned ON before + * calling the function, and it MUST use either the 10KHZ clock or the external pin clock. */ -void enter_deep_sleep(void); +void enter_deep_sleep(uint32_t wakeup_freq_sel, uint32_t self_wakeup); + + +/* Prevent deep power down entry */ +void prevent_deep_power_down(void); /* Read from or write to the general purpose retain registers * Values in these registers are kept over resets, whatever the reset source, so long as * the part remains powered. (Even accross watchdog resets or flashing the device !) + * + * Read (read_gp_retain()) or Write (save_gp_retain()) to the general purpose + * retain registers. + * There are four 32 bits general purpose retain registers on the LPC82x uC. + * Data written to these registers is retained even across uC resets or + * reprogrammations so long as the uC remains powered. + * These functions read (or write) "nb" contiguous "values" from (or to) general purpose + * registers, starting from "offset" general purpose register. + * - "values" is a user provided buffer for the data read from (or to be written to) + * the general purpose registers + * - "nb" is the buffer size. + * - "offset" is the first (starting at 0) general purpose register to be read + * from (or written to) + * "offset + nb" must be <= 4 */ int save_gp_retain(uint32_t* values, uint8_t nb, uint8_t offset); int read_gp_retain(uint32_t* values, uint8_t nb, uint8_t offset);