diff --git a/components/esp_driver_i2c/test_apps/i2c_test_apps/main/test_i2c_common.cpp b/components/esp_driver_i2c/test_apps/i2c_test_apps/main/test_i2c_common.cpp index 8bdc1e41f4..9c559a4393 100644 --- a/components/esp_driver_i2c/test_apps/i2c_test_apps/main/test_i2c_common.cpp +++ b/components/esp_driver_i2c/test_apps/i2c_test_apps/main/test_i2c_common.cpp @@ -198,10 +198,9 @@ TEST_CASE("I2C master clock frequency test", "[i2c]") TEST_ESP_OK(i2c_master_transmit(dev_handle, data_wr, 500, 0)); - uart_bitrate_detect_config_t conf = { - .rx_io_num = I2C_MASTER_SCL_IO, - .source_clk = UART_SCLK_DEFAULT, - }; + uart_bitrate_detect_config_t conf = {}; + conf.rx_io_num = I2C_MASTER_SCL_IO; + conf.source_clk = UART_SCLK_DEFAULT; uart_bitrate_res_t res = {}; uart_detect_bitrate_start(UART_NUM_1, &conf); vTaskDelay(pdMS_TO_TICKS(50)); diff --git a/components/esp_driver_i3c/test_apps/i3c_test_apps/main/test_i3c_master_common.cpp b/components/esp_driver_i3c/test_apps/i3c_test_apps/main/test_i3c_master_common.cpp index 2a13e170e7..4fef13cfe8 100644 --- a/components/esp_driver_i3c/test_apps/i3c_test_apps/main/test_i3c_master_common.cpp +++ b/components/esp_driver_i3c/test_apps/i3c_test_apps/main/test_i3c_master_common.cpp @@ -186,10 +186,9 @@ TEST_CASE("I3C master clock frequency test", "[i3c]") i3c_master_i2c_device_handle_t dev_1; TEST_ESP_OK(i3c_master_bus_add_i2c_device(bus_handle, &dev_cfg_1, &dev_1)); - uart_bitrate_detect_config_t conf = { - .rx_io_num = I3C_MASTER_SCL_IO, - .source_clk = UART_SCLK_DEFAULT, - }; + uart_bitrate_detect_config_t conf = {}; + conf.rx_io_num = I3C_MASTER_SCL_IO; + conf.source_clk = UART_SCLK_DEFAULT; uart_bitrate_res_t res = {}; uart_detect_bitrate_start(UART_NUM_1, &conf); diff --git a/components/esp_driver_ledc/test_apps/ledc/main/test_ledc_etm.cpp b/components/esp_driver_ledc/test_apps/ledc/main/test_ledc_etm.cpp index 9f302ea192..008c9be710 100644 --- a/components/esp_driver_ledc/test_apps/ledc/main/test_ledc_etm.cpp +++ b/components/esp_driver_ledc/test_apps/ledc/main/test_ledc_etm.cpp @@ -50,10 +50,9 @@ TEST_CASE("ledc etm event and task", "[ledc]") TEST_ESP_OK(esp_etm_channel_enable(etm_channel_a)); // use UART auto baud rate detection feature to count the PWM pulse count - uart_bitrate_detect_config_t conf = { - .rx_io_num = PULSE_IO, - .source_clk = UART_SCLK_DEFAULT, - }; + uart_bitrate_detect_config_t conf = {}; + conf.rx_io_num = PULSE_IO; + conf.source_clk = UART_SCLK_DEFAULT; uart_bitrate_res_t res = {}; uart_detect_bitrate_start(UART_NUM_1, &conf); diff --git a/components/esp_driver_ledc/test_apps/ledc/main/test_ledc_utils.cpp b/components/esp_driver_ledc/test_apps/ledc/main/test_ledc_utils.cpp index b0478bc706..ba86a2f70d 100644 --- a/components/esp_driver_ledc/test_apps/ledc/main/test_ledc_utils.cpp +++ b/components/esp_driver_ledc/test_apps/ledc/main/test_ledc_utils.cpp @@ -37,10 +37,9 @@ ledc_timer_config_t create_default_timer_config(void) // use UART auto baud rate detection feature to test the waveform of LEDC int wave_count(int last_time) { - uart_bitrate_detect_config_t conf = { - .rx_io_num = PULSE_IO, - .source_clk = UART_SCLK_DEFAULT, - }; + uart_bitrate_detect_config_t conf = {}; + conf.rx_io_num = PULSE_IO; + conf.source_clk = UART_SCLK_DEFAULT; uart_bitrate_res_t res = {}; uart_detect_bitrate_start(UART_NUM_1, &conf); vTaskDelay(pdMS_TO_TICKS(last_time)); diff --git a/components/esp_driver_uart/include/driver/uart.h b/components/esp_driver_uart/include/driver/uart.h index 0e806a07cc..58815f9bb2 100644 --- a/components/esp_driver_uart/include/driver/uart.h +++ b/components/esp_driver_uart/include/driver/uart.h @@ -34,12 +34,13 @@ extern "C" { */ typedef struct { int baud_rate; /*!< UART baud rate - Note that the actual baud rate set could have a slight deviation from the user-configured value due to rounding error*/ - uart_word_length_t data_bits; /*!< UART byte size*/ - uart_parity_t parity; /*!< UART parity mode*/ - uart_stop_bits_t stop_bits; /*!< UART stop bits*/ - uart_hw_flowcontrol_t flow_ctrl; /*!< UART HW flow control mode (cts/rts)*/ - uint8_t rx_flow_ctrl_thresh; /*!< UART HW RTS threshold*/ + Note that the actual baud rate set could have a slight deviation from the user-configured value due to rounding error */ + uart_word_length_t data_bits; /*!< UART byte size */ + uart_parity_t parity; /*!< UART parity mode */ + uart_stop_bits_t stop_bits; /*!< UART stop bits */ + uart_hw_flowcontrol_t flow_ctrl; /*!< UART HW flow control mode (cts/rts) */ + uint8_t rx_flow_ctrl_thresh; /*!< UART HW RTS threshold */ + uint32_t rx_glitch_filt_thresh; /*!< The width of the glitch on the RX signal to be filtered (unit: ns). If set to 0, then RX signal filter is disabled. */ union { uart_sclk_t source_clk; /*!< UART source clock selection */ #if (SOC_UART_LP_NUM >= 1) @@ -896,6 +897,7 @@ typedef struct { int rx_io_num; /*!< GPIO pin number for the incoming signal */ uart_sclk_t source_clk; /*!< The higher the frequency of the clock source, the more accurate the detected bitrate value; The slower the frequency of the clock source, the slower the bitrate can be measured */ + uint32_t rx_glitch_filt_thresh; /*!< The width of the glitch on the incoming signal to be filtered (unit: ns). If set to 0, then glitch filter is disabled. */ } uart_bitrate_detect_config_t; /** diff --git a/components/esp_driver_uart/src/uart.c b/components/esp_driver_uart/src/uart.c index f70f3c8cb6..498314695a 100644 --- a/components/esp_driver_uart/src/uart.c +++ b/components/esp_driver_uart/src/uart.c @@ -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 */ @@ -1060,6 +1060,9 @@ esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_conf ESP_RETURN_ON_FALSE((uart_config->rx_flow_ctrl_thresh < UART_HW_FIFO_LEN(uart_num)), ESP_FAIL, UART_TAG, "rx flow thresh error"); ESP_RETURN_ON_FALSE((uart_config->flow_ctrl < UART_HW_FLOWCTRL_MAX), ESP_FAIL, UART_TAG, "hw_flowctrl mode error"); ESP_RETURN_ON_FALSE((uart_config->data_bits < UART_DATA_BITS_MAX), ESP_FAIL, UART_TAG, "data bit error"); +#if UART_LL_GLITCH_FILT_ONLY_ON_AUTOBAUD + ESP_RETURN_ON_FALSE((uart_config->rx_glitch_filt_thresh == 0), ESP_FAIL, UART_TAG, "glitch filter on RX signal is not supported"); +#endif bool allow_pd __attribute__((unused)) = (uart_config->flags.allow_pd || uart_config->flags.backup_before_sleep); #if !SOC_UART_SUPPORT_SLEEP_RETENTION @@ -1105,6 +1108,9 @@ esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_conf success = lp_uart_ll_set_baudrate(uart_context[uart_num].hal.dev, uart_config->baud_rate, sclk_freq); } #endif + uart_hal_set_glitch_filt_thrd(&(uart_context[uart_num].hal), uart_config->rx_glitch_filt_thresh, sclk_freq); + uart_hal_enable_glitch_filt(&(uart_context[uart_num].hal), uart_config->rx_glitch_filt_thresh > 0); + uart_hal_set_parity(&(uart_context[uart_num].hal), uart_config->parity); uart_hal_set_data_bit_num(&(uart_context[uart_num].hal), uart_config->data_bits); uart_hal_set_stop_bits(&(uart_context[uart_num].hal), uart_config->stop_bits); @@ -2345,14 +2351,37 @@ esp_err_t uart_detect_bitrate_start(uart_port_t uart_num, const uart_bitrate_det periph_rtc_dig_clk8m_enable(); } #endif + UART_ENTER_CRITICAL(&(uart_context[uart_num].spinlock)); HP_UART_SRC_CLK_ATOMIC() { uart_hal_set_sclk(&(uart_context[uart_num].hal), uart_sclk_sel); uart_hal_set_baudrate(&(uart_context[uart_num].hal), 57600, sclk_freq); // set to any baudrate } uart_context[uart_num].sclk_sel = uart_sclk_sel; + +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 + // On such targets, the reference tick for filter is APB clock, regardless the UART func clock sel + esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_APB, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &sclk_freq); +#endif + uart_hal_set_glitch_filt_thrd(&(uart_context[uart_num].hal), config->rx_glitch_filt_thresh, sclk_freq); + uart_hal_enable_glitch_filt(&(uart_context[uart_num].hal), config->rx_glitch_filt_thresh > 0); + UART_EXIT_CRITICAL(&(uart_context[uart_num].spinlock)); + _uart_set_pin6(uart_num, UART_PIN_NO_CHANGE, config->rx_io_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); } else if (config != NULL) { - ESP_LOGW(UART_TAG, "unable to re-configure for an acquired port, ignoring the new config"); +#if UART_LL_GLITCH_FILT_ONLY_ON_AUTOBAUD + if (config->rx_glitch_filt_thresh > 0) { + // On ESP32 and ESP32S2, the reference tick for filter is APB clock, regardless the UART func clock sel + UART_ENTER_CRITICAL(&(uart_context[uart_num].spinlock)); + soc_module_clk_t src_clk = SOC_MOD_CLK_APB; + uint32_t sclk_freq = 0; + esp_clk_tree_src_get_freq_hz(src_clk, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &sclk_freq); + uart_hal_set_glitch_filt_thrd(&(uart_context[uart_num].hal), config->rx_glitch_filt_thresh, sclk_freq); + UART_EXIT_CRITICAL(&(uart_context[uart_num].spinlock)); + } else +#endif + { + ESP_LOGW(UART_TAG, "unable to re-configure such parameters for an acquired port, ignoring the new config"); + } } // start auto baud rate detection diff --git a/components/esp_driver_uart/test_apps/uart/main/test_common.h b/components/esp_driver_uart/test_apps/uart/main/test_common.h index 3035626b55..0e2376b9f6 100644 --- a/components/esp_driver_uart/test_apps/uart/main/test_common.h +++ b/components/esp_driver_uart/test_apps/uart/main/test_common.h @@ -1,9 +1,10 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ #include "driver/uart.h" +#include "sdkconfig.h" typedef struct { uart_port_t port_num; @@ -14,3 +15,11 @@ typedef struct { } uart_port_param_t; bool port_select(uart_port_param_t *port_param); + +#if CONFIG_IDF_TARGET_ESP32H4 +#define TEST_UART_TX_PIN_NUM 2 +#define TEST_UART_RX_PIN_NUM 3 +#else +#define TEST_UART_TX_PIN_NUM 4 +#define TEST_UART_RX_PIN_NUM 5 +#endif diff --git a/components/esp_driver_uart/test_apps/uart/main/test_uart.c b/components/esp_driver_uart/test_apps/uart/main/test_uart.c index 6b11b39ecc..2bce25f10c 100644 --- a/components/esp_driver_uart/test_apps/uart/main/test_uart.c +++ b/components/esp_driver_uart/test_apps/uart/main/test_uart.c @@ -7,20 +7,24 @@ #include #include "unity.h" #include "test_utils.h" +#include "unity_test_utils.h" #include "driver/uart.h" #include "esp_log.h" #include "esp_rom_gpio.h" #include "esp_private/gpio.h" +#include "hal/gpio_ll.h" #if SOC_LP_GPIO_MATRIX_SUPPORTED #include "driver/lp_io.h" #include "driver/rtc_io.h" #include "hal/rtc_io_ll.h" #endif +#include "hal/uart_ll.h" #include "hal/uart_periph.h" #include "soc/uart_pins.h" #include "soc/soc_caps.h" #include "soc/clk_tree_defs.h" #include "test_common.h" +#include "esp_attr.h" #define BUF_SIZE (100) #define UART_BAUD_11520 (11520) @@ -35,8 +39,8 @@ bool port_select(uart_port_param_t *port_param) if (strcmp(argv, "uart") == 0) { port_param->port_num = UART_NUM_1; // Test HP_UART with UART1 port port_param->default_src_clk = UART_SCLK_DEFAULT; - port_param->tx_pin_num = 4; - port_param->rx_pin_num = 5; + port_param->tx_pin_num = TEST_UART_TX_PIN_NUM; + port_param->rx_pin_num = TEST_UART_RX_PIN_NUM; port_param->rx_flow_ctrl_thresh = 120; return true; #if SOC_UART_LP_NUM > 0 @@ -303,15 +307,21 @@ TEST_CASE("uart general API test", "[uart]") uart_wakeup_set_get_test(uart_num); } +typedef struct { + uart_port_t port_num; + int test_times; +} uart_write_task_param_t; + static void uart_write_task(void *param) { - uart_port_t uart_num = (uart_port_t)param; + uart_write_task_param_t *task_param = (uart_write_task_param_t *)param; + uart_port_t uart_num = task_param->port_num; uint8_t *tx_buf = (uint8_t *)malloc(1024); TEST_ASSERT_NOT_NULL(tx_buf); for (int i = 1; i < 1023; i++) { tx_buf[i] = (i & 0xff); } - for (int i = 0; i < 1024; i++) { + for (int i = 0; i < task_param->test_times; i++) { //d[0] and d[1023] are header tx_buf[0] = (i & 0xff); tx_buf[1023] = ((~i) & 0xff); @@ -322,33 +332,11 @@ static void uart_write_task(void *param) vTaskDelete(NULL); } -TEST_CASE("uart read write test", "[uart]") +static void uart_read_data_with_check(uart_port_t uart_num, int test_times) { - uart_port_param_t port_param = {}; - TEST_ASSERT(port_select(&port_param)); - - uart_port_t uart_num = port_param.port_num; uint8_t *rd_data = (uint8_t *)malloc(1024); TEST_ASSERT_NOT_NULL(rd_data); - uart_config_t uart_config = { - .baud_rate = 2000000, - .data_bits = UART_DATA_8_BITS, - .parity = UART_PARITY_DISABLE, - .stop_bits = UART_STOP_BITS_1, - .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS, - .source_clk = port_param.default_src_clk, - .rx_flow_ctrl_thresh = port_param.rx_flow_ctrl_thresh, - }; - TEST_ESP_OK(uart_driver_install(uart_num, BUF_SIZE * 2, 0, 20, NULL, 0)); - TEST_ESP_OK(uart_param_config(uart_num, &uart_config)); - // Use loop back feature to connect TX signal to RX signal, CTS signal to RTS signal internally. Then no need to configure uart pins. - TEST_ESP_OK(uart_set_loop_back(uart_num, true)); - - TEST_ESP_OK(uart_wait_tx_done(uart_num, portMAX_DELAY)); - vTaskDelay(pdMS_TO_TICKS(20)); // make sure last byte has flushed from TX FIFO - TEST_ESP_OK(uart_flush_input(uart_num)); - xTaskCreate(uart_write_task, "uart_write_task", 8192, (void *)uart_num, 5, NULL); - for (int i = 0; i < 1024; i++) { + for (int i = 0; i < test_times; i++) { int bytes_remaining = 1024; memset(rd_data, 0, 1024); while (bytes_remaining) { @@ -382,9 +370,43 @@ TEST_CASE("uart read write test", "[uart]") TEST_FAIL(); } } + free(rd_data); +} + +TEST_CASE("uart read write test", "[uart]") +{ + uart_port_param_t port_param = {}; + TEST_ASSERT(port_select(&port_param)); + + uart_port_t uart_num = port_param.port_num; + uart_config_t uart_config = { + .baud_rate = 2000000, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS, + .source_clk = port_param.default_src_clk, + .rx_flow_ctrl_thresh = port_param.rx_flow_ctrl_thresh, + }; + TEST_ESP_OK(uart_driver_install(uart_num, BUF_SIZE * 2, 0, 20, NULL, 0)); + TEST_ESP_OK(uart_param_config(uart_num, &uart_config)); + // Use loop back feature to connect TX signal to RX signal, CTS signal to RTS signal internally. Then no need to configure uart pins. + TEST_ESP_OK(uart_set_loop_back(uart_num, true)); + + TEST_ESP_OK(uart_wait_tx_done(uart_num, portMAX_DELAY)); + vTaskDelay(pdMS_TO_TICKS(20)); // make sure last byte has flushed from TX FIFO + TEST_ESP_OK(uart_flush_input(uart_num)); + + uart_write_task_param_t task_param = { + .port_num = uart_num, + .test_times = 1024, + }; + xTaskCreate(uart_write_task, "uart_write_task", 8192, (void *)&task_param, 5, NULL); + + uart_read_data_with_check(uart_num, task_param.test_times); + uart_wait_tx_done(uart_num, portMAX_DELAY); uart_driver_delete(uart_num); - free(rd_data); vTaskDelay(2); // wait for uart_write_task to exit } @@ -695,3 +717,136 @@ TEST_CASE("uart auto baud rate detection", "[uart]") vTaskDelete(console_write_task); } } + +IRAM_ATTR static void uart_signal_inject_glitch_task(void *param) +{ + uart_port_param_t *port_param = (uart_port_param_t *)param; + uart_port_t uart_num = port_param->port_num; + uint32_t tx_pin = port_param->tx_pin_num; + // while sending, frequently disconnect from UART TX signal and set level to high to create glitches + gpio_ll_set_level(&GPIO, tx_pin, 1); +#if SOC_UART_LP_NUM > 0 && SOC_LP_GPIO_MATRIX_SUPPORTED + uint32_t rtc_gpio_num = rtc_io_number_get(tx_pin); + rtcio_ll_set_level(rtc_gpio_num, 1); +#endif + + // esp_rom_delay_us(1000); // wait for uart write task to start sending data + + while (1) { + // make sure the glitch is always less than 5us + portDISABLE_INTERRUPTS(); + if (uart_num < SOC_UART_HP_NUM) { + esp_rom_gpio_connect_out_signal(tx_pin, SIG_GPIO_OUT_IDX, false, false); + gpio_ll_set_output_enable_ctrl(&GPIO, tx_pin, false, false); + esp_rom_gpio_connect_out_signal(tx_pin, UART_PERIPH_SIGNAL(uart_num, SOC_UART_PERIPH_SIGNAL_TX), false, false); +#if SOC_UART_LP_NUM > 0 && SOC_LP_GPIO_MATRIX_SUPPORTED + } else { + rtcio_ll_matrix_out(rtc_gpio_num, SIG_LP_GPIO_OUT_IDX, false, false); + LP_GPIO.func_out_sel_cfg[rtc_gpio_num].oe_sel = 1; + rtcio_ll_matrix_out(rtc_gpio_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_PERIPH_SIGNAL_TX), false, false); +#endif + } + portENABLE_INTERRUPTS(); + vTaskDelay(pdMS_TO_TICKS(1)); + } +} + +TEST_CASE("uart rx glitch filter (read write test + auto baud rate detection test)", "[uart]") +{ + // The test will be constructed as: + // Configuring TX and RX signal on the same GPIO pad + // And while the TX data is sending, the TX output configuration will be manipulated to generate glitches on the signal + uart_port_param_t port_param = {}; + TEST_ASSERT(port_select(&port_param)); + port_param.tx_pin_num = port_param.rx_pin_num; // let tx and rx use the same pin + + uart_port_t uart_num = port_param.port_num; + // High speed clock source may not able to filter a 5us glitch, therefore, lower the source clock frequency + if (uart_num < SOC_UART_HP_NUM) { +#if SOC_UART_SUPPORT_XTAL_CLK + port_param.default_src_clk = UART_SCLK_XTAL; +#endif + } + uart_config_t uart_config = { + .baud_rate = 50000, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .source_clk = port_param.default_src_clk, +#if !UART_LL_GLITCH_FILT_ONLY_ON_AUTOBAUD + .rx_glitch_filt_thresh = 5000, // filter all glitches with width less than 5us +#endif + }; + + TEST_ESP_OK(uart_driver_install(uart_num, BUF_SIZE * 2, 0, 20, NULL, 0)); + TEST_ESP_OK(uart_param_config(uart_num, &uart_config)); + esp_err_t err = uart_set_pin(uart_num, port_param.tx_pin_num, port_param.rx_pin_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + if (uart_num < SOC_UART_HP_NUM) { + TEST_ESP_OK(err); +#if SOC_UART_LP_NUM > 0 + } else { +#if !SOC_LP_GPIO_MATRIX_SUPPORTED + TEST_ESP_ERR(ESP_FAIL, err); // For LP UART port, if no LP GPIO Matrix, unable to be used in one-wire mode +#else + TEST_ESP_OK(err); +#endif +#endif // SOC_UART_LP_NUM > 0 + } + + // If successfully route TX and RX on the same pad, then we can continue with the test + if (err == ESP_OK) { + TEST_ESP_OK(uart_wait_tx_done(uart_num, portMAX_DELAY)); + vTaskDelay(pdMS_TO_TICKS(20)); // make sure last byte has flushed from TX FIFO + TEST_ESP_OK(uart_flush_input(uart_num)); + + uart_write_task_param_t task_param = { + .port_num = uart_num, + .test_times = 10, + }; + TaskHandle_t inject_glitch_task_handle; + xTaskCreate(uart_signal_inject_glitch_task, "uart_signal_inject_glitch_task", 1024, (void *)&port_param, 6, &inject_glitch_task_handle); + + // 1. read write test +#if !UART_LL_GLITCH_FILT_ONLY_ON_AUTOBAUD + xTaskCreate(uart_write_task, "uart_write_task", 8192, (void *)&task_param, 5, NULL); + uart_read_data_with_check(uart_num, task_param.test_times); + uart_wait_tx_done(uart_num, portMAX_DELAY); +#endif + + // 2. auto baud rate detection test + if (uart_num < SOC_UART_HP_NUM) { + TaskHandle_t write_task_handle; + task_param.test_times = 5; + xTaskCreate(uart_write_task, "uart_write_task", 8192, (void *)&task_param, 5, &write_task_handle); + uart_bitrate_res_t res = {}; +#if UART_LL_GLITCH_FILT_ONLY_ON_AUTOBAUD + // Set the glitch filter threshold for auto baud rate detection on ESP32 and ESP32S2 + // And since the reference tick for filter is APB clock (80MHz) for the two targets, so set the threshold to 3us to avoid exceeding the register value limit + // The glitch is much less than 3us + uart_bitrate_detect_config_t conf = { + .rx_glitch_filt_thresh = 3000, + }; + TEST_ESP_OK(uart_detect_bitrate_start(uart_num, &conf)); +#else + TEST_ESP_OK(uart_detect_bitrate_start(uart_num, NULL)); +#endif + vTaskDelay(pdMS_TO_TICKS(10)); + TEST_ESP_OK(uart_detect_bitrate_stop(uart_num, false, &res)); + uint32_t detected_baudrate = res.clk_freq_hz * 2 / res.pos_period; // assume the wave has a slow falling slew rate + uint32_t actual_baudrate = 0; + uart_get_baudrate(uart_num, &actual_baudrate); + TEST_ASSERT_INT32_WITHIN(actual_baudrate * 0.03, actual_baudrate, detected_baudrate); + // wait for write task to finish and self deleted + while (eTaskGetState(write_task_handle) != eDeleted) { + vTaskDelay(1); + } + } + + unity_utils_task_delete(inject_glitch_task_handle); + } + + uart_driver_delete(uart_num); + + vTaskDelay(2); // wait for tasks to exit +} diff --git a/components/esp_hal_uart/esp32/include/hal/uart_ll.h b/components/esp_hal_uart/esp32/include/hal/uart_ll.h index 9600d23231..f0ece1f813 100644 --- a/components/esp_hal_uart/esp32/include/hal/uart_ll.h +++ b/components/esp_hal_uart/esp32/include/hal/uart_ll.h @@ -16,6 +16,9 @@ #include "soc/uart_struct.h" #include "soc/dport_reg.h" #include "hal/uart_types.h" +#include "hal/assert.h" + +#define UART_LL_GLITCH_FILT_ONLY_ON_AUTOBAUD 1 // On ESP32, only autobaud can filter glitches. The data UART RX hardware processes is always the one without getting filtered. // The default fifo depth #define UART_LL_FIFO_DEF_LEN (SOC_UART_FIFO_LEN) @@ -248,6 +251,24 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_baudrate(uart_dev_t *hw, uint32_t sclk_fr return ((sclk_freq << 4)) / ((div_reg.div_int << 4) | div_reg.div_frag); } +/** + * @brief Set the UART glitch filter threshold. Any high pulse lasting shorter than this value will be ignored when the filter is enabled. + * + * @param hw Beginning address of the peripheral registers. + * @param glitch_filt_thrd The glitch filter threshold to be set (unit: ns) + * @param sclk_freq Frequency of the clock source of UART, in Hz. + */ +FORCE_INLINE_ATTR void uart_ll_set_glitch_filt_thrd(uart_dev_t *hw, uint32_t glitch_filt_thrd, uint32_t sclk_freq) +{ + uint32_t clk_cycles = 0; + if (glitch_filt_thrd > 0) { + uint32_t ref_clk_freq = sclk_freq; // no pre-divider + clk_cycles = ((uint64_t)glitch_filt_thrd * ref_clk_freq + 1000000000 - 1) / 1000000000; // round up to always filter something + HAL_ASSERT(clk_cycles <= UART_GLITCH_FILT_V); + } + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->auto_baud, glitch_filt, clk_cycles); +} + /** * @brief Enable the UART interrupt based on the given mask. * @@ -1067,6 +1088,20 @@ FORCE_INLINE_ATTR void uart_ll_set_autobaud_en(uart_dev_t *hw, bool enable) hw->auto_baud.en = enable ? 1 : 0; } +/** + * @brief Enable or disable the UART glitch filter + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to enable the filter, False to disable the filter + */ +FORCE_INLINE_ATTR void uart_ll_enable_glitch_filt(uart_dev_t *hw, bool enable) +{ + // No such register on ESP32 to independently enable or disable the glitch filter + // Glitch filter is coupled with auto baudrate detection + (void)hw; + (void)enable; +} + /** * @brief Get the RXD edge count. * diff --git a/components/esp_hal_uart/esp32c2/include/hal/uart_ll.h b/components/esp_hal_uart/esp32c2/include/hal/uart_ll.h index aa32d30c8f..ed7f7a72de 100644 --- a/components/esp_hal_uart/esp32c2/include/hal/uart_ll.h +++ b/components/esp_hal_uart/esp32c2/include/hal/uart_ll.h @@ -19,6 +19,7 @@ #include "soc/system_reg.h" #include "soc/dport_access.h" #include "esp_attr.h" +#include "hal/assert.h" // The default fifo depth #define UART_LL_FIFO_DEF_LEN (SOC_UART_FIFO_LEN) @@ -259,6 +260,35 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_baudrate(uart_dev_t *hw, uint32_t sclk_fr (((div_reg.div_int << 4) | div_reg.div_frag) * (HAL_FORCE_READ_U32_REG_FIELD(hw->clk_conf, sclk_div_num) + 1)); } +/** + * @brief Set the UART glitch filter threshold. Any high pulse lasting shorter than this value will be ignored when the filter is enabled. + * + * @param hw Beginning address of the peripheral registers. + * @param glitch_filt_thrd The glitch filter threshold to be set (unit: ns) + * @param sclk_freq Frequency of the clock source of UART, in Hz. + */ +FORCE_INLINE_ATTR void uart_ll_set_glitch_filt_thrd(uart_dev_t *hw, uint32_t glitch_filt_thrd, uint32_t sclk_freq) +{ + uint32_t clk_cycles = 0; + if (glitch_filt_thrd > 0) { + uint32_t ref_clk_freq = sclk_freq / (HAL_FORCE_READ_U32_REG_FIELD(hw->clk_conf, sclk_div_num) + 1); + clk_cycles = ((uint64_t)glitch_filt_thrd * ref_clk_freq + 1000000000 - 1) / 1000000000; // round up to always filter something + HAL_ASSERT(clk_cycles <= UART_GLITCH_FILT_V); + } + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_filt, glitch_filt, clk_cycles); +} + +/** + * @brief Enable or disable the UART glitch filter + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to enable the filter, False to disable the filter + */ +FORCE_INLINE_ATTR void uart_ll_enable_glitch_filt(uart_dev_t *hw, bool enable) +{ + hw->rx_filt.glitch_filt_en = enable; +} + /** * @brief Enable the UART interrupt based on the given mask. * diff --git a/components/esp_hal_uart/esp32c3/include/hal/uart_ll.h b/components/esp_hal_uart/esp32c3/include/hal/uart_ll.h index e5d5ae3eb2..0371050a9f 100644 --- a/components/esp_hal_uart/esp32c3/include/hal/uart_ll.h +++ b/components/esp_hal_uart/esp32c3/include/hal/uart_ll.h @@ -18,6 +18,7 @@ #include "soc/system_reg.h" #include "soc/dport_access.h" #include "esp_attr.h" +#include "hal/assert.h" // The default fifo depth #define UART_LL_FIFO_DEF_LEN (SOC_UART_FIFO_LEN) @@ -264,6 +265,35 @@ static inline uint32_t uart_ll_get_baudrate(uart_dev_t *hw, uint32_t sclk_freq) return ((sclk_freq << 4)) / (((div_reg.div_int << 4) | div_reg.div_frag) * (HAL_FORCE_READ_U32_REG_FIELD(hw->clk_conf, sclk_div_num) + 1)); } +/** + * @brief Set the UART glitch filter threshold. Any high pulse lasting shorter than this value will be ignored when the filter is enabled. + * + * @param hw Beginning address of the peripheral registers. + * @param glitch_filt_thrd The glitch filter threshold to be set (unit: ns) + * @param sclk_freq Frequency of the clock source of UART, in Hz. + */ +FORCE_INLINE_ATTR void uart_ll_set_glitch_filt_thrd(uart_dev_t *hw, uint32_t glitch_filt_thrd, uint32_t sclk_freq) +{ + uint32_t clk_cycles = 0; + if (glitch_filt_thrd > 0) { + uint32_t ref_clk_freq = sclk_freq / (HAL_FORCE_READ_U32_REG_FIELD(hw->clk_conf, sclk_div_num) + 1); + clk_cycles = ((uint64_t)glitch_filt_thrd * ref_clk_freq + 1000000000 - 1) / 1000000000; // round up to always filter something + HAL_ASSERT(clk_cycles <= UART_GLITCH_FILT_V); + } + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_filt, glitch_filt, clk_cycles); +} + +/** + * @brief Enable or disable the UART glitch filter + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to enable the filter, False to disable the filter + */ +FORCE_INLINE_ATTR void uart_ll_enable_glitch_filt(uart_dev_t *hw, bool enable) +{ + hw->rx_filt.glitch_filt_en = enable; +} + /** * @brief Enable the UART interrupt based on the given mask. * diff --git a/components/esp_hal_uart/esp32c5/include/hal/uart_ll.h b/components/esp_hal_uart/esp32c5/include/hal/uart_ll.h index 10c2fa04b4..83ed7a8192 100644 --- a/components/esp_hal_uart/esp32c5/include/hal/uart_ll.h +++ b/components/esp_hal_uart/esp32c5/include/hal/uart_ll.h @@ -478,7 +478,7 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_baudrate(uart_dev_t *hw, uint32_t sclk_fr { typeof(hw->clkdiv_sync) div_reg; div_reg.val = hw->clkdiv_sync.val; - int sclk_div; + int sclk_div = 1; if ((hw) == &LP_UART) { sclk_div = 1; // no pre-divider for LP UART clock source on the target } else { @@ -487,6 +487,41 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_baudrate(uart_dev_t *hw, uint32_t sclk_fr return ((sclk_freq << 4)) / (((div_reg.clkdiv_int << 4) | div_reg.clkdiv_frag) * sclk_div); } +/** + * @brief Set the UART glitch filter threshold. Any high pulse lasting shorter than this value will be ignored when the filter is enabled. + * + * @param hw Beginning address of the peripheral registers. + * @param glitch_filt_thrd The glitch filter threshold to be set (unit: ns) + * @param sclk_freq Frequency of the clock source of UART, in Hz. + */ +FORCE_INLINE_ATTR void uart_ll_set_glitch_filt_thrd(uart_dev_t *hw, uint32_t glitch_filt_thrd, uint32_t sclk_freq) +{ + uint32_t clk_cycles = 0; + if (glitch_filt_thrd > 0) { + int sclk_div = 1; + if ((hw) == &LP_UART) { + sclk_div = 1; // no pre-divider for LP UART clock source on the target + } else { + sclk_div = UART_LL_PCR_REG_U32_GET(hw, sclk_conf, sclk_div_num) + 1; + } + uint32_t ref_clk_freq = sclk_freq / sclk_div; + clk_cycles = ((uint64_t)glitch_filt_thrd * ref_clk_freq + 1000000000 - 1) / 1000000000; // round up to always filter something + HAL_ASSERT(clk_cycles <= UART_GLITCH_FILT_V); + } + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_filt, glitch_filt, clk_cycles); +} + +/** + * @brief Enable or disable the UART glitch filter + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to enable the filter, False to disable the filter + */ +FORCE_INLINE_ATTR void uart_ll_enable_glitch_filt(uart_dev_t *hw, bool enable) +{ + hw->rx_filt.glitch_filt_en = enable; +} + /** * @brief Enable the UART interrupt based on the given mask. * diff --git a/components/esp_hal_uart/esp32c6/include/hal/uart_ll.h b/components/esp_hal_uart/esp32c6/include/hal/uart_ll.h index e48f7cda4e..b47dd7e86f 100644 --- a/components/esp_hal_uart/esp32c6/include/hal/uart_ll.h +++ b/components/esp_hal_uart/esp32c6/include/hal/uart_ll.h @@ -459,7 +459,7 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_baudrate(uart_dev_t *hw, uint32_t sclk_fr { typeof(hw->clkdiv_sync) div_reg; div_reg.val = hw->clkdiv_sync.val; - int sclk_div; + int sclk_div = 1; if ((hw) == &LP_UART) { sclk_div = 1; // no pre-divider for LP UART clock source on the target } else { @@ -468,6 +468,41 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_baudrate(uart_dev_t *hw, uint32_t sclk_fr return ((sclk_freq << 4)) / (((div_reg.clkdiv_int << 4) | div_reg.clkdiv_frag) * sclk_div); } +/** + * @brief Set the UART glitch filter threshold. Any high pulse lasting shorter than this value will be ignored when the filter is enabled. + * + * @param hw Beginning address of the peripheral registers. + * @param glitch_filt_thrd The glitch filter threshold to be set (unit: ns) + * @param sclk_freq Frequency of the clock source of UART, in Hz. + */ +FORCE_INLINE_ATTR void uart_ll_set_glitch_filt_thrd(uart_dev_t *hw, uint32_t glitch_filt_thrd, uint32_t sclk_freq) +{ + uint32_t clk_cycles = 0; + if (glitch_filt_thrd > 0) { + int sclk_div = 1; + if ((hw) == &LP_UART) { + sclk_div = 1; // no pre-divider for LP UART clock source on the target + } else { + sclk_div = UART_LL_PCR_REG_U32_GET(hw, sclk_conf, sclk_div_num) + 1; + } + uint32_t ref_clk_freq = sclk_freq / sclk_div; + clk_cycles = ((uint64_t)glitch_filt_thrd * ref_clk_freq + 1000000000 - 1) / 1000000000; // round up to always filter something + HAL_ASSERT(clk_cycles <= UART_GLITCH_FILT_V); + } + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_filt, glitch_filt, clk_cycles); +} + +/** + * @brief Enable or disable the UART glitch filter + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to enable the filter, False to disable the filter + */ +FORCE_INLINE_ATTR void uart_ll_enable_glitch_filt(uart_dev_t *hw, bool enable) +{ + hw->rx_filt.glitch_filt_en = enable; +} + /** * @brief Enable the UART interrupt based on the given mask. * diff --git a/components/esp_hal_uart/esp32c61/include/hal/uart_ll.h b/components/esp_hal_uart/esp32c61/include/hal/uart_ll.h index 1fa6f31294..0e0821de01 100644 --- a/components/esp_hal_uart/esp32c61/include/hal/uart_ll.h +++ b/components/esp_hal_uart/esp32c61/include/hal/uart_ll.h @@ -305,6 +305,35 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_baudrate(uart_dev_t *hw, uint32_t sclk_fr return ((sclk_freq << 4)) / (((div_reg.clkdiv_int << 4) | div_reg.clkdiv_frag) * sclk_div); } +/** + * @brief Set the UART glitch filter threshold. Any high pulse lasting shorter than this value will be ignored when the filter is enabled. + * + * @param hw Beginning address of the peripheral registers. + * @param glitch_filt_thrd The glitch filter threshold to be set (unit: ns) + * @param sclk_freq Frequency of the clock source of UART, in Hz. + */ +FORCE_INLINE_ATTR void uart_ll_set_glitch_filt_thrd(uart_dev_t *hw, uint32_t glitch_filt_thrd, uint32_t sclk_freq) +{ + uint32_t clk_cycles = 0; + if (glitch_filt_thrd > 0) { + uint32_t ref_clk_freq = sclk_freq / (UART_LL_PCR_REG_U32_GET(hw, sclk_conf, sclk_div_num) + 1); + clk_cycles = ((uint64_t)glitch_filt_thrd * ref_clk_freq + 1000000000 - 1) / 1000000000; // round up to always filter something + HAL_ASSERT(clk_cycles <= UART_GLITCH_FILT_V); + } + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_filt, glitch_filt, clk_cycles); +} + +/** + * @brief Enable or disable the UART glitch filter + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to enable the filter, False to disable the filter + */ +FORCE_INLINE_ATTR void uart_ll_enable_glitch_filt(uart_dev_t *hw, bool enable) +{ + hw->rx_filt.glitch_filt_en = enable; +} + /** * @brief Enable the UART interrupt based on the given mask. * diff --git a/components/esp_hal_uart/esp32h2/include/hal/uart_ll.h b/components/esp_hal_uart/esp32h2/include/hal/uart_ll.h index c823e94661..2de57a9880 100644 --- a/components/esp_hal_uart/esp32h2/include/hal/uart_ll.h +++ b/components/esp_hal_uart/esp32h2/include/hal/uart_ll.h @@ -18,6 +18,7 @@ #include "soc/pcr_struct.h" #include "soc/pcr_reg.h" #include "esp_attr.h" +#include "hal/assert.h" // The default fifo depth #define UART_LL_FIFO_DEF_LEN (SOC_UART_FIFO_LEN) @@ -284,6 +285,35 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_baudrate(uart_dev_t *hw, uint32_t sclk_fr return ((sclk_freq << 4)) / (((div_reg.clkdiv_int << 4) | div_reg.clkdiv_frag) * (UART_LL_PCR_REG_U32_GET(hw, sclk_conf, sclk_div_num) + 1)); } +/** + * @brief Set the UART glitch filter threshold. Any high pulse lasting shorter than this value will be ignored when the filter is enabled. + * + * @param hw Beginning address of the peripheral registers. + * @param glitch_filt_thrd The glitch filter threshold to be set (unit: ns) + * @param sclk_freq Frequency of the clock source of UART, in Hz. + */ +FORCE_INLINE_ATTR void uart_ll_set_glitch_filt_thrd(uart_dev_t *hw, uint32_t glitch_filt_thrd, uint32_t sclk_freq) +{ + uint32_t clk_cycles = 0; + if (glitch_filt_thrd > 0) { + uint32_t ref_clk_freq = sclk_freq / (UART_LL_PCR_REG_U32_GET(hw, sclk_conf, sclk_div_num) + 1); + clk_cycles = ((uint64_t)glitch_filt_thrd * ref_clk_freq + 1000000000 - 1) / 1000000000; // round up to always filter something + HAL_ASSERT(clk_cycles <= UART_GLITCH_FILT_V); + } + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_filt, glitch_filt, clk_cycles); +} + +/** + * @brief Enable or disable the UART glitch filter + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to enable the filter, False to disable the filter + */ +FORCE_INLINE_ATTR void uart_ll_enable_glitch_filt(uart_dev_t *hw, bool enable) +{ + hw->rx_filt.glitch_filt_en = enable; +} + /** * @brief Enable the UART interrupt based on the given mask. * diff --git a/components/esp_hal_uart/esp32h21/include/hal/uart_ll.h b/components/esp_hal_uart/esp32h21/include/hal/uart_ll.h index 3e0264942a..7196d574e7 100644 --- a/components/esp_hal_uart/esp32h21/include/hal/uart_ll.h +++ b/components/esp_hal_uart/esp32h21/include/hal/uart_ll.h @@ -18,6 +18,7 @@ #include "soc/pcr_struct.h" #include "soc/pcr_reg.h" #include "esp_attr.h" +#include "hal/assert.h" // The default fifo depth #define UART_LL_FIFO_DEF_LEN (SOC_UART_FIFO_LEN) @@ -284,6 +285,35 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_baudrate(uart_dev_t *hw, uint32_t sclk_fr return ((sclk_freq << 4)) / (((div_reg.clkdiv << 4) | div_reg.clkdiv_frag) * (UART_LL_PCR_REG_U32_GET(hw, sclk_conf, sclk_div_num) + 1)); } +/** + * @brief Set the UART glitch filter threshold. Any high pulse lasting shorter than this value will be ignored when the filter is enabled. + * + * @param hw Beginning address of the peripheral registers. + * @param glitch_filt_thrd The glitch filter threshold to be set (unit: ns) + * @param sclk_freq Frequency of the clock source of UART, in Hz. + */ +FORCE_INLINE_ATTR void uart_ll_set_glitch_filt_thrd(uart_dev_t *hw, uint32_t glitch_filt_thrd, uint32_t sclk_freq) +{ + uint32_t clk_cycles = 0; + if (glitch_filt_thrd > 0) { + uint32_t ref_clk_freq = sclk_freq / (UART_LL_PCR_REG_U32_GET(hw, sclk_conf, sclk_div_num) + 1); + clk_cycles = ((uint64_t)glitch_filt_thrd * ref_clk_freq + 1000000000 - 1) / 1000000000; // round up to always filter something + HAL_ASSERT(clk_cycles <= UART_GLITCH_FILT_V); + } + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_filt, glitch_filt, clk_cycles); +} + +/** + * @brief Enable or disable the UART glitch filter + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to enable the filter, False to disable the filter + */ +FORCE_INLINE_ATTR void uart_ll_enable_glitch_filt(uart_dev_t *hw, bool enable) +{ + hw->rx_filt.glitch_filt_en = enable; +} + /** * @brief Enable the UART interrupt based on the given mask. * diff --git a/components/esp_hal_uart/esp32h4/include/hal/uart_ll.h b/components/esp_hal_uart/esp32h4/include/hal/uart_ll.h index c821233712..81aa1cf83b 100644 --- a/components/esp_hal_uart/esp32h4/include/hal/uart_ll.h +++ b/components/esp_hal_uart/esp32h4/include/hal/uart_ll.h @@ -297,11 +297,39 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_baudrate(uart_dev_t *hw, uint32_t sclk_fr { typeof(hw->clkdiv_sync) div_reg; div_reg.val = hw->clkdiv_sync.val; - int sclk_div; - sclk_div = UART_LL_PCR_REG_U32_GET(hw, sclk_conf, sclk_div_num) + 1; + int sclk_div = UART_LL_PCR_REG_U32_GET(hw, sclk_conf, sclk_div_num) + 1; return ((sclk_freq << 4)) / (((div_reg.clkdiv << 4) | div_reg.clkdiv_frag) * sclk_div); } +/** + * @brief Set the UART glitch filter threshold. Any high pulse lasting shorter than this value will be ignored when the filter is enabled. + * + * @param hw Beginning address of the peripheral registers. + * @param glitch_filt_thrd The glitch filter threshold to be set (unit: ns) + * @param sclk_freq Frequency of the clock source of UART, in Hz. + */ +FORCE_INLINE_ATTR void uart_ll_set_glitch_filt_thrd(uart_dev_t *hw, uint32_t glitch_filt_thrd, uint32_t sclk_freq) +{ + uint32_t clk_cycles = 0; + if (glitch_filt_thrd > 0) { + uint32_t ref_clk_freq = sclk_freq / (UART_LL_PCR_REG_U32_GET(hw, sclk_conf, sclk_div_num) + 1); + clk_cycles = ((uint64_t)glitch_filt_thrd * ref_clk_freq + 1000000000 - 1) / 1000000000; // round up to always filter something + HAL_ASSERT(clk_cycles <= UART_GLITCH_FILT_V); + } + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_filt, glitch_filt, clk_cycles); +} + +/** + * @brief Enable or disable the UART glitch filter + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to enable the filter, False to disable the filter + */ +FORCE_INLINE_ATTR void uart_ll_enable_glitch_filt(uart_dev_t *hw, bool enable) +{ + hw->rx_filt.glitch_filt_en = enable; +} + /** * @brief Enable the UART interrupt based on the given mask. * diff --git a/components/esp_hal_uart/esp32p4/include/hal/uart_ll.h b/components/esp_hal_uart/esp32p4/include/hal/uart_ll.h index 86c5a26ecc..0098c6da70 100644 --- a/components/esp_hal_uart/esp32p4/include/hal/uart_ll.h +++ b/components/esp_hal_uart/esp32p4/include/hal/uart_ll.h @@ -591,7 +591,7 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_baudrate(uart_dev_t *hw, uint32_t sclk_fr { typeof(hw->clkdiv_sync) div_reg; div_reg.val = hw->clkdiv_sync.val; - int sclk_div = 0; + int sclk_div = 1; if ((hw) == &UART0) { sclk_div = HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl111, reg_uart0_sclk_div_num) + 1; } else if ((hw) == &UART1) { @@ -608,6 +608,49 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_baudrate(uart_dev_t *hw, uint32_t sclk_fr return ((sclk_freq << 4)) / (((div_reg.clkdiv << 4) | div_reg.clkdiv_frag) * sclk_div); } +/** + * @brief Set the UART glitch filter threshold. Any high pulse lasting shorter than this value will be ignored when the filter is enabled. + * + * @param hw Beginning address of the peripheral registers. + * @param glitch_filt_thrd The glitch filter threshold to be set (unit: ns) + * @param sclk_freq Frequency of the clock source of UART, in Hz. + */ +FORCE_INLINE_ATTR void uart_ll_set_glitch_filt_thrd(uart_dev_t *hw, uint32_t glitch_filt_thrd, uint32_t sclk_freq) +{ + uint32_t clk_cycles = 0; + if (glitch_filt_thrd > 0) { + int sclk_div = 1; + if ((hw) == &UART0) { + sclk_div = HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl111, reg_uart0_sclk_div_num) + 1; + } else if ((hw) == &UART1) { + sclk_div = HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl112, reg_uart1_sclk_div_num) + 1; + } else if ((hw) == &UART2) { + sclk_div = HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl113, reg_uart2_sclk_div_num) + 1; + } else if ((hw) == &UART3) { + sclk_div = HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl114, reg_uart3_sclk_div_num) + 1; + } else if ((hw) == &UART4) { + sclk_div = HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl115, reg_uart4_sclk_div_num) + 1; + } else if ((hw) == &LP_UART) { + sclk_div = 1; // no pre-divider for LP UART clock source on the target + } + uint32_t ref_clk_freq = sclk_freq / sclk_div; + clk_cycles = ((uint64_t)glitch_filt_thrd * ref_clk_freq + 1000000000 - 1) / 1000000000; // round up to always filter something + HAL_ASSERT(clk_cycles <= UART_GLITCH_FILT_V); + } + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_filt, glitch_filt, clk_cycles); +} + +/** + * @brief Enable or disable the UART glitch filter + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to enable the filter, False to disable the filter + */ +FORCE_INLINE_ATTR void uart_ll_enable_glitch_filt(uart_dev_t *hw, bool enable) +{ + hw->rx_filt.glitch_filt_en = enable; +} + /** * @brief Enable the UART interrupt based on the given mask. * diff --git a/components/esp_hal_uart/esp32s2/include/hal/uart_ll.h b/components/esp_hal_uart/esp32s2/include/hal/uart_ll.h index 328511a03d..4039252d91 100644 --- a/components/esp_hal_uart/esp32s2/include/hal/uart_ll.h +++ b/components/esp_hal_uart/esp32s2/include/hal/uart_ll.h @@ -17,6 +17,9 @@ #include "soc/system_reg.h" #include "soc/dport_reg.h" #include "esp_attr.h" +#include "hal/assert.h" + +#define UART_LL_GLITCH_FILT_ONLY_ON_AUTOBAUD 1 // On ESP32-S2, only autobaud can filter glitches. The data UART RX hardware processes is always the one without getting filtered. // The default fifo depth #define UART_LL_FIFO_DEF_LEN (SOC_UART_FIFO_LEN) @@ -237,6 +240,24 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_baudrate(uart_dev_t *hw, uint32_t sclk_fr return ((sclk_freq << 4)) / ((div_reg.div_int << 4) | div_reg.div_frag); } +/** + * @brief Set the UART glitch filter threshold. Any high pulse lasting shorter than this value will be ignored when the filter is enabled. + * + * @param hw Beginning address of the peripheral registers. + * @param glitch_filt_thrd The glitch filter threshold to be set (unit: ns) + * @param sclk_freq Frequency of the clock source of UART, in Hz. + */ +FORCE_INLINE_ATTR void uart_ll_set_glitch_filt_thrd(uart_dev_t *hw, uint32_t glitch_filt_thrd, uint32_t sclk_freq) +{ + uint32_t clk_cycles = 0; + if (glitch_filt_thrd > 0) { + uint32_t ref_clk_freq = sclk_freq; // no pre-divider + clk_cycles = ((uint64_t)glitch_filt_thrd * ref_clk_freq + 1000000000 - 1) / 1000000000; // round up to always filter something + HAL_ASSERT(clk_cycles <= UART_GLITCH_FILT_V); + } + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->auto_baud, glitch_filt, clk_cycles); +} + /** * @brief Enable the UART interrupt based on the given mask. * @@ -987,6 +1008,20 @@ FORCE_INLINE_ATTR void uart_ll_set_autobaud_en(uart_dev_t *hw, bool enable) hw->auto_baud.en = enable ? 1 : 0; } +/** + * @brief Enable or disable the UART glitch filter + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to enable the filter, False to disable the filter + */ +FORCE_INLINE_ATTR void uart_ll_enable_glitch_filt(uart_dev_t *hw, bool enable) +{ + // No such register on ESP32-S2 to independently enable or disable the glitch filter + // Glitch filter is coupled with auto baudrate detection + (void)hw; + (void)enable; +} + /** * @brief Get the RXD edge count. * diff --git a/components/esp_hal_uart/esp32s3/include/hal/uart_ll.h b/components/esp_hal_uart/esp32s3/include/hal/uart_ll.h index 0048d64b57..193d8d152d 100644 --- a/components/esp_hal_uart/esp32s3/include/hal/uart_ll.h +++ b/components/esp_hal_uart/esp32s3/include/hal/uart_ll.h @@ -18,6 +18,7 @@ #include "soc/system_reg.h" #include "soc/dport_access.h" #include "esp_attr.h" +#include "hal/assert.h" // The default fifo depth #define UART_LL_FIFO_DEF_LEN (SOC_UART_FIFO_LEN) @@ -269,6 +270,35 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_baudrate(uart_dev_t *hw, uint32_t sclk_fr (((div_reg.clkdiv << 4) | div_reg.clkdiv_frag) * (HAL_FORCE_READ_U32_REG_FIELD(hw->clk_conf, sclk_div_num) + 1)); } +/** + * @brief Set the UART glitch filter threshold. Any high pulse lasting shorter than this value will be ignored when the filter is enabled. + * + * @param hw Beginning address of the peripheral registers. + * @param glitch_filt_thrd The glitch filter threshold to be set (unit: ns) + * @param sclk_freq Frequency of the clock source of UART, in Hz. + */ +FORCE_INLINE_ATTR void uart_ll_set_glitch_filt_thrd(uart_dev_t *hw, uint32_t glitch_filt_thrd, uint32_t sclk_freq) +{ + uint32_t clk_cycles = 0; + if (glitch_filt_thrd > 0) { + uint32_t ref_clk_freq = sclk_freq / (HAL_FORCE_READ_U32_REG_FIELD(hw->clk_conf, sclk_div_num) + 1); + clk_cycles = ((uint64_t)glitch_filt_thrd * ref_clk_freq + 1000000000 - 1) / 1000000000; // round up to always filter something + HAL_ASSERT(clk_cycles <= UART_GLITCH_FILT_V); + } + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_filt, glitch_filt, clk_cycles); +} + +/** + * @brief Enable or disable the UART glitch filter + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to enable the filter, False to disable the filter + */ +FORCE_INLINE_ATTR void uart_ll_enable_glitch_filt(uart_dev_t *hw, bool enable) +{ + hw->rx_filt.glitch_filt_en = enable; +} + /** * @brief Enable the UART interrupt based on the given mask. * diff --git a/components/esp_hal_uart/esp32s31/include/hal/uart_ll.h b/components/esp_hal_uart/esp32s31/include/hal/uart_ll.h index 7c61881cfd..2ea5c72547 100644 --- a/components/esp_hal_uart/esp32s31/include/hal/uart_ll.h +++ b/components/esp_hal_uart/esp32s31/include/hal/uart_ll.h @@ -468,7 +468,7 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_baudrate(uart_dev_t *hw, uint32_t sclk_fr { typeof(hw->clkdiv_sync) div_reg; div_reg.val = hw->clkdiv_sync.val; - int sclk_div = 0; + int sclk_div = 1; if ((hw) == &UART0) { sclk_div = HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.uart0_ctrl0, reg_uart0_sclk_div_num) + 1; } else if ((hw) == &UART1) { @@ -483,6 +483,47 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_baudrate(uart_dev_t *hw, uint32_t sclk_fr return ((sclk_freq << 4)) / (((div_reg.clkdiv << 4) | div_reg.clkdiv_frag) * sclk_div); } +/** + * @brief Set the UART glitch filter threshold. Any high pulse lasting shorter than this value will be ignored when the filter is enabled. + * + * @param hw Beginning address of the peripheral registers. + * @param glitch_filt_thrd The glitch filter threshold to be set (unit: ns) + * @param sclk_freq Frequency of the clock source of UART, in Hz. + */ +FORCE_INLINE_ATTR void uart_ll_set_glitch_filt_thrd(uart_dev_t *hw, uint32_t glitch_filt_thrd, uint32_t sclk_freq) +{ + uint32_t clk_cycles = 0; + if (glitch_filt_thrd > 0) { + int sclk_div = 1; + if ((hw) == &UART0) { + sclk_div = HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.uart0_ctrl0, reg_uart0_sclk_div_num) + 1; + } else if ((hw) == &UART1) { + sclk_div = HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.uart1_ctrl0, reg_uart1_sclk_div_num) + 1; + } else if ((hw) == &UART2) { + sclk_div = HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.uart2_ctrl0, reg_uart2_sclk_div_num) + 1; + } else if ((hw) == &UART3) { + sclk_div = HAL_FORCE_READ_U32_REG_FIELD(HP_SYS_CLKRST.uart3_ctrl0, reg_uart3_sclk_div_num) + 1; + // } else if ((hw) == &LP_UART) { + // sclk_div = HAL_FORCE_READ_U32_REG_FIELD(hw->clk_conf, sclk_div_num) + 1; + } + uint32_t ref_clk_freq = sclk_freq / sclk_div; + clk_cycles = ((uint64_t)glitch_filt_thrd * ref_clk_freq + 1000000000 - 1) / 1000000000; // round up to always filter something + HAL_ASSERT(clk_cycles <= UART_GLITCH_FILT_V); + } + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_filt, glitch_filt, clk_cycles); +} + +/** + * @brief Enable or disable the UART glitch filter + * + * @param hw Beginning address of the peripheral registers. + * @param enable True to enable the filter, False to disable the filter + */ +FORCE_INLINE_ATTR void uart_ll_enable_glitch_filt(uart_dev_t *hw, bool enable) +{ + hw->rx_filt.glitch_filt_en = enable; +} + /** * @brief Enable the UART interrupt based on the given mask. * diff --git a/components/esp_hal_uart/include/hal/uart_hal.h b/components/esp_hal_uart/include/hal/uart_hal.h index 5546e5a038..206e89553c 100644 --- a/components/esp_hal_uart/include/hal/uart_hal.h +++ b/components/esp_hal_uart/include/hal/uart_hal.h @@ -488,6 +488,23 @@ uint16_t uart_hal_get_max_rx_timeout_thrd(uart_hal_context_t *hal); */ #define uart_hal_get_rxfifo_len(hal) uart_ll_get_rxfifo_len((hal)->dev) +/** + * @brief Set the UART glitch filter threshold. Any high pulse lasting shorter than this value will be ignored when the filter is enabled. + * + * @param hal Context of the HAL layer + * @param glitch_filt_thrd The glitch filter threshold to be set (unit: ns) + * @param sclk_freq Frequency of the clock source of UART, in Hz. + */ +#define uart_hal_set_glitch_filt_thrd(hal, glitch_filt_thrd, sclk_freq) uart_ll_set_glitch_filt_thrd((hal)->dev, glitch_filt_thrd, sclk_freq) + +/** + * @brief Enable or disable the UART glitch filter + * + * @param hal Context of the HAL layer + * @param enable True to enable the filter, False to disable the filter + */ +#define uart_hal_enable_glitch_filt(hal, enable) uart_ll_enable_glitch_filt((hal)->dev, enable) + /** * @brief Enable or disable the auto baudrate detection * diff --git a/docs/en/api-reference/peripherals/uart.rst b/docs/en/api-reference/peripherals/uart.rst index 6f684a34de..421113a6bd 100644 --- a/docs/en/api-reference/peripherals/uart.rst +++ b/docs/en/api-reference/peripherals/uart.rst @@ -107,6 +107,8 @@ For more information on how to configure the hardware flow control options, plea Additionally, :cpp:member:`uart_config_t::allow_pd` can be set to enable the backup of the UART configuration registers before entering sleep and restore these registers after exiting sleep. This allows the UART to continue working properly after waking up even when the UART module power domain is entirely off during sleep. This option implies an balance between power consumption and memory usage. If the power consumption is not a concern, you can disable this option to save memory. +If glitches may occur on the RX signal, :cpp:member:`uart_config_t::rx_glitch_filt_thresh` can be set to filter the glitches to ensure the correct data is received (note that this feature is not supported on ESP32 and ESP32-S2). The unit of the :cpp:member:`uart_config_t::rx_glitch_filt_thresh` is nanoseconds. The default value is 0, which means no filtering. + Multiple Steps """""""""""""" diff --git a/docs/zh_CN/api-reference/peripherals/uart.rst b/docs/zh_CN/api-reference/peripherals/uart.rst index aaa3f45496..9188db58c7 100644 --- a/docs/zh_CN/api-reference/peripherals/uart.rst +++ b/docs/zh_CN/api-reference/peripherals/uart.rst @@ -107,6 +107,8 @@ UART 驱动程序函数通过 :cpp:type:`uart_port_t` 识别不同的 UART 控 此外,置位 :cpp:member:`uart_config_t::allow_pd` 会使能在进入睡眠模式前备份 UART 配置寄存器并在退出睡眠后恢复这些寄存器。这个功能使 UART 能够在系统唤醒后继续正常工作,即使其电源域在睡眠过程中被完全关闭。此选项需要用户在功耗和内存使用之间取得平衡。如果功耗不是一个问题,可以禁用这个选项来节省内存。 +如果 RX 信号可能出现抖动,可以设置 :cpp:member:`uart_config_t::rx_glitch_filt_thresh` 来过滤抖动以确保接收到正确的数据(注意:该功能在 ESP32 和 ESP32-S2 上不支持)。:cpp:member:`uart_config_t::rx_glitch_filt_thresh` 的单位是纳秒。默认值为 0,即表示不进行过滤。 + 分步依次配置每个参数 """""""""""""""""""""""""""""""