Merge branch 'feat/support_hp_uarts_wakeup_modes_during_light_sleep' into 'master'

support hp uarts wakeup modes during light sleep

See merge request espressif/esp-idf!37730
This commit is contained in:
Li Shuai
2025-04-22 17:49:02 +08:00
15 changed files with 627 additions and 18 deletions

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
)

View File

@@ -0,0 +1,424 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/param.h>
#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

View File

@@ -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)

View File

@@ -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 | \

View File

@@ -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;

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 <string.h>
#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;
}