Merge branch 'feature/light_sleep_gpio_uart_wakeup' into 'master'

sleep: add support for GPIO and UART wakeup from light sleep

See merge request idf/esp-idf!3004
This commit is contained in:
Ivan Grokhotkov 2018-09-07 10:33:33 +08:00
commit 13107d5968
12 changed files with 404 additions and 95 deletions

View file

@ -423,16 +423,19 @@ esp_err_t gpio_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags,
return esp_intr_alloc(ETS_GPIO_INTR_SOURCE, intr_alloc_flags, fn, arg, handle);
}
/*only level interrupt can be used for wake-up function*/
esp_err_t gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type)
{
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
esp_err_t ret = ESP_OK;
if (( intr_type == GPIO_INTR_LOW_LEVEL ) || ( intr_type == GPIO_INTR_HIGH_LEVEL )) {
GPIO.pin[gpio_num].int_type = intr_type;
GPIO.pin[gpio_num].wakeup_enable = 0x1;
if (RTC_GPIO_IS_VALID_GPIO(gpio_num)) {
ret = rtc_gpio_wakeup_enable(gpio_num, intr_type);
} else {
GPIO.pin[gpio_num].int_type = intr_type;
GPIO.pin[gpio_num].wakeup_enable = 0x1;
}
} else {
ESP_LOGE(GPIO_TAG, "GPIO wakeup only support Level mode,but edge mode set. gpio_num:%u", gpio_num);
ESP_LOGE(GPIO_TAG, "GPIO wakeup only supports level mode, but edge mode set. gpio_num:%u", gpio_num);
ret = ESP_ERR_INVALID_ARG;
}
return ret;

View file

@ -359,27 +359,27 @@ esp_err_t gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode);
esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull);
/**
* @brief Enable GPIO wake-up function.
*
* @param gpio_num GPIO number.
*
* @param intr_type GPIO wake-up type. Only GPIO_INTR_LOW_LEVEL or GPIO_INTR_HIGH_LEVEL can be used.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
* @brief Enable GPIO wake-up function.
*
* @param gpio_num GPIO number.
*
* @param intr_type GPIO wake-up type. Only GPIO_INTR_LOW_LEVEL or GPIO_INTR_HIGH_LEVEL can be used.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type);
/**
* @brief Disable GPIO wake-up function.
*
* @param gpio_num GPIO number
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
* @brief Disable GPIO wake-up function.
*
* @param gpio_num GPIO number
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num);
/**

View file

@ -223,29 +223,52 @@ esp_err_t rtc_gpio_isolate(gpio_num_t gpio_num);
void rtc_gpio_force_hold_dis_all();
/**
* @brief Set RTC GPIO pad drive capability
*
* @param gpio_num GPIO number, only support output GPIOs
* @param strength Drive capability of the pad
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
* @brief Set RTC GPIO pad drive capability
*
* @param gpio_num GPIO number, only support output GPIOs
* @param strength Drive capability of the pad
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t rtc_gpio_set_drive_capability(gpio_num_t gpio_num, gpio_drive_cap_t strength);
/**
* @brief Get RTC GPIO pad drive capability
*
* @param gpio_num GPIO number, only support output GPIOs
* @param strength Pointer to accept drive capability of the pad
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
* @brief Get RTC GPIO pad drive capability
*
* @param gpio_num GPIO number, only support output GPIOs
* @param strength Pointer to accept drive capability of the pad
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t rtc_gpio_get_drive_capability(gpio_num_t gpio_num, gpio_drive_cap_t* strength);
/**
* @brief Enable wakeup from sleep mode using specific GPIO
* @param gpio_num GPIO number
* @param intr_type Wakeup on high level (GPIO_INTR_HIGH_LEVEL) or low level
* (GPIO_INTR_LOW_LEVEL)
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if gpio_num is not an RTC IO, or intr_type is not
* one of GPIO_INTR_HIGH_LEVEL, GPIO_INTR_LOW_LEVEL.
*/
esp_err_t rtc_gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type);
/**
* @brief Disable wakeup from sleep mode using specific GPIO
* @param gpio_num GPIO number
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if gpio_num is not an RTC IO
*/
esp_err_t rtc_gpio_wakeup_disable(gpio_num_t gpio_num);
#ifdef __cplusplus
}
#endif

