From 3b854fe46a0216d7fbc7e6f5248bfb837fb85f95 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 8 Dec 2016 22:22:10 +0800 Subject: [PATCH] deep sleep: implement wake up using ULP, EXT0, EXT1 sources This adds the following APIs to enable various wakeup sources: - esp_deep_sleep_enable_timer_wakeup - esp_deep_sleep_enable_ulp_wakeup - esp_deep_sleep_enable_ext0_wakeup - esp_deep_sleep_enable_ext1_wakeup And an API to start deep sleep: - esp_deep_sleep_start --- components/driver/gpio.c | 2 +- components/driver/include/driver/rtc_io.h | 2 + components/driver/rtc_module.c | 80 ++++++------- components/esp32/deepsleep.c | 134 ++++++++++++++++++++-- components/esp32/include/esp_deepsleep.h | 107 +++++++++++++++-- components/esp32/test/test_deepsleep.c | 75 ++++++++++++ 6 files changed, 339 insertions(+), 61 deletions(-) create mode 100644 components/esp32/test/test_deepsleep.c diff --git a/components/driver/gpio.c b/components/driver/gpio.c index 1f916921b..4bdd716f3 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -77,7 +77,7 @@ esp_err_t gpio_pullup_en(gpio_num_t gpio_num) { if(RTC_GPIO_IS_VALID_GPIO(gpio_num)){ rtc_gpio_pullup_en(gpio_num); }else{ - REG_SET_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PU); + REG_SET_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PU); } return ESP_OK; } diff --git a/components/driver/include/driver/rtc_io.h b/components/driver/include/driver/rtc_io.h index 2e351cf13..04f699e50 100644 --- a/components/driver/include/driver/rtc_io.h +++ b/components/driver/include/driver/rtc_io.h @@ -33,6 +33,8 @@ typedef struct { uint32_t ie; /*!< Mask of input enable */ uint32_t pullup; /*!< Mask of pullup enable */ uint32_t pulldown; /*!< Mask of pulldown enable */ + uint32_t slpsel; /*!< Mask of the bit to select pin as wakeup pin */ + uint32_t slpie; /*!< Mask of input enable in sleep mode */ int rtc_num; /*!< RTC IO number, or -1 if not an RTC GPIO */ } rtc_gpio_desc_t; diff --git a/components/driver/rtc_module.c b/components/driver/rtc_module.c index ecb7e434d..804ad6c0d 100644 --- a/components/driver/rtc_module.c +++ b/components/driver/rtc_module.c @@ -40,46 +40,46 @@ portMUX_TYPE rtc_spinlock = portMUX_INITIALIZER_UNLOCKED; //Reg,Mux,Fun,IE,Up,Down,Rtc_number const rtc_gpio_desc_t rtc_gpio_desc[GPIO_PIN_COUNT] = { - {RTC_IO_TOUCH_PAD1_REG, RTC_IO_TOUCH_PAD1_MUX_SEL_M, RTC_IO_TOUCH_PAD1_FUN_SEL_S, RTC_IO_TOUCH_PAD1_FUN_IE_M, RTC_IO_TOUCH_PAD1_RUE_M, RTC_IO_TOUCH_PAD1_RDE_M, 11}, //0 - {0, 0, 0, 0, 0, 0, -1}, //1 - {RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_MUX_SEL_M, RTC_IO_TOUCH_PAD2_FUN_SEL_S, RTC_IO_TOUCH_PAD2_FUN_IE_M, RTC_IO_TOUCH_PAD2_RUE_M, RTC_IO_TOUCH_PAD2_RDE_M, 12}, //2 - {0, 0, 0, 0, 0, 0, -1}, //3 - {RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_MUX_SEL_M, RTC_IO_TOUCH_PAD0_FUN_SEL_S, RTC_IO_TOUCH_PAD0_FUN_IE_M, RTC_IO_TOUCH_PAD0_RUE_M, RTC_IO_TOUCH_PAD0_RDE_M, 10}, //4 - {0, 0, 0, 0, 0, 0, -1}, //5 - {0, 0, 0, 0, 0, 0, -1}, //6 - {0, 0, 0, 0, 0, 0, -1}, //7 - {0, 0, 0, 0, 0, 0, -1}, //8 - {0, 0, 0, 0, 0, 0, -1}, //9 - {0, 0, 0, 0, 0, 0, -1}, //10 - {0, 0, 0, 0, 0, 0, -1}, //11 - {RTC_IO_TOUCH_PAD5_REG, RTC_IO_TOUCH_PAD5_MUX_SEL_M, RTC_IO_TOUCH_PAD5_FUN_SEL_S, RTC_IO_TOUCH_PAD5_FUN_IE_M, RTC_IO_TOUCH_PAD5_RUE_M, RTC_IO_TOUCH_PAD5_RDE_M, 15}, //12 - {RTC_IO_TOUCH_PAD4_REG, RTC_IO_TOUCH_PAD4_MUX_SEL_M, RTC_IO_TOUCH_PAD4_FUN_SEL_S, RTC_IO_TOUCH_PAD4_FUN_IE_M, RTC_IO_TOUCH_PAD4_RUE_M, RTC_IO_TOUCH_PAD4_RDE_M, 14}, //13 - {RTC_IO_TOUCH_PAD6_REG, RTC_IO_TOUCH_PAD6_MUX_SEL_M, RTC_IO_TOUCH_PAD6_FUN_SEL_S, RTC_IO_TOUCH_PAD6_FUN_IE_M, RTC_IO_TOUCH_PAD6_RUE_M, RTC_IO_TOUCH_PAD6_RDE_M, 16}, //14 - {RTC_IO_TOUCH_PAD3_REG, RTC_IO_TOUCH_PAD3_MUX_SEL_M, RTC_IO_TOUCH_PAD3_FUN_SEL_S, RTC_IO_TOUCH_PAD3_FUN_IE_M, RTC_IO_TOUCH_PAD3_RUE_M, RTC_IO_TOUCH_PAD3_RDE_M, 13}, //15 - {0, 0, 0, 0, 0, 0, -1}, //16 - {0, 0, 0, 0, 0, 0, -1}, //17 - {0, 0, 0, 0, 0, 0, -1}, //18 - {0, 0, 0, 0, 0, 0, -1}, //19 - {0, 0, 0, 0, 0, 0, -1}, //20 - {0, 0, 0, 0, 0, 0, -1}, //21 - {0, 0, 0, 0, 0, 0, -1}, //22 - {0, 0, 0, 0, 0, 0, -1}, //23 - {0, 0, 0, 0, 0, 0, -1}, //24 - {RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_MUX_SEL_M, RTC_IO_PDAC1_FUN_SEL_S, RTC_IO_PDAC1_FUN_IE_M, RTC_IO_PDAC1_RUE_M, RTC_IO_PDAC1_RDE_M, 6}, //25 - {RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_MUX_SEL_M, RTC_IO_PDAC2_FUN_SEL_S, RTC_IO_PDAC2_FUN_IE_M, RTC_IO_PDAC2_RUE_M, RTC_IO_PDAC2_RDE_M, 7}, //26 - {RTC_IO_TOUCH_PAD7_REG, RTC_IO_TOUCH_PAD7_MUX_SEL_M, RTC_IO_TOUCH_PAD7_FUN_SEL_S, RTC_IO_TOUCH_PAD7_FUN_IE_M, RTC_IO_TOUCH_PAD7_RUE_M, RTC_IO_TOUCH_PAD7_RDE_M, 17}, //27 - {0, 0, 0, 0, 0, 0, -1}, //28 - {0, 0, 0, 0, 0, 0, -1}, //29 - {0, 0, 0, 0, 0, 0, -1}, //30 - {0, 0, 0, 0, 0, 0, -1}, //31 - {RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_MUX_SEL_M, RTC_IO_X32P_FUN_SEL_S, RTC_IO_X32P_FUN_IE_M, RTC_IO_X32P_RUE_M, RTC_IO_X32P_RDE_M, 9}, //32 - {RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL_M, RTC_IO_X32N_FUN_SEL_S, RTC_IO_X32N_FUN_IE_M, RTC_IO_X32N_RUE_M, RTC_IO_X32N_RDE_M, 8}, //33 - {RTC_IO_ADC_PAD_REG, RTC_IO_ADC1_MUX_SEL_M, RTC_IO_ADC1_FUN_SEL_S, RTC_IO_ADC1_FUN_IE_M, 0, 0, 4}, //34 - {RTC_IO_ADC_PAD_REG, RTC_IO_ADC2_MUX_SEL_M, RTC_IO_ADC2_FUN_SEL_S, RTC_IO_ADC2_FUN_IE_M, 0, 0, 5}, //35 - {RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE1_MUX_SEL_M, RTC_IO_SENSE1_FUN_SEL_S, RTC_IO_SENSE1_FUN_IE_M, 0, 0, 0}, //36 - {RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE2_MUX_SEL_M, RTC_IO_SENSE2_FUN_SEL_S, RTC_IO_SENSE2_FUN_IE_M, 0, 0, 1}, //37 - {RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE3_MUX_SEL_M, RTC_IO_SENSE3_FUN_SEL_S, RTC_IO_SENSE3_FUN_IE_M, 0, 0, 2}, //38 - {RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE4_MUX_SEL_M, RTC_IO_SENSE4_FUN_SEL_S, RTC_IO_SENSE4_FUN_IE_M, 0, 0, 3}, //39 + {RTC_IO_TOUCH_PAD1_REG, RTC_IO_TOUCH_PAD1_MUX_SEL_M, RTC_IO_TOUCH_PAD1_FUN_SEL_S, RTC_IO_TOUCH_PAD1_FUN_IE_M, RTC_IO_TOUCH_PAD1_RUE_M, RTC_IO_TOUCH_PAD1_RDE_M, RTC_IO_TOUCH_PAD1_SLP_SEL_M, RTC_IO_TOUCH_PAD1_SLP_IE_M, 11}, //0 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //1 + {RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_MUX_SEL_M, RTC_IO_TOUCH_PAD2_FUN_SEL_S, RTC_IO_TOUCH_PAD2_FUN_IE_M, RTC_IO_TOUCH_PAD2_RUE_M, RTC_IO_TOUCH_PAD2_RDE_M, RTC_IO_TOUCH_PAD2_SLP_SEL_M, RTC_IO_TOUCH_PAD2_SLP_IE_M, 12}, //2 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //3 + {RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_MUX_SEL_M, RTC_IO_TOUCH_PAD0_FUN_SEL_S, RTC_IO_TOUCH_PAD0_FUN_IE_M, RTC_IO_TOUCH_PAD0_RUE_M, RTC_IO_TOUCH_PAD0_RDE_M, RTC_IO_TOUCH_PAD0_SLP_SEL_M, RTC_IO_TOUCH_PAD0_SLP_IE_M, 10}, //4 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //5 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //6 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //7 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //8 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //9 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //10 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //11 + {RTC_IO_TOUCH_PAD5_REG, RTC_IO_TOUCH_PAD5_MUX_SEL_M, RTC_IO_TOUCH_PAD5_FUN_SEL_S, RTC_IO_TOUCH_PAD5_FUN_IE_M, RTC_IO_TOUCH_PAD5_RUE_M, RTC_IO_TOUCH_PAD5_RDE_M, RTC_IO_TOUCH_PAD5_SLP_SEL_M, RTC_IO_TOUCH_PAD5_SLP_IE_M, 15}, //12 + {RTC_IO_TOUCH_PAD4_REG, RTC_IO_TOUCH_PAD4_MUX_SEL_M, RTC_IO_TOUCH_PAD4_FUN_SEL_S, RTC_IO_TOUCH_PAD4_FUN_IE_M, RTC_IO_TOUCH_PAD4_RUE_M, RTC_IO_TOUCH_PAD4_RDE_M, RTC_IO_TOUCH_PAD4_SLP_SEL_M, RTC_IO_TOUCH_PAD4_SLP_IE_M, 14}, //13 + {RTC_IO_TOUCH_PAD6_REG, RTC_IO_TOUCH_PAD6_MUX_SEL_M, RTC_IO_TOUCH_PAD6_FUN_SEL_S, RTC_IO_TOUCH_PAD6_FUN_IE_M, RTC_IO_TOUCH_PAD6_RUE_M, RTC_IO_TOUCH_PAD6_RDE_M, RTC_IO_TOUCH_PAD6_SLP_SEL_M, RTC_IO_TOUCH_PAD6_SLP_IE_M, 16}, //14 + {RTC_IO_TOUCH_PAD3_REG, RTC_IO_TOUCH_PAD3_MUX_SEL_M, RTC_IO_TOUCH_PAD3_FUN_SEL_S, RTC_IO_TOUCH_PAD3_FUN_IE_M, RTC_IO_TOUCH_PAD3_RUE_M, RTC_IO_TOUCH_PAD3_RDE_M, RTC_IO_TOUCH_PAD3_SLP_SEL_M, RTC_IO_TOUCH_PAD3_SLP_IE_M, 13}, //15 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //16 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //17 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //18 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //19 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //20 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //21 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //22 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //23 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //24 + {RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_MUX_SEL_M, RTC_IO_PDAC1_FUN_SEL_S, RTC_IO_PDAC1_FUN_IE_M, RTC_IO_PDAC1_RUE_M, RTC_IO_PDAC1_RDE_M, RTC_IO_PDAC1_SLP_SEL_M, RTC_IO_PDAC1_SLP_IE_M, 6}, //25 + {RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_MUX_SEL_M, RTC_IO_PDAC2_FUN_SEL_S, RTC_IO_PDAC2_FUN_IE_M, RTC_IO_PDAC2_RUE_M, RTC_IO_PDAC2_RDE_M, RTC_IO_PDAC2_SLP_SEL_M, RTC_IO_PDAC2_SLP_IE_M, 7}, //26 + {RTC_IO_TOUCH_PAD7_REG, RTC_IO_TOUCH_PAD7_MUX_SEL_M, RTC_IO_TOUCH_PAD7_FUN_SEL_S, RTC_IO_TOUCH_PAD7_FUN_IE_M, RTC_IO_TOUCH_PAD7_RUE_M, RTC_IO_TOUCH_PAD7_RDE_M, RTC_IO_TOUCH_PAD7_SLP_SEL_M, RTC_IO_TOUCH_PAD7_SLP_IE_M, 17}, //27 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //28 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //29 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //30 + {0, 0, 0, 0, 0, 0, 0, 0, -1}, //31 + {RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_MUX_SEL_M, RTC_IO_X32P_FUN_SEL_S, RTC_IO_X32P_FUN_IE_M, RTC_IO_X32P_RUE_M, RTC_IO_X32P_RDE_M, RTC_IO_X32P_SLP_SEL_M, RTC_IO_X32P_SLP_IE_M, 9}, //32 + {RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL_M, RTC_IO_X32N_FUN_SEL_S, RTC_IO_X32N_FUN_IE_M, RTC_IO_X32N_RUE_M, RTC_IO_X32N_RDE_M, RTC_IO_X32N_SLP_SEL_M, RTC_IO_X32N_SLP_IE_M, 8}, //33 + {RTC_IO_ADC_PAD_REG, RTC_IO_ADC1_MUX_SEL_M, RTC_IO_ADC1_FUN_SEL_S, RTC_IO_ADC1_FUN_IE_M, 0, 0, RTC_IO_ADC1_SLP_SEL_M, RTC_IO_ADC1_SLP_IE_M, 4}, //34 + {RTC_IO_ADC_PAD_REG, RTC_IO_ADC2_MUX_SEL_M, RTC_IO_ADC2_FUN_SEL_S, RTC_IO_ADC2_FUN_IE_M, 0, 0, RTC_IO_ADC2_SLP_SEL_M, RTC_IO_ADC2_SLP_IE_M, 5}, //35 + {RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE1_MUX_SEL_M, RTC_IO_SENSE1_FUN_SEL_S, RTC_IO_SENSE1_FUN_IE_M, 0, 0, RTC_IO_SENSE1_SLP_SEL_M, RTC_IO_SENSE1_SLP_IE_M, 0}, //36 + {RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE2_MUX_SEL_M, RTC_IO_SENSE2_FUN_SEL_S, RTC_IO_SENSE2_FUN_IE_M, 0, 0, RTC_IO_SENSE2_SLP_SEL_M, RTC_IO_SENSE2_SLP_IE_M, 1}, //37 + {RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE3_MUX_SEL_M, RTC_IO_SENSE3_FUN_SEL_S, RTC_IO_SENSE3_FUN_IE_M, 0, 0, RTC_IO_SENSE3_SLP_SEL_M, RTC_IO_SENSE3_SLP_IE_M, 2}, //38 + {RTC_IO_SENSOR_PADS_REG, RTC_IO_SENSE4_MUX_SEL_M, RTC_IO_SENSE4_FUN_SEL_S, RTC_IO_SENSE4_FUN_IE_M, 0, 0, RTC_IO_SENSE4_SLP_SEL_M, RTC_IO_SENSE4_SLP_IE_M, 3}, //39 }; /*--------------------------------------------------------------- diff --git a/components/esp32/deepsleep.c b/components/esp32/deepsleep.c index b8b73a3b1..cf9ee852e 100644 --- a/components/esp32/deepsleep.c +++ b/components/esp32/deepsleep.c @@ -1,7 +1,17 @@ -/* Wake from deep sleep stub +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. - See esp_deepsleep.h esp_wake_deep_sleep() comments for details. -*/ #include #include #include "rom/cache.h" @@ -10,12 +20,24 @@ #include "soc/dport_reg.h" #include "esp_attr.h" #include "esp_deepsleep.h" +#include "esp_log.h" +#include "soc/cpu.h" #include "rtc.h" +#include "driver/rtc_io.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" /* Updating RTC_MEMORY_CRC_REG register via set_rtc_memory_crc() is not thread-safe. */ static _lock_t lock_rtc_memory_crc; +static uint32_t s_wakeup_options = 0; +static uint64_t s_sleep_duration = 0; +static const char* TAG = "deepsleep"; + +/* Wake from deep sleep stub + See esp_deepsleep.h esp_wake_deep_sleep() comments for details. +*/ esp_deep_sleep_wake_stub_fn_t esp_get_deep_sleep_wake_stub(void) { _lock_acquire(&lock_rtc_memory_crc); @@ -50,18 +72,112 @@ void __attribute__((weak, alias("esp_default_wake_deep_sleep"))) esp_wake_deep_s void esp_deep_sleep(uint64_t time_in_us) { - rtc_set_cpu_freq(CPU_XTAL); + esp_deep_sleep_enable_timer_wakeup(time_in_us); + esp_deep_sleep_start(); +} + +void IRAM_ATTR esp_deep_sleep_start() +{ if (esp_get_deep_sleep_wake_stub() == NULL) { esp_set_deep_sleep_wake_stub(esp_wake_deep_sleep); } - uint32_t period = rtc_slowck_cali(CALI_RTC_MUX, 128); - uint32_t cycle_l, cycle_h; - rtc_usec2rtc(time_in_us >> 32, time_in_us, period, &cycle_h, &cycle_l); - rtc_slp_prep_lite(1, 0); - rtc_sleep(cycle_h, cycle_l, TIMER_EXPIRE_EN, 0); + + rtc_set_cpu_freq(CPU_XTAL); + uint32_t cycle_h = 0; + uint32_t cycle_l = 0; + if (s_sleep_duration > 0) { + uint32_t period = rtc_slowck_cali(CALI_RTC_MUX, 128); + rtc_usec2rtc(s_sleep_duration >> 32, s_sleep_duration & 0xffffffff, period, &cycle_h, &cycle_l); + } + rtc_slp_prep_lite(DEEP_SLEEP_PD_NORMAL, 0); + rtc_sleep(cycle_h, cycle_l, s_wakeup_options, 0); while (1) { ; } } void system_deep_sleep(uint64_t) __attribute__((alias("esp_deep_sleep"))); + +esp_err_t esp_deep_sleep_enable_ulp_wakeup() +{ +#ifdef CONFIG_ULP_COPROC_ENABLED + s_wakeup_options |= SAR_TRIG_EN; + return ESP_OK; +#else + return ESP_ERR_INVALID_STATE; +#endif +} + +esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us) +{ + s_wakeup_options |= TIMER_EXPIRE_EN; + s_sleep_duration = time_in_us; + return ESP_OK; +} + +esp_err_t esp_deep_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level) +{ + if (level < 0 || level > 1) { + return ESP_ERR_INVALID_ARG; + } + if (!RTC_GPIO_IS_VALID_GPIO(gpio_num)) { + return ESP_ERR_INVALID_ARG; + } + const rtc_gpio_desc_t* desc = &rtc_gpio_desc[gpio_num]; + REG_SET_FIELD(RTC_IO_EXT_WAKEUP0_REG, RTC_IO_EXT_WAKEUP0_SEL, desc->rtc_num); + SET_PERI_REG_BITS(RTC_CNTL_EXT_WAKEUP_CONF_REG, 0x1, level, RTC_CNTL_EXT_WAKEUP0_LV_S); + REG_SET_BIT(desc->reg, desc->slpsel); + REG_SET_BIT(desc->reg, desc->slpie); + s_wakeup_options |= RTC_EXT_EVENT0_TRIG_EN; + return ESP_OK; +} + +esp_err_t esp_deep_sleep_enable_ext1_wakeup(uint64_t mask, esp_ext1_wakeup_mode_t mode) +{ + if (mode > EXT1_WAKEUP_ANY_HIGH) { + return ESP_ERR_INVALID_ARG; + } + // Translate bit map of GPIO numbers into the bit map of RTC IO numbers + uint32_t rtc_gpio_mask = 0; + for (int gpio = 0; mask; ++gpio, mask >>= 1) { + if ((mask & 1) == 0) { + continue; + } + if (!RTC_GPIO_IS_VALID_GPIO(gpio)) { + ESP_LOGE(TAG, "Not an RTC IO: GPIO%d", gpio); + return ESP_ERR_INVALID_ARG; + } + const rtc_gpio_desc_t* desc = &rtc_gpio_desc[gpio]; + int rtc_pin = desc->rtc_num; + rtc_gpio_mask |= BIT(rtc_pin); + REG_SET_BIT(desc->reg, desc->slpsel); + REG_SET_BIT(desc->reg, desc->slpie); + } + REG_SET_BIT(RTC_CNTL_EXT_WAKEUP1_REG, RTC_CNTL_EXT_WAKEUP1_STATUS_CLR); + REG_SET_FIELD(RTC_CNTL_EXT_WAKEUP1_REG, RTC_CNTL_EXT_WAKEUP1_SEL, rtc_gpio_mask); + SET_PERI_REG_BITS(RTC_CNTL_EXT_WAKEUP_CONF_REG, 0x1, mode, RTC_CNTL_EXT_WAKEUP1_LV_S); + s_wakeup_options |= RTC_EXT_EVENT1_TRIG_EN; + return ESP_OK; +} + +uint64_t esp_deep_sleep_get_ext1_wakeup_status() +{ + int wakeup_reason = REG_GET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_CAUSE); + if (wakeup_reason != RTC_EXT_EVENT1_TRIG) { + return 0; + } + uint32_t status = REG_GET_FIELD(RTC_CNTL_EXT_WAKEUP1_STATUS_REG, RTC_CNTL_EXT_WAKEUP1_STATUS); + // Translate bit map of RTC IO numbers into the bit map of GPIO numbers + uint64_t gpio_mask = 0; + for (int gpio = 0; gpio < 40; ++gpio) { + if (!RTC_GPIO_IS_VALID_GPIO(gpio)) { + continue; + } + int rtc_pin = rtc_gpio_desc[gpio].rtc_num; + if ((status & BIT(rtc_pin)) == 0) { + continue; + } + gpio_mask |= BIT(gpio); + } + return gpio_mask; +} diff --git a/components/esp32/include/esp_deepsleep.h b/components/esp32/include/esp_deepsleep.h index 051ad14c3..ab2e2cbc5 100644 --- a/components/esp32/include/esp_deepsleep.h +++ b/components/esp32/include/esp_deepsleep.h @@ -3,7 +3,7 @@ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at - +// // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software @@ -12,10 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef __ESP_DEEPSLEEP_H__ -#define __ESP_DEEPSLEEP_H_ +#pragma once #include +#include "esp_err.h" +#include "driver/gpio.h" #ifdef __cplusplus extern "C" { @@ -29,6 +30,14 @@ extern "C" { * @{ */ +/** + * @brief Logic function used for EXT1 wakeup mode. + */ +typedef enum { + EXT1_WAKEUP_ALL_LOW = 0, /*!< Wake the chip when all selected GPIOs go low */ + EXT1_WAKEUP_ANY_HIGH = 1 /*!< Wake the chip when any of the selected GPIOs go high */ +} esp_ext1_wakeup_mode_t; + /** * @brief Enter deep-sleep mode * @@ -36,12 +45,88 @@ extern "C" { * Upon waking up, the device calls deep sleep wake stub, and then proceeds * to load application. * + * Call to this function is equivalent to a call to esp_deep_sleep_enable_timer_wakeup + * followed by a call to esp_deep_sleep_start. + * * This function does not return. * * @param time_in_us deep-sleep time, unit: microsecond */ void esp_deep_sleep(uint64_t time_in_us) __attribute__((noreturn)); +/** + * @brief Enable wakeup by ULP coprocessor + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if ULP co-processor is not enabled. + */ +esp_err_t esp_deep_sleep_enable_ulp_wakeup(); + +/** + * @brief Enable wakeup by timer + * @param time_in_us time before wakeup, in microseconds + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if value is out of range (TBD) + */ +esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us); + +/** + * @brief Enable wakeup using a pin + * + * This function uses external wakeup feature of RTC_IO peripheral. + * It will work only if RTC peripherals are kept on during deep sleep. + * + * This feature can monitor any pin which is an RTC IO. Once the pin transitions + * into the state given by level argument, the chip will be woken up. + * + * @param gpio_num GPIO number used as wakeup source. Only GPIOs which are have RTC + * functionality can be used: 0,2,4,12-15,25-27,32-39. + * @param level input level which will trigger wakeup (0=low, 1=high) + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if either of the arguments is out of range + */ +esp_err_t esp_deep_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level); + +/** + * @brief Enable wakeup using multiple pins + * + * This function uses external wakeup feature of RTC controller. + * It will work even if RTC peripherals are shut down during deep sleep. + * + * This feature can monitor any number of pins which are in RTC IOs. + * Once any of the selected pins goes into the state given by level argument, + * the chip will be woken up. + * + * @param mask bit mask of GPIO numbers which will cause wakeup. Only GPIOs + * which are have RTC functionality can be used in this bit map: + * 0,2,4,12-15,25-27,32-39. + * @param mode select logic function used to determine wakeup condition: + * - EXT1_WAKEUP_ALL_LOW: wake up when all selected GPIOs are low + * - EXT1_WAKEUP_ANY_HIGH: wake up when any of the selected GPIOs is high + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if either of the arguments is out of range + */ +esp_err_t esp_deep_sleep_enable_ext1_wakeup(uint64_t mask, esp_ext1_wakeup_mode_t mode); + + +/** + * @brief Get the bit mask of GPIOs which caused wakeup (ext1) + * + * If wakeup was caused by another source, this function will return 0. + * + * @return bit mask, if GPIOn caused wakeup, BIT(n) will be set + */ +uint64_t esp_deep_sleep_get_ext1_wakeup_status(); + +/** + * @brief Enter deep sleep with the configured wakeup options + * + * This function does not return. + */ +void esp_deep_sleep_start() __attribute__((noreturn)); /** * @brief Enter deep-sleep mode @@ -87,15 +172,17 @@ typedef void (*esp_deep_sleep_wake_stub_fn_t)(void); void esp_set_deep_sleep_wake_stub(esp_deep_sleep_wake_stub_fn_t new_stub); /** - * @brief Return current wake from deep sleep stub, or NULL if - * no stub is installed. + * @brief Get current wake from deep sleep stub + * @return Return current wake from deep sleep stub, or NULL if + * no stub is installed. */ esp_deep_sleep_wake_stub_fn_t esp_get_deep_sleep_wake_stub(void); -/* The default esp-idf-provided esp_wake_deep_sleep() stub. - - See docs/deep-sleep-stub.rst for details. -*/ +/** + * @brief The default esp-idf-provided esp_wake_deep_sleep() stub. + * + * See docs/deep-sleep-stub.rst for details. + */ void esp_default_wake_deep_sleep(void); /** @@ -110,5 +197,3 @@ void esp_default_wake_deep_sleep(void); #ifdef __cplusplus } #endif - -#endif /* __ESP_SYSTEM_H__ */ diff --git a/components/esp32/test/test_deepsleep.c b/components/esp32/test/test_deepsleep.c new file mode 100644 index 000000000..fb05f01f1 --- /dev/null +++ b/components/esp32/test/test_deepsleep.c @@ -0,0 +1,75 @@ +#include "unity.h" +#include "esp_deepsleep.h" +#include "driver/rtc_io.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +TEST_CASE("esp_deepsleep works", "[deepsleep]") +{ + esp_deep_sleep(2000000); +} + +static void deep_sleep_task(void* arg) +{ + esp_deep_sleep_start(); +} + +static void do_deep_sleep_from_app_cpu() +{ + xTaskCreatePinnedToCore(&deep_sleep_task, "ds", 2048, NULL, 5, NULL, 1); + + // keep running some non-IRAM code + vTaskSuspendAll(); + while(true) { + ; + } +} + +TEST_CASE("can wake up from deep sleep using timer", "[deepsleep]") +{ + esp_deep_sleep_enable_timer_wakeup(2000000); + esp_deep_sleep_start(); +} + +TEST_CASE("go into deep sleep from APP CPU and wake up using timer", "[deepsleep]") +{ + esp_deep_sleep_enable_timer_wakeup(2000000); + do_deep_sleep_from_app_cpu(); +} + + +TEST_CASE("can wake up from deep sleep using ext0 (13 high)", "[deepsleep]") +{ + ESP_ERROR_CHECK(rtc_gpio_init(GPIO_NUM_13)); + ESP_ERROR_CHECK(gpio_pullup_dis(GPIO_NUM_13)); + ESP_ERROR_CHECK(gpio_pulldown_en(GPIO_NUM_13)); + ESP_ERROR_CHECK(esp_deep_sleep_enable_ext0_wakeup(GPIO_NUM_13, 1)); + esp_deep_sleep_start(); +} + +TEST_CASE("can wake up from deep sleep using ext0 (13 low)", "[deepsleep]") +{ + ESP_ERROR_CHECK(rtc_gpio_init(GPIO_NUM_13)); + ESP_ERROR_CHECK(gpio_pullup_en(GPIO_NUM_13)); + ESP_ERROR_CHECK(gpio_pulldown_dis(GPIO_NUM_13)); + ESP_ERROR_CHECK(esp_deep_sleep_enable_ext0_wakeup(GPIO_NUM_13, 0)); + esp_deep_sleep_start(); +} + +TEST_CASE("can wake up from deep sleep using ext1 (13 high)", "[deepsleep]") +{ + ESP_ERROR_CHECK(rtc_gpio_init(GPIO_NUM_13)); + ESP_ERROR_CHECK(gpio_pullup_dis(GPIO_NUM_13)); + ESP_ERROR_CHECK(gpio_pulldown_en(GPIO_NUM_13)); + ESP_ERROR_CHECK(esp_deep_sleep_enable_ext1_wakeup(BIT(GPIO_NUM_13), EXT1_WAKEUP_ANY_HIGH)); + esp_deep_sleep_start(); +} + +TEST_CASE("can wake up from deep sleep using ext1 (13 low)", "[deepsleep]") +{ + ESP_ERROR_CHECK(rtc_gpio_init(GPIO_NUM_13)); + ESP_ERROR_CHECK(gpio_pullup_en(GPIO_NUM_13)); + ESP_ERROR_CHECK(gpio_pulldown_dis(GPIO_NUM_13)); + ESP_ERROR_CHECK(esp_deep_sleep_enable_ext1_wakeup(BIT(GPIO_NUM_13), EXT1_WAKEUP_ALL_LOW)); + esp_deep_sleep_start(); +}