diff --git a/components/esp_driver_uart/include/driver/uart_wakeup.h b/components/esp_driver_uart/include/driver/uart_wakeup.h index 5697fbd699..7e735d7d29 100644 --- a/components/esp_driver_uart/include/driver/uart_wakeup.h +++ b/components/esp_driver_uart/include/driver/uart_wakeup.h @@ -67,6 +67,16 @@ typedef struct { */ esp_err_t uart_wakeup_setup(uart_port_t uart_num, const uart_wakeup_cfg_t *cfg); +/** + * @brief Clear the UART wakeup configuration. + * + * This function will clear the UART wakeup behavior and set to its default configuration. + * + * @param uart_num The UART port to initialize for wakeup (e.g., UART_NUM_0, UART_NUM_1, etc.). + * @param wakeup_mode The UART wakeup mode set in `uart_wakeup_setup`. + */ +void uart_wakeup_clear(uart_port_t uart_num, uart_wakeup_mode_t wakeup_mode); + #ifdef __cplusplus } #endif diff --git a/components/esp_driver_uart/src/uart_wakeup.c b/components/esp_driver_uart/src/uart_wakeup.c index deb8e2b89c..174dedf14c 100644 --- a/components/esp_driver_uart/src/uart_wakeup.c +++ b/components/esp_driver_uart/src/uart_wakeup.c @@ -13,6 +13,10 @@ #include "driver/uart_wakeup.h" #include "hal/uart_hal.h" +#include "esp_private/esp_sleep_internal.h" +#include "esp_log.h" + +const __attribute__((unused)) static char *TAG = "uart_wakeup"; #if SOC_UART_WAKEUP_SUPPORT_CHAR_SEQ_MODE static esp_err_t uart_char_seq_wk_configure(uart_dev_t *hw, const char* phrase) @@ -59,6 +63,26 @@ esp_err_t uart_wakeup_setup(uart_port_t uart_num, const uart_wakeup_cfg_t *cfg) // This should be mocked at ll level if the selection of the UART wakeup mode is not supported by this SOC. uart_ll_set_wakeup_mode(hw, cfg->wakeup_mode); +#if SOC_PM_SUPPORT_PMU_CLK_ICG + // When hp uarts are utilized, the main XTAL need to be PU and UARTx & IOMX ICG need to be ungate + bool __attribute__((unused)) is_hp_uart = (uart_num < SOC_UART_HP_NUM); + uart_hal_context_t hal = { + .dev = hw, + }; + soc_module_clk_t src_clk; + uart_hal_get_sclk(&hal, &src_clk); + + if (is_hp_uart && cfg->wakeup_mode != UART_WK_MODE_ACTIVE_THRESH) { + if (src_clk != SOC_MOD_CLK_XTAL) { + ESP_LOGE(TAG, "Failed to setup uart wakeup due to the clock source is not XTAL!"); + return ESP_ERR_NOT_SUPPORTED; + } + esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_ON); + esp_sleep_clock_config(UART_LL_SLEEP_CLOCK(uart_num), ESP_SLEEP_CLOCK_OPTION_UNGATE); + esp_sleep_clock_config(ESP_SLEEP_CLOCK_IOMUX, ESP_SLEEP_CLOCK_OPTION_UNGATE); + } +#endif + switch (cfg->wakeup_mode) { #if SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE case UART_WK_MODE_ACTIVE_THRESH: @@ -89,3 +113,18 @@ esp_err_t uart_wakeup_setup(uart_port_t uart_num, const uart_wakeup_cfg_t *cfg) return ESP_ERR_INVALID_ARG; } + +void uart_wakeup_clear(uart_port_t uart_num, uart_wakeup_mode_t wakeup_mode) +{ +#if SOC_PM_SUPPORT_PMU_CLK_ICG + // When hp uarts are utilized, the main XTAL need to be PU and UARTx & IOMX ICG need to be ungate + bool __attribute__((unused)) is_hp_uart = (uart_num < SOC_UART_HP_NUM); + + if (is_hp_uart && wakeup_mode != UART_WK_MODE_ACTIVE_THRESH) { + esp_sleep_clock_config(UART_LL_SLEEP_CLOCK(uart_num), ESP_SLEEP_CLOCK_OPTION_GATE); + esp_sleep_clock_config(ESP_SLEEP_CLOCK_IOMUX, ESP_SLEEP_CLOCK_OPTION_GATE); + esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_OFF); + } +#endif + +} diff --git a/components/esp_driver_uart/test_apps/uart/main/CMakeLists.txt b/components/esp_driver_uart/test_apps/uart/main/CMakeLists.txt index 27ff3d84b7..87e2d10f42 100644 --- a/components/esp_driver_uart/test_apps/uart/main/CMakeLists.txt +++ b/components/esp_driver_uart/test_apps/uart/main/CMakeLists.txt @@ -10,11 +10,16 @@ if(CONFIG_SOC_UART_SUPPORT_SLEEP_RETENTION AND CONFIG_PM_ENABLE) list(APPEND srcs "test_uart_retention.c") endif() +# Only if the target support uart wakeup +if(CONFIG_SOC_LIGHT_SLEEP_SUPPORTED) + list(APPEND srcs "test_hp_uart_wakeup.c") +endif() + # In order for the cases defined by `TEST_CASE` to be linked into the final elf, # the component can be registered as WHOLE_ARCHIVE idf_component_register( SRCS ${srcs} - REQUIRES esp_driver_uart unity esp_psram test_utils esp_driver_gpio esp_pm + REQUIRES esp_driver_uart unity esp_psram test_utils esp_driver_gpio esp_pm esp_timer PRIV_INCLUDE_DIRS . WHOLE_ARCHIVE ) diff --git a/components/esp_driver_uart/test_apps/uart/main/test_hp_uart_wakeup.c b/components/esp_driver_uart/test_apps/uart/main/test_hp_uart_wakeup.c new file mode 100644 index 0000000000..6b35b76d97 --- /dev/null +++ b/components/esp_driver_uart/test_apps/uart/main/test_hp_uart_wakeup.c @@ -0,0 +1,424 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include "unity.h" +#include "test_utils.h" +#include "driver/uart.h" +#include "driver/uart_wakeup.h" +#include "esp_log.h" +#include "esp_rom_gpio.h" +#include "esp_private/gpio.h" +#include "esp_sleep.h" +#include "esp_timer.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 "soc/uart_periph.h" +#include "soc/uart_pins.h" +#include "soc/soc_caps.h" +#include "soc/clk_tree_defs.h" +#include "test_common.h" + +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4 +#define DEFAULT_UART1_TX_IO_NUM U1TXD_GPIO_NUM +#define DEFAULT_UART1_RX_IO_NUM U1RXD_GPIO_NUM +#elif CONFIG_IDF_TARGET_ESP32 +#define DEFAULT_UART1_TX_IO_NUM GPIO_NUM_25 +#define DEFAULT_UART1_RX_IO_NUM GPIO_NUM_26 +#elif CONFIG_IDF_TARGET_ESP32C2 +#define DEFAULT_UART1_TX_IO_NUM GPIO_NUM_1 +#define DEFAULT_UART1_RX_IO_NUM GPIO_NUM_0 +#elif CONFIG_IDF_TARGET_ESP32C6 +#define DEFAULT_UART1_TX_IO_NUM GPIO_NUM_1 +#define DEFAULT_UART1_RX_IO_NUM GPIO_NUM_0 +#elif CONFIG_IDF_TARGET_ESP32H2 +#define DEFAULT_UART1_TX_IO_NUM GPIO_NUM_4 +#define DEFAULT_UART1_RX_IO_NUM GPIO_NUM_5 +#elif CONFIG_IDF_TARGET_ESP32C3 +#define DEFAULT_UART1_TX_IO_NUM GPIO_NUM_4 +#define DEFAULT_UART1_RX_IO_NUM GPIO_NUM_5 +#elif CONFIG_IDF_TARGET_ESP32C61 +#define DEFAULT_UART1_TX_IO_NUM GPIO_NUM_4 +#define DEFAULT_UART1_RX_IO_NUM GPIO_NUM_5 +#elif CONFIG_IDF_TARGET_ESP32C5 +#define DEFAULT_UART1_TX_IO_NUM GPIO_NUM_2 +#define DEFAULT_UART1_RX_IO_NUM GPIO_NUM_3 +#endif + +#define MASTER_UART_NUM (1) +#define MASTER_UART_TX_IO_NUM DEFAULT_UART1_TX_IO_NUM +#define MASTER_UART_RX_IO_NUM DEFAULT_UART1_RX_IO_NUM +#define SLAVE_UART_NUM (1) +#define SLAVE_UART_TX_IO_NUM DEFAULT_UART1_RX_IO_NUM +#define SLAVE_UART_RX_IO_NUM DEFAULT_UART1_TX_IO_NUM +#define UART_BAUD_RATE (115200) +#define BUF_SIZE (1024) +#define TIMER_WAKEUP_TIME_US (5 * 100 * 1000) + +static void force_stdout(void) +{ + fflush(stdout); + fsync(fileno(stdout)); +} + +/* Initialize UART */ +static esp_err_t uart_initialization(uart_port_param_t *port_param) +{ + uart_port_t uart_num = port_param->port_num; + uart_config_t uart_config = { + .baud_rate = UART_BAUD_RATE, + .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, + .rx_flow_ctrl_thresh = port_param->rx_flow_ctrl_thresh, + }; + const int uart_tx = port_param->tx_pin_num; + const int uart_rx = port_param->rx_pin_num; + TEST_ESP_OK(uart_param_config(uart_num, &uart_config)); + TEST_ESP_OK(uart_set_pin(uart_num, uart_tx, uart_rx, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); + TEST_ESP_OK(uart_driver_install(uart_num, BUF_SIZE * 2, BUF_SIZE * 2, 20, NULL, 0)); + + return ESP_OK; +} + +/* Configure UART wakeup */ +static esp_err_t uart_wakeup_config(uart_port_param_t *port_param, uart_wakeup_cfg_t *uart_wakeup_cfg) +{ + /* This function configures the wakeup behavior for a specified UART port based on the provided configuration. + * The behavior depends on the selected wakeup mode and additional parameters such as active threshold or + * character sequence, if applicable. It is important that the provided configuration matches the capabilities + * of the SOC to ensure proper operation. Besides, the Rx pin need extra configuration to enable it can work during light sleep */ + + uart_port_t uart_num = port_param->port_num; + int rx_io_num = port_param->rx_pin_num; + // Keep configure of rx_io + TEST_ESP_OK(gpio_sleep_sel_dis(rx_io_num)); + // Initializes the UART wakeup functionality. + TEST_ESP_OK(uart_wakeup_setup(uart_num, uart_wakeup_cfg)); + TEST_ESP_OK(esp_sleep_enable_uart_wakeup(uart_num)); + + return ESP_OK; +} + +void init_master(void) +{ + uart_port_param_t port_param = { + .port_num = MASTER_UART_NUM, +#if SOC_UART_SUPPORT_REF_TICK + .default_src_clk = UART_SCLK_REF_TICK, +#else + .default_src_clk = UART_SCLK_XTAL, +#endif + .tx_pin_num = MASTER_UART_TX_IO_NUM, + .rx_pin_num = MASTER_UART_RX_IO_NUM, + .rx_flow_ctrl_thresh = 120 + }; + TEST_ESP_OK(uart_initialization(&port_param)); + unity_send_signal("Master Ready"); +} + +void deinit_master(void) +{ + TEST_ESP_OK(uart_driver_delete(MASTER_UART_NUM)); +} + +void send_and_verify_recived_data(const char* message, uint8_t length, bool should_wake_up) +{ + unity_wait_for_signal("Slave Ready"); + force_stdout(); + + uart_flush_input(MASTER_UART_NUM); + uart_write_bytes(MASTER_UART_NUM, message, length); + + char *data = (char *) malloc(BUF_SIZE); + int len = uart_read_bytes(MASTER_UART_NUM, data, (BUF_SIZE - 1), 1000 / portTICK_PERIOD_MS); + + bool wake_up_detected = false; + const char *target = "Wakeup OK!"; + int target_len = 11; + bool match = true; + if (len > 0) { + if (len != target_len) { + match = false; + } else { + for (int i = 0; i < target_len; i++) { + if (data[i] != target[i]) { + match = false; + break; + } + } + if (match) { + wake_up_detected = true; + } + } + } + + data[len] = '\0'; // Null-terminate the received data + + free(data); + + TEST_ESP_OK(should_wake_up != wake_up_detected); + +} + +static void init_slave(uart_wakeup_cfg_t *wake_up_cfg) +{ + uart_port_param_t port_param = { + .port_num = SLAVE_UART_NUM, +#if SOC_UART_SUPPORT_REF_TICK + .default_src_clk = UART_SCLK_REF_TICK, +#else + .default_src_clk = UART_SCLK_XTAL, +#endif + .tx_pin_num = SLAVE_UART_TX_IO_NUM, + .rx_pin_num = SLAVE_UART_RX_IO_NUM, + .rx_flow_ctrl_thresh = 120 + }; + unity_wait_for_signal("Master Ready"); + TEST_ESP_OK(uart_initialization(&port_param)); + TEST_ESP_OK(uart_wakeup_config(&port_param, wake_up_cfg)); + unity_send_signal("Slave Ready"); + force_stdout(); +} + +static void deinit_slave(void) +{ + TEST_ESP_OK(uart_driver_delete(SLAVE_UART_NUM)); +} + +static void enter_sleep_and_send_respond(void) +{ + /* Get timestamp before entering sleep */ + int64_t t_before_us = esp_timer_get_time(); + + /* Enter sleep mode */ + esp_light_sleep_start(); + + /* Get timestamp after waking up from sleep */ + int64_t t_after_us = esp_timer_get_time(); + + uart_flush_input(SLAVE_UART_NUM); + printf("sleep duration: %lld\n", t_after_us - t_before_us); + switch (esp_sleep_get_wakeup_cause()) { + case ESP_SLEEP_WAKEUP_UART: + /* Hang-up for a while to switch and execute the uart task + * Otherwise the chip may fall sleep again before running uart task */ + vTaskDelay(1); + uart_write_bytes(SLAVE_UART_NUM, "Wakeup OK!", 11); + break; + default: + uart_write_bytes(SLAVE_UART_NUM, "Wakeup failed!", 15); + break; + } + + /* Wait for uart write finish */ + uart_wait_tx_idle_polling(SLAVE_UART_NUM); +} + +// slave +#if SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE +static void test_uart_wakeup_mode_0(void) +{ + TEST_ESP_OK(esp_sleep_enable_timer_wakeup(TIMER_WAKEUP_TIME_US)); + uart_wakeup_cfg_t wake_up_cfg = { + .wakeup_mode = UART_WK_MODE_ACTIVE_THRESH, + .rx_edge_threshold = 8, + }; + init_slave(&wake_up_cfg); + + enter_sleep_and_send_respond(); + + deinit_slave(); +} +#endif + +#if SOC_UART_WAKEUP_SUPPORT_FIFO_THRESH_MODE +static void test_uart_wakeup_mode_1(void) +{ + TEST_ESP_OK(esp_sleep_enable_timer_wakeup(TIMER_WAKEUP_TIME_US)); + uart_wakeup_cfg_t wake_up_cfg = { + .wakeup_mode = UART_WK_MODE_FIFO_THRESH, + .rx_fifo_threshold = 8 + }; + init_slave(&wake_up_cfg); + + enter_sleep_and_send_respond(); + + deinit_slave(); +} +#endif + +#if SOC_UART_WAKEUP_SUPPORT_START_BIT_MODE +static void test_uart_wakeup_mode_2(void) +{ + TEST_ESP_OK(esp_sleep_enable_timer_wakeup(TIMER_WAKEUP_TIME_US)); + uart_wakeup_cfg_t wake_up_cfg = { + .wakeup_mode = UART_WK_MODE_START_BIT, + }; + init_slave(&wake_up_cfg); + + enter_sleep_and_send_respond(); + + deinit_slave(); +} +#endif + +#if SOC_UART_WAKEUP_SUPPORT_CHAR_SEQ_MODE +static void test_uart_wakeup_mode_3(void) +{ + TEST_ESP_OK(esp_sleep_enable_timer_wakeup(TIMER_WAKEUP_TIME_US)); + uart_wakeup_cfg_t wake_up_cfg = { + .wakeup_mode = UART_WK_MODE_CHAR_SEQ, + .wake_chars_seq = "hello" + }; + init_slave(&wake_up_cfg); + + enter_sleep_and_send_respond(); + + deinit_slave(); +} +#endif + +// master +#if SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE +static void send_uart_wakeup_mode_0_edge_8(void) +{ + + /* + * This is only true in 8N1 mode (8 data bits, no parity, 1 stop bit). + * Sending "TT" corresponds to: + * +-------+--------------+------+--------+ + * | Start | Data Bits | Stop | Raises | + * +-------+--------------+------+--------+ + * | 0 | 00101010(T) | 1 | 4 | + * | 0 | 00101010(T) | 1 | 4 | + * +-------+--------------+------+--------+ + */ + init_master(); + + send_and_verify_recived_data("TT", 2, true); + + deinit_master(); +} + +static void send_uart_wakeup_mode_0_edge_7(void) +{ + /* + * This is only true in 8N1 mode (8 data bits, no parity, 1 stop bit). + * Sending "Ta" corresponds to: + * +-------+--------------+------+--------+ + * | Start | Data Bits | Stop | Raises | + * +-------+--------------+------+--------+ + * | 0 | 00101010(T) | 1 | 4 | + * | 0 | 10000110(a) | 1 | 3 | + * +-------+--------------+------+--------+ + */ + init_master(); + + send_and_verify_recived_data("Ta", 2, false); + + deinit_master(); +} +#endif + +#if SOC_UART_WAKEUP_SUPPORT_FIFO_THRESH_MODE +static void send_uart_wakeup_mode_1_9_bytes(void) +{ + init_master(); + + send_and_verify_recived_data("123456789", 9, true); + + deinit_master(); +} + +static void send_uart_wakeup_mode_1_8_bytes(void) +{ + init_master(); + + send_and_verify_recived_data("12345678", 8, false); + + deinit_master(); +} +#endif + +#if SOC_UART_WAKEUP_SUPPORT_START_BIT_MODE +static void send_uart_wakeup_mode_2_start_bit(void) +{ + init_master(); + + send_and_verify_recived_data("@", 1, true); + + deinit_master(); +} + +static void send_uart_wakeup_mode_2_no_start_bit(void) +{ + init_master(); + + send_and_verify_recived_data("", 0, false); + + deinit_master(); +} +#endif + +#if SOC_UART_WAKEUP_SUPPORT_CHAR_SEQ_MODE +static void send_uart_wakeup_mode_3_positive_sequence(void) +{ + init_master(); + + send_and_verify_recived_data("hello", 5, true); + + deinit_master(); +} + +static void send_uart_wakeup_mode_3_negative_sequence(void) +{ + init_master(); + + send_and_verify_recived_data("hwllo", 5, false); + + deinit_master(); +} +#endif + +#if SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE +TEST_CASE_MULTIPLE_DEVICES("Mode 0 - Active threshold - 8 - success", "[uart][wakeup][Mode_0][timeout=5]", + test_uart_wakeup_mode_0, send_uart_wakeup_mode_0_edge_8); + +TEST_CASE_MULTIPLE_DEVICES("Mode 0 - Active threshold - 8 - fail", "[uart][wakeup][Mode_0][timeout=5]", + test_uart_wakeup_mode_0, send_uart_wakeup_mode_0_edge_7); +#endif + +#if SOC_UART_WAKEUP_SUPPORT_FIFO_THRESH_MODE +TEST_CASE_MULTIPLE_DEVICES("Mode 1 - RX FIFO - 9 bytes - success", "[uart][wakeup][Mode_1][timeout=5]", + test_uart_wakeup_mode_1, send_uart_wakeup_mode_1_9_bytes); + +TEST_CASE_MULTIPLE_DEVICES("Mode 1 - RX FIFO - 8 bytes- fail", "[uart][wakeup][Mode_1][timeout=5]", + test_uart_wakeup_mode_1, send_uart_wakeup_mode_1_8_bytes); +#endif + +#if SOC_UART_WAKEUP_SUPPORT_START_BIT_MODE +TEST_CASE_MULTIPLE_DEVICES("Mode 2 - Start bit sequence - valid data - success", "[uart][wakeup][Mode_2][timeout=5]", + test_uart_wakeup_mode_2, send_uart_wakeup_mode_2_start_bit); + +TEST_CASE_MULTIPLE_DEVICES("Mode 2 - Start bit sequence - no data- fail", "[uart][wakeup][Mode_2][timeout=5]", + test_uart_wakeup_mode_2, send_uart_wakeup_mode_2_no_start_bit); +#endif + +#if SOC_UART_WAKEUP_SUPPORT_CHAR_SEQ_MODE +TEST_CASE_MULTIPLE_DEVICES("Mode 3 - Wakeup sequence - positive hello - success", "[uart][wakeup][Mode_3][timeout=5]", + test_uart_wakeup_mode_3, send_uart_wakeup_mode_3_positive_sequence); + +TEST_CASE_MULTIPLE_DEVICES("Mode 3 - Wakeup sequence - negative hello- fail", "[uart][wakeup][Mode_3][timeout=5]", + test_uart_wakeup_mode_3, send_uart_wakeup_mode_3_negative_sequence); +#endif diff --git a/components/esp_driver_uart/test_apps/uart/pytest_uart.py b/components/esp_driver_uart/test_apps/uart/pytest_uart.py index da106d3d95..d25191962c 100644 --- a/components/esp_driver_uart/test_apps/uart/pytest_uart.py +++ b/components/esp_driver_uart/test_apps/uart/pytest_uart.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: CC0-1.0 import pytest +from pytest_embedded_idf import CaseTester from pytest_embedded_idf.utils import idf_parametrize input_argv = { @@ -36,7 +37,7 @@ def test_uart_single_dev(case_tester) -> None: # type: ignore assert uart_ports, f"Error: Chip type '{chip_type}' is not defined in input_argv. Aborting..." for case in case_tester.test_menu: - if 'hp-uart-only' not in case.groups: + if 'hp-uart-only' not in case.groups and 'wakeup' not in case.groups: for uart_port in uart_ports: dut.serial.hard_reset() dut._get_ready() @@ -45,7 +46,7 @@ def test_uart_single_dev(case_tester) -> None: # type: ignore dut.expect("select to test 'uart' or 'lp_uart' port", timeout=10) dut.write(f'{uart_port}') dut.expect_unity_test_output() - else: + elif 'wakeup' not in case.groups: dut._run_normal_case(case, reset=True) @@ -62,10 +63,30 @@ def test_uart_single_dev(case_tester) -> None: # type: ignore def test_uart_single_dev_psram(case_tester) -> None: # type: ignore dut = case_tester.first_dut for case in case_tester.test_menu: - dut.serial.hard_reset() - dut._get_ready() - dut.confirm_write(case.index, expect_str=f'Running {case.name}...') + if 'wakeup' not in case.groups: + dut.serial.hard_reset() + dut._get_ready() + dut.confirm_write(case.index, expect_str=f'Running {case.name}...') - dut.expect("select to test 'uart' or 'lp_uart' port", timeout=10) - dut.write('uart') - dut.expect_unity_test_output() + dut.expect("select to test 'uart' or 'lp_uart' port", timeout=10) + dut.write('uart') + dut.expect_unity_test_output() + + +# ESP32 only supports uart wakeup if signal routes through IOMUX, ESP32S3 multi device runner has no psram IDF-12837, +# ESP32C61 lack of runner IDF-10949, ESP32P4 not yet supported IDF-12839. +@pytest.mark.temp_skip_ci(targets=['esp32', 'esp32s3', 'esp32c61', 'esp32p4'], reason='no multi-dev runner') +@pytest.mark.generic_multi_device +@idf_parametrize('target', ['supported_targets'], indirect=['target']) +@pytest.mark.parametrize( + 'config', + [ + 'release', + ], + indirect=True, +) +@pytest.mark.parametrize('count', [2], indirect=True) +def test_hp_uart_wakeup_modes(case_tester: CaseTester) -> None: + relevant_cases = [case for case in case_tester.test_menu if {'wakeup', 'uart'}.issubset(case.groups)] + for case in relevant_cases: + case_tester.run_multi_dev_case(case=case, reset=True) diff --git a/components/esp_hw_support/include/esp_private/esp_pmu.h b/components/esp_hw_support/include/esp_private/esp_pmu.h index c63b7d1486..64d9143c46 100644 --- a/components/esp_hw_support/include/esp_private/esp_pmu.h +++ b/components/esp_hw_support/include/esp_private/esp_pmu.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -86,9 +86,15 @@ typedef enum { #if SOC_UART_SUPPORT_WAKEUP_INT #define RTC_UART0_TRIG_EN PMU_UART0_WAKEUP_EN //!< UART0 wakeup (light sleep only) #define RTC_UART1_TRIG_EN PMU_UART1_WAKEUP_EN //!< UART1 wakeup (light sleep only) +#if SOC_UART_HP_NUM > 2 +#define RTC_UART2_TRIG_EN PMU_UART2_WAKEUP_EN //!< UART2 wakeup (light sleep only) +#else +#define RTC_UART2_TRIG_EN 0 +#endif #else #define RTC_UART0_TRIG_EN 0 #define RTC_UART1_TRIG_EN 0 +#define RTC_UART2_TRIG_EN 0 #endif #if SOC_BT_SUPPORTED @@ -130,6 +136,7 @@ typedef enum { RTC_WIFI_TRIG_EN | \ RTC_UART0_TRIG_EN | \ RTC_UART1_TRIG_EN | \ + RTC_UART2_TRIG_EN | \ RTC_BT_TRIG_EN | \ RTC_LP_CORE_TRIG_EN | \ RTC_TOUCH_TRIG_EN | \ diff --git a/components/esp_hw_support/include/esp_private/esp_sleep_internal.h b/components/esp_hw_support/include/esp_private/esp_sleep_internal.h index f1eaecc50e..19f02f7ae3 100644 --- a/components/esp_hw_support/include/esp_private/esp_sleep_internal.h +++ b/components/esp_hw_support/include/esp_private/esp_sleep_internal.h @@ -92,6 +92,9 @@ typedef enum { ESP_SLEEP_CLOCK_LEDC, //!< The clock ICG cell mapping of LEDC ESP_SLEEP_CLOCK_UART0, //!< The clock ICG cell mapping of UART0 ESP_SLEEP_CLOCK_UART1, //!< The clock ICG cell mapping of UART1 +#if SOC_UART_HP_NUM > 2 + ESP_SLEEP_CLOCK_UART2, //!< The clock ICG cell mapping of UART2 +#endif ESP_SLEEP_CLOCK_MAX //!< Number of ICG cells } esp_sleep_clock_t; diff --git a/components/esp_hw_support/port/esp32c61/private_include/pmu_bit_defs.h b/components/esp_hw_support/port/esp32c61/private_include/pmu_bit_defs.h index 842b61fbe1..a0fbf6381c 100644 --- a/components/esp_hw_support/port/esp32c61/private_include/pmu_bit_defs.h +++ b/components/esp_hw_support/port/esp32c61/private_include/pmu_bit_defs.h @@ -18,6 +18,7 @@ extern "C" { #define PMU_WIFI_SOC_WAKEUP_EN BIT(5) #define PMU_UART0_WAKEUP_EN BIT(6) #define PMU_UART1_WAKEUP_EN BIT(7) +#define PMU_UART2_WAKEUP_EN BIT(9) #define PMU_BLE_SOC_WAKEUP_EN BIT(10) // #define PMU_LP_CORE_WAKEUP_EN BIT(11) #define PMU_USB_WAKEUP_EN BIT(14) diff --git a/components/esp_hw_support/sleep_modes.c b/components/esp_hw_support/sleep_modes.c index 922845b545..b0155b294a 100644 --- a/components/esp_hw_support/sleep_modes.c +++ b/components/esp_hw_support/sleep_modes.c @@ -1644,9 +1644,15 @@ esp_err_t esp_sleep_disable_wakeup_source(esp_sleep_source_t source) #endif } else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_GPIO, RTC_GPIO_TRIG_EN)) { s_config.wakeup_triggers &= ~RTC_GPIO_TRIG_EN; +#if SOC_PMU_SUPPORTED && (SOC_UART_HP_NUM > 2) + } else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_UART, (RTC_UART0_TRIG_EN | RTC_UART1_TRIG_EN | RTC_UART2_TRIG_EN))) { + s_config.wakeup_triggers &= ~(RTC_UART0_TRIG_EN | RTC_UART1_TRIG_EN | RTC_UART2_TRIG_EN); + } +#else } 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); } +#endif #if CONFIG_ULP_COPROC_TYPE_FSM else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_ULP, RTC_ULP_TRIG_EN)) { s_config.wakeup_triggers &= ~RTC_ULP_TRIG_EN; @@ -2125,6 +2131,10 @@ esp_err_t esp_sleep_enable_uart_wakeup(int uart_num) s_config.wakeup_triggers |= RTC_UART0_TRIG_EN; } else if (uart_num == UART_NUM_1) { s_config.wakeup_triggers |= RTC_UART1_TRIG_EN; +#if SOC_PMU_SUPPORTED && (SOC_UART_HP_NUM > 2) + } else if (uart_num == UART_NUM_2) { + s_config.wakeup_triggers |= RTC_UART2_TRIG_EN; +#endif } else { return ESP_ERR_INVALID_ARG; } @@ -2208,7 +2218,11 @@ esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause(void) return ESP_SLEEP_WAKEUP_TIMER; } else if (wakeup_cause & RTC_GPIO_TRIG_EN) { return ESP_SLEEP_WAKEUP_GPIO; +#if SOC_PMU_SUPPORTED && (SOC_UART_HP_NUM > 2) + } else if (wakeup_cause & (RTC_UART0_TRIG_EN | RTC_UART1_TRIG_EN | RTC_UART2_TRIG_EN)) { +#else } else if (wakeup_cause & (RTC_UART0_TRIG_EN | RTC_UART1_TRIG_EN)) { +#endif return ESP_SLEEP_WAKEUP_UART; #if SOC_PM_SUPPORT_EXT0_WAKEUP } else if (wakeup_cause & RTC_EXT0_TRIG_EN) { @@ -2614,6 +2628,11 @@ static SLEEP_FN_ATTR uint32_t get_sleep_clock_icg_flags(void) if (s_config.clock_icg_refs[ESP_SLEEP_CLOCK_UART1] > 0) { clk_flags |= BIT(PMU_ICG_FUNC_ENA_UART1); } +#if SOC_UART_HP_NUM > 2 + if (s_config.clock_icg_refs[ESP_SLEEP_CLOCK_UART2] > 0) { + clk_flags |= BIT(PMU_ICG_FUNC_ENA_UART2); + } +#endif #endif /* SOC_PM_SUPPORT_PMU_CLK_ICG */ return clk_flags; } diff --git a/components/hal/esp32c5/include/hal/uart_ll.h b/components/hal/esp32c5/include/hal/uart_ll.h index 0101350403..28c94dd106 100644 --- a/components/hal/esp32c5/include/hal/uart_ll.h +++ b/components/hal/esp32c5/include/hal/uart_ll.h @@ -30,6 +30,8 @@ extern "C" { #define LP_UART_LL_FIFO_DEF_LEN (SOC_LP_UART_FIFO_LEN) // Get UART hardware instance with giving uart num #define UART_LL_GET_HW(num) (((num) == UART_NUM_0) ? (&UART0) : (((num) == UART_NUM_1) ? (&UART1) : (&LP_UART))) +// Get UART sleep clock with giving uart num +#define UART_LL_SLEEP_CLOCK(num) (((num) == UART_NUM_0) ? (ESP_SLEEP_CLOCK_UART0) : (ESP_SLEEP_CLOCK_UART1)) #define UART_LL_REG_FIELD_BIT_SHIFT(hw) (((hw) == &LP_UART) ? 3 : 0) diff --git a/components/hal/esp32c6/include/hal/uart_ll.h b/components/hal/esp32c6/include/hal/uart_ll.h index aece4844ec..6ff2ff323f 100644 --- a/components/hal/esp32c6/include/hal/uart_ll.h +++ b/components/hal/esp32c6/include/hal/uart_ll.h @@ -30,6 +30,8 @@ extern "C" { #define LP_UART_LL_FIFO_DEF_LEN (SOC_LP_UART_FIFO_LEN) // Get UART hardware instance with giving uart num #define UART_LL_GET_HW(num) (((num) == UART_NUM_0) ? (&UART0) : (((num) == UART_NUM_1) ? (&UART1) : (&LP_UART))) +// Get UART sleep clock with giving uart num +#define UART_LL_SLEEP_CLOCK(num) (((num) == UART_NUM_0) ? (ESP_SLEEP_CLOCK_UART0) : (ESP_SLEEP_CLOCK_UART1)) #define UART_LL_REG_FIELD_BIT_SHIFT(hw) (((hw) == &LP_UART) ? 3 : 0) diff --git a/components/hal/esp32c61/include/hal/uart_ll.h b/components/hal/esp32c61/include/hal/uart_ll.h index cb1e031cf9..a2682baae6 100644 --- a/components/hal/esp32c61/include/hal/uart_ll.h +++ b/components/hal/esp32c61/include/hal/uart_ll.h @@ -29,6 +29,8 @@ extern "C" { #define UART_LL_FIFO_DEF_LEN (SOC_UART_FIFO_LEN) // Get UART hardware instance with giving uart num #define UART_LL_GET_HW(num) (((num) == UART_NUM_0) ? (&UART0) : (((num) == UART_NUM_1) ? (&UART1) : (&UART2))) +// Get UART sleep clock with giving uart num +#define UART_LL_SLEEP_CLOCK(num) (((num) == UART_NUM_0) ? (ESP_SLEEP_CLOCK_UART0) : (((num) == UART_NUM_1) ? (ESP_SLEEP_CLOCK_UART1) : (ESP_SLEEP_CLOCK_UART2))) #define UART_LL_PULSE_TICK_CNT_MAX UART_LOWPULSE_MIN_CNT_V diff --git a/components/hal/esp32h2/include/hal/uart_ll.h b/components/hal/esp32h2/include/hal/uart_ll.h index 9189071f4d..55c1cd9a3b 100644 --- a/components/hal/esp32h2/include/hal/uart_ll.h +++ b/components/hal/esp32h2/include/hal/uart_ll.h @@ -28,6 +28,8 @@ extern "C" { #define UART_LL_FIFO_DEF_LEN (SOC_UART_FIFO_LEN) // Get UART hardware instance with giving uart num #define UART_LL_GET_HW(num) (((num) == UART_NUM_0) ? (&UART0) : (&UART1)) +// Get UART sleep clock with giving uart num +#define UART_LL_SLEEP_CLOCK(num) (((num) == UART_NUM_0) ? (ESP_SLEEP_CLOCK_UART0) : (ESP_SLEEP_CLOCK_UART1)) #define UART_LL_PULSE_TICK_CNT_MAX UART_LOWPULSE_MIN_CNT_V diff --git a/examples/system/light_sleep/main/Kconfig.projbuild b/examples/system/light_sleep/main/Kconfig.projbuild new file mode 100644 index 0000000000..698e25a5d8 --- /dev/null +++ b/examples/system/light_sleep/main/Kconfig.projbuild @@ -0,0 +1,33 @@ +menu "Example Configuration" + + choice EXAMPLE_UART_WAKEUP_MODE + prompt "uart wakeup mode" + default UART_WK_MODE_ACTIVE_THRESH + help + Uart wakeup MODE_ACTIVE_THRESH | MODE_FIFO_THRESH | MODE_START_BIT | MODE_CHAR_SEQ to be selected + for uart wakeup during light sleep. Specifically, wakeup MODE_ACTIVE_THRESH doesn't require a clock. + In contrast, the other three wakeup modes need XTAL (not RC FAST for instability) ungated during light + sleep, where the chips used should support sleep clock icg control. However, they consume more power + as XTAL must be powered on. + + config UART_WK_MODE_ACTIVE_THRESH + bool "MODE_ACTIVE_THRESH" + config UART_WK_MODE_FIFO_THRESH + bool "MODE_FIFO_THRESH" + depends on SOC_UART_WAKEUP_SUPPORT_FIFO_THRESH_MODE && SOC_PM_SUPPORT_PMU_CLK_ICG && SOC_PMU_SUPPORTED + config UART_WK_MODE_START_BIT + bool "MODE_START_BIT" + depends on SOC_UART_WAKEUP_SUPPORT_START_BIT_MODE && SOC_PM_SUPPORT_PMU_CLK_ICG && SOC_PMU_SUPPORTED + config UART_WK_MODE_CHAR_SEQ + bool "MODE_CHAR_SEQ" + depends on SOC_UART_WAKEUP_SUPPORT_CHAR_SEQ_MODE && SOC_PM_SUPPORT_PMU_CLK_ICG && SOC_PMU_SUPPORTED + endchoice + + config EXAMPLE_UART_WAKEUP_MODE_SELCTED + int + default 0 if UART_WK_MODE_ACTIVE_THRESH + default 1 if UART_WK_MODE_FIFO_THRESH + default 2 if UART_WK_MODE_START_BIT + default 3 if UART_WK_MODE_CHAR_SEQ + +endmenu diff --git a/examples/system/light_sleep/main/uart_wakeup.c b/examples/system/light_sleep/main/uart_wakeup.c index a5753b43d6..6bba8e2798 100644 --- a/examples/system/light_sleep/main/uart_wakeup.c +++ b/examples/system/light_sleep/main/uart_wakeup.c @@ -1,14 +1,17 @@ /* - * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ +#include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_check.h" +#include "esp_err.h" #include "esp_sleep.h" #include "soc/uart_pins.h" #include "driver/uart.h" +#include "driver/uart_wakeup.h" #include "driver/gpio.h" #include "sdkconfig.h" @@ -18,7 +21,10 @@ #define EXAMPLE_UART_TX_IO_NUM U0TXD_GPIO_NUM #define EXAMPLE_UART_RX_IO_NUM U0RXD_GPIO_NUM -#define EXAMPLE_UART_WAKEUP_THRESHOLD 3 +#define EXAMPLE_UART_WAKEUP_EDGE_THRESHOLD 3 +#define EXAMPLE_UART_WAKEUP_FIFO_THRESHOLD 8 +#define EXAMPLE_UART_WAKEUP_CHARS_SEQ "ok" +#define EXAMPLE_UART_WAKEUP_CHARS_SEQ_LEN SOC_UART_WAKEUP_CHARS_SEQ_MAX_LEN #define EXAMPLE_READ_BUF_SIZE 1024 #define EXAMPLE_UART_BUF_SIZE (EXAMPLE_READ_BUF_SIZE * 2) @@ -123,12 +129,45 @@ static esp_err_t uart_initialization(void) static esp_err_t uart_wakeup_config(void) { - /* UART will wakeup the chip up from light sleep if the edges that RX pin received has reached the threshold */ - ESP_RETURN_ON_ERROR(uart_set_wakeup_threshold(EXAMPLE_UART_NUM, EXAMPLE_UART_WAKEUP_THRESHOLD), - TAG, "Set uart wakeup threshold failed"); - /* Only uart0 and uart1 (if has) support to be configured as wakeup source */ - ESP_RETURN_ON_ERROR(esp_sleep_enable_uart_wakeup(EXAMPLE_UART_NUM), - TAG, "Configure uart as wakeup source failed"); + uart_wakeup_cfg_t uart_wakeup_cfg = {}; + uint8_t wakeup_mode = CONFIG_EXAMPLE_UART_WAKEUP_MODE_SELCTED; + switch (wakeup_mode) { + /* UART will wakeup the chip up from light sleep if the edges that RX pin received reaches the threshold */ +#if SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE + case UART_WK_MODE_ACTIVE_THRESH: + uart_wakeup_cfg.wakeup_mode = UART_WK_MODE_ACTIVE_THRESH; + uart_wakeup_cfg.rx_edge_threshold = EXAMPLE_UART_WAKEUP_EDGE_THRESHOLD; + break; +#endif + /* UART will wakeup the chip up from light sleep if the number of chars that RX FIFO received reaches the threshold */ +#if SOC_UART_WAKEUP_SUPPORT_FIFO_THRESH_MODE + case UART_WK_MODE_FIFO_THRESH: + uart_wakeup_cfg.wakeup_mode = UART_WK_MODE_FIFO_THRESH; + uart_wakeup_cfg.rx_fifo_threshold = EXAMPLE_UART_WAKEUP_FIFO_THRESHOLD; + break; +#endif + /* UART will wakeup the chip up from light sleep if RX FIFO receives a start bit */ +#if SOC_UART_WAKEUP_SUPPORT_START_BIT_MODE + case UART_WK_MODE_START_BIT: + uart_wakeup_cfg.wakeup_mode = UART_WK_MODE_START_BIT; + break; +#endif + /* UART will wakeup the chip up from light sleep if the chars sequence that RX FIFO received matches the predefined value */ +#if SOC_UART_WAKEUP_SUPPORT_CHAR_SEQ_MODE + case UART_WK_MODE_CHAR_SEQ: + uart_wakeup_cfg.wakeup_mode = UART_WK_MODE_CHAR_SEQ; + // uart wakeup chars len need less than SOC_UART_WAKEUP_CHARS_SEQ_MAX_LEN + strncpy(uart_wakeup_cfg.wake_chars_seq, EXAMPLE_UART_WAKEUP_CHARS_SEQ, EXAMPLE_UART_WAKEUP_CHARS_SEQ_LEN); + break; +#endif + default: + ESP_LOGE(TAG, "Unknown UART wakeup mode"); + return ESP_FAIL; + break; + } + + ESP_ERROR_CHECK(uart_wakeup_setup(EXAMPLE_UART_NUM, &uart_wakeup_cfg)); + ESP_ERROR_CHECK(esp_sleep_enable_uart_wakeup(EXAMPLE_UART_NUM)); return ESP_OK; }