View file

@ -792,6 +792,53 @@ esp_err_t uart_set_rx_timeout(uart_port_t uart_num, const uint8_t tout_thresh);
*/
esp_err_t uart_get_collision_flag(uart_port_t uart_num, bool* collision_flag);
/**
* @brief Set the number of RX pin signal edges for light sleep wakeup
*
* UART can be used to wake up the system from light sleep. This feature works
* by counting the number of positive edges on RX pin and comparing the count to
* the threshold. When the count exceeds the threshold, system is woken up from
* light sleep. This function allows setting the threshold value.
*
* Stop bit and parity bits (if enabled) also contribute to the number of edges.
* For example, letter 'a' with ASCII code 97 is encoded as 010001101 on the wire
* (with 8n1 configuration), start and stop bits included. This sequence has 3
* positive edges (transitions from 0 to 1). Therefore, to wake up the system
* when 'a' is sent, set wakeup_threshold=3.
*
* The character that triggers wakeup is not received by UART (i.e. it can not
* be obtained from UART FIFO). Depending on the baud rate, a few characters
* after that will also not be received. Note that when the chip enters and exits
* light sleep mode, APB frequency will be changing. To make sure that UART has
* correct baud rate all the time, select REF_TICK as UART clock source,
* by setting use_ref_tick field in uart_config_t to true.
*
* @note in ESP32, UART2 does not support light sleep wakeup feature.
*
* @param uart_num UART number
* @param wakeup_threshold number of RX edges for light sleep wakeup, value is 3 .. 0x3ff.
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if uart_num is incorrect or wakeup_threshold is
* outside of [3, 0x3ff] range.
*/
esp_err_t uart_set_wakeup_threshold(uart_port_t uart_num, int wakeup_threshold);
/**
* @brief Get the number of RX pin signal edges for light sleep wakeup.
*
* See description of uart_set_wakeup_threshold for the explanation of UART
* wakeup feature.
*
* @param uart_num UART number
* @param[out] out_wakeup_threshold output, set to the current value of wakeup
* threshold for the given UART.
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if out_wakeup_threshold is NULL
*/
esp_err_t uart_get_wakeup_threshold(uart_port_t uart_num, int* out_wakeup_threshold);
#ifdef __cplusplus
}
#endif

View file

