From: Nathael Pajani Date: Mon, 7 Jan 2019 22:21:37 +0000 (+0100) Subject: Add support for deep sleep Wake-up not fully implemented, use NVIC_SystemReset()... X-Git-Url: http://git.techno-innov.fr/?a=commitdiff_plain;h=02baa0fa263d5d56e810d6406f83b5707bce2641;p=soft%2Flpc122x%2Fcore Add support for deep sleep Wake-up not fully implemented, use NVIC_SystemReset() to reset the micro-controller if you do not want to fix the wake-up part. --- diff --git a/core/system.c b/core/system.c index c3e8f9c..02f3f5d 100644 --- a/core/system.c +++ b/core/system.c @@ -28,6 +28,8 @@ */ #include "core/system.h" +#include "core/pio.h" +#include "drivers/gpio.h" /* Private defines */ @@ -105,26 +107,177 @@ void system_set_default_power_state(void) sys_config->sys_AHB_clk_ctrl = LPC_SYS_ABH_CLK_CTRL_MEM_ALL; } -/* Enter deep sleep. - * NOTE : entering deep sleep implies a lot of side effects. I'll try to list them all here +/* Enter sleep. + * NOTE : entering sleep implies some 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 + * Note : sleep mode is described in section 4.7.2 of UM10441 */ -void enter_deep_sleep(void) +void enter_sleep(void) +{ + struct lpc_pm_unit* pmu = LPC_PMU; + struct syst_ctrl_block_regs* scb = LPC_SCB; + + /* Set sleep config */ + pmu->power_ctrl &= ~(LPC_DPD_EN); + scb->scr &= ~(SCB_SCR_SLEEPDEEP); + /* Enter sleep */ + wfi(); +} + +/* Enter deep sleep. + * IMPORTANT : Entering deep sleep implies a lot of side effects. I'll try to list them all + * here so this can be done right. + * + * If pc_wake_up (peripheral controlled wake up) is 1, the watchdog clock will be enabled + * and set to the lowest possible rate. + * If wdt_off is 1 and both pc_wake_up is 0 and watchdog clock is not locked, then the + * watchdog clock will be turned off and wakeup will only be possible using external + * interrupt sources or RTC wakeup + * + * The programmer has to handle all special cases such as locked watchdog clock source + * which would prevent deep sleep entry. + * Refer to chapter 4.8 of UM10441 for Deep-sleep mode details + * Refer to chapter 4.7.3 of UM10441 for indications about how to enter Deep-sleep mode. + * + * If external pin wakeup is used the programmer must provide a "WAKEUP_Handler". + * This handler may either be empty, or hold the waking related code. + * + * Before calling this function, the programmer must : + * stop the watchdog if it was configured (unless ths watchdog clock was locked) + * flush communication buffers + * configure used (and maybe unused) GPIO to output low in order to reduce power used + * during deep sleep + * turn off the systick_timer + * + * The code executed after the call to enter_deep_sleep() will run on the internal RC clock. + * The programmer has to restore the running state (system clock, PIO/GPIO configuration, + * systick timer, ...) according to his needs. + * Note that comparator input pins (port 0 pin 19 to 26) will have been turned to output low + * by the enter_deep_sleep() function. + * The easiest way to do so is to call NVIC_SystemReset() within the WAKEUP_Handler() + * callback, which will reset the micro-controller. + * This function will have to be completed to restore most of the system state, disable + * start logic interrupts, restore previous main clock, and so on. + * + * Note : See remark about RTC and deep sleep in section 5.3.3 of UM10441 + */ +#include "drivers/serial.h" +void enter_deep_sleep(int wdt_off, int pc_wake_up, int rtc_off, const struct wakeup_pin* wakers) { struct lpc_sys_config* sys_config = LPC_SYS_CONFIG; + struct lpc_pm_unit* pmu = LPC_PMU; + struct syst_ctrl_block_regs* scb = LPC_SCB; + struct lpc_watchdog* wdt = LPC_WDT; + struct lpc_sys_start_logic_ctrl* stlog = sys_config->start_log_ctrl; + uint32_t tmp = 0, old_sys_clk = 0; + int i = 0; + + /* Avoid any interference */ + lpc_disable_irq(); + + /* Do not try to turn off watchdog clock if it is locked */ + if (wdt->mode & LPC_WDT_CLK_POWER_LOCK) { + wdt_off = 0; + } + + /* Disable deep power down */ + pmu->power_ctrl &= ~(LPC_DPD_EN); + + /* Set deep-sleep config */ + if (wdt_off == 1) { + if (lpc_private.brown_out_detection_enabled) { + sys_config->powerdown_sleep_cfg = LPC_DEEP_SLEEP_CFG_WD_OFF_BOD_ON; + } else { + sys_config->powerdown_sleep_cfg = LPC_DEEP_SLEEP_CFG_WD_OFF_BOD_OFF; + } + } else { + if (lpc_private.brown_out_detection_enabled) { + sys_config->powerdown_sleep_cfg = LPC_DEEP_SLEEP_CFG_WD_ON_BOD_ON; + } else { + sys_config->powerdown_sleep_cfg = LPC_DEEP_SLEEP_CFG_WD_ON_BOD_OFF; + } + } /* Ask for the same clock status when waking up */ sys_config->powerdown_wake_cfg = sys_config->powerdown_run_cfg; - /* Set deep_sleep config */ - if (lpc_private.brown_out_detection_enabled) { - sys_config->powerdown_sleep_cfg = LPC_DEEP_SLEEP_CFG_NOWDTLOCK_BOD_ON; + + /* Change current clock source */ + if (pc_wake_up == 1) { + /* Switch to low-speed WDO (watchdog oscilator) */ + sys_config->powerdown_run_cfg &= ~(LPC_POWER_DOWN_WDT_OSC); + sys_config->main_clk_sel = LPC_MAIN_CLK_SRC_WATCHDOG_OSC; } else { - sys_config->powerdown_sleep_cfg = LPC_DEEP_SLEEP_CFG_NOWDTLOCK_BOD_OFF; + sys_config->main_clk_sel = LPC_MAIN_CLK_SRC_IRC_OSC; + } + sys_config->main_clk_upd_en = 0; + sys_config->main_clk_upd_en = 1; + while (!(sys_config->main_clk_upd_en & 0x01)); + + /* Configure start logic for GPIO */ + if (wakers != NULL) { + uint32_t ext_pin_bits = 0; + uint32_t ext_edges_rising = 0; + i = 0; + while (wakers[i].pin <= 11) { + ext_pin_bits |= LPC_STL(wakers[i].pin); + if (wakers[i].edge == EDGE_RISING) { /* Defined in drivers/gpio.h */ + ext_edges_rising |= LPC_STL(wakers[i].pin); + } + i++; + } + stlog[0].reset = ext_pin_bits; + stlog[0].edge_ctrl = ext_edges_rising; + stlog[0].signal_en = ext_pin_bits; + } + /* Enable the start logic interrupts in NVIC */ + if (wakers != NULL) { + i = 0; + while (wakers[i].pin <= 11) { + NVIC_EnableIRQ(wakers[i].pin); + i++; + } } + + /* FIXME : Configure start logic for RTC, Watchdog and BOD */ + + + /* Turn all comparator inputs to GPIO, set them to output low */ + { + const struct pio tmp_pins[] = { + LPC_GPIO_0_19, LPC_GPIO_0_20, LPC_GPIO_0_21, LPC_GPIO_0_22, + LPC_GPIO_0_23, LPC_GPIO_0_24, LPC_GPIO_0_25, LPC_GPIO_0_26, + }; + for (i = 0; i < 8; i++) { + config_gpio(&(tmp_pins[i]), LPC_IO_MODE_INACTIVE, GPIO_DIR_OUT, 0); + } + } + + /* Configure sys_AHB_clk_ctrl to leave only Watchdog clock running */ + tmp = LPC_SYS_ABH_CLK_CTRL_MEM_ALL; + if (wdt_off == 0) { + tmp |= LPC_SYS_ABH_CLK_CTRL_Watchdog; + } + if (rtc_off == 0) { + tmp |= LPC_SYS_ABH_CLK_CTRL_RTC; + } + old_sys_clk = sys_config->sys_AHB_clk_ctrl; + sys_config->sys_AHB_clk_ctrl = tmp; + /* Enter deep sleep */ + scb->scr |= SCB_SCR_SLEEPDEEP; + /* Turn interrupts on */ + lpc_enable_irq(); + wfi(); + + /* Avoid any interference ... again */ + lpc_disable_irq(); + /* Restore running state */ + sys_config->sys_AHB_clk_ctrl = old_sys_clk; /* FIXME */ + + /* Turn interrupts on once again */ + lpc_enable_irq(); } /* Enter deep power down. @@ -262,7 +415,7 @@ void clock_config(uint32_t freq_sel) /* And call all clock updaters */ propagate_main_clock(); - /* Turn interrupts on once again*/ + /* Turn interrupts on once again */ lpc_enable_irq(); } diff --git a/include/core/system.h b/include/core/system.h index b7be0bd..3959ad9 100644 --- a/include/core/system.h +++ b/include/core/system.h @@ -30,6 +30,7 @@ #include "core/lpc_regs.h" #include "core/lpc_core.h" #include "core/watchdog.h" +#include "core/pio.h" /***************************************************************************** */ @@ -42,13 +43,59 @@ void system_set_default_power_state(void); /***************************************************************************** */ /* Power */ /***************************************************************************** */ -/* Enter deep sleep. - * NOTE : entering deep sleep implies a lot of side effects. I'll try to list them all here +/* Enter sleep. + * NOTE : entering sleep implies some 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 + * Note : sleep mode is described in section 4.7.2 of UM10441 */ -void enter_deep_sleep(void); +void enter_sleep(void); + + +struct wakeup_pin { + uint8_t pin; + uint8_t edge; +}; +#define ARRAY_LAST_WAKER {0xFF, 0xFF} + +/* Enter deep sleep. + * IMPORTANT : Entering deep sleep implies a lot of side effects. I'll try to list them all + * here so this can be done right. + * + * If pc_wake_up (peripheral controlled wake up) is 1, the watchdog clock will be enabled + * and set to the lowest possible rate. + * If wdt_off is 1 and both pc_wake_up is 0 and watchdog clock is not locked, then the + * watchdog clock will be turned off and wakeup will only be possible using external + * interrupt sources or RTC wakeup + * + * The programmer has to handle all special cases such as locked watchdog clock source + * which would prevent deep sleep entry. + * Refer to chapter 4.8 of UM10441 for Deep-sleep mode details + * Refer to chapter 4.7.3 of UM10441 for indications about how to enter Deep-sleep mode. + * + * If external pin wakeup is used the programmer must provide a "WAKEUP_Handler". + * This handler may either be empty, or hold the waking related code. + * + * Before calling this function, the programmer must : + * stop the watchdog if it was configured (unless ths watchdog clock was locked) + * flush communication buffers + * configure used (and maybe unused) GPIO to output low in order to reduce power used + * during deep sleep + * turn off the systick_timer + * + * The code executed after the call to enter_deep_sleep() will run on the internal RC clock. + * The programmer has to restore the running state (system clock, PIO/GPIO configuration, + * systick timer, ...) according to his needs. + * Note that comparator input pins (port 0 pin 19 to 26) will have been turned to output low + * by the enter_deep_sleep() function. + * The easiest way to do so is to call NVIC_SystemReset() within the WAKEUP_Handler() + * callback, which will reset the micro-controller. + * This function will have to be completed to restore most of the system state, disable + * start logic interrupts, restore previous main clock, and so on. + * + * Note : See remark about RTC and deep sleep in section 5.3.3 of UM10441 + */ +void enter_deep_sleep(int wdt_off, int pc_wake_up, int rtc_off, const struct wakeup_pin* wakers); /* Enter deep power down. * NOTE : entering deep power down implies a lot of side effects. I'll try to list them all here @@ -128,6 +175,11 @@ struct lpc_sys_start_logic_ctrl volatile uint32_t reset; /* 0x08 : reset Register 0 (-/W) */ volatile uint32_t status; /* 0x0C : status Register 0 (R/-) */ }; +#define LPC_STLOGIC_EDGE_FALLING(x) 0x00 +#define LPC_STLOGIC_EDGE_RISING(x) (0x01 << (x)) +#define LPC_STL(x) (0x01 << (x)) + + struct lpc_sys_config { volatile uint32_t sys_mem_remap; /* 0x000 System memory remap (R/W) */ @@ -237,8 +289,10 @@ struct lpc_sys_config #define LPC_POWER_DOWN_SYSPLL (1 << 7) #define LPC_POWER_DOWN_COPARATOR (1 << 15) -#define LPC_DEEP_SLEEP_CFG_NOWDTLOCK_BOD_ON 0x0000FFF7 -#define LPC_DEEP_SLEEP_CFG_NOWDTLOCK_BOD_OFF 0x0000FFFF +#define LPC_DEEP_SLEEP_CFG_WD_OFF_BOD_ON 0x0000FFF7 +#define LPC_DEEP_SLEEP_CFG_WD_OFF_BOD_OFF 0x0000FFFF +#define LPC_DEEP_SLEEP_CFG_WD_ON_BOD_ON 0x0000FFB7 +#define LPC_DEEP_SLEEP_CFG_WD_ON_BOD_OFF 0x0000FFBF #define LPC_MAIN_CLK_SRC_IRC_OSC 0x00 #define LPC_MAIN_CLK_SRC_PLL_IN 0x01