Skip to content

Commit ce09cfd

Browse files
committed
esp32: Deactivate wakeup trigger after first wakeup
Added function esp_sleep_disable_wakeup_source() to deactivate wakeup trigger for selected source. Updated documentation for this function in sleep_modes.rst file. Updated unit test to check this functionality for light sleep. The test_sleep.c unit test is updated to add reliability for unit testing. (TW#18952) Closes #1677
1 parent 2d90da0 commit ce09cfd

File tree

5 files changed

+189
-82
lines changed

5 files changed

+189
-82
lines changed

components/esp32/include/esp_deep_sleep.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ typedef esp_sleep_pd_option_t esp_deep_sleep_pd_option_t;
3737
typedef esp_sleep_ext1_wakeup_mode_t esp_ext1_wakeup_mode_t;
3838
typedef esp_sleep_wakeup_cause_t esp_deep_sleep_wakeup_cause_t;
3939

40+
inline static esp_err_t esp_deep_sleep_disable_wakeup_source(esp_sleep_source_t source)
41+
{
42+
return esp_sleep_disable_wakeup_source(source);
43+
}
44+
4045
inline static esp_err_t esp_deep_sleep_enable_ulp_wakeup(void)
4146
{
4247
return esp_sleep_enable_ulp_wakeup();
@@ -47,11 +52,6 @@ inline static esp_err_t esp_deep_sleep_enable_timer_wakeup(uint64_t time_in_us)
4752
return esp_sleep_enable_timer_wakeup(time_in_us);
4853
}
4954

50-
inline static esp_err_t esp_deep_sleep_disable_timer_wakeup(void)
51-
{
52-
return esp_sleep_disable_timer_wakeup();
53-
}
54-
5555
inline static esp_err_t esp_deep_sleep_enable_touchpad_wakeup(void)
5656
{
5757
return esp_sleep_enable_touchpad_wakeup();

components/esp32/include/esp_sleep.h

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "esp_err.h"
1919
#include "driver/gpio.h"
2020
#include "driver/touch_pad.h"
21+
#include "soc/rtc.h"
2122

2223
#ifdef __cplusplus
2324
extern "C" {
@@ -62,6 +63,35 @@ typedef enum {
6263
ESP_SLEEP_WAKEUP_ULP, //!< Wakeup caused by ULP program
6364
} esp_sleep_wakeup_cause_t;
6465

66+
/**
67+
* @brief Sleep wakeup sources for esp_sleep_disable_wakeup_source function
68+
*/
69+
typedef enum {
70+
ESP_SLEEP_SOURCE_UNDEFINED, //!< Wakeup source is not defined
71+
ESP_SLEEP_SOURCE_EXT0, //!< Wakeup source for external signal using RTC_IO
72+
ESP_SLEEP_SOURCE_EXT1, //!< Wakeup source for external signal using RTC_CNTL
73+
ESP_SLEEP_SOURCE_TIMER, //!< Wakeup source for timer
74+
ESP_SLEEP_SOURCE_TOUCHPAD, //!< Wakeup source for touchpad
75+
ESP_SLEEP_SOURCE_ULP, //!< Wakeup source for ULP program
76+
} esp_sleep_source_t;
77+
78+
/**
79+
* @brief Disable wakeup source
80+
*
81+
* This function is used to deactivate wake up trigger for source
82+
* defined as parameter of the function.
83+
*
84+
* @note This function does not modify wake up configuration in RTC.
85+
* It will be performed in esp_sleep_start function.
86+
*
87+
* See docs/sleep-modes.rst for details.
88+
*
89+
* @param source - number of source to disable of type esp_sleep_source_t
90+
* @return
91+
* - ESP_OK on success
92+
* - ESP_ERR_INVALID_STATE if trigger was not active
93+
*/
94+
esp_err_t esp_sleep_disable_wakeup_source(esp_sleep_source_t source);
6595

6696
/**
6797
* @brief Enable wakeup by ULP coprocessor
@@ -75,7 +105,7 @@ typedef enum {
75105
*/
76106
esp_err_t esp_sleep_enable_ulp_wakeup();
77107

78-
/**
108+
/**
79109
* @brief Enable wakeup by timer
80110
* @param time_in_us time before wakeup, in microseconds
81111
* @return
@@ -84,23 +114,6 @@ esp_err_t esp_sleep_enable_ulp_wakeup();
84114
*/
85115
esp_err_t esp_sleep_enable_timer_wakeup(uint64_t time_in_us);
86116

87-
/**
88-
* @brief Disable timer wakeup
89-
*
90-
* This function is used to deactivate timer wakeup trigger
91-
* after first sleep for example to allow wakeup from other sources.
92-
*
93-
* @note This function does not modify wakeup configuration in RTC.
94-
* It will be performed in esp_sleep_start function.
95-
*
96-
* See docs/sleep-modes.rst for details.
97-
*
98-
* @return
99-
* - ESP_OK on success
100-
* - ESP_ERR_INVALID_STATE if trigger was not active
101-
*/
102-
esp_err_t esp_sleep_disable_timer_wakeup();
103-
104117
/**
105118
* @brief Enable wakeup by touch sensor
106119
*

components/esp32/sleep_modes.c

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242
// Time from VDD_SDIO power up to first flash read in ROM code
4343
#define VDD_SDIO_POWERUP_TO_FLASH_READ_US 700
4444

45+
#define CHECK_SOURCE(source, value, mask) ((s_config.wakeup_triggers & mask) && \
46+
(source == value))
47+
4548
/**
4649
* Internal structure which holds all requested deep sleep parameters
4750
*/
@@ -282,6 +285,43 @@ esp_err_t esp_light_sleep_start()
282285

283286
void system_deep_sleep(uint64_t) __attribute__((alias("esp_deep_sleep")));
284287

288+
esp_err_t esp_sleep_disable_wakeup_source(esp_sleep_source_t source)
289+
{
290+
// For most of sources it is enough to set trigger mask in local
291+
// configuration structure. The actual RTC wake up options
292+
// will be updated by esp_sleep_start().
293+
if (CHECK_SOURCE(source, ESP_SLEEP_SOURCE_TIMER, RTC_TIMER_TRIG_EN)) {
294+
s_config.wakeup_triggers &= ~RTC_TIMER_TRIG_EN;
295+
s_config.sleep_duration = 0;
296+
}
297+
else if (CHECK_SOURCE(source, ESP_SLEEP_SOURCE_EXT0, RTC_EXT0_TRIG_EN)) {
298+
s_config.ext0_rtc_gpio_num = 0;
299+
s_config.ext0_trigger_level = 0;
300+
s_config.wakeup_triggers &= ~RTC_EXT0_TRIG_EN;
301+
}
302+
else if (CHECK_SOURCE(source, ESP_SLEEP_SOURCE_EXT1, RTC_EXT1_TRIG_EN)) {
303+
s_config.ext1_rtc_gpio_mask = 0;
304+
s_config.ext1_trigger_mode = 0;
305+
s_config.wakeup_triggers &= ~RTC_EXT1_TRIG_EN;
306+
}
307+
else if (CHECK_SOURCE(source, ESP_SLEEP_SOURCE_TOUCHPAD, RTC_TOUCH_TRIG_EN)) {
308+
s_config.wakeup_triggers &= ~RTC_TOUCH_TRIG_EN;
309+
}
310+
#ifdef CONFIG_ULP_COPROC_ENABLED
311+
else if (CHECK_SOURCE(source, ESP_SLEEP_SOURCE_ULP, RTC_ULP_TRIG_EN)) {
312+
// The ulp wake up option is disabled immediately
313+
CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_WAKEUP_FORCE_EN);
314+
CLEAR_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN);
315+
s_config.wakeup_triggers &= ~RTC_ULP_TRIG_EN;
316+
}
317+
#endif
318+
else {
319+
ESP_LOGE(TAG, "Incorrect wakeup source (%d) to disable.", (int) source);
320+
return ESP_ERR_INVALID_STATE;
321+
}
322+
return ESP_OK;
323+
}
324+
285325
esp_err_t esp_sleep_enable_ulp_wakeup()
286326
{
287327
#ifdef CONFIG_ULP_COPROC_ENABLED
@@ -303,22 +343,6 @@ esp_err_t esp_sleep_enable_timer_wakeup(uint64_t time_in_us)
303343
return ESP_OK;
304344
}
305345

306-
esp_err_t esp_sleep_disable_timer_wakeup()
307-
{
308-
if (s_config.wakeup_triggers & RTC_TIMER_TRIG_EN) {
309-
// The only timer wakeup trigger should be disabled, setup will
310-
// be performed in rtc_sleep_start() which updates wakeup options
311-
// in RTC peripheral registers
312-
s_config.wakeup_triggers &= ~RTC_TIMER_TRIG_EN;
313-
s_config.sleep_duration = 0;
314-
}
315-
else {
316-
ESP_LOGE(TAG, "The timer wake-up trigger is not set.");
317-
return ESP_ERR_INVALID_STATE;
318-
}
319-
return ESP_OK;
320-
}
321-
322346
static void timer_wakeup_prepare()
323347
{
324348
uint32_t period = esp_clk_slowclk_cal_get();

components/esp32/test/test_sleep.c

Lines changed: 105 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,27 @@
55
#include "freertos/FreeRTOS.h"
66
#include "freertos/task.h"
77

8+
#include "soc/rtc_cntl_reg.h" // for read rtc registers directly (cause)
9+
#include "soc/soc.h"
10+
811
#define ESP_EXT0_WAKEUP_LEVEL_LOW 0
912
#define ESP_EXT0_WAKEUP_LEVEL_HIGH 1
1013

14+
// These are flags for wakeup cause to get it directly from RTC
15+
#define RTC_EXT0_TRIG_EN BIT(0) //!< EXT0 GPIO wakeup
16+
#define RTC_EXT1_TRIG_EN BIT(1) //!< EXT1 GPIO wakeup
17+
#define RTC_GPIO_TRIG_EN BIT(2) //!< GPIO wakeup (light sleep only)
18+
#define RTC_TIMER_TRIG_EN BIT(3) //!< Timer wakeup
19+
#define RTC_SDIO_TRIG_EN BIT(4) //!< SDIO wakeup (light sleep only)
20+
#define RTC_MAC_TRIG_EN BIT(5) //!< MAC wakeup (light sleep only)
21+
#define RTC_UART0_TRIG_EN BIT(6) //!< UART0 wakeup (light sleep only)
22+
#define RTC_UART1_TRIG_EN BIT(7) //!< UART1 wakeup (light sleep only)
23+
#define RTC_TOUCH_TRIG_EN BIT(8) //!< Touch wakeup
24+
#define RTC_ULP_TRIG_EN BIT(9) //!< ULP wakeup
25+
#define RTC_BT_TRIG_EN BIT(10) //!< BT wakeup (light sleep only)
26+
27+
static struct timeval tv_start, tv_stop;
28+
1129
TEST_CASE("esp_deepsleep works", "[deepsleep][reset=DEEPSLEEP_RESET]")
1230
{
1331
esp_deep_sleep(2000000);
@@ -48,44 +66,6 @@ TEST_CASE("wake up from light sleep using timer", "[deepsleep]")
4866
TEST_ASSERT_INT32_WITHIN(500, 2000, (int) dt);
4967
}
5068

51-
TEST_CASE("wake up disable timer for ext0 wakeup (13 low)", "[deepsleep][ignore]")
52-
{
53-
// Setup timer to wakeup with timeout
54-
esp_sleep_enable_timer_wakeup(2000000);
55-
struct timeval tv_start, tv_stop;
56-
gettimeofday(&tv_start, NULL);
57-
esp_light_sleep_start();
58-
gettimeofday(&tv_stop, NULL);
59-
float dt = (tv_stop.tv_sec - tv_start.tv_sec) * 1e3f +
60-
(tv_stop.tv_usec - tv_start.tv_usec) * 1e-3f;
61-
printf("Timer sleep time = %d\r\n", (int)dt);
62-
63-
TEST_ASSERT_INT32_WITHIN(500, 2000, (int) dt);
64-
65-
// Setup ext0 configuration to wake up
66-
ESP_ERROR_CHECK(rtc_gpio_init(GPIO_NUM_13));
67-
ESP_ERROR_CHECK(gpio_pullup_en(GPIO_NUM_13));
68-
ESP_ERROR_CHECK(gpio_pulldown_dis(GPIO_NUM_13));
69-
ESP_ERROR_CHECK(esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, ESP_EXT0_WAKEUP_LEVEL_LOW));
70-
71-
// Disable timer wakeup trigger to wakeup from ext0 source
72-
// instead of timer wakeup
73-
ESP_ERROR_CHECK(esp_sleep_disable_timer_wakeup());
74-
printf("Waiting low level on GPIO_13\r\n");
75-
76-
gettimeofday(&tv_start, NULL);
77-
esp_light_sleep_start();
78-
gettimeofday(&tv_stop, NULL);
79-
80-
dt = (tv_stop.tv_sec - tv_start.tv_sec) * 1e3f +
81-
(tv_stop.tv_usec - tv_start.tv_usec) * 1e-3f;
82-
printf("Ext0 sleep time = %d\r\n", (int)dt);
83-
84-
// Check error message
85-
esp_err_t err_code = esp_sleep_disable_timer_wakeup();
86-
TEST_ASSERT(err_code == ESP_ERR_INVALID_STATE);
87-
}
88-
8969
#ifndef CONFIG_FREERTOS_UNICORE
9070
TEST_CASE("enter deep sleep on APP CPU and wake up using timer", "[deepsleep][reset=DEEPSLEEP_RESET]")
9171
{
@@ -148,3 +128,90 @@ TEST_CASE("wake up using ext1 when RTC_PERIPH is on (13 low)", "[deepsleep][igno
148128
ESP_ERROR_CHECK(esp_sleep_enable_ext1_wakeup(BIT(GPIO_NUM_13), ESP_EXT1_WAKEUP_ALL_LOW));
149129
esp_deep_sleep_start();
150130
}
131+
132+
static float get_time(void)
133+
{
134+
gettimeofday(&tv_stop, NULL);
135+
136+
float dt = (tv_stop.tv_sec - tv_start.tv_sec) * 1e3f +
137+
(tv_stop.tv_usec - tv_start.tv_usec) * 1e-3f;
138+
return abs(dt);
139+
}
140+
141+
static uint32_t get_cause()
142+
{
143+
uint32_t wakeup_cause = REG_GET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, \
144+
RTC_CNTL_WAKEUP_CAUSE);
145+
return wakeup_cause;
146+
}
147+
148+
// This test case verifies deactivation of trigger for wake up sources
149+
TEST_CASE("disable source behavior", "[deepsleep][ignore]")
150+
{
151+
float dt = 0;
152+
153+
printf("Setup timer and ext0 to wakeup imediately from GPIO_13 \n");
154+
155+
// Setup ext0 configuration to wake up almost immediately
156+
// The wakeup time is proportional to input capacitance * pullup resistance
157+
ESP_ERROR_CHECK(rtc_gpio_init(GPIO_NUM_13));
158+
ESP_ERROR_CHECK(gpio_pullup_en(GPIO_NUM_13));
159+
ESP_ERROR_CHECK(gpio_pulldown_dis(GPIO_NUM_13));
160+
ESP_ERROR_CHECK(esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, ESP_EXT0_WAKEUP_LEVEL_HIGH));
161+
162+
// Setup timer to wakeup with timeout
163+
esp_sleep_enable_timer_wakeup(2000000);
164+
165+
// Save start time
166+
gettimeofday(&tv_start, NULL);
167+
esp_light_sleep_start();
168+
169+
dt = get_time();
170+
printf("Ext0 sleep time = %d \n", (int) dt);
171+
172+
// Check wakeup from Ext0 using time measurement because wakeup cause is
173+
// not available in light sleep mode
174+
TEST_ASSERT_INT32_WITHIN(297, 300, (int) dt);
175+
176+
TEST_ASSERT((get_cause() & RTC_EXT0_TRIG_EN) != 0);
177+
178+
// Disable Ext0 source. Timer source should be triggered
179+
ESP_ERROR_CHECK(esp_sleep_disable_wakeup_source(ESP_SLEEP_SOURCE_EXT0));
180+
printf("Disable ext0 source leave timer active.\n");
181+
182+
gettimeofday(&tv_start, NULL);
183+
esp_light_sleep_start();
184+
185+
dt = get_time();
186+
printf("Timer sleep time = %d \n", (int) dt);
187+
188+
TEST_ASSERT_INT32_WITHIN(500, 2000, (int) dt);
189+
190+
// Additionaly check wakeup cause
191+
TEST_ASSERT((get_cause() & RTC_TIMER_TRIG_EN) != 0);
192+
193+
// Disable timer source.
194+
ESP_ERROR_CHECK(esp_sleep_disable_wakeup_source(ESP_SLEEP_SOURCE_TIMER));
195+
196+
// Setup ext0 configuration to wake up immediately
197+
ESP_ERROR_CHECK(rtc_gpio_init(GPIO_NUM_13));
198+
ESP_ERROR_CHECK(gpio_pullup_en(GPIO_NUM_13));
199+
ESP_ERROR_CHECK(gpio_pulldown_dis(GPIO_NUM_13));
200+
ESP_ERROR_CHECK(esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, ESP_EXT0_WAKEUP_LEVEL_HIGH));
201+
202+
printf("Disable timer and wake up from ext0 source.\n");
203+
204+
gettimeofday(&tv_start, NULL);
205+
esp_light_sleep_start();
206+
207+
dt = get_time();
208+
printf("Ext0 sleep time = %d \n", (int) dt);
209+
210+
TEST_ASSERT_INT32_WITHIN(198, 200, (int) dt);
211+
TEST_ASSERT((get_cause() & RTC_EXT0_TRIG_EN) != 0);
212+
213+
// Check error message when source is already disabled
214+
esp_err_t err_code = esp_sleep_disable_wakeup_source(ESP_SLEEP_SOURCE_TIMER);
215+
TEST_ASSERT(err_code == ESP_ERR_INVALID_STATE);
216+
}
217+