@ -383,6 +383,37 @@ void rtc_gpio_force_hold_dis_all()
}
}
esp_err_t rtc_gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type)
{
int rtc_num = rtc_gpio_desc[gpio_num].rtc_num;
if (rtc_num < 0) {
return ESP_ERR_INVALID_ARG;
}
if (( intr_type != GPIO_INTR_LOW_LEVEL ) && ( intr_type != GPIO_INTR_HIGH_LEVEL )) {
return ESP_ERR_INVALID_ARG;
}
uint32_t reg = RTC_GPIO_PIN0_REG + rtc_num * sizeof(uint32_t);
/* each pin has its own register, spinlock not needed */
REG_SET_BIT(reg, RTC_GPIO_PIN0_WAKEUP_ENABLE);
REG_SET_FIELD(reg, RTC_GPIO_PIN0_INT_TYPE, intr_type);
return ESP_OK;
}
esp_err_t rtc_gpio_wakeup_disable(gpio_num_t gpio_num)
{
int rtc_num = rtc_gpio_desc[gpio_num].rtc_num;
if (rtc_num < 0) {
return ESP_ERR_INVALID_ARG;
}
uint32_t reg = RTC_GPIO_PIN0_REG + rtc_num * sizeof(uint32_t);
/* each pin has its own register, spinlock not needed */
REG_CLR_BIT(reg, RTC_GPIO_PIN0_WAKEUP_ENABLE);
REG_SET_FIELD(reg, RTC_GPIO_PIN0_INT_TYPE, 0);
return ESP_OK;
}
/*---------------------------------------------------------------
Touch Pad

View file

@ -48,6 +48,7 @@ static const char* UART_TAG = "uart";
#define UART_TOUT_REF_FACTOR_DEFAULT (UART_CLK_FREQ/(REF_CLK_FREQ<<UART_CLKDIV_FRAG_BIT_WIDTH))
#define UART_TX_IDLE_NUM_DEFAULT (0)
#define UART_PATTERN_DET_QLEN_DEFAULT (10)
#define UART_MIN_WAKEUP_THRESH (2)
#define UART_ENTER_CRITICAL_ISR(mux) portENTER_CRITICAL_ISR(mux)
#define UART_EXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL_ISR(mux)
@ -1530,3 +1531,23 @@ esp_err_t uart_get_collision_flag(uart_port_t uart_num, bool* collision_flag)
*collision_flag = p_uart_obj[uart_num]->coll_det_flg;
return ESP_OK;
}
esp_err_t uart_set_wakeup_threshold(uart_port_t uart_num, int wakeup_threshold)
{
UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG);
UART_CHECK((wakeup_threshold <= UART_ACTIVE_THRESHOLD_V &&
wakeup_threshold > UART_MIN_WAKEUP_THRESH),
"wakeup_threshold out of bounds", ESP_ERR_INVALID_ARG);
UART[uart_num]->sleep_conf.active_threshold = wakeup_threshold - UART_MIN_WAKEUP_THRESH;
return ESP_OK;
}
esp_err_t uart_get_wakeup_threshold(uart_port_t uart_num, int* out_wakeup_threshold)
{
UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_ERR_INVALID_ARG);
UART_CHECK((out_wakeup_threshold != NULL), "argument is NULL", ESP_ERR_INVALID_ARG);
*out_wakeup_threshold = UART[uart_num]->sleep_conf.active_threshold + UART_MIN_WAKEUP_THRESH;
return ESP_OK;
}

View file

@ -56,11 +56,14 @@ typedef enum {
*/
typedef enum {
ESP_SLEEP_WAKEUP_UNDEFINED, //!< In case of deep sleep, reset was not caused by exit from deep sleep
ESP_SLEEP_WAKEUP_ALL, //!< Not a wakeup cause, used to disable all wakeup sources with esp_sleep_disable_wakeup_source
ESP_SLEEP_WAKEUP_EXT0, //!< Wakeup caused by external signal using RTC_IO
ESP_SLEEP_WAKEUP_EXT1, //!< Wakeup caused by external signal using RTC_CNTL
ESP_SLEEP_WAKEUP_TIMER, //!< Wakeup caused by timer
ESP_SLEEP_WAKEUP_TOUCHPAD, //!< Wakeup caused by touchpad
ESP_SLEEP_WAKEUP_ULP, //!< Wakeup caused by ULP program
ESP_SLEEP_WAKEUP_GPIO, //!< Wakeup caused by GPIO (light sleep only)
ESP_SLEEP_WAKEUP_UART, //!< Wakeup caused by UART (light sleep only)
} esp_sleep_source_t;
/* Leave this type define for compatibility */
@ -189,6 +192,43 @@ esp_err_t esp_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level);
*/
esp_err_t esp_sleep_enable_ext1_wakeup(uint64_t mask, esp_sleep_ext1_wakeup_mode_t mode);
/**
* @brief Enable wakeup from light sleep using GPIOs
*
* Each GPIO supports wakeup function, which can be triggered on either low level
* or high level. Unlike EXT0 and EXT1 wakeup sources, this method can be used
* both for all IOs: RTC IOs and digital IOs. It can only be used to wakeup from
* light sleep though.
*
* To enable wakeup, first call gpio_wakeup_enable, specifying gpio number and
* wakeup level, for each GPIO which is used for wakeup.
* Then call this function to enable wakeup feature.
*
* @note In revisions 0 and 1 of the ESP32, GPIO wakeup source
* can not be used together with touch or ULP wakeup sources.
*
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_STATE if wakeup triggers conflict
*/
esp_err_t esp_sleep_enable_gpio_wakeup();
/**
* @brief Enable wakeup from light sleep using UART
*
* Use uart_set_wakeup_threshold function to configure UART wakeup threshold.
*
* Wakeup from light sleep takes some time, so not every character sent
* to the UART can be received by the application.
*
* @note ESP32 does not support wakeup from UART2.
*
* @param uart_num UART port to wake up from
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if wakeup from given UART is not supported
*/
esp_err_t esp_sleep_enable_uart_wakeup(int uart_num);
/**
* @brief Get the bit mask of GPIOs which caused wakeup (ext1)
@ -265,9 +305,9 @@ void system_deep_sleep(uint64_t time_in_us) __attribute__((noreturn, deprecated)
/**
* @brief Get the source which caused wakeup from sleep
* @brief Get the wakeup source which caused wakeup from sleep
*
* @return wakeup cause, or ESP_DEEP_SLEEP_WAKEUP_UNDEFINED if reset happened for reason other than deep sleep wakeup
* @return cause of wake up from last sleep (deep sleep or light sleep)
*/
esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause();

