Merge branch 'feat/rtcio_hysterisys_filter_support' into 'master'

feat(driver_gpio): rtcio hysterisys filter support

See merge request espressif/esp-idf!52335
This commit is contained in:
Wan Lei
2026-09-14 10:23:23 +08:00
13 changed files with 312 additions and 26 deletions

View File

@@ -172,6 +172,30 @@ esp_err_t rtc_gpio_pullup_dis(gpio_num_t gpio_num);
*/
esp_err_t rtc_gpio_pulldown_dis(gpio_num_t gpio_num);
#if SOC_GPIO_SUPPORT_PIN_HYS_FILTER
/**
* @brief Enable RTC GPIO input hysteresis.
*
* @param gpio_num GPIO number (e.g. GPIO_NUM_12)
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG The IO is not an RTC IO
*/
esp_err_t rtc_gpio_hysteresis_en(gpio_num_t gpio_num);
/**
* @brief Disable RTC GPIO input hysteresis.
*
* @param gpio_num GPIO number (e.g. GPIO_NUM_12)
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG The IO is not an RTC IO
*/
esp_err_t rtc_gpio_hysteresis_dis(gpio_num_t gpio_num);
#endif
/**
* @brief Set RTC GPIO pad drive capability
*

View File

@@ -184,6 +184,28 @@ esp_err_t rtc_gpio_pulldown_dis(gpio_num_t gpio_num)
return ESP_OK;
}
#if SOC_GPIO_SUPPORT_PIN_HYS_FILTER
esp_err_t rtc_gpio_hysteresis_en(gpio_num_t gpio_num)
{
ESP_RETURN_ON_FALSE(rtc_gpio_is_valid_gpio(gpio_num), ESP_ERR_INVALID_ARG, RTCIO_TAG, "RTCIO number error");
RTCIO_ENTER_CRITICAL();
rtcio_hal_input_hysteresis_soft_enable(rtc_io_number_get(gpio_num), true);
RTCIO_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t rtc_gpio_hysteresis_dis(gpio_num_t gpio_num)
{
ESP_RETURN_ON_FALSE(rtc_gpio_is_valid_gpio(gpio_num), ESP_ERR_INVALID_ARG, RTCIO_TAG, "RTCIO number error");
RTCIO_ENTER_CRITICAL();
rtcio_hal_input_hysteresis_soft_enable(rtc_io_number_get(gpio_num), false);
RTCIO_EXIT_CRITICAL();
return ESP_OK;
}
#endif
esp_err_t rtc_gpio_iomux_func_sel(gpio_num_t gpio_num, int func)
{
ESP_RETURN_ON_FALSE(rtc_gpio_is_valid_gpio(gpio_num), ESP_ERR_INVALID_ARG, RTCIO_TAG, "RTCIO number error");

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -8,6 +8,7 @@
#include "freertos/task.h"
#include "unity.h"
#include "driver/gpio.h"
#include "driver/rtc_io.h"
/**
* NOTE: To run this special feature test case, a slope analog signal is needed.
@@ -21,7 +22,7 @@
* | | +-----+ || | |
* | ESP32 | R:10k ++ + |
* | | |
* | GND |------------------------------+
* | GND_IO |------------------------------+
* +--------------+
*
* or you can connect your slop signals from signal generator to ESP32 pin
@@ -30,47 +31,119 @@
#define TEST_GPIO_HYS_IO 4
#define TEST_GPIO_WAVE_IO 5
#define TEST_GPIO_GND_IO 10 // The pin alwayse output 0 to use as near GND
static void test_gpio_hysteresis_intr_handler(void *args)
static int test_count_io_glitch_in_loop(int (*get_level_func)(gpio_num_t), gpio_num_t glitch_io, int loop_cnt)
{
esp_rom_printf("%" PRIu32 "\n", ++ * ((uint32_t *)args));
int glitch_cnt = 0;
int init_level = get_level_func(glitch_io);
while (init_level == get_level_func(glitch_io));
for (int i = 0; i < loop_cnt; i++) {
if (init_level != get_level_func(glitch_io)) {
init_level = !init_level;
glitch_cnt++;
}
}
return glitch_cnt;
}
static void test_check_glitch_result(int glitch_cnt, bool hys_en)
{
if (hys_en) {
TEST_ASSERT_EQUAL(1, glitch_cnt); // must only 1 edge if hysteresis is enabled
} else {
TEST_ASSERT_GREATER_THAN(10, glitch_cnt);
}
}
// This case is now tested only manually
TEST_CASE("GPIO Input hysteresis filter", "[gpio_filter][timeout=50][ignore]")
{
uint32_t intr_cnt = 0;
// prepare for wave and ground io
gpio_config_t gpio_cfg = {
.pin_bit_mask = (1 << TEST_GPIO_WAVE_IO),
.pin_bit_mask = BIT64(TEST_GPIO_WAVE_IO) | BIT64(TEST_GPIO_GND_IO),
.mode = GPIO_MODE_OUTPUT,
.pull_down_en = GPIO_PULLDOWN_ENABLE,
};
TEST_ESP_OK(gpio_config(&gpio_cfg));
TEST_ESP_OK(gpio_set_level(TEST_GPIO_GND_IO, 0));
gpio_cfg.pin_bit_mask = 1 << TEST_GPIO_HYS_IO;
// prepare for hysteresis io
gpio_cfg.pin_bit_mask = BIT64(TEST_GPIO_HYS_IO);
gpio_cfg.mode = GPIO_MODE_INPUT;
gpio_cfg.intr_type = GPIO_INTR_ANYEDGE;
gpio_cfg.hys_ctrl_mode = GPIO_HYS_SOFT_ENABLE;
TEST_ESP_OK(gpio_config(&gpio_cfg));
gpio_install_isr_service(0);
gpio_isr_handler_add(TEST_GPIO_HYS_IO, test_gpio_hysteresis_intr_handler, &intr_cnt);
printf("Initial level ...\n");
gpio_set_level(TEST_GPIO_WAVE_IO, 0);
vTaskDelay(3000);
for (int hys_en = 0; hys_en < 2; hys_en++) {
printf("Test with hysteresis %s\n", hys_en ? "enabled" : "disabled");
gpio_cfg.hys_ctrl_mode = hys_en ? GPIO_HYS_SOFT_ENABLE : GPIO_HYS_SOFT_DISABLE;
TEST_ESP_OK(gpio_config(&gpio_cfg));
// generate 3 rising and falling slopes to test gpio interrupt
for (uint8_t i = 0; i < 3; i++) {
printf("----rising %dth: ", i);
gpio_set_level(TEST_GPIO_WAVE_IO, 1);
int glitch_cnt = test_count_io_glitch_in_loop(gpio_get_level, TEST_GPIO_HYS_IO, 1000000);
printf("glitch_cnt: %d\n", glitch_cnt);
test_check_glitch_result(glitch_cnt, hys_en);
printf("----falling %dth: ", i);
gpio_set_level(TEST_GPIO_WAVE_IO, 0);
glitch_cnt = test_count_io_glitch_in_loop(gpio_get_level, TEST_GPIO_HYS_IO, 1000000);
printf("glitch_cnt: %d\n", glitch_cnt);
test_check_glitch_result(glitch_cnt, hys_en);
}
// generate 5 rising and falling slopes to test gpio interrupt
for (uint8_t i = 0; i < 5; i++) {
printf("----falling %dth\n", i);
gpio_set_level(TEST_GPIO_WAVE_IO, 0);
vTaskDelay(1500);
printf("----rising %dth\n", i);
gpio_set_level(TEST_GPIO_WAVE_IO, 1);
vTaskDelay(1500);
}
gpio_isr_handler_remove(TEST_GPIO_HYS_IO);
gpio_uninstall_isr_service();
TEST_ESP_OK(gpio_reset_pin(TEST_GPIO_HYS_IO));
TEST_ESP_OK(gpio_reset_pin(TEST_GPIO_WAVE_IO));
// should shot ISR exactly 10 times
TEST_ASSERT_UINT32_WITHIN(1, 10, intr_cnt);
TEST_ESP_OK(gpio_reset_pin(TEST_GPIO_GND_IO));
}
#if SOC_RTCIO_INPUT_OUTPUT_SUPPORTED
TEST_CASE("RTCIO Input hysteresis filter", "[gpio_filter][timeout=50][ignore]")
{
// prepare for wave and ground io
gpio_config_t gpio_cfg = {
.pin_bit_mask = BIT64(TEST_GPIO_WAVE_IO) | BIT64(TEST_GPIO_GND_IO),
.mode = GPIO_MODE_OUTPUT,
.pull_down_en = GPIO_PULLDOWN_ENABLE,
};
TEST_ESP_OK(gpio_config(&gpio_cfg));
TEST_ESP_OK(gpio_set_level(TEST_GPIO_GND_IO, 0));
// prepare for hysteresis io
TEST_ESP_OK(rtc_gpio_init(TEST_GPIO_HYS_IO));
TEST_ESP_OK(rtc_gpio_set_direction(TEST_GPIO_HYS_IO, RTC_GPIO_MODE_INPUT_ONLY));
int (*io_get_level)(gpio_num_t) = (int (*)(gpio_num_t)) rtc_gpio_get_level;
printf("Initial level ...\n");
gpio_set_level(TEST_GPIO_WAVE_IO, 0);
vTaskDelay(3000);
for (int hys_en = 0; hys_en < 2; hys_en++) {
printf("Test with hysteresis %s\n", hys_en ? "enabled" : "disabled");
hys_en ? rtc_gpio_hysteresis_en(TEST_GPIO_HYS_IO) : rtc_gpio_hysteresis_dis(TEST_GPIO_HYS_IO);
// generate 3 rising and falling slopes to test gpio interrupt
for (uint8_t i = 0; i < 3; i++) {
printf("----rising %dth: ", i);
gpio_set_level(TEST_GPIO_WAVE_IO, 1);
int glitch_cnt = test_count_io_glitch_in_loop(io_get_level, TEST_GPIO_HYS_IO, 1000000);
printf("glitch_cnt: %d\n", glitch_cnt);
test_check_glitch_result(glitch_cnt, hys_en);
printf("----falling %dth: ", i);
gpio_set_level(TEST_GPIO_WAVE_IO, 0);
glitch_cnt = test_count_io_glitch_in_loop(io_get_level, TEST_GPIO_HYS_IO, 1000000);
printf("glitch_cnt: %d\n", glitch_cnt);
test_check_glitch_result(glitch_cnt, hys_en);
}
}
TEST_ESP_OK(rtc_gpio_deinit(TEST_GPIO_HYS_IO));
TEST_ESP_OK(gpio_reset_pin(TEST_GPIO_WAVE_IO));
TEST_ESP_OK(gpio_reset_pin(TEST_GPIO_GND_IO));
}
#endif

View File

@@ -162,6 +162,28 @@ static inline void rtcio_ll_input_disable(int rtcio_num)
LP_IO_MUX.gpion[rtcio_num].gpion_fun_ie = 0;
}
/**
* Enable RTCIO input hysteresis.
*
* @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio).
*/
static inline void rtcio_ll_pin_input_hysteresis_enable(int rtcio_num)
{
LP_IO_MUX.gpion[rtcio_num].gpion_hys_sel = 1;
LP_IO_MUX.gpion[rtcio_num].gpion_hys_en = 1;
}
/**
* Disable RTCIO input hysteresis.
*
* @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio).
*/
static inline void rtcio_ll_pin_input_hysteresis_disable(int rtcio_num)
{
LP_IO_MUX.gpion[rtcio_num].gpion_hys_sel = 1;
LP_IO_MUX.gpion[rtcio_num].gpion_hys_en = 0;
}
/**
* Get RTCIO input level.
*

View File

@@ -152,6 +152,28 @@ static inline void rtcio_ll_input_disable(int rtcio_num)
LP_IO_MUX.gpion[rtcio_num].gpion_fun_ie = 0;
}
/**
* @brief Enable RTCIO input hysteresis.
*
* @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio).
*/
static inline void rtcio_ll_pin_input_hysteresis_enable(int rtcio_num)
{
LP_IO_MUX.gpion[rtcio_num].gpion_hys_sel = 1;
LP_IO_MUX.gpion[rtcio_num].gpion_hys_en = 1;
}
/**
* @brief Disable RTCIO input hysteresis.
*
* @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio).
*/
static inline void rtcio_ll_pin_input_hysteresis_disable(int rtcio_num)
{
LP_IO_MUX.gpion[rtcio_num].gpion_hys_sel = 1;
LP_IO_MUX.gpion[rtcio_num].gpion_hys_en = 0;
}
/**
* @brief Get RTCIO input level.
*

View File

@@ -277,6 +277,34 @@ static inline void gpio_ll_pin_filter_disable(gpio_dev_t *hw, uint32_t gpio_num)
IO_MUX.gpio[gpio_num].filter_en = 0;
}
/**
* @brief Enable GPIO hysteresis
*
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
static inline void gpio_ll_pin_input_hysteresis_enable(gpio_dev_t *hw, uint32_t gpio_num)
{
// On ESP32H4, there is an efuse bit that controls the hysteresis enable or not for all IOs.
// We are not going to use the hardware control for H4.
// Therefore, we need to always switch to use software control first.
// i.e. Swt hys_sel to 1, so that hys_en determines whether hysteresis is enabled or not
IO_MUX.gpio[gpio_num].hys_sel = 1;
IO_MUX.gpio[gpio_num].hys_en = 1;
}
/**
* @brief Disable GPIO hysteresis
*
* @param hw Peripheral GPIO hardware instance address.
* @param gpio_num GPIO number
*/
static inline void gpio_ll_pin_input_hysteresis_disable(gpio_dev_t *hw, uint32_t gpio_num)
{
IO_MUX.gpio[gpio_num].hys_sel = 1;
IO_MUX.gpio[gpio_num].hys_en = 0;
}
/**
* @brief Disable output mode on GPIO.
*

View File

@@ -152,6 +152,28 @@ static inline void rtcio_ll_input_disable(int rtcio_num)
LP_IO_MUX.gpion[rtcio_num].gpion_fun_ie = 0;
}
/**
* @brief Enable RTCIO input hysteresis.
*
* @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio).
*/
static inline void rtcio_ll_pin_input_hysteresis_enable(int rtcio_num)
{
LP_IO_MUX.gpion[rtcio_num].gpion_hys_sel = 1;
LP_IO_MUX.gpion[rtcio_num].gpion_hys_en = 1;
}
/**
* @brief Disable RTCIO input hysteresis.
*
* @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio).
*/
static inline void rtcio_ll_pin_input_hysteresis_disable(int rtcio_num)
{
LP_IO_MUX.gpion[rtcio_num].gpion_hys_sel = 1;
LP_IO_MUX.gpion[rtcio_num].gpion_hys_en = 0;
}
/**
* @brief Get RTCIO input level.
*

View File

@@ -297,6 +297,9 @@ static inline void gpio_ll_pin_input_hysteresis_enable(gpio_dev_t *hw, uint32_t
uint64_t bit_mask = 1ULL << gpio_num;
if (!(bit_mask & SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK)) {
// GPIO0-15
// On esp32p4, hysteresis of rtcio has different design.
// It share same suite of registers with LP control instead of independent registers.
// The `rtcio_ll_function_select` don't affect the hysteresis config.
uint32_t hys_mask = HAL_FORCE_READ_U32_REG_FIELD(LP_IOMUX.lp_pad_hys, reg_lp_gpio_hys);
hys_mask |= bit_mask;
HAL_FORCE_MODIFY_U32_REG_FIELD(LP_IOMUX.lp_pad_hys, reg_lp_gpio_hys, hys_mask);

View File

@@ -207,6 +207,33 @@ static inline void rtcio_ll_input_disable(int rtcio_num)
LP_IOMUX.pad[rtcio_num].fun_ie = 0;
}
/**
* @brief Enable RTCIO input hysteresis.
* @note On esp32p4, hysteresis of rtcio has different design.
* It share same suite of registers with HP control instead of independent registers.
* The `rtcio_ll_function_select` don't affect the hysteresis config.
*
* @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio).
*/
static inline void rtcio_ll_pin_input_hysteresis_enable(int rtcio_num)
{
uint32_t hys_mask = HAL_FORCE_READ_U32_REG_FIELD(LP_IOMUX.lp_pad_hys, reg_lp_gpio_hys);
hys_mask |= BIT(rtcio_num);
HAL_FORCE_MODIFY_U32_REG_FIELD(LP_IOMUX.lp_pad_hys, reg_lp_gpio_hys, hys_mask);
}
/**
* @brief Disable RTCIO input hysteresis.
*
* @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio).
*/
static inline void rtcio_ll_pin_input_hysteresis_disable(int rtcio_num)
{
uint32_t hys_mask = HAL_FORCE_READ_U32_REG_FIELD(LP_IOMUX.lp_pad_hys, reg_lp_gpio_hys);
hys_mask &= ~BIT(rtcio_num);
HAL_FORCE_MODIFY_U32_REG_FIELD(LP_IOMUX.lp_pad_hys, reg_lp_gpio_hys, hys_mask);
}
/**
* @brief Get RTCIO input level.
*

View File

@@ -151,6 +151,28 @@ static inline void rtcio_ll_input_disable(int rtcio_num)
LP_IO_MUX.gpion[rtcio_num].gpion_fun_ie = 0;
}
/**
* @brief Enable RTCIO input hysteresis.
*
* @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio).
*/
static inline void rtcio_ll_pin_input_hysteresis_enable(int rtcio_num)
{
LP_IO_MUX.gpion[rtcio_num].gpion_hys_sel = 1;
LP_IO_MUX.gpion[rtcio_num].gpion_hys_en = 1;
}
/**
* @brief Disable RTCIO input hysteresis.
*
* @param rtcio_num The index of rtcio. 0 ~ MAX(rtcio).
*/
static inline void rtcio_ll_pin_input_hysteresis_disable(int rtcio_num)
{
LP_IO_MUX.gpion[rtcio_num].gpion_hys_sel = 1;
LP_IO_MUX.gpion[rtcio_num].gpion_hys_en = 0;
}
/**
* @brief Get RTCIO input level.
*

View File

@@ -78,6 +78,22 @@ extern "C" {
*/
#define rtcio_hal_input_disable(rtcio_num) rtcio_ll_input_disable(rtcio_num)
#if SOC_GPIO_SUPPORT_PIN_HYS_FILTER
/**
* Control RTCIO input hysteresis enable/disable by software.
*
* @param rtcio_num The index of rtcio. 0 ~ SOC_RTCIO_PIN_COUNT.
* @param enable enable or disable the hysteresis.
*/
#define rtcio_hal_input_hysteresis_soft_enable(rtcio_num, enable) do { \
if (enable) { \
rtcio_ll_pin_input_hysteresis_enable(rtcio_num); \
} else { \
rtcio_ll_pin_input_hysteresis_disable(rtcio_num); \
} \
} while (0)
#endif
/**
* @brief Set RTC GPIO pad drive capability.
*

View File

@@ -495,6 +495,10 @@ config SOC_GPIO_FLEX_GLITCH_FILTER_NUM
int
default 8
config SOC_GPIO_SUPPORT_PIN_HYS_FILTER
bool
default y
config SOC_GPIO_SUPPORT_ETM
bool
default y

View File

@@ -206,6 +206,7 @@
#define SOC_GPIO_SUPPORT_PIN_GLITCH_FILTER 1
#define SOC_GPIO_FLEX_GLITCH_FILTER_NUM 8
#define SOC_GPIO_SUPPORT_PIN_HYS_FILTER 1
// GPIO peripheral has the ETM extension
#define SOC_GPIO_SUPPORT_ETM 1