docs/api-reference/system/sleep_modes.rst

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ In light sleep mode, digital peripherals, most of the RAM, and CPUs are clock-ga
1010

1111
In deep sleep mode, CPUs, most of the RAM, and all the digital peripherals which are clocked from APB_CLK are powered off. The only parts of the chip which can still be powered on are: RTC controller, RTC peripherals (including ULP coprocessor), and RTC memories (slow and fast).
1212

13-
Wakeup from deep and light sleep modes can be done using several sources. These sources can be combined, in this case the chip will wake up when any one of the sources is triggered. Wakeup sources can be enabled using ``esp_sleep_enable_X_wakeup`` APIs. Next section describes these APIs in detail. Wakeup sources can be configured at any moment before entering light or deep sleep mode.
13+
Wakeup from deep and light sleep modes can be done using several sources. These sources can be combined, in this case the chip will wake up when any one of the sources is triggered. Wakeup sources can be enabled using ``esp_sleep_enable_X_wakeup`` APIs and can be disabled using ``esp_sleep_disable_wakeup_source`` API. Next section describes these APIs in detail. Wakeup sources can be configured at any moment before entering light or deep sleep mode.
1414

1515
Additionally, the application can force specific powerdown modes for the RTC peripherals and RTC memories using ``esp_sleep_pd_config`` API.
1616

@@ -37,10 +37,6 @@ The following function can be used to enable deep sleep wakeup using a timer.
3737

3838
.. doxygenfunction:: esp_sleep_enable_timer_wakeup
3939

40-
The timer wakeup functionality can be disabled to use other wakeup sources instead of timer after first sleep. The function below can be used in this case.
41-
42-
.. doxygenfunction:: esp_sleep_disable_timer_wakeup
43-
4440
Touch pad
4541
^^^^^^^^^
4642

@@ -150,6 +146,13 @@ The following function can be used to check which wakeup source has triggered wa
150146
.. doxygenfunction:: esp_sleep_get_touchpad_wakeup_status
151147
.. doxygenfunction:: esp_sleep_get_ext1_wakeup_status
152148

149+
Disable sleep wakeup source
150+
---------------------------
151+
152+
Previously configured wakeup source can be disabled later using ``esp_sleep_disable_wakeup_source`` API. This function deactivates trigger for source defined as input parameter if it should not be used to wake up from sleep.
153+
154+
.. doxygenenum:: esp_sleep_source_t
155+
.. doxygenfunction:: esp_sleep_disable_wakeup_source
153156

154157
Application Example
155158
-------------------

0 commit comments

Comments
 (0)