View file

@ -34,6 +34,7 @@
#include "soc/dport_reg.h"
#include "soc/rtc_wdt.h"
#include "driver/rtc_io.h"
#include "driver/uart.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sdkconfig.h"
@ -80,6 +81,8 @@ static sleep_config_t s_config = {
.wakeup_triggers = 0
};
bool s_light_sleep_wakeup = false;
/* Updating RTC_MEMORY_CRC_REG register via set_rtc_memory_crc()
is not thread-safe. */
static _lock_t lock_rtc_memory_crc;
@ -326,6 +329,8 @@ esp_err_t esp_light_sleep_start()
esp_err_t err = esp_light_sleep_inner(pd_flags,
flash_enable_time_us, vddsdio_config);
s_light_sleep_wakeup = true;
// FRC1 has been clock gated for the duration of the sleep, correct for that.
uint64_t rtc_ticks_at_end = rtc_time_get();
uint64_t frc_time_at_end = esp_timer_get_time();
@ -360,22 +365,25 @@ esp_err_t esp_sleep_disable_wakeup_source(esp_sleep_source_t source)
// For most of sources it is enough to set trigger mask in local
// configuration structure. The actual RTC wake up options
// will be updated by esp_sleep_start().
if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_TIMER, RTC_TIMER_TRIG_EN)) {
if (source == ESP_SLEEP_WAKEUP_ALL) {
s_config.wakeup_triggers = 0;
} else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_TIMER, RTC_TIMER_TRIG_EN)) {
s_config.wakeup_triggers &= ~RTC_TIMER_TRIG_EN;
s_config.sleep_duration = 0;
}
else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_EXT0, RTC_EXT0_TRIG_EN)) {
} else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_EXT0, RTC_EXT0_TRIG_EN)) {
s_config.ext0_rtc_gpio_num = 0;
s_config.ext0_trigger_level = 0;
s_config.wakeup_triggers &= ~RTC_EXT0_TRIG_EN;
}
else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_EXT1, RTC_EXT1_TRIG_EN)) {
} else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_EXT1, RTC_EXT1_TRIG_EN)) {
s_config.ext1_rtc_gpio_mask = 0;
s_config.ext1_trigger_mode = 0;
s_config.wakeup_triggers &= ~RTC_EXT1_TRIG_EN;
}
else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_TOUCHPAD, RTC_TOUCH_TRIG_EN)) {
} else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_TOUCHPAD, RTC_TOUCH_TRIG_EN)) {
s_config.wakeup_triggers &= ~RTC_TOUCH_TRIG_EN;
} else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_GPIO, RTC_GPIO_TRIG_EN)) {
s_config.wakeup_triggers &= ~RTC_GPIO_TRIG_EN;
} else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_UART, (RTC_UART0_TRIG_EN | RTC_UART1_TRIG_EN))) {
s_config.wakeup_triggers &= ~(RTC_UART0_TRIG_EN | RTC_UART1_TRIG_EN);
}
#ifdef CONFIG_ULP_COPROC_ENABLED
else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_ULP, RTC_ULP_TRIG_EN)) {
@ -561,9 +569,32 @@ uint64_t esp_sleep_get_ext1_wakeup_status()
return gpio_mask;
}
esp_err_t esp_sleep_enable_gpio_wakeup()
{
if (s_config.wakeup_triggers & (RTC_TOUCH_TRIG_EN | RTC_ULP_TRIG_EN)) {
ESP_LOGE(TAG, "Conflicting wake-up triggers: touch / ULP");
return ESP_ERR_INVALID_STATE;
}
s_config.wakeup_triggers |= RTC_GPIO_TRIG_EN;
return ESP_OK;
}
esp_err_t esp_sleep_enable_uart_wakeup(int uart_num)
{
if (uart_num == UART_NUM_0) {
s_config.wakeup_triggers |= RTC_UART0_TRIG_EN;
} else if (uart_num == UART_NUM_1) {
s_config.wakeup_triggers |= RTC_UART1_TRIG_EN;
} else {
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause()
{
if (rtc_get_reset_reason(0) != DEEPSLEEP_RESET) {
if (rtc_get_reset_reason(0) != DEEPSLEEP_RESET && !s_light_sleep_wakeup) {
return ESP_SLEEP_WAKEUP_UNDEFINED;
}
@ -578,6 +609,10 @@ esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause()
return ESP_SLEEP_WAKEUP_TOUCHPAD;
} else if (wakeup_cause & RTC_ULP_TRIG_EN) {
return ESP_SLEEP_WAKEUP_ULP;
} else if (wakeup_cause & RTC_GPIO_TRIG_EN) {
return ESP_SLEEP_WAKEUP_GPIO;
} else if (wakeup_cause & (RTC_UART0_TRIG_EN | RTC_UART1_TRIG_EN)) {
return ESP_SLEEP_WAKEUP_UART;
} else {
return ESP_SLEEP_WAKEUP_UNDEFINED;
}
@ -620,10 +655,10 @@ 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.
// If RTC_PERIPH is auto, and EXT0 isn't enabled, power down RTC_PERIPH.
// RTC_PERIPH is needed for EXT0 wakeup and GPIO wakeup.
// If RTC_PERIPH is auto, and EXT0/GPIO aren'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_EXT0_TRIG_EN) {
if (s_config.wakeup_triggers & (RTC_EXT0_TRIG_EN | RTC_GPIO_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 | RTC_ULP_TRIG_EN)) {
// In both rev. 0 and rev. 1 of ESP32, forcing power up of RTC_PERIPH

View file

@ -147,7 +147,6 @@ INPUT = \
## ESP HTTPS OTA
../../components/esp_https_ota/include/esp_https_ota.h \
## Sleep
## NOTE: for line below header_file.inc is not used
../../components/esp32/include/esp_sleep.h \
## Logging
../../components/log/include/esp_log.h \

View file

@ -33,9 +33,7 @@ RTC controller has a built in timer which can be used to wake up the chip after
This wakeup mode doesn't require RTC peripherals or RTC memories to be powered on during sleep.
The following function can be used to enable deep sleep wakeup using a timer.
.. doxygenfunction:: esp_sleep_enable_timer_wakeup
:cpp:func:`esp_sleep_enable_timer_wakeup` function can be used to enable deep sleep wakeup using a timer.
Touch pad
^^^^^^^^^
@ -44,7 +42,7 @@ RTC IO module contains logic to trigger wakeup when a touch sensor interrupt occ
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_sleep_enable_touchpad_wakeup
:cpp:func:`esp_sleep_enable_touchpad_wakeup` function can be used to enable this wakeup source.
External wakeup (ext0)
@ -56,9 +54,9 @@ Because RTC IO module is enabled in this mode, internal pullup or pulldown resis
In revisions 0 and 1 of the ESP32, this wakeup source is incompatible with ULP and touch wakeup sources.
.. warning:: After wake up from sleep, IO pad used for wakeup will be configured as RTC IO. Before using this pad as digital GPIO, reconfigure it using ``rtc_gpio_deinit(gpio_num)`` function.
:cpp:func:`esp_sleep_enable_ext0_wakeup` function can be used to enable this wakeup source.
.. doxygenfunction:: esp_sleep_enable_ext0_wakeup
.. warning:: After wake up from sleep, IO pad used for wakeup will be configured as RTC IO. Before using this pad as digital GPIO, reconfigure it using ``rtc_gpio_deinit(gpio_num)`` function.
External wakeup (ext1)
^^^^^^^^^^^^^^^^^^^^^^
@ -76,51 +74,51 @@ This wakeup source is implemented by the RTC controller. As such, RTC peripheral
.. warning:: After wake up from sleep, IO pad(s) used for wakeup will be configured as RTC IO. Before using these pads as digital GPIOs, reconfigure them using ``rtc_gpio_deinit(gpio_num)`` function.
The following function can be used to enable this wakeup mode:
.. doxygenfunction:: esp_sleep_enable_ext1_wakeup
.. doxygenenum:: esp_sleep_ext1_wakeup_mode_t
:cpp:func:`esp_sleep_enable_ext1_wakeup` function can be used to enable this wakeup source.
ULP coprocessor wakeup
^^^^^^^^^^^^^^^^^^^^^^
ULP coprocessor can run while the chip is in sleep mode, 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 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.
ULP coprocessor can run while the chip is in sleep mode, 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 memory. RTC slow memory will be powered on during 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:
:cpp:func:`esp_sleep_enable_ulp_wakeup` function can be used to enable this wakeup source.
GPIO wakeup (light sleep only)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In addition to EXT0 and EXT1 wakeup sources described above, one more method of wakeup from external inputs is available in light sleep mode. With this wakeup source, each pin can be individually configured to trigger wakeup on high or low level using :cpp:func:`gpio_wakeup_enable` function. Unlike EXT0 and EXT1 wakeup sources, which can only be used with RTC IOs, this wakeup source can be used with any IO (RTC or digital).
:cpp:func:`esp_sleep_enable_gpio_wakeup` function can be used to enable this wakeup source.
UART wakeup (light sleep only)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When ESP32 receives UART input from external devices, it is often required to wake up the chip when input data is available. UART peripheral contains a feature which allows waking up the chip from light sleep when a certain number of positive edges on RX pin are seen. This number of positive edges can be set using :cpp:func:`uart_set_wakeup_threshold` function. Note that the character which triggers wakeup (and any characters before it) will not be received by the UART after wakeup. This means that the external device typically needs to send an extra character to the ESP32 to trigger wakeup, before sending the data.
:cpp:func:`esp_sleep_enable_uart_wakeup` function can be used to enable this wakeup source.
.. doxygenfunction:: esp_sleep_enable_ulp_wakeup
Power-down of RTC peripherals and memories
------------------------------------------
By default, :cpp:func:`esp_deep_sleep_start` and :cpp:func:`esp_light_sleep_start` functions will power down all RTC power domains which are not needed by the enabled wakeup sources. To override this behaviour, :cpp:func:`esp_sleep_pd_config` function is provided.
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.
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 overridden, 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 :cpp:func:`esp_sleep_pd_config` function, if desired.
.. doxygenfunction:: esp_sleep_pd_config
.. doxygenenum:: esp_sleep_pd_domain_t
.. doxygenenum:: esp_sleep_pd_option_t
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 overridden using :cpp:func:`esp_sleep_pd_config` function, if desired.
Entering light sleep
--------------------
The following function can be used to enter light sleep once wakeup sources are configured. It is also possible to go into light sleep with no wakeup sources configured, in this case the chip will be in light sleep mode indefinetly, until external reset is applied.
.. doxygenfunction:: esp_light_sleep_start
:cpp:func:`esp_light_sleep_start` function can be used to enter light sleep once wakeup sources are configured. It is also possible to go into light sleep with no wakeup sources configured, in this case the chip will be in light sleep mode indefinitely, until external reset is applied.
Entering deep sleep
-------------------
The following function can be used to enter deep sleep once wakeup sources are configured. It is also possible to go into deep sleep with no wakeup sources configured, in this case the chip will be in deep sleep mode indefinetly, until external reset is applied.
.. doxygenfunction:: esp_deep_sleep_start
:cpp:func:`esp_deep_sleep_start` function can be used to enter deep sleep once wakeup sources are configured. It is also possible to go into deep sleep with no wakeup sources configured, in this case the chip will be in deep sleep mode indefinitely, until external reset is applied.
Configuring IOs
---------------
@ -130,11 +128,10 @@ Some ESP32 IOs have internal pullups or pulldowns, which are enabled by default.
To isolate a pin, preventing extra current draw, call :cpp:func:`rtc_gpio_isolate` function.
For example, on ESP32-WROVER module, GPIO12 is pulled up externally. GPIO12 also has an internal pulldown in the ESP32 chip. This means that in deep sleep, some current will flow through these external and internal resistors, increasing deep sleep current above the minimal possible value.
Add the following code before :cpp:func:`esp_deep_sleep_start` to remove this extra current:
Add the following code before :cpp:func:`esp_deep_sleep_start` to remove this extra current::
rtc_gpio_isolate(GPIO_NUM_12);
```c++
rtc_gpio_isolate(GPIO_NUM_12);
```
UART output handling
--------------------
@ -146,24 +143,26 @@ When entering light sleep mode using :cpp:func:`esp_light_sleep_start`, UART FIF
Checking sleep wakeup cause
---------------------------
The following function can be used to check which wakeup source has triggered wakeup from sleep mode. For touch pad and ext1 wakeup sources, it is possible to identify pin or touch pad which has caused wakeup.
:cpp:func:`esp_sleep_get_wakeup_cause` function can be used to check which wakeup source has triggered wakeup from sleep mode.
For touch pad and ext1 wakeup sources, it is possible to identify pin or touch pad which has caused wakeup using :cpp:func:`esp_sleep_get_touchpad_wakeup_status` and :cpp:func:`esp_sleep_get_ext1_wakeup_status` functions.
.. doxygenfunction:: esp_sleep_get_wakeup_cause
.. doxygentypedef:: esp_sleep_wakeup_cause_t
.. doxygenfunction:: esp_sleep_get_touchpad_wakeup_status
.. doxygenfunction:: esp_sleep_get_ext1_wakeup_status
Disable sleep wakeup source
---------------------------
Previously configured wakeup source can be disabled later using :cpp:func:`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.
.. doxygenenum:: esp_sleep_source_t
.. doxygenfunction:: esp_sleep_disable_wakeup_source
Previously configured wakeup source can be disabled later using :cpp:func:`esp_sleep_disable_wakeup_source` API. This function deactivates trigger for the given wakeup source. Additionally it can disable all triggers if the argument is ``ESP_SLEEP_WAKEUP_ALL``.
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.
Implementation of basic functionality of deep sleep is shown in :example:`protocols/sntp` example, where ESP module is periodically waken up to retrieve time from NTP server.
More extensive example in :example:`system/deep_sleep` illustrates usage of various deep sleep wakeup triggers and ULP coprocessor programming.
API Reference
-------------
.. include:: /_build/inc/esp_sleep.inc

View file

@ -15,11 +15,13 @@
#include "esp_system.h"
#include "esp_sleep.h"
#include "driver/rtc_io.h"
#include "driver/uart.h"
#include "argtable3/argtable3.h"
#include "cmd_decl.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "soc/rtc_cntl_reg.h"
#include "rom/uart.h"
#include "sdkconfig.h"
#ifdef CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS
@ -29,6 +31,7 @@
static void register_free();
static void register_restart();
static void register_deep_sleep();
static void register_light_sleep();
static void register_make();
#if WITH_TASKS_INFO
static void register_tasks();
@ -39,6 +42,7 @@ void register_system()
register_free();
register_restart();
register_deep_sleep();
register_light_sleep();
register_make();
#if WITH_TASKS_INFO
register_tasks();
@ -186,6 +190,101 @@ static void register_deep_sleep()
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
/** 'light_sleep' command puts the chip into light sleep mode */
static struct {
struct arg_int *wakeup_time;
struct arg_int *wakeup_gpio_num;
struct arg_int *wakeup_gpio_level;
struct arg_end *end;
} light_sleep_args;
static int light_sleep(int argc, char** argv)
{
int nerrors = arg_parse(argc, argv, (void**) &light_sleep_args);
if (nerrors != 0) {
arg_print_errors(stderr, light_sleep_args.end, argv[0]);
return 1;
}
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_ALL);
if (light_sleep_args.wakeup_time->count) {
uint64_t timeout = 1000ULL * light_sleep_args.wakeup_time->ival[0];
ESP_LOGI(__func__, "Enabling timer wakeup, timeout=%lluus", timeout);
ESP_ERROR_CHECK( esp_sleep_enable_timer_wakeup(timeout) );
}
int io_count = light_sleep_args.wakeup_gpio_num->count;
if (io_count != light_sleep_args.wakeup_gpio_level->count) {
ESP_LOGE(__func__, "Should have same number of 'io' and 'io_level' arguments");
return 1;
}
for (int i = 0; i < io_count; ++i) {
int io_num = light_sleep_args.wakeup_gpio_num->ival[i];
int level = light_sleep_args.wakeup_gpio_level->ival[i];
if (level != 0 && level != 1) {
ESP_LOGE(__func__, "Invalid wakeup level: %d", level);
return 1;
}
ESP_LOGI(__func__, "Enabling wakeup on GPIO%d, wakeup on %s level",
io_num, level ? "HIGH" : "LOW");
ESP_ERROR_CHECK( gpio_wakeup_enable(io_num, level ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL) );
}
if (io_count > 0) {
ESP_ERROR_CHECK( esp_sleep_enable_gpio_wakeup() );
}
if (CONFIG_CONSOLE_UART_NUM <= UART_NUM_1) {
ESP_LOGI(__func__, "Enabling UART wakeup (press ENTER to exit light sleep)");
ESP_ERROR_CHECK( uart_set_wakeup_threshold(CONFIG_CONSOLE_UART_NUM, 3) );
ESP_ERROR_CHECK( esp_sleep_enable_uart_wakeup(CONFIG_CONSOLE_UART_NUM) );
}
fflush(stdout);
uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);
esp_light_sleep_start();
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
const char* cause_str;
switch (cause) {
case ESP_SLEEP_WAKEUP_GPIO:
cause_str = "GPIO";
break;
case ESP_SLEEP_WAKEUP_UART:
cause_str = "UART";
break;
case ESP_SLEEP_WAKEUP_TIMER:
cause_str = "timer";
break;
default:
cause_str = "unknown";
printf("%d\n", cause);
}
ESP_LOGI(__func__, "Woke up from: %s", cause_str);
return 0;
}
static void register_light_sleep()
{
light_sleep_args.wakeup_time =
arg_int0("t", "time", "<t>", "Wake up time, ms");
light_sleep_args.wakeup_gpio_num =
arg_intn(NULL, "io", "<n>", 0, 8,
"If specified, wakeup using GPIO with given number");
light_sleep_args.wakeup_gpio_level =
arg_intn(NULL, "io_level", "<0|1>", 0, 8, "GPIO level to trigger wakeup");
light_sleep_args.end = arg_end(3);
const esp_console_cmd_t cmd = {
.command = "light_sleep",
.help = "Enter light sleep mode. "
"Two wakeup modes are supported: timer and GPIO. "
"Multiple GPIO pins can be specified using pairs of "
"'io' and 'io_level' arguments. "
"Will also wake up on UART input.",
.hint = NULL,
.func = &light_sleep,
.argtable = &light_sleep_args
};
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
/** This command helps maintain sanity when testing console example from a console */
static int make(int argc, char** argv)

View file

@ -68,6 +68,18 @@ static void initialize_console()
/* Move the caret to the beginning of the next line on '\n' */
esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
/* Configure UART. Note that REF_TICK is used so that the baud rate remains
* correct while APB frequency is changing in light sleep mode.
*/
const uart_config_t uart_config = {
.baud_rate = CONFIG_CONSOLE_UART_BAUDRATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.use_ref_tick = true
};
ESP_ERROR_CHECK( uart_param_config(CONFIG_CONSOLE_UART_NUM, &uart_config) );
/* Install UART driver for interrupt-driven reads and writes */
ESP_ERROR_CHECK( uart_driver_install(CONFIG_CONSOLE_UART_NUM,
256, 0, 0, NULL, 0) );