diff --git a/components/esp_driver_gpio/include/driver/gpio.h b/components/esp_driver_gpio/include/driver/gpio.h index 13fbe124079..ff5d452e7c0 100644 --- a/components/esp_driver_gpio/include/driver/gpio.h +++ b/components/esp_driver_gpio/include/driver/gpio.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -575,7 +575,10 @@ esp_err_t gpio_sleep_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull); * * @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. + * @param intr_type GPIO wake-up type. + * - Always supported: GPIO_INTR_LOW_LEVEL, GPIO_INTR_HIGH_LEVEL + * - If SOC_RTC_GPIO_EDGE_WAKEUP_SUPPORTED: + * GPIO_INTR_POSEDGE, GPIO_INTR_NEGEDGE, GPIO_INTR_ANYEDGE * * @note Called by the SDK. User shouldn't call this directly in the APP. * diff --git a/components/esp_driver_gpio/include/driver/rtc_io.h b/components/esp_driver_gpio/include/driver/rtc_io.h index 42294a8c053..5b7622100e9 100644 --- a/components/esp_driver_gpio/include/driver/rtc_io.h +++ b/components/esp_driver_gpio/include/driver/rtc_io.h @@ -309,12 +309,13 @@ esp_err_t rtc_gpio_isolate(gpio_num_t gpio_num); /** * @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) + * @param intr_type Wakeup trigger type: + * - Always supported: GPIO_INTR_HIGH_LEVEL, GPIO_INTR_LOW_LEVEL + * - If SOC_RTC_GPIO_EDGE_WAKEUP_SUPPORTED: + * GPIO_INTR_POSEDGE, GPIO_INTR_NEGEDGE, GPIO_INTR_ANYEDGE * @return * - ESP_OK Success - * - ESP_ERR_INVALID_ARG The IO is not an RTC IO, or intr_type is not - * one of GPIO_INTR_HIGH_LEVEL, GPIO_INTR_LOW_LEVEL. + * - ESP_ERR_INVALID_ARG The IO is not an RTC IO, or intr_type is not supported by the target. */ esp_err_t rtc_gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type); diff --git a/components/esp_driver_gpio/src/gpio.c b/components/esp_driver_gpio/src/gpio.c index 0c0a6723ab1..9118f41f451 100644 --- a/components/esp_driver_gpio/src/gpio.c +++ b/components/esp_driver_gpio/src/gpio.c @@ -1067,10 +1067,17 @@ esp_err_t gpio_wakeup_enable_on_hp_periph_powerdown_sleep(gpio_num_t gpio_num, g ESP_LOGE(GPIO_TAG, "GPIO %d does not support wakeup on peripheral powerdown sleep", gpio_num); return ESP_ERR_INVALID_ARG; } +#if SOC_RTC_GPIO_EDGE_WAKEUP_SUPPORTED + if (intr_type == GPIO_INTR_DISABLE || intr_type > GPIO_INTR_HIGH_LEVEL) { + ESP_LOGE(GPIO_TAG, "invalid intr_type for wakeup, gpio_num:%u", gpio_num); + return ESP_ERR_INVALID_ARG; + } +#else if ((intr_type != GPIO_INTR_LOW_LEVEL) && (intr_type != GPIO_INTR_HIGH_LEVEL)) { ESP_LOGE(GPIO_TAG, "GPIO wakeup only supports level mode, but edge mode set. gpio_num:%u", gpio_num); return ESP_ERR_INVALID_ARG; } +#endif portENTER_CRITICAL(&gpio_context.gpio_spinlock); #if SOC_LP_IO_CLOCK_IS_INDEPENDENT io_mux_enable_lp_io_clock(gpio_num, true); diff --git a/components/esp_hal_gpio/esp32c5/include/hal/rtc_io_ll.h b/components/esp_hal_gpio/esp32c5/include/hal/rtc_io_ll.h index f29b0c36779..2d971d3ee35 100644 --- a/components/esp_hal_gpio/esp32c5/include/hal/rtc_io_ll.h +++ b/components/esp_hal_gpio/esp32c5/include/hal/rtc_io_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -335,6 +335,16 @@ static inline void rtcio_ll_wakeup_disable(int rtcio_num) LP_GPIO.pinn[rtcio_num].pinn_int_type = 0; } +/** + * Clear edge-wakeup latch. + * + * @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio). + */ +static inline void rtcio_ll_clear_edge_wakeup_latch(int rtcio_num) +{ + LP_GPIO.pinn[rtcio_num].pinn_edge_wakeup_clr = 1; +} + /** * Enable interrupt function and set interrupt type * diff --git a/components/esp_hal_gpio/esp32c6/include/hal/rtc_io_ll.h b/components/esp_hal_gpio/esp32c6/include/hal/rtc_io_ll.h index a1551cd865f..956eaa26a3e 100644 --- a/components/esp_hal_gpio/esp32c6/include/hal/rtc_io_ll.h +++ b/components/esp_hal_gpio/esp32c6/include/hal/rtc_io_ll.h @@ -341,6 +341,16 @@ static inline void rtcio_ll_wakeup_disable(int rtcio_num) LP_IO.pin[rtcio_num].int_type = 0; } +/** + * Clear edge-wakeup latch. + * + * @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio). + */ +static inline void rtcio_ll_clear_edge_wakeup_latch(int rtcio_num) +{ + LP_IO.pin[rtcio_num].edge_wakeup_clr = 1; +} + /** * Enable interrupt function and set interrupt type * diff --git a/components/esp_hal_gpio/esp32c61/include/hal/rtc_io_ll.h b/components/esp_hal_gpio/esp32c61/include/hal/rtc_io_ll.h index db0ced69ea9..a1a1f79b971 100644 --- a/components/esp_hal_gpio/esp32c61/include/hal/rtc_io_ll.h +++ b/components/esp_hal_gpio/esp32c61/include/hal/rtc_io_ll.h @@ -334,6 +334,16 @@ static inline void rtcio_ll_wakeup_disable(int rtcio_num) LP_GPIO.pinn[rtcio_num].pinn_int_type = 0; } +/** + * Clear edge-wakeup latch. + * + * @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio). + */ +static inline void rtcio_ll_clear_edge_wakeup_latch(int rtcio_num) +{ + LP_GPIO.pinn[rtcio_num].pinn_edge_wakeup_clr = 1; +} + /** * @brief Enable interrupt function and set interrupt type * diff --git a/components/esp_hal_gpio/esp32h4/include/hal/rtc_io_ll.h b/components/esp_hal_gpio/esp32h4/include/hal/rtc_io_ll.h index 1e18cc7f8a4..bacde507910 100644 --- a/components/esp_hal_gpio/esp32h4/include/hal/rtc_io_ll.h +++ b/components/esp_hal_gpio/esp32h4/include/hal/rtc_io_ll.h @@ -334,6 +334,16 @@ static inline void rtcio_ll_wakeup_disable(int rtcio_num) LP_GPIO.pinn[rtcio_num].pinn_int_type = 0; } +/** + * Clear edge-wakeup latch. + * + * @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio). + */ +static inline void rtcio_ll_clear_edge_wakeup_latch(int rtcio_num) +{ + LP_GPIO.pinn[rtcio_num].pinn_edge_wakeup_clr = 1; +} + /** * @brief Enable interrupt function and set interrupt type * diff --git a/components/esp_hal_gpio/esp32p4/include/hal/rtc_io_ll.h b/components/esp_hal_gpio/esp32p4/include/hal/rtc_io_ll.h index d61d6116d73..596fa274bb9 100644 --- a/components/esp_hal_gpio/esp32p4/include/hal/rtc_io_ll.h +++ b/components/esp_hal_gpio/esp32p4/include/hal/rtc_io_ll.h @@ -404,6 +404,16 @@ static inline void rtcio_ll_wakeup_disable(int rtcio_num) LP_GPIO.pin[rtcio_num].int_type = 0; } +/** + * Clear edge-wakeup latch. + * + * @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio). + */ +static inline void rtcio_ll_clear_edge_wakeup_latch(int rtcio_num) +{ + LP_GPIO.pin[rtcio_num].edge_wakeup_clr = 1; +} + /** * Enable interrupt function and set interrupt type * diff --git a/components/esp_hal_gpio/esp32s31/include/hal/rtc_io_ll.h b/components/esp_hal_gpio/esp32s31/include/hal/rtc_io_ll.h index 407ee0e3a45..5d0937f6846 100644 --- a/components/esp_hal_gpio/esp32s31/include/hal/rtc_io_ll.h +++ b/components/esp_hal_gpio/esp32s31/include/hal/rtc_io_ll.h @@ -333,6 +333,16 @@ static inline void rtcio_ll_wakeup_disable(int rtcio_num) LP_GPIO.pinn[rtcio_num].pinn_int_type = 0; } +/** + * Clear edge-wakeup latch. + * + * @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio). + */ +static inline void rtcio_ll_clear_edge_wakeup_latch(int rtcio_num) +{ + LP_GPIO.pinn[rtcio_num].pinn_edge_wakeup_clr = 1; +} + /** * @brief Enable interrupt function and set interrupt type * diff --git a/components/esp_hal_gpio/include/hal/rtc_io_hal.h b/components/esp_hal_gpio/include/hal/rtc_io_hal.h index b86dfa9c13f..e27792c8c28 100644 --- a/components/esp_hal_gpio/include/hal/rtc_io_hal.h +++ b/components/esp_hal_gpio/include/hal/rtc_io_hal.h @@ -320,6 +320,20 @@ void rtcio_hal_isolate(int rtcio_num); #define rtc_hal_gpio_get_wakeup_status() rtcio_hal_get_interrupt_status() #define rtc_hal_gpio_clear_wakeup_status() rtcio_hal_clear_interrupt_status() +#if SOC_RTC_GPIO_EDGE_WAKEUP_SUPPORTED +/** + * @brief Clear the latched edge-wakeup state for a GPIO that is enabled for + * peripheral-powerdown-sleep wakeup in edge mode. + * + * Must be called before entering sleep on every pin configured for posedge/negedge/anyedge + * wakeup, otherwise a stale latched edge would immediately wake the chip. + * + * @param hal Context of the HAL layer (unused, kept for API symmetry). + * @param gpio_num GPIO number. + */ +#define gpio_hal_clear_hp_periph_pd_sleep_edge_wakeup_latch(hal, gpio_num) rtcio_ll_clear_edge_wakeup_latch(rtc_io_num_map[gpio_num]) +#endif //SOC_RTC_GPIO_EDGE_WAKEUP_SUPPORTED + /** * @brief Get the status of whether an IO is used for sleep wake-up. * diff --git a/components/esp_hw_support/include/esp_sleep.h b/components/esp_hw_support/include/esp_sleep.h index cc331c851dc..8d0941435d7 100644 --- a/components/esp_hw_support/include/esp_sleep.h +++ b/components/esp_hw_support/include/esp_sleep.h @@ -44,8 +44,15 @@ typedef enum { #if SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP typedef enum { ESP_GPIO_WAKEUP_GPIO_LOW = 0, - ESP_GPIO_WAKEUP_GPIO_HIGH = 1 + ESP_GPIO_WAKEUP_GPIO_HIGH = 1, +#if SOC_RTC_GPIO_EDGE_WAKEUP_SUPPORTED + ESP_GPIO_WAKEUP_GPIO_POSEDGE = 2, + ESP_GPIO_WAKEUP_GPIO_NEGEDGE = 3, + ESP_GPIO_WAKEUP_GPIO_ANYEDGE = 4, +#endif } esp_sleep_gpio_wake_up_mode_t; + +#define WAKEUP_MODE_2_INT_TYPE(mode) ((gpio_int_type_t)((0x32154 >> ((mode) * 4)) & 0xF)) #endif /** @@ -482,11 +489,14 @@ __attribute__((deprecated("please use 'esp_sleep_enable_ext1_wakeup_io' and 'esp * and external resistors may cause interference. BTW, when you use low level to wake up the * chip, we strongly recommend you to add external resistors (pull-up). * + * @note The wakeup signal must be held (level mode) or have a pulse width (edge mode) + * of at least 3 RTC slow-clock cycles to be reliably sampled by the wakeup logic. + * The duration of one slow-clock cycle depends on `CONFIG_RTC_CLK_SRC` (e.g. + * RC_SLOW @ ~136 kHz ~= 7.4 us/cycle, XTAL32K @ 32.768 kHz ~= 30.5 us/cycle). + * * @param gpio_pin_mask Bit mask of GPIO numbers which will cause wakeup. Only GPIOs * which have RTC functionality (pads that powered by VDD3P3_RTC) can be used in this bit map. - * @param mode Select logic function used to determine wakeup condition: - * - ESP_GPIO_WAKEUP_GPIO_LOW: wake up when the gpio turn to low. - * - ESP_GPIO_WAKEUP_GPIO_HIGH: wake up when the gpio turn to high. + * @param mode Wakeup condition, see esp_sleep_gpio_wake_up_mode_t. * @return * - ESP_OK on success * - ESP_ERR_INVALID_ARG if the mask contains any invalid wakeup pin or wakeup mode is invalid diff --git a/components/esp_hw_support/sleep_modes.c b/components/esp_hw_support/sleep_modes.c index 5781683bfeb..91c349b6f38 100644 --- a/components/esp_hw_support/sleep_modes.c +++ b/components/esp_hw_support/sleep_modes.c @@ -301,7 +301,7 @@ typedef struct { #endif #if SOC_GPIO_SUPPORT_HP_PERIPH_PD_SLEEP_WAKEUP uint64_t gpio_wakeup_mask; - uint64_t gpio_trigger_mode; + uint8_t gpio_trigger_mode[SOC_GPIO_PIN_COUNT]; #endif uint32_t sleep_time_adjustment; uint32_t ccount_ticks_record; @@ -315,7 +315,6 @@ typedef struct { bool overhead_out_need_remeasure; } sleep_config_t; - #if CONFIG_ESP_SLEEP_DEBUG static esp_sleep_context_t *s_sleep_ctx = NULL; @@ -1782,7 +1781,7 @@ esp_err_t esp_sleep_disable_wakeup_source(esp_sleep_source_t source) } else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_GPIO, RTC_GPIO_TRIG_EN)) { #if SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP s_config.gpio_wakeup_mask = 0; - s_config.gpio_trigger_mode = 0; + memset(s_config.gpio_trigger_mode, 0, sizeof(s_config.gpio_trigger_mode)); #endif s_config.wakeup_triggers &= ~RTC_GPIO_TRIG_EN; } else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_UART0, RTC_UART0_TRIG_EN)) { @@ -2260,18 +2259,28 @@ static void esp_sleep_gpio_wakeup_prepare_on_hp_periph_powerdown(void) int __DECLARE_RCC_ATOMIC_ENV __attribute__ ((unused)); rtcio_ll_enable_io_clock(true); #endif + __attribute__ ((unused)) gpio_int_type_t intr_type = (gpio_int_type_t)s_config.gpio_trigger_mode[gpio_idx]; #if CONFIG_ESP_SLEEP_GPIO_ENABLE_INTERNAL_RESISTORS if (GPIO_IS_VALID_OUTPUT_GPIO(gpio_idx)) { - if (s_config.gpio_trigger_mode & BIT(gpio_idx)) { + if (intr_type == GPIO_INTR_LOW_LEVEL || intr_type == GPIO_INTR_NEGEDGE) { + gpio_pullup_en(gpio_idx); + gpio_pulldown_dis(gpio_idx); + } else if (intr_type == GPIO_INTR_HIGH_LEVEL || intr_type == GPIO_INTR_POSEDGE) { gpio_pullup_dis(gpio_idx); gpio_pulldown_en(gpio_idx); } else { - gpio_pullup_en(gpio_idx); + gpio_pullup_dis(gpio_idx); gpio_pulldown_dis(gpio_idx); } } else { ESP_EARLY_LOGE(TAG, "GPIO%d not support internal PU/PD", gpio_idx); } +#endif +#if SOC_RTC_GPIO_EDGE_WAKEUP_SUPPORTED + /* Clear any pending edge-wakeup latch so a stale event does not immediately re-wake the chip. */ + if (intr_type == GPIO_INTR_POSEDGE || intr_type == GPIO_INTR_NEGEDGE || intr_type == GPIO_INTR_ANYEDGE) { + gpio_hal_clear_hp_periph_pd_sleep_edge_wakeup_latch(NULL, gpio_idx); + } #endif ESP_ERROR_CHECK(gpio_hold_en(gpio_idx)); valid_wake_io_mask &= valid_wake_io_mask - 1; @@ -2282,14 +2291,20 @@ static void esp_sleep_gpio_wakeup_prepare_on_hp_periph_powerdown(void) esp_err_t esp_sleep_enable_gpio_wakeup_on_hp_periph_powerdown(uint64_t gpio_pin_mask, esp_sleep_gpio_wake_up_mode_t mode) { - if (mode > ESP_GPIO_WAKEUP_GPIO_HIGH) { - ESP_LOGE(TAG, "invalid mode"); - return ESP_ERR_INVALID_ARG; - } if (gpio_pin_mask == 0) { return ESP_ERR_INVALID_ARG; } - gpio_int_type_t intr_type = ((mode == ESP_GPIO_WAKEUP_GPIO_LOW) ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL); + const gpio_int_type_t intr_type = WAKEUP_MODE_2_INT_TYPE(mode); + if (intr_type == GPIO_INTR_DISABLE || intr_type > GPIO_INTR_HIGH_LEVEL) { + ESP_LOGE(TAG, "invalid mode"); + return ESP_ERR_INVALID_ARG; + } +#if !SOC_RTC_GPIO_EDGE_WAKEUP_SUPPORTED + if (intr_type != GPIO_INTR_LOW_LEVEL && intr_type != GPIO_INTR_HIGH_LEVEL) { + ESP_LOGE(TAG, "invalid mode"); + return ESP_ERR_INVALID_ARG; + } +#endif esp_err_t err = ESP_OK; uint64_t invalid_io_mask = gpio_pin_mask & ~SOC_GPIO_HP_PERIPH_PD_SLEEP_WAKEABLE_MASK; @@ -2306,11 +2321,7 @@ esp_err_t esp_sleep_enable_gpio_wakeup_on_hp_periph_powerdown(uint64_t gpio_pin_ err = gpio_wakeup_enable_on_hp_periph_powerdown_sleep(gpio_idx, intr_type); if (err != ESP_OK) return err; s_config.gpio_wakeup_mask |= BIT64(gpio_idx); - if (mode == ESP_GPIO_WAKEUP_GPIO_HIGH) { - s_config.gpio_trigger_mode |= BIT64(gpio_idx); - } else { - s_config.gpio_trigger_mode &= ~BIT64(gpio_idx); - } + s_config.gpio_trigger_mode[gpio_idx] = (uint8_t)intr_type; gpio_pin_mask &= gpio_pin_mask - 1; } s_config.wakeup_triggers |= RTC_GPIO_TRIG_EN;