From cc13b0ea058e81d8f893d4883bd6818ba36b7f2c Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Tue, 24 Jan 2017 12:32:30 +0800 Subject: [PATCH 01/15] deep sleep: allow wakeup from touch sensor interrupt --- components/esp32/deep_sleep.c | 6 ++++++ components/esp32/include/esp_deep_sleep.h | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/components/esp32/deep_sleep.c b/components/esp32/deep_sleep.c index 4d672402e..1c2b60361 100644 --- a/components/esp32/deep_sleep.c +++ b/components/esp32/deep_sleep.c @@ -177,6 +177,12 @@ esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us) return ESP_OK; } +esp_err_t esp_deep_sleep_enable_touchpad_wakeup() +{ + s_config.wakeup_triggers |= TOUCH_TRIG_EN; + return ESP_OK; +} + esp_err_t esp_deep_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level) { if (level < 0 || level > 1) { diff --git a/components/esp32/include/esp_deep_sleep.h b/components/esp32/include/esp_deep_sleep.h index aba74b30d..fda075eb2 100644 --- a/components/esp32/include/esp_deep_sleep.h +++ b/components/esp32/include/esp_deep_sleep.h @@ -68,6 +68,13 @@ esp_err_t esp_deep_sleep_enable_ulp_wakeup(); */ esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us); +/** + * @brief Enable wakeup by touch sensor + * @return + * - ESP_OK on success + */ +esp_err_t esp_deep_sleep_enable_touchpad_wakeup(); + /** * @brief Enable wakeup using a pin * From fb261c0bd5f118d3d7298332594f705e3855ac3a Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Tue, 24 Jan 2017 15:53:59 +0800 Subject: [PATCH 02/15] 1. deal with conflicting wakeup triggers 2. modify deep_sleep.rst, add description of touch wakeup. --- components/esp32/deep_sleep.c | 17 ++++++++++++++++- components/esp32/include/esp_deep_sleep.h | 7 ++++++- docs/api/system/deep_sleep.rst | 9 +++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/components/esp32/deep_sleep.c b/components/esp32/deep_sleep.c index 1c2b60361..2b76025e0 100644 --- a/components/esp32/deep_sleep.c +++ b/components/esp32/deep_sleep.c @@ -163,6 +163,10 @@ 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 + if(s_config.wakeup_triggers & RTC_TOUCH_TRIG_EN) { + ESP_LOGE(TAG, "Conflict wake-up triggers: touch"); + return ESP_ERR_INVALID_STATE; + } s_config.wakeup_triggers |= RTC_SAR_TRIG_EN; return ESP_OK; #else @@ -179,7 +183,11 @@ esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us) esp_err_t esp_deep_sleep_enable_touchpad_wakeup() { - s_config.wakeup_triggers |= TOUCH_TRIG_EN; + if (s_config.wakeup_triggers & (RTC_SAR_TRIG_EN | RTC_EXT_EVENT0_TRIG_EN)) { + ESP_LOGE(TAG, "Conflict wake-up triggers: ulp/ext0"); + return ESP_ERR_INVALID_STATE; + } + s_config.wakeup_triggers |= RTC_TOUCH_TRIG_EN; return ESP_OK; } @@ -191,6 +199,10 @@ esp_err_t esp_deep_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level) if (!RTC_GPIO_IS_VALID_GPIO(gpio_num)) { return ESP_ERR_INVALID_ARG; } + if (s_config.wakeup_triggers & RTC_TOUCH_TRIG_EN) { + ESP_LOGE(TAG, "Conflict wake-up triggers: touch"); + return ESP_ERR_INVALID_STATE; + } s_config.ext0_rtc_gpio_num = rtc_gpio_desc[gpio_num].rtc_num; s_config.ext0_trigger_level = level; s_config.wakeup_triggers |= RTC_EXT_EVENT0_TRIG_EN; @@ -346,6 +358,9 @@ static uint32_t get_power_down_flags() if (s_config.wakeup_triggers & (RTC_SAR_TRIG_EN | RTC_EXT_EVENT0_TRIG_EN)) { s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] = ESP_PD_OPTION_ON; + } else if (s_config.wakeup_triggers & RTC_TOUCH_TRIG_EN) { + // We have to set power down PERIPH so as to enable wake-up from touch sensor. + s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] = ESP_PD_OPTION_OFF; } } diff --git a/components/esp32/include/esp_deep_sleep.h b/components/esp32/include/esp_deep_sleep.h index fda075eb2..47a8a15aa 100644 --- a/components/esp32/include/esp_deep_sleep.h +++ b/components/esp32/include/esp_deep_sleep.h @@ -53,9 +53,10 @@ typedef enum { /** * @brief Enable wakeup by ULP coprocessor + * @note ulp wakeup conflicts with touch wakeup. * @return * - ESP_OK on success - * - ESP_ERR_INVALID_STATE if ULP co-processor is not enabled. + * - ESP_ERR_INVALID_STATE if ULP co-processor is not enabled or if wakeup triggers conflict */ esp_err_t esp_deep_sleep_enable_ulp_wakeup(); @@ -70,8 +71,10 @@ esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us); /** * @brief Enable wakeup by touch sensor + * @note Can not set touch wake-up if ulp or ext0 wake-up is enabled. * @return * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if wakeup triggers conflict */ esp_err_t esp_deep_sleep_enable_touchpad_wakeup(); @@ -87,6 +90,7 @@ esp_err_t esp_deep_sleep_enable_touchpad_wakeup(); * @note This function does not modify pin configuration. The pin is * configured in esp_deep_sleep_start, immediately before * entering deep sleep. + * @note This ext0 wakeup conflicts with touch wakeup. * * @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. @@ -95,6 +99,7 @@ esp_err_t esp_deep_sleep_enable_touchpad_wakeup(); * - ESP_OK on success * - ESP_ERR_INVALID_ARG if the selected GPIO is not an RTC GPIO, * or the mode is invalid + * - ESP_ERR_INVALID_STATE if wakeup triggers conflict */ esp_err_t esp_deep_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level); diff --git a/docs/api/system/deep_sleep.rst b/docs/api/system/deep_sleep.rst index 87986026d..0b83062a4 100644 --- a/docs/api/system/deep_sleep.rst +++ b/docs/api/system/deep_sleep.rst @@ -26,6 +26,15 @@ The following function can be used to enable deep sleep wakeup using a timer. .. doxygenfunction:: esp_deep_sleep_enable_timer_wakeup +Touch pad +^^^^^ + +RTC IO module contains logic to trigger wakeup when a touch sensor interrupt occurs. You need to configure the touch pad interrupt before the chip starts deep sleep. + +Note that, for now, this wakeup requires RTC peripherals to be powered off during deep sleep. + +.. doxygenfunction:: esp_deep_sleep_enable_touchpad_wakeup + External wakeup (ext0) ^^^^^^^^^^^^^^^^^^^^^^ From 0483548a39bd5b9523d56b0bca37d07fbafd8f6b Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 26 Jan 2017 11:50:04 +0800 Subject: [PATCH 03/15] ulp: fix I_{RD,WR}_REG definitions - I_RD_REG used the wrong union member (.rd_reg) due to a copy-paste mistake - Peripheral register address in bits[7:0] should be given in words, not in bytes Fixes https://github.com/espressif/esp-idf/issues/297 --- components/ulp/include/esp32/ulp.h | 6 ++--- components/ulp/test/test_ulp.c | 39 ++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/components/ulp/include/esp32/ulp.h b/components/ulp/include/esp32/ulp.h index f4e37e924..66865c5b9 100644 --- a/components/ulp/include/esp32/ulp.h +++ b/components/ulp/include/esp32/ulp.h @@ -307,7 +307,7 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { * This instruction can access RTC_CNTL_, RTC_IO_, and SENS_ peripheral registers. */ #define I_WR_REG(reg, low_bit, high_bit, val) {.wr_reg = {\ - .addr = reg & 0xff, \ + .addr = (reg & 0xff) / sizeof(uint32_t), \ .periph_sel = SOC_REG_TO_ULP_PERIPH_SEL(reg), \ .data = val, \ .low = low_bit, \ @@ -320,8 +320,8 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { * R0 = reg[high_bit : low_bit] * This instruction can access RTC_CNTL_, RTC_IO_, and SENS_ peripheral registers. */ -#define I_RD_REG(reg, low_bit, high_bit, val) {.wr_reg = {\ - .addr = reg & 0xff, \ +#define I_RD_REG(reg, low_bit, high_bit) {.rd_reg = {\ + .addr = (reg & 0xff) / sizeof(uint32_t), \ .periph_sel = SOC_REG_TO_ULP_PERIPH_SEL(reg), \ .unused = 0, \ .low = low_bit, \ diff --git a/components/ulp/test/test_ulp.c b/components/ulp/test/test_ulp.c index f04178623..7ee0dbf9f 100644 --- a/components/ulp/test/test_ulp.c +++ b/components/ulp/test/test_ulp.c @@ -121,6 +121,45 @@ TEST_CASE("ulp wakeup test", "[ulp][ignore]") esp_deep_sleep_start(); } +TEST_CASE("ulp can write and read peripheral registers", "[ulp]") +{ + assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); + CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); + memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM); + REG_WRITE(RTC_CNTL_STORE1_REG, 0x89abcdef); + + const ulp_insn_t program[] = { + I_MOVI(R1, 64), + I_RD_REG(RTC_CNTL_STORE1_REG, 0, 15), + I_ST(R0, R1, 0), + I_RD_REG(RTC_CNTL_STORE1_REG, 4, 11), + I_ST(R0, R1, 1), + I_RD_REG(RTC_CNTL_STORE1_REG, 16, 31), + I_ST(R0, R1, 2), + I_RD_REG(RTC_CNTL_STORE1_REG, 20, 27), + I_ST(R0, R1, 3), + I_WR_REG(RTC_CNTL_STORE0_REG, 0, 7, 0x89), + I_WR_REG(RTC_CNTL_STORE0_REG, 8, 15, 0xab), + I_WR_REG(RTC_CNTL_STORE0_REG, 16, 23, 0xcd), + I_WR_REG(RTC_CNTL_STORE0_REG, 24, 31, 0xef), + I_LD(R0, R1, 4), + I_ADDI(R0, R0, 1), + I_ST(R0, R1, 4), + I_END(0) + }; + size_t size = sizeof(program)/sizeof(ulp_insn_t); + TEST_ESP_OK(ulp_process_macros_and_load(0, program, &size)); + TEST_ESP_OK(ulp_run(0)); + vTaskDelay(100/portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL_HEX32(0xefcdab89, REG_READ(RTC_CNTL_STORE0_REG)); + TEST_ASSERT_EQUAL_HEX16(0xcdef, RTC_SLOW_MEM[64] & 0xffff); + TEST_ASSERT_EQUAL_HEX16(0xde, RTC_SLOW_MEM[65] & 0xffff); + TEST_ASSERT_EQUAL_HEX16(0x89ab, RTC_SLOW_MEM[66] & 0xffff); + TEST_ASSERT_EQUAL_HEX16(0x9a, RTC_SLOW_MEM[67] & 0xffff); + TEST_ASSERT_EQUAL_HEX32(1 | (15 << 21) | (1 << 16), RTC_SLOW_MEM[68]); +} + TEST_CASE("ulp controls RTC_IO", "[ulp][ignore]") { assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); From 611e510c4993b9eff3e977461e9355a367200700 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 20 Feb 2017 15:44:29 +0800 Subject: [PATCH 04/15] ulp: add I_SLEEP instruction and improve comments --- components/ulp/include/esp32/ulp.h | 31 +++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/components/ulp/include/esp32/ulp.h b/components/ulp/include/esp32/ulp.h index 66865c5b9..9620a5ac9 100644 --- a/components/ulp/include/esp32/ulp.h +++ b/components/ulp/include/esp32/ulp.h @@ -271,7 +271,12 @@ _Static_assert(sizeof(ulp_insn_t) == 4, "ULP coprocessor instruction size should .cycles = cycles_ } } /** - * Halt the coprocessor + * Halt the coprocessor. + * + * This instruction halts the coprocessor, but keeps ULP timer active. + * As such, ULP program will be restarted again by timer. + * To stop the program and prevent the timer from restarting the program, + * use I_END(0) instruction. */ #define I_HALT() { .halt = {\ .unused = 0, \ @@ -331,7 +336,10 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { /** * End program. * - * If wake == 1, wake up main CPU. + * This instruction halts the coprocessor, and disables the ULP timer. + * If wake == 1, the main CPU is woken up from deep sleep. + * To stop the program but allow it to be restarted by timer, use I_HALT() + * or I_SLEEP() instructions. */ #define I_END(wake) { .end = { \ .wakeup = wake, \ @@ -339,15 +347,28 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { .sub_opcode = SUB_OPCODE_END, \ .opcode = OPCODE_END } } +/** + * End program and restart it after given amount of time. + * + * Time to restart the program is determined by the value of + * SENS_SLEEP_CYCLES_Sx register, where x == timer_idx. + * There are 5 SENS_SLEEP_CYCLES_Sx registers, so 0 <= timer_idx < 5. + */ +#define I_SLEEP(timer_idx) { .sleep = { \ + .cycle_sel = timer_idx, \ + .unused = 0, \ + .sub_opcode = SUB_OPCODE_SLEEP, \ + .opcode = OPCODE_END } } + /** * Store value from register reg_val into RTC memory. * * The value is written to an offset calculated by adding value of * reg_addr register and offset_ field (this offset is expressed in 32-bit words). * 32 bits written to RTC memory are built as follows: - * - 5 MSBs are zero - * - next 11 bits hold the PC of current instruction, expressed in 32-bit words - * - next 16 bits hold the actual value to be written + * - bits [31:21] hold the PC of current instruction, expressed in 32-bit words + * - bits [20:16] = 5'b1 + * - bits [15:0] are assigned the contents of reg_val * * RTC_SLOW_MEM[addr + offset_] = { 5'b0, insn_PC[10:0], val[15:0] } */ From 07ff47f1039c53075aad550e736c5c15bc5db2fd Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 27 Jan 2017 23:36:52 +0800 Subject: [PATCH 05/15] deep sleep: clarify compatibility issues between wakeup sources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ULP and touch FSMs in ESP32 revisions 0 and 1 do not operate correctly if RTC_PERIPH power domain is force powered on (ESP_PD_OPTION_ON). Both ULP and touch still work, but clock frequency of the ULP may be incorrect and touch values may be off by considerable amount. As such, when these wakeup modes are used, RTC_PERIPH power domain has to be set to ESP_PD_OPTION_AUTO (or, in the current implementation, ESP_PD_OPTION_OFF — though this will change in the future when _OFF will actually *force* the power domain to be powered off). Because EXT0 wakeup source requires RTC_PERIPH to be powered ON, mark ULP and touch wakeup sources as incompatible with EXT0. Workaround for this is to use EXT1 wakeup source instead, which offers similar or better functions without having to keep RTC_PERIPH powered on. --- components/esp32/deep_sleep.c | 25 +++++++++++------------ components/esp32/include/esp_deep_sleep.h | 16 ++++++++++++--- docs/api/system/deep_sleep.rst | 14 ++++++++----- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/components/esp32/deep_sleep.c b/components/esp32/deep_sleep.c index 2b76025e0..c6785375d 100644 --- a/components/esp32/deep_sleep.c +++ b/components/esp32/deep_sleep.c @@ -163,8 +163,8 @@ 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 - if(s_config.wakeup_triggers & RTC_TOUCH_TRIG_EN) { - ESP_LOGE(TAG, "Conflict wake-up triggers: touch"); + if(s_config.wakeup_triggers & RTC_EXT_EVENT0_TRIG_EN) { + ESP_LOGE(TAG, "Conflicting wake-up trigger: ext0"); return ESP_ERR_INVALID_STATE; } s_config.wakeup_triggers |= RTC_SAR_TRIG_EN; @@ -183,8 +183,8 @@ esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us) esp_err_t esp_deep_sleep_enable_touchpad_wakeup() { - if (s_config.wakeup_triggers & (RTC_SAR_TRIG_EN | RTC_EXT_EVENT0_TRIG_EN)) { - ESP_LOGE(TAG, "Conflict wake-up triggers: ulp/ext0"); + if (s_config.wakeup_triggers & (RTC_EXT_EVENT0_TRIG_EN)) { + ESP_LOGE(TAG, "Conflicting wake-up trigger: ext0"); return ESP_ERR_INVALID_STATE; } s_config.wakeup_triggers |= RTC_TOUCH_TRIG_EN; @@ -199,8 +199,8 @@ esp_err_t esp_deep_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level) if (!RTC_GPIO_IS_VALID_GPIO(gpio_num)) { return ESP_ERR_INVALID_ARG; } - if (s_config.wakeup_triggers & RTC_TOUCH_TRIG_EN) { - ESP_LOGE(TAG, "Conflict wake-up triggers: touch"); + if (s_config.wakeup_triggers & (RTC_TOUCH_TRIG_EN | RTC_SAR_TRIG_EN)) { + ESP_LOGE(TAG, "Conflicting wake-up triggers: touch / ULP"); return ESP_ERR_INVALID_STATE; } s_config.ext0_rtc_gpio_num = rtc_gpio_desc[gpio_num].rtc_num; @@ -351,15 +351,14 @@ static uint32_t get_power_down_flags() s_config.pd_options[ESP_PD_DOMAIN_RTC_FAST_MEM] = ESP_PD_OPTION_ON; } - // RTC_PERIPH is needed for EXT0 wakeup and for ULP. - // If RTC_PERIPH is auto, and both EXT0 and ULP aren't enabled, - // power down RTC_PERIPH. + // RTC_PERIPH is needed for EXT0 wakeup. + // If RTC_PERIPH is auto, and EXT0 isn't enabled, power down RTC_PERIPH. if (s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] == ESP_PD_OPTION_AUTO) { - if (s_config.wakeup_triggers & - (RTC_SAR_TRIG_EN | RTC_EXT_EVENT0_TRIG_EN)) { + if (s_config.wakeup_triggers & RTC_EXT_EVENT0_TRIG_EN) { s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] = ESP_PD_OPTION_ON; - } else if (s_config.wakeup_triggers & RTC_TOUCH_TRIG_EN) { - // We have to set power down PERIPH so as to enable wake-up from touch sensor. + } else if (s_config.wakeup_triggers & (RTC_TOUCH_TRIG_EN | RTC_SAR_TRIG_EN)) { + // In both rev. 0 and rev. 1 of ESP32, forcing power up of RTC_PERIPH + // prevents ULP timer and touch FSMs from working correctly. s_config.pd_options[ESP_PD_DOMAIN_RTC_PERIPH] = ESP_PD_OPTION_OFF; } } diff --git a/components/esp32/include/esp_deep_sleep.h b/components/esp32/include/esp_deep_sleep.h index 47a8a15aa..ed53b9496 100644 --- a/components/esp32/include/esp_deep_sleep.h +++ b/components/esp32/include/esp_deep_sleep.h @@ -53,7 +53,10 @@ typedef enum { /** * @brief Enable wakeup by ULP coprocessor - * @note ulp wakeup conflicts with touch wakeup. + * @note In revisions 0 and 1 of the ESP32, ULP wakeup source + * can not be used when RTC_PERIPH power domain is forced + * to be powered on (ESP_PD_OPTION_ON) or when ext0 wakeup + * source is used. * @return * - ESP_OK on success * - ESP_ERR_INVALID_STATE if ULP co-processor is not enabled or if wakeup triggers conflict @@ -71,7 +74,12 @@ esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us); /** * @brief Enable wakeup by touch sensor - * @note Can not set touch wake-up if ulp or ext0 wake-up is enabled. + * + * @note In revisions 0 and 1 of the ESP32, touch wakeup source + * can not be used when RTC_PERIPH power domain is forced + * to be powered on (ESP_PD_OPTION_ON) or when ext0 wakeup + * source is used. + * * @return * - ESP_OK on success * - ESP_ERR_INVALID_STATE if wakeup triggers conflict @@ -90,7 +98,9 @@ esp_err_t esp_deep_sleep_enable_touchpad_wakeup(); * @note This function does not modify pin configuration. The pin is * configured in esp_deep_sleep_start, immediately before * entering deep sleep. - * @note This ext0 wakeup conflicts with touch wakeup. + * + * @note In revisions 0 and 1 of the ESP32, ext0 wakeup source + * can not be used together with touch or ULP wakeup sources. * * @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. diff --git a/docs/api/system/deep_sleep.rst b/docs/api/system/deep_sleep.rst index 0b83062a4..14904881a 100644 --- a/docs/api/system/deep_sleep.rst +++ b/docs/api/system/deep_sleep.rst @@ -31,7 +31,7 @@ Touch pad RTC IO module contains logic to trigger wakeup when a touch sensor interrupt occurs. You need to configure the touch pad interrupt before the chip starts deep sleep. -Note that, for now, this wakeup requires RTC peripherals to be powered off during deep sleep. +Revisions 0 and 1 of the ESP32 only support this wakeup mode when RTC peripherals are not forced to be powered on (i.e. ESP_PD_DOMAIN_RTC_PERIPH should be set to ESP_PD_OPTION_AUTO). .. doxygenfunction:: esp_deep_sleep_enable_touchpad_wakeup @@ -43,6 +43,8 @@ RTC IO module contains logic to trigger wakeup when one of RTC GPIOs is set to a Because RTC IO module is enabled in this mode, internal pullup or pulldown resistors can also be used. They need to be configured by the application using ``rtc_gpio_pullup_en`` and ``rtc_gpio_pulldown_en`` functions, before calling ``esp_deep_sleep_start``. +In revisions 0 and 1 of the ESP32, this wakeup source is incompatible with ULP and touch wakeup sources. + .. doxygenfunction:: esp_deep_sleep_enable_ext0_wakeup External wakeup (ext1) @@ -50,8 +52,8 @@ External wakeup (ext1) RTC controller contains logic to trigger wakeup using multiple RTC GPIOs. One of the two logic functions can be used to trigger wakeup: - - wake up if any of the selected pins is low - - wake up if all the selected pins are high + - wake up if any of the selected pins is high (``ESP_EXT1_WAKEUP_ANY_HIGH``) + - wake up if all the selected pins are low (``ESP_EXT1_WAKEUP_ALL_LOW``) This wakeup source is implemented by the RTC controller. As such, RTC peripherals and RTC memories can be powered off in this mode. However, if RTC peripherals are powered down, internal pullup and pulldown resistors will be disabled. To use internal pullup or pulldown resistors, request RTC peripherals power domain to be kept on during deep sleep, and configure pullup/pulldown resistors using ``rtc_gpio_`` functions, before entering deep sleep:: @@ -69,7 +71,9 @@ The following function can be used to enable this wakeup mode: ULP coprocessor wakeup ^^^^^^^^^^^^^^^^^^^^^^ -ULP coprocessor can run while the chip is in deep sleep, and may be used to poll sensors, monitor ADC or touch sensor values, and wake up the chip when a specific event is detected. ULP coprocessor is part of RTC peripherals power domain, and it runs the program stored in RTC slow memeory. Therefore RTC peripherals and RTC slow memory will be powered on during deep sleep if this wakeup mode is requested. +ULP coprocessor can run while the chip is in deep sleep, and may be used to poll sensors, monitor ADC or touch sensor values, and wake up the chip when a specific event is detected. ULP coprocessor is part of RTC peripherals power domain, and it runs the program stored in RTC slow memeory. RTC slow memory will be powered on during deep sleep if this wakeup mode is requested. RTC peripherals will be automatically powered on before ULP coprocessor starts running the program; once the program stops running, RTC peripherals are automatically powered down again. + +Revisions 0 and 1 of the ESP32 only support this wakeup mode when RTC peripherals are not forced to be powered on (i.e. ESP_PD_DOMAIN_RTC_PERIPH should be set to ESP_PD_OPTION_AUTO). The following function can be used to enable this wakeup mode: @@ -80,7 +84,7 @@ Power-down of RTC peripherals and memories By default, ``esp_deep_sleep_start`` function will power down all RTC power domains which are not needed by the enabled wakeup sources. To override this behaviour, the following function is provided: -Note: on the first revision of the ESP32, RTC fast memory will always be kept enabled in deep sleep, so that the deep sleep stub can run after reset. This can be overriden, if the application doesn't need clean reset behaviour after deep sleep. +Note: in revision 0 of the ESP32, RTC fast memory will always be kept enabled in deep sleep, so that the deep sleep stub can run after reset. This can be overriden, if the application doesn't need clean reset behaviour after deep sleep. If some variables in the program are placed into RTC slow memory (for example, using ``RTC_DATA_ATTR`` attribute), RTC slow memory will be kept powered on by default. This can be overriden using ``esp_deep_sleep_pd_config`` function, if desired. From a64181f14f3e0f48104b4c84eeaf7f9a1104160c Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 27 Jan 2017 23:48:00 +0800 Subject: [PATCH 06/15] deep sleep: add functions to check wakeup cause This change adds esp_deep_sleep_get_wakeup_cause, which returns the source which has caused wakeup from deep sleep. Similar to esp_deep_sleep_get_ext1_wakeup_status, a function is added to check touch pad wakeup status: esp_deep_sleep_get_touchpad_wakeup_status. This function returns the touch pad which has caused wakeup. --- components/esp32/deep_sleep.c | 35 +++++++++++++++++++++-- components/esp32/include/esp_deep_sleep.h | 31 ++++++++++++++++++++ docs/api/system/deep_sleep.rst | 13 ++++++++- 3 files changed, 76 insertions(+), 3 deletions(-) diff --git a/components/esp32/deep_sleep.c b/components/esp32/deep_sleep.c index c6785375d..aadb6067e 100644 --- a/components/esp32/deep_sleep.c +++ b/components/esp32/deep_sleep.c @@ -191,6 +191,16 @@ esp_err_t esp_deep_sleep_enable_touchpad_wakeup() return ESP_OK; } +touch_pad_t esp_deep_sleep_get_touchpad_wakeup_status() +{ + if (esp_deep_sleep_get_wakeup_cause() != ESP_DEEP_SLEEP_WAKEUP_TOUCHPAD) { + return TOUCH_PAD_MAX; + } + uint32_t touch_mask = REG_GET_FIELD(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_MEAS_EN); + assert(touch_mask != 0 && "wakeup reason is RTC_TOUCH_TRIG_EN but SENS_TOUCH_MEAS_EN is zero"); + return (touch_pad_t) (__builtin_ffs(touch_mask) - 1); +} + esp_err_t esp_deep_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level) { if (level < 0 || level > 1) { @@ -294,8 +304,7 @@ static void ext1_wakeup_prepare() 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) { + if (esp_deep_sleep_get_wakeup_cause() != ESP_DEEP_SLEEP_WAKEUP_EXT1) { return 0; } uint32_t status = REG_GET_FIELD(RTC_CNTL_EXT_WAKEUP1_STATUS_REG, RTC_CNTL_EXT_WAKEUP1_STATUS); @@ -314,6 +323,28 @@ uint64_t esp_deep_sleep_get_ext1_wakeup_status() return gpio_mask; } +esp_deep_sleep_wakeup_cause_t esp_deep_sleep_get_wakeup_cause() +{ + if (rtc_get_reset_reason(0) != DEEPSLEEP_RESET) { + return ESP_DEEP_SLEEP_WAKEUP_UNDEFINED; + } + + uint32_t wakeup_cause = REG_GET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_CAUSE); + if (wakeup_cause & RTC_EXT_EVENT0_TRIG) { + return ESP_DEEP_SLEEP_WAKEUP_EXT0; + } else if (wakeup_cause & RTC_EXT_EVENT1_TRIG) { + return ESP_DEEP_SLEEP_WAKEUP_EXT1; + } else if (wakeup_cause & RTC_TIMER_EXPIRE) { + return ESP_DEEP_SLEEP_WAKEUP_TIMER; + } else if (wakeup_cause & RTC_TOUCH_TRIG) { + return ESP_DEEP_SLEEP_WAKEUP_TOUCHPAD; + } else if (wakeup_cause & RTC_SAR_TRIG) { + return ESP_DEEP_SLEEP_WAKEUP_ULP; + } else { + return ESP_DEEP_SLEEP_WAKEUP_UNDEFINED; + } +} + esp_err_t esp_deep_sleep_pd_config(esp_deep_sleep_pd_domain_t domain, esp_deep_sleep_pd_option_t option) { diff --git a/components/esp32/include/esp_deep_sleep.h b/components/esp32/include/esp_deep_sleep.h index ed53b9496..61d3642b5 100644 --- a/components/esp32/include/esp_deep_sleep.h +++ b/components/esp32/include/esp_deep_sleep.h @@ -17,6 +17,7 @@ #include #include "esp_err.h" #include "driver/gpio.h" +#include "driver/touch_pad.h" #ifdef __cplusplus extern "C" { @@ -50,6 +51,18 @@ typedef enum { ESP_PD_OPTION_AUTO //!< Keep power domain enabled in deep sleep, if it is needed by one of the wakeup options. Otherwise power it down. } esp_deep_sleep_pd_option_t; +/** + * @brief Deep sleep wakeup cause + */ +typedef enum { + ESP_DEEP_SLEEP_WAKEUP_UNDEFINED, //! Wakeup was not caused by deep sleep + ESP_DEEP_SLEEP_WAKEUP_EXT0, //! Wakeup caused by external signal using RTC_IO + ESP_DEEP_SLEEP_WAKEUP_EXT1, //! Wakeup caused by external signal using RTC_CNTL + ESP_DEEP_SLEEP_WAKEUP_TIMER, //! Wakeup caused by timer + ESP_DEEP_SLEEP_WAKEUP_TOUCHPAD, //! Wakeup caused by touchpad + ESP_DEEP_SLEEP_WAKEUP_ULP, //! Wakeup caused by ULP program +} esp_deep_sleep_wakeup_cause_t; + /** * @brief Enable wakeup by ULP coprocessor @@ -86,6 +99,15 @@ esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us); */ esp_err_t esp_deep_sleep_enable_touchpad_wakeup(); +/** + * @brief Get the touch pad which caused wakeup + * + * If wakeup was caused by another source, this function will return TOUCH_PAD_MAX; + * + * @return touch pad which caused wakeup + */ +touch_pad_t esp_deep_sleep_get_touchpad_wakeup_status(); + /** * @brief Enable wakeup using a pin * @@ -210,6 +232,15 @@ void esp_deep_sleep(uint64_t time_in_us) __attribute__((noreturn)); */ void system_deep_sleep(uint64_t time_in_us) __attribute__((noreturn, deprecated)); + +/** + * @brief Get the source which caused deep sleep wakeup + * + * @return wakeup cause, or ESP_DEEP_SLEEP_WAKEUP_UNDEFINED if reset reason is other than deep sleep reset. + */ +esp_deep_sleep_wakeup_cause_t esp_deep_sleep_get_wakeup_cause(); + + /** * @brief Default stub to run on wake from deep sleep. * diff --git a/docs/api/system/deep_sleep.rst b/docs/api/system/deep_sleep.rst index 14904881a..aacb25218 100644 --- a/docs/api/system/deep_sleep.rst +++ b/docs/api/system/deep_sleep.rst @@ -27,7 +27,7 @@ The following function can be used to enable deep sleep wakeup using a timer. .. doxygenfunction:: esp_deep_sleep_enable_timer_wakeup Touch pad -^^^^^ +^^^^^^^^^ RTC IO module contains logic to trigger wakeup when a touch sensor interrupt occurs. You need to configure the touch pad interrupt before the chip starts deep sleep. @@ -100,6 +100,17 @@ The following function can be used to enter deep sleep once wakeup sources are c .. doxygenfunction:: esp_deep_sleep_start +Checking deep sleep wakeup cause +-------------------------------- + +The following function can be used to check which wakeup source has triggered wakeup from deep sleep mode. For touch pad and ext1 wakeup sources, it is possible to identify pin or touch pad which has caused wakeup. + +.. doxygenfunction:: esp_deep_sleep_get_wakeup_cause +.. doxygenenum:: esp_deep_sleep_wakeup_cause_t +.. doxygenfunction:: esp_deep_sleep_get_touchpad_wakeup_status +.. doxygenfunction:: esp_deep_sleep_get_ext1_wakeup_status + + Application Example ------------------- From dbf60eb9222e6a16cf00f0ddcb61c774fa62e5dd Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 27 Jan 2017 23:48:50 +0800 Subject: [PATCH 07/15] rtc: fix typo in RTC_CNTL_SLOWCLK_FREQ macro name --- components/esp32/include/soc/rtc_cntl_reg.h | 2 +- components/newlib/time.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esp32/include/soc/rtc_cntl_reg.h b/components/esp32/include/soc/rtc_cntl_reg.h index d99cec186..40d65f21f 100644 --- a/components/esp32/include/soc/rtc_cntl_reg.h +++ b/components/esp32/include/soc/rtc_cntl_reg.h @@ -240,7 +240,7 @@ #define RTC_CNTL_TIME_VALID_S 30 /* frequency of RTC slow clock, Hz */ -#define RTC_CTNL_SLOWCLK_FREQ 150000 +#define RTC_CNTL_SLOWCLK_FREQ 150000 #define RTC_CNTL_TIME0_REG (DR_REG_RTCCNTL_BASE + 0x10) /* RTC_CNTL_TIME_LO : RO ;bitpos:[31:0] ;default: 32'h0 ; */ diff --git a/components/newlib/time.c b/components/newlib/time.c index 7595ab82b..fc7dec645 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -52,7 +52,7 @@ static uint64_t get_rtc_time_us() uint64_t low = READ_PERI_REG(RTC_CNTL_TIME0_REG); uint64_t high = READ_PERI_REG(RTC_CNTL_TIME1_REG); uint64_t ticks = (high << 32) | low; - return ticks * 100 / (RTC_CTNL_SLOWCLK_FREQ / 10000); // scale RTC_CTNL_SLOWCLK_FREQ to avoid overflow + return ticks * 100 / (RTC_CNTL_SLOWCLK_FREQ / 10000); // scale RTC_CNTL_SLOWCLK_FREQ to avoid overflow } #endif // WITH_RTC From d0d2c4cb49a3bedba4ed0655d13949feea70eb23 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 20 Feb 2017 15:44:49 +0800 Subject: [PATCH 08/15] esp32,ulp: add tests for TSENS --- components/esp32/test/test_tsens.c | 20 ++++++++++++ components/ulp/include/esp32/ulp.h | 14 +++++++- components/ulp/test/test_ulp.c | 52 ++++++++++++++++++++++++++++++ components/ulp/ulp.c | 3 ++ 4 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 components/esp32/test/test_tsens.c diff --git a/components/esp32/test/test_tsens.c b/components/esp32/test/test_tsens.c new file mode 100644 index 000000000..ff2660d1b --- /dev/null +++ b/components/esp32/test/test_tsens.c @@ -0,0 +1,20 @@ +#include +#include "unity.h" +#include "rom/ets_sys.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/sens_reg.h" + +TEST_CASE("can control TSENS using registers", "[rtc][ignore]") +{ + SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 3, SENS_FORCE_XPD_SAR_S); + SET_PERI_REG_BITS(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_CLK_DIV, 10, SENS_TSENS_CLK_DIV_S); + CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP); + CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_DUMP_OUT); + SET_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP_FORCE); + SET_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP); + ets_delay_us(100); + SET_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_DUMP_OUT); + ets_delay_us(5); + int res = GET_PERI_REG_BITS2(SENS_SAR_SLAVE_ADDR3_REG, SENS_TSENS_OUT, SENS_TSENS_OUT_S); + printf("res=%d\n", res); +} diff --git a/components/ulp/include/esp32/ulp.h b/components/ulp/include/esp32/ulp.h index 9620a5ac9..6d4bb18b1 100644 --- a/components/ulp/include/esp32/ulp.h +++ b/components/ulp/include/esp32/ulp.h @@ -222,7 +222,7 @@ typedef union { struct { uint32_t dreg : 2; /*!< Register where to store temperature measurement result */ uint32_t wait_delay: 14; /*!< Cycles to wait after measurement is done */ - uint32_t cycles: 12; /*!< Cycles used to perform measurement */ + uint32_t reserved: 12; /*!< Reserved, set to 0 */ uint32_t opcode: 4; /*!< Opcode (OPCODE_TSENS) */ } tsens; /*!< Format of TSENS instruction */ @@ -360,6 +360,18 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { .sub_opcode = SUB_OPCODE_SLEEP, \ .opcode = OPCODE_END } } +/** + * Perform temperature sensor measurement and store it into reg_dest. + * + * Delay can be set between 1 and ((1 << 14) - 1). Higher values give + * higher measurement resolution. + */ +#define I_TSENS(reg_dest, delay) { .tsens = { \ + .dreg = reg_dest, \ + .wait_delay = delay, \ + .reserved = 0, \ + .opcode = OPCODE_TSENS } } + /** * Store value from register reg_val into RTC memory. * diff --git a/components/ulp/test/test_ulp.c b/components/ulp/test/test_ulp.c index 7ee0dbf9f..6a9d40253 100644 --- a/components/ulp/test/test_ulp.c +++ b/components/ulp/test/test_ulp.c @@ -207,3 +207,55 @@ TEST_CASE("ulp controls RTC_IO", "[ulp][ignore]") esp_deep_sleep_start(); } + +TEST_CASE("ulp can use TSENS in deep sleep", "[ulp][ignore]") +{ + assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); + + hexdump(RTC_SLOW_MEM, CONFIG_ULP_COPROC_RESERVE_MEM / 4); + printf("\n\n"); + memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM); + + // Allow TSENS to be controlled by the ULP + SET_PERI_REG_BITS(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_CLK_DIV, 10, SENS_TSENS_CLK_DIV_S); + SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 3, SENS_FORCE_XPD_SAR_S); + CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP); + CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_DUMP_OUT); + CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP_FORCE); + + // data start offset + size_t offset = 20; + // number of samples to collect + RTC_SLOW_MEM[offset] = (CONFIG_ULP_COPROC_RESERVE_MEM) / 4 - offset - 8; + // sample counter + RTC_SLOW_MEM[offset + 1] = 0; + + const ulp_insn_t program[] = { + I_MOVI(R1, offset), // r1 <- offset + I_LD(R2, R1, 1), // r2 <- counter + I_LD(R3, R1, 0), // r3 <- length + I_SUBI(R3, R3, 1), // end = length - 1 + I_SUBR(R3, R3, R2), // r3 = length - counter + M_BXF(1), // if overflow goto 1: + I_WR_REG(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_S, SENS_FORCE_XPD_SAR_S + 1, 3), + I_TSENS(R0, 16383), // r0 <- tsens + I_WR_REG(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_S, SENS_FORCE_XPD_SAR_S + 1, 0), + I_ST(R0, R2, offset + 4), + I_ADDI(R2, R2, 1), // counter += 1 + I_ST(R2, R1, 1), // save counter + I_SLEEP(0), // enter sleep + I_HALT(), + M_LABEL(1), // done with measurements + I_END(0), // stop ULP timer + I_HALT() + }; + + size_t size = sizeof(program)/sizeof(ulp_insn_t); + TEST_ESP_OK(ulp_process_macros_and_load(0, program, &size)); + assert(offset >= size); + + TEST_ESP_OK(ulp_run(0)); + esp_deep_sleep_enable_timer_wakeup(4000000); + esp_deep_sleep_start(); +} + diff --git a/components/ulp/ulp.c b/components/ulp/ulp.c index 60fa292f7..424a468c3 100644 --- a/components/ulp/ulp.c +++ b/components/ulp/ulp.c @@ -271,6 +271,9 @@ esp_err_t ulp_run(uint32_t entry_point) CLEAR_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_FORCE_START_TOP_M); // make sure voltage is raised when RTC 8MCLK is enabled SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FOLW_8M); + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_CORE_FOLW_8M); + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_SLEEP_FOLW_8M); + SET_PERI_REG_BITS(SENS_ULP_CP_SLEEP_CYC0_REG, SENS_SLEEP_CYCLES_S0, 1000, SENS_SLEEP_CYCLES_S0_S); // enable ULP timer SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); return ESP_OK; From 7df75c00c706889ff990191379688f6eb6261ec7 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 20 Feb 2017 15:25:16 +0800 Subject: [PATCH 09/15] ulp: fix a bug that ULP may not be restarted correctly --- components/ulp/ulp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/ulp/ulp.c b/components/ulp/ulp.c index 424a468c3..c7ac15b49 100644 --- a/components/ulp/ulp.c +++ b/components/ulp/ulp.c @@ -265,6 +265,8 @@ esp_err_t ulp_run(uint32_t entry_point) { // disable ULP timer CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); + // wait for at least 1 RTC_SLOW_CLK cycle + ets_delay_us(10); // set entry point SET_PERI_REG_BITS(SENS_SAR_START_FORCE_REG, SENS_PC_INIT_V, entry_point, SENS_PC_INIT_S); // disable force start From cdf122baa0e75f6f8e7cd21da421c1778a72a7ba Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 20 Feb 2017 15:37:39 +0800 Subject: [PATCH 10/15] ulp: add I_WR_REG instruction test --- components/ulp/test/test_ulp.c | 54 ++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/components/ulp/test/test_ulp.c b/components/ulp/test/test_ulp.c index 6a9d40253..7f1bb5da7 100644 --- a/components/ulp/test/test_ulp.c +++ b/components/ulp/test/test_ulp.c @@ -160,6 +160,60 @@ TEST_CASE("ulp can write and read peripheral registers", "[ulp]") TEST_ASSERT_EQUAL_HEX32(1 | (15 << 21) | (1 << 16), RTC_SLOW_MEM[68]); } +TEST_CASE("ULP I_WR_REG instruction test", "[ulp]") +{ + assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); + memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM); + typedef struct { + int low; + int width; + } wr_reg_test_item_t; + + const wr_reg_test_item_t test_items[] = { + {0, 1}, {0, 2}, {0, 3}, {0, 4}, {0, 5}, {0, 6}, {0, 7}, {0, 8}, + {3, 1}, {3, 2}, {3, 3}, {3, 4}, {3, 5}, {3, 6}, {3, 7}, {3, 8}, + {15, 1}, {15, 2}, {15, 3}, {15, 4}, {15, 5}, {15, 6}, {15, 7}, {15, 8}, + {16, 1}, {16, 2}, {16, 3}, {16, 4}, {16, 5}, {16, 6}, {16, 7}, {16, 8}, + {18, 1}, {18, 2}, {18, 3}, {18, 4}, {18, 5}, {18, 6}, {18, 7}, {18, 8}, + {24, 1}, {24, 2}, {24, 3}, {24, 4}, {24, 5}, {24, 6}, {24, 7}, {24, 8}, + }; + + const size_t test_items_count = + sizeof(test_items)/sizeof(test_items[0]); + for (size_t i = 0; i < test_items_count; ++i) { + const uint32_t mask = (uint32_t) (((1ULL << test_items[i].width) - 1) << test_items[i].low); + const uint32_t not_mask = ~mask; + printf("#%2d: low: %2d width: %2d mask: %08x expected: %08x ", i, + test_items[i].low, test_items[i].width, + mask, not_mask); + + REG_WRITE(RTC_CNTL_STORE0_REG, 0xffffffff); + REG_WRITE(RTC_CNTL_STORE1_REG, 0x00000000); + const ulp_insn_t program[] = { + I_WR_REG(RTC_CNTL_STORE0_REG, + test_items[i].low, + test_items[i].low + test_items[i].width - 1, + 0), + I_WR_REG(RTC_CNTL_STORE1_REG, + test_items[i].low, + test_items[i].low + test_items[i].width - 1, + 0xff & ((1 << test_items[i].width) - 1)), + I_END(0), + I_HALT() + }; + size_t size = sizeof(program)/sizeof(ulp_insn_t); + ulp_process_macros_and_load(0, program, &size); + ulp_run(0); + vTaskDelay(10/portTICK_PERIOD_MS); + uint32_t clear = REG_READ(RTC_CNTL_STORE0_REG); + uint32_t set = REG_READ(RTC_CNTL_STORE1_REG); + printf("clear: %08x set: %08x\n", clear, set); + TEST_ASSERT_EQUAL_HEX32(not_mask, clear); + TEST_ASSERT_EQUAL_HEX32(mask, set); + } +} + + TEST_CASE("ulp controls RTC_IO", "[ulp][ignore]") { assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); From 0fcc8918ddc65b0bf0c4b4099b126df60d7cd435 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Mon, 20 Feb 2017 15:40:21 +0800 Subject: [PATCH 11/15] ulp: add I_WR_REG_BIT convenience macro --- components/ulp/include/esp32/ulp.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/components/ulp/include/esp32/ulp.h b/components/ulp/include/esp32/ulp.h index 6d4bb18b1..3978e7086 100644 --- a/components/ulp/include/esp32/ulp.h +++ b/components/ulp/include/esp32/ulp.h @@ -333,6 +333,14 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { .high = high_bit, \ .opcode = OPCODE_RD_REG } } +/** + * Set or clear a bit in the peripheral register. + * + * Sets bit (1 << shift) of register reg to value val. + * This instruction can access RTC_CNTL_, RTC_IO_, and SENS_ peripheral registers. + */ +#define I_WR_REG_BIT(reg, shift, val) I_WR_REG(reg, shift, shift, val) + /** * End program. * From 0465528431fc63359d12c9514cfb17b6040e20fc Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 21 Feb 2017 12:11:51 +0800 Subject: [PATCH 12/15] =?UTF-8?q?ulp:=20don=E2=80=99t=20override=20SENS=5F?= =?UTF-8?q?SLEEP=5FCYCLES=5FS0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SENS_SLEEP_CYCLES_S0 may be set by the application to control ULP program timer. --- components/ulp/ulp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/components/ulp/ulp.c b/components/ulp/ulp.c index c7ac15b49..a6e462d1b 100644 --- a/components/ulp/ulp.c +++ b/components/ulp/ulp.c @@ -275,7 +275,6 @@ esp_err_t ulp_run(uint32_t entry_point) SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FOLW_8M); SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_CORE_FOLW_8M); SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_SLEEP_FOLW_8M); - SET_PERI_REG_BITS(SENS_ULP_CP_SLEEP_CYC0_REG, SENS_SLEEP_CYCLES_S0, 1000, SENS_SLEEP_CYCLES_S0_S); // enable ULP timer SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); return ESP_OK; From 5cab04075e3cd86d8eb939cf0a4ea2a58dbce66f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 22 Feb 2017 14:59:15 +0800 Subject: [PATCH 13/15] ulp: rename I_SLEEP, redefine I_WAKE, add I_ADC, add tests This fixes incorrect descriptions of I_END/I_SLEEP instructions and changes the definition of I_END. New instruction, I_WAKE, is added, which wakes up the SoC. Macro for ADC instruction is defined, and new tests are added. --- components/ulp/include/esp32/ulp.h | 70 ++++++++++--- components/ulp/test/test_ulp.c | 160 +++++++++++++++++++++++++++-- 2 files changed, 211 insertions(+), 19 deletions(-) diff --git a/components/ulp/include/esp32/ulp.h b/components/ulp/include/esp32/ulp.h index 3978e7086..7b434a06a 100644 --- a/components/ulp/include/esp32/ulp.h +++ b/components/ulp/include/esp32/ulp.h @@ -81,7 +81,7 @@ extern "C" { #define B_CMP_L 0 /*!< Branch if R0 is less than an immediate */ #define B_CMP_GE 1 /*!< Branch if R0 is greater than or equal to an immediate */ -#define OPCODE_END 9 /*!< Stop executing the program (not implemented yet) */ +#define OPCODE_END 9 /*!< Stop executing the program */ #define SUB_OPCODE_END 0 /*!< Stop executing the program and optionally wake up the chip */ #define SUB_OPCODE_SLEEP 1 /*!< Stop executing the program and run it again after selected interval */ @@ -342,27 +342,56 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { #define I_WR_REG_BIT(reg, shift, val) I_WR_REG(reg, shift, shift, val) /** - * End program. + * Wake the SoC from deep sleep. * - * This instruction halts the coprocessor, and disables the ULP timer. - * If wake == 1, the main CPU is woken up from deep sleep. - * To stop the program but allow it to be restarted by timer, use I_HALT() - * or I_SLEEP() instructions. + * This instruction initiates wake up from deep sleep. + * Use esp_deep_sleep_enable_ulp_wakeup to enable deep sleep wakeup + * triggered by the ULP before going into deep sleep. + * Note that ULP program will still keep running until the I_HALT + * instruction, and it will still be restarted by timer at regular + * intervals, even when the SoC is woken up. + * + * To stop the ULP program, use I_HALT instruction. + * + * To disable the timer which start ULP program, use I_END() + * instruction. I_END instruction clears the + * RTC_CNTL_ULP_CP_SLP_TIMER_EN_S bit of RTC_CNTL_STATE0_REG + * register, which controls the ULP timer. */ -#define I_END(wake) { .end = { \ - .wakeup = wake, \ +#define I_WAKE() { .end = { \ + .wakeup = 1, \ .unused = 0, \ .sub_opcode = SUB_OPCODE_END, \ .opcode = OPCODE_END } } /** - * End program and restart it after given amount of time. + * Stop ULP program timer. * - * Time to restart the program is determined by the value of - * SENS_SLEEP_CYCLES_Sx register, where x == timer_idx. - * There are 5 SENS_SLEEP_CYCLES_Sx registers, so 0 <= timer_idx < 5. + * This is a convenience macro which disables the ULP program timer. + * Once this instruction is used, ULP program will not be restarted + * anymore until ulp_run function is called. + * + * ULP program will continue running after this instruction. To stop + * the currently running program, use I_HALT(). */ -#define I_SLEEP(timer_idx) { .sleep = { \ +#define I_END() \ + I_WR_REG_BIT(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN_S, 0) +/** + * Select the time interval used to run ULP program. + * + * This instructions selects which of the SENS_SLEEP_CYCLES_Sx + * registers' value is used by the ULP program timer. + * When the ULP program stops at I_HALT instruction, ULP program + * timer start counting. When the counter reaches the value of + * the selected SENS_SLEEP_CYCLES_Sx register, ULP program + * start running again from the start address (passed to the ulp_run + * function). + * There are 5 SENS_SLEEP_CYCLES_Sx registers, so 0 <= timer_idx < 5. + * + * By default, SENS_SLEEP_CYCLES_S0 register is used by the ULP + * program timer. + */ +#define I_SLEEP_CYCLE_SEL(timer_idx) { .sleep = { \ .cycle_sel = timer_idx, \ .unused = 0, \ .sub_opcode = SUB_OPCODE_SLEEP, \ @@ -380,6 +409,21 @@ static inline uint32_t SOC_REG_TO_ULP_PERIPH_SEL(uint32_t reg) { .reserved = 0, \ .opcode = OPCODE_TSENS } } +/** + * Perform ADC measurement and store result in reg_dest. + * + * adc_idx selects ADC (0 or 1). + * pad_idx selects ADC pad (0 - 7). + */ +#define I_ADC(reg_dest, adc_idx, pad_idx) { .adc = {\ + .dreg = reg_dest, \ + .mux = pad_idx + 1, \ + .sar_sel = adc_idx, \ + .unused1 = 0, \ + .cycles = 0, \ + .unused2 = 0, \ + .opcode = OPCODE_ADC } } + /** * Store value from register reg_val into RTC memory. * diff --git a/components/ulp/test/test_ulp.c b/components/ulp/test/test_ulp.c index 7f1bb5da7..4f62c3c0b 100644 --- a/components/ulp/test/test_ulp.c +++ b/components/ulp/test/test_ulp.c @@ -112,7 +112,9 @@ TEST_CASE("ulp wakeup test", "[ulp][ignore]") I_MOVI(R2, 42), I_MOVI(R3, 15), I_ST(R2, R3, 0), - I_END(1) + I_WAKE(), + I_END(), + I_HALT() }; size_t size = sizeof(program)/sizeof(ulp_insn_t); ulp_process_macros_and_load(0, program, &size); @@ -145,7 +147,8 @@ TEST_CASE("ulp can write and read peripheral registers", "[ulp]") I_LD(R0, R1, 4), I_ADDI(R0, R0, 1), I_ST(R0, R1, 4), - I_END(0) + I_END(), + I_HALT() }; size_t size = sizeof(program)/sizeof(ulp_insn_t); TEST_ESP_OK(ulp_process_macros_and_load(0, program, &size)); @@ -198,7 +201,7 @@ TEST_CASE("ULP I_WR_REG instruction test", "[ulp]") test_items[i].low, test_items[i].low + test_items[i].width - 1, 0xff & ((1 << test_items[i].width) - 1)), - I_END(0), + I_END(), I_HALT() }; size_t size = sizeof(program)/sizeof(ulp_insn_t); @@ -242,7 +245,9 @@ TEST_CASE("ulp controls RTC_IO", "[ulp][ignore]") M_LABEL(5), M_BX(4), M_LABEL(6), - I_END(1) // wake up the SoC + I_WAKE(), // wake up the SoC + I_END(), // stop ULP program timer + I_HALT() }; const gpio_num_t led_gpios[] = { GPIO_NUM_2, @@ -261,6 +266,72 @@ TEST_CASE("ulp controls RTC_IO", "[ulp][ignore]") esp_deep_sleep_start(); } +TEST_CASE("ulp power consumption in deep sleep", "[ulp]") +{ + assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 4 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); + ulp_insn_t insn = I_HALT(); + RTC_SLOW_MEM[0] = *(uint32_t*) &insn; + + REG_WRITE(SENS_ULP_CP_SLEEP_CYC0_REG, 0x8000); + + ulp_run(0); + + esp_deep_sleep_enable_ulp_wakeup(); + esp_deep_sleep_enable_timer_wakeup(10 * 1000000); + esp_deep_sleep_start(); +} + +TEST_CASE("ulp timer setting", "[ulp]") +{ + /* + * Run a simple ULP program which increments the counter, for one second. + * Program calls I_HALT each time and gets restarted by the timer. + * Compare the expected number of times the program runs with the actual. + */ + assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 32 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); + memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM); + + const int offset = 6; + const ulp_insn_t program[] = { + I_MOVI(R1, offset), // r1 <- offset + I_LD(R2, R1, 0), // load counter + I_ADDI(R2, R2, 1), // counter += 1 + I_ST(R2, R1, 0), // save counter + I_HALT(), + }; + + size_t size = sizeof(program)/sizeof(ulp_insn_t); + TEST_ESP_OK(ulp_process_macros_and_load(0, program, &size)); + assert(offset >= size && "data offset needs to be greater or equal to program size"); + TEST_ESP_OK(ulp_run(0)); + // disable the ULP program timer — we will enable it later + CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); + + const uint32_t cycles_to_test[] = {0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000}; + const size_t tests_count = sizeof(cycles_to_test) / sizeof(cycles_to_test[0]); + for (size_t i = 0; i < tests_count; ++i) { + // zero out the counter + RTC_SLOW_MEM[offset] = 0; + // set the number of slow clock cycles + REG_WRITE(SENS_ULP_CP_SLEEP_CYC0_REG, cycles_to_test[i]); + // enable the timer and wait for a second + SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); + vTaskDelay(1000 / portTICK_PERIOD_MS); + // get the counter value and stop the timer + uint32_t counter = RTC_SLOW_MEM[offset] & 0xffff; + CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN); + // compare the actual and expected numbers of iterations of ULP program + float expected_period = (cycles_to_test[i] + 16) / (float) RTC_CNTL_SLOWCLK_FREQ + 5 / 8e6f; + float error = 1.0f - counter * expected_period; + printf("%u\t%u\t%.01f\t%.04f\n", cycles_to_test[i], counter, 1.0f / expected_period, error); + // Should be within 15% + TEST_ASSERT_INT_WITHIN(15, 0, (int) error * 100); + // Note: currently RTC_CNTL_SLOWCLK_FREQ is ballpark value — we need to determine it + // Precisely by running calibration similar to the one done in deep sleep. + // This may cause the test to fail on some chips which have the slow clock frequency + // way off. + } +} TEST_CASE("ulp can use TSENS in deep sleep", "[ulp][ignore]") { @@ -297,10 +368,87 @@ TEST_CASE("ulp can use TSENS in deep sleep", "[ulp][ignore]") I_ST(R0, R2, offset + 4), I_ADDI(R2, R2, 1), // counter += 1 I_ST(R2, R1, 1), // save counter - I_SLEEP(0), // enter sleep + I_HALT(), // enter sleep + M_LABEL(1), // done with measurements + I_END(), // stop ULP timer + I_WAKE(), // initiate wakeup + I_HALT() + }; + + size_t size = sizeof(program)/sizeof(ulp_insn_t); + TEST_ESP_OK(ulp_process_macros_and_load(0, program, &size)); + assert(offset >= size); + + TEST_ESP_OK(ulp_run(0)); + esp_deep_sleep_enable_timer_wakeup(4000000); + esp_deep_sleep_enable_ulp_wakeup(); + esp_deep_sleep_start(); +} + +TEST_CASE("can use ADC in deep sleep", "[ulp][ignore]") +{ + assert(CONFIG_ULP_COPROC_RESERVE_MEM >= 260 && "this test needs ULP_COPROC_RESERVE_MEM option set in menuconfig"); + + hexdump(RTC_SLOW_MEM, CONFIG_ULP_COPROC_RESERVE_MEM / 4); + printf("\n\n"); + memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM); + + SET_PERI_REG_BITS(SENS_SAR_START_FORCE_REG, SENS_SAR1_BIT_WIDTH, 3, SENS_SAR1_BIT_WIDTH_S); + SET_PERI_REG_BITS(SENS_SAR_START_FORCE_REG, SENS_SAR2_BIT_WIDTH, 3, SENS_SAR2_BIT_WIDTH_S); + + SET_PERI_REG_BITS(SENS_SAR_READ_CTRL_REG, SENS_SAR1_SAMPLE_BIT, 0x3, SENS_SAR1_SAMPLE_BIT_S); + SET_PERI_REG_BITS(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_SAMPLE_BIT, 0x3, SENS_SAR2_SAMPLE_BIT_S); + + CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_START2_REG, SENS_MEAS2_START_FORCE); + CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_START1_REG, SENS_MEAS1_START_FORCE); + + SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 0, SENS_FORCE_XPD_SAR_S); + SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_AMP, 2, SENS_FORCE_XPD_AMP_S); + +// SAR1 invert result + SET_PERI_REG_MASK(SENS_SAR_READ_CTRL_REG, SENS_SAR1_DATA_INV); + SET_PERI_REG_MASK(SENS_SAR_READ_CTRL_REG, SENS_SAR2_DATA_INV); + + +// const int adc = 1; +// const int channel = 1; +// const int atten = 3; +// const int gpio_num = 0; + + const int adc = 0; + const int channel = 0; + const int atten = 0; + const int gpio_num = 36; + + rtc_gpio_init(gpio_num); + + CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_START1_REG, SENS_SAR1_EN_PAD_FORCE_M); + CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_START2_REG, SENS_SAR2_EN_PAD_FORCE_M); + + SET_PERI_REG_BITS(SENS_SAR_ATTEN1_REG, 3, atten, 2 * channel); //set SAR1 attenuation + SET_PERI_REG_BITS(SENS_SAR_ATTEN2_REG, 3, atten, 2 * channel); //set SAR2 attenuation + + // data start offset + size_t offset = 20; + // number of samples to collect + RTC_SLOW_MEM[offset] = (CONFIG_ULP_COPROC_RESERVE_MEM) / 4 - offset - 8; + // sample counter + RTC_SLOW_MEM[offset + 1] = 0; + + const ulp_insn_t program[] = { + I_MOVI(R1, offset), // r1 <- offset + I_LD(R2, R1, 1), // r2 <- counter + I_LD(R3, R1, 0), // r3 <- length + I_SUBI(R3, R3, 1), // end = length - 1 + I_SUBR(R3, R3, R2), // r3 = length - counter + M_BXF(1), // if overflow goto 1: + I_ADC(R0, adc, channel), // r0 <- ADC + I_ST(R0, R2, offset + 4), + I_ADDI(R2, R2, 1), // counter += 1 + I_ST(R2, R1, 1), // save counter I_HALT(), M_LABEL(1), // done with measurements - I_END(0), // stop ULP timer + I_END(), // stop ULP program timer I_HALT() }; From e96d653c9ecff6d90113bcead8ef003050d87562 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 22 Feb 2017 17:04:11 +0800 Subject: [PATCH 14/15] ulp: make sure I_WAKE instruction works with ULP timer disabled Setting ULP_CP_WAKEUP_FORCE_EN forces ULP wakeup to work even if the ULP timer has already been disabled. --- components/esp32/deep_sleep.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/esp32/deep_sleep.c b/components/esp32/deep_sleep.c index aadb6067e..55e92a05e 100644 --- a/components/esp32/deep_sleep.c +++ b/components/esp32/deep_sleep.c @@ -126,6 +126,9 @@ void IRAM_ATTR esp_deep_sleep_start() if (s_config.wakeup_triggers & EXT_EVENT1_TRIG_EN) { ext1_wakeup_prepare(); } + if (s_config.wakeup_triggers & SAR_TRIG_EN) { + SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_WAKEUP_FORCE_EN); + } // TODO: move timer wakeup configuration into a similar function // once rtc_sleep is opensourced. From 4af5f572f49f21d23af3c21073178a9c40828503 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Sat, 28 Jan 2017 00:00:31 +0800 Subject: [PATCH 15/15] add deep sleep wake up example --- docs/api/system/deep_sleep.rst | 1 + examples/system/deep_sleep/Makefile | 9 + examples/system/deep_sleep/README.md | 11 + .../system/deep_sleep/main/Kconfig.projbuild | 20 ++ examples/system/deep_sleep/main/component.mk | 3 + .../deep_sleep/main/deep_sleep_wakeup.c | 296 ++++++++++++++++++ examples/system/deep_sleep/sdkconfig.defaults | 7 + 7 files changed, 347 insertions(+) create mode 100644 examples/system/deep_sleep/Makefile create mode 100644 examples/system/deep_sleep/README.md create mode 100644 examples/system/deep_sleep/main/Kconfig.projbuild create mode 100644 examples/system/deep_sleep/main/component.mk create mode 100644 examples/system/deep_sleep/main/deep_sleep_wakeup.c create mode 100644 examples/system/deep_sleep/sdkconfig.defaults diff --git a/docs/api/system/deep_sleep.rst b/docs/api/system/deep_sleep.rst index aacb25218..c66127d63 100644 --- a/docs/api/system/deep_sleep.rst +++ b/docs/api/system/deep_sleep.rst @@ -116,3 +116,4 @@ Application Example Implementation of basic functionality of deep sleep is shown in :example:`protocols/sntp` example, where ESP module is periodically waken up to retrive time from NTP server. +More extensive example in :example:`system/deep_sleep` illustrates usage of various deep sleep wakeup triggers and ULP coprocessor programming. diff --git a/examples/system/deep_sleep/Makefile b/examples/system/deep_sleep/Makefile new file mode 100644 index 000000000..4d0fae132 --- /dev/null +++ b/examples/system/deep_sleep/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := deep_sleep + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/system/deep_sleep/README.md b/examples/system/deep_sleep/README.md new file mode 100644 index 000000000..3d4438de7 --- /dev/null +++ b/examples/system/deep_sleep/README.md @@ -0,0 +1,11 @@ +# Example: deep sleep + +This example illustrates usage of deep sleep mode and various wakeup sources. + +The following wake up sources are configured: + +- Timer: wake up the chip in 20 seconds +- EXT1: wake up the chip if any of the two buttons are pressed (GPIO25, GPIO26) +- Touch: wake up the chip if any of the touch pads are pressed (GPIO32, GPIO33) +- ULP: wake up when the chip temperature changes by more than ~5 degrees Celsius (this value hasn't been characterized exactly yet). + diff --git a/examples/system/deep_sleep/main/Kconfig.projbuild b/examples/system/deep_sleep/main/Kconfig.projbuild new file mode 100644 index 000000000..cb8632d68 --- /dev/null +++ b/examples/system/deep_sleep/main/Kconfig.projbuild @@ -0,0 +1,20 @@ +menu "Example Configuration" + +config ENABLE_TOUCH_WAKEUP + bool "Enable touch wake up" + default y + help + This option enables wake up from deep sleep using touch pads + TOUCH8 and TOUCH9, which correspond to GPIO33 and GPIO32. + +config ENABLE_ULP_TEMPERATURE_WAKEUP + bool "Enable temperature monitoring by ULP" + default y + help + This option enables wake up from deep sleep using ULP. + ULP program monitors the on-chip temperature sensor and + wakes up the chip when the temperature goes outside of + the window defined by the initial temperature and a threshold + around it. + +endmenu \ No newline at end of file diff --git a/examples/system/deep_sleep/main/component.mk b/examples/system/deep_sleep/main/component.mk new file mode 100644 index 000000000..44bd2b527 --- /dev/null +++ b/examples/system/deep_sleep/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# diff --git a/examples/system/deep_sleep/main/deep_sleep_wakeup.c b/examples/system/deep_sleep/main/deep_sleep_wakeup.c new file mode 100644 index 000000000..959fdfbad --- /dev/null +++ b/examples/system/deep_sleep/main/deep_sleep_wakeup.c @@ -0,0 +1,296 @@ +/* Deep sleep wake up example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_deep_sleep.h" +#include "esp_log.h" +#include "esp32/ulp.h" +#include "driver/touch_pad.h" +#include "driver/adc.h" +#include "driver/rtc_io.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/sens_reg.h" + +static RTC_DATA_ATTR struct timeval sleep_enter_time; + +#ifdef CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP + +/* + * Offset (in 32-bit words) in RTC Slow memory where the data is placed + * by the ULP coprocessor. It can be chosen to be any value greater or equal + * to ULP program size, and less than the CONFIG_ULP_COPROC_RESERVE_MEM/4 - 6, + * where 6 is the number of words used by the ULP coprocessor. + */ +#define ULP_DATA_OFFSET 36 + +_Static_assert(ULP_DATA_OFFSET < CONFIG_ULP_COPROC_RESERVE_MEM/4 - 6, + "ULP_DATA_OFFSET is set too high, or CONFIG_ULP_COPROC_RESERVE_MEM is not sufficient"); + +/** + * @brief Start ULP temperature monitoring program + * + * This function loads a program into the RTC Slow memory and starts up the ULP. + * The program monitors on-chip temperature sensor and wakes up the SoC when + * the temperature goes lower or higher than certain thresholds. + */ +static void start_ulp_temperature_monitoring(); + +/** + * @brief Utility function which reads data written by ULP program + * + * @param offset offset from ULP_DATA_OFFSET in RTC Slow memory, in words + * @return lower 16-bit part of the word writable by the ULP + */ +static inline uint16_t ulp_data_read(size_t offset) +{ + return RTC_SLOW_MEM[ULP_DATA_OFFSET + offset] & 0xffff; +} + +/** + * @brief Utility function which writes data to be ready by ULP program + * + * @param offset offset from ULP_DATA_OFFSET in RTC Slow memory, in words + * @param value lower 16-bit part of the word to be stored + */ +static inline void ulp_data_write(size_t offset, uint16_t value) +{ + RTC_SLOW_MEM[ULP_DATA_OFFSET + offset] = value; +} + +#endif // CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP + +#ifdef CONFIG_ENABLE_TOUCH_WAKEUP +static void calibrate_touch_pad(touch_pad_t pad); +#endif + +void app_main() +{ + struct timeval now; + gettimeofday(&now, NULL); + int sleep_time_ms = (now.tv_sec - sleep_enter_time.tv_sec) * 1000 + (now.tv_usec - sleep_enter_time.tv_usec) / 1000; + + switch (esp_deep_sleep_get_wakeup_cause()) { + case ESP_DEEP_SLEEP_WAKEUP_EXT1: { + uint64_t wakeup_pin_mask = esp_deep_sleep_get_ext1_wakeup_status(); + if (wakeup_pin_mask != 0) { + int pin = __builtin_ffsll(wakeup_pin_mask) - 1; + printf("Wake up from GPIO %d\n", pin); + } else { + printf("Wake up from GPIO\n"); + } + break; + } + case ESP_DEEP_SLEEP_WAKEUP_TIMER: { + printf("Wake up from timer. Time spent in deep sleep: %dms\n", sleep_time_ms); + break; + } +#ifdef CONFIG_ENABLE_TOUCH_WAKEUP + case ESP_DEEP_SLEEP_WAKEUP_TOUCHPAD: { + printf("Wake up from touch on pad %d\n", esp_deep_sleep_get_touchpad_wakeup_status()); + break; + } +#endif // CONFIG_ENABLE_TOUCH_WAKEUP +#ifdef CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP + case ESP_DEEP_SLEEP_WAKEUP_ULP: { + printf("Wake up from ULP\n"); + int16_t diff_high = (int16_t) ulp_data_read(3); + int16_t diff_low = (int16_t) ulp_data_read(4); + if (diff_high < 0) { + printf("High temperature alarm was triggered\n"); + } else if (diff_low < 0) { + printf("Low temperature alarm was triggered\n"); + } else { + assert(false && "temperature has stayed within limits, but got ULP wakeup\n"); + } + break; + } +#endif // CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP + case ESP_DEEP_SLEEP_WAKEUP_UNDEFINED: + default: + printf("Not a deep sleep reset\n"); + } + +#ifdef CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP + if (esp_deep_sleep_get_wakeup_cause() != ESP_DEEP_SLEEP_WAKEUP_UNDEFINED) { + printf("ULP did %d temperature measurements in %d ms\n", ulp_data_read(1), sleep_time_ms); + printf("Initial T=%d, latest T=%d\n", ulp_data_read(0), ulp_data_read(2)); + } +#endif // CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP + + vTaskDelay(1000 / portTICK_PERIOD_MS); + + const int wakeup_time_sec = 20; + printf("Enabling timer wakeup, %ds\n", wakeup_time_sec); + esp_deep_sleep_enable_timer_wakeup(wakeup_time_sec * 1000000); + + const int ext_wakeup_pin_1 = 25; + const uint64_t ext_wakeup_pin_1_mask = 1ULL << ext_wakeup_pin_1; + const int ext_wakeup_pin_2 = 26; + const uint64_t ext_wakeup_pin_2_mask = 1ULL << ext_wakeup_pin_2; + + printf("Enabling EXT1 wakeup on pins GPIO%d, GPIO%d\n", ext_wakeup_pin_1, ext_wakeup_pin_2); + esp_deep_sleep_enable_ext1_wakeup(ext_wakeup_pin_1_mask | ext_wakeup_pin_2_mask, ESP_EXT1_WAKEUP_ANY_HIGH); + +#ifdef CONFIG_ENABLE_TOUCH_WAKEUP + touch_pad_init(); + calibrate_touch_pad(TOUCH_PAD_NUM8); + calibrate_touch_pad(TOUCH_PAD_NUM9); + printf("Enabling touch pad wakeup\n"); + esp_deep_sleep_enable_touchpad_wakeup(); +#endif // CONFIG_ENABLE_TOUCH_WAKEUP + +#ifdef CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP + printf("Enabling ULP wakeup\n"); + esp_deep_sleep_enable_ulp_wakeup(); +#endif + + printf("Entering deep sleep\n"); + gettimeofday(&sleep_enter_time, NULL); + +#ifdef CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP + start_ulp_temperature_monitoring(); +#endif + + esp_deep_sleep_start(); +} + +#ifdef CONFIG_ENABLE_TOUCH_WAKEUP +static void calibrate_touch_pad(touch_pad_t pad) +{ + touch_pad_config(pad, 1000); + + int avg = 0; + const size_t calibration_count = 128; + for (int i = 0; i < calibration_count; ++i) { + uint16_t val; + touch_pad_read(pad, &val); + avg += val; + } + avg /= calibration_count; + const int min_reading = 300; + if (avg < min_reading) { + printf("Touch pad #%d average reading is too low: %d (expecting at least %d). " + "Not using for deep sleep wakeup.\n", pad, avg, min_reading); + touch_pad_config(pad, 0); + } else { + int threshold = avg - 100; + printf("Touch pad #%d average: %d, wakeup threshold set to %d.\n", pad, avg, threshold); + touch_pad_config(pad, threshold); + } +} +#endif // CONFIG_ENABLE_TOUCH_WAKEUP + +#ifdef CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP +static void start_ulp_temperature_monitoring() +{ + /* + * This ULP program monitors the on-chip temperature sensor and wakes the chip up when + * the temperature goes outside of certain window. + * When the program runs for the first time, it saves the temperature measurement, + * it is considered initial temperature (T0). + * + * On each subsequent run, temperature measured and compared to T0. + * If the measured value is higher than T0 + max_temp_diff or lower than T0 - max_temp_diff, + * the chip is woken up from deep sleep. + */ + + /* Temperature difference threshold which causes wakeup + * With settings here (TSENS_CLK_DIV=2, 8000 cycles), + * TSENS measurement is done in units of 0.73 degrees Celsius. + * Therefore, max_temp_diff below is equivalent to ~2.2 degrees Celsius. + */ + const int16_t max_temp_diff = 3; + + // Number of measurements ULP should do per second + const uint32_t measurements_per_sec = 5; + + // Allow TSENS to be controlled by the ULP + SET_PERI_REG_BITS(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_CLK_DIV, 2, SENS_TSENS_CLK_DIV_S); + SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 3, SENS_FORCE_XPD_SAR_S); + CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP); + CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_DUMP_OUT); + CLEAR_PERI_REG_MASK(SENS_SAR_TSENS_CTRL_REG, SENS_TSENS_POWER_UP_FORCE); + + // Clear the part of RTC_SLOW_MEM reserved for the ULP. Makes debugging easier. + memset(RTC_SLOW_MEM, 0, CONFIG_ULP_COPROC_RESERVE_MEM); + + // The first word of memory (at data offset) is used to store the initial temperature (T0) + // Zero it out here, then ULP will update it on the first run. + ulp_data_write(0, 0); + // The second word is used to store measurement count, zero it out as well. + ulp_data_write(1, 0); + + const ulp_insn_t program[] = { + // load data offset into R2 + I_MOVI(R2, ULP_DATA_OFFSET), + // load/increment/store measurement counter using R1 + I_LD(R1, R2, 1), + I_ADDI(R1, R1, 1), + I_ST(R1, R2, 1), + // enable temperature sensor + I_WR_REG(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_S, SENS_FORCE_XPD_SAR_S + 1, 3), + // do temperature measurement and store result in R3 + I_TSENS(R3, 8000), + // disable temperature sensor + I_WR_REG(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_S, SENS_FORCE_XPD_SAR_S + 1, 0), + // Save current measurement at offset+2 + I_ST(R3, R2, 2), + // load initial value into R0 + I_LD(R0, R2, 0), + // if threshold value >=1 (i.e. initialized), goto 1 + M_BGE(1, 1), + // otherwise, save the current value as initial (T0) + I_MOVR(R0, R3), + I_ST(R0, R2, 0), + M_LABEL(1), + // check if the temperature is greater or equal (T0 + max_temp_diff) + // uses R1 as scratch register, difference is saved at offset + 3 + I_ADDI(R1, R0, max_temp_diff - 1), + I_SUBR(R1, R1, R3), + I_ST(R1, R2, 3), + M_BXF(2), + // check if the temperature is less or equal (T0 - max_temp_diff) + // uses R1 as scratch register, difference is saved at offset + 4 + I_SUBI(R1, R0, max_temp_diff - 1), + I_SUBR(R1, R3, R1), + I_ST(R1, R2, 4), + M_BXF(2), + // temperature is within (T0 - max_temp_diff; T0 + max_temp_diff) + // stop ULP until the program timer starts it again + I_HALT(), + M_LABEL(2), + // temperature is out of bounds + // disable ULP program timer + I_WR_REG_BIT(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN_S, 0), + // initiate wakeup of the SoC + I_WAKE(), + // stop the ULP program + I_HALT() + }; + + // Load ULP program into RTC_SLOW_MEM, at offset 0 + size_t size = sizeof(program)/sizeof(ulp_insn_t); + ESP_ERROR_CHECK( ulp_process_macros_and_load(0, program, &size) ); + assert(size < ULP_DATA_OFFSET && "ULP_DATA_OFFSET needs to be greater or equal to the program size"); + + // Set ULP wakeup period + const uint32_t sleep_cycles = RTC_CNTL_SLOWCLK_FREQ / measurements_per_sec; + REG_WRITE(SENS_ULP_CP_SLEEP_CYC0_REG, sleep_cycles); + + // Start ULP + ESP_ERROR_CHECK( ulp_run(0) ); +} +#endif // CONFIG_ENABLE_ULP_TEMPERATURE_WAKEUP + diff --git a/examples/system/deep_sleep/sdkconfig.defaults b/examples/system/deep_sleep/sdkconfig.defaults new file mode 100644 index 000000000..dd7da3f88 --- /dev/null +++ b/examples/system/deep_sleep/sdkconfig.defaults @@ -0,0 +1,7 @@ +CONFIG_ESP32_DEFAULT_CPU_FREQ_80=y +CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=80 +CONFIG_ULP_COPROC_ENABLED=y +CONFIG_ULP_COPROC_RESERVE_MEM=512 +CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y +CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y +CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=500