Merge branch 'feature/add_uart_io_deinit_process_v5.4' into 'release/v5.4'

fix(uart): eliminate garbled data on TX/RX line in sleep (v5.4)

See merge request espressif/esp-idf!39263
This commit is contained in:
Jiang Jiang Jian
2025-05-29 13:50:38 +08:00
9 changed files with 165 additions and 54 deletions

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -32,6 +32,8 @@
#include "hal/dedic_gpio_ll.h"
#endif
#define DEDIC_GPIO_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
static const char *TAG = "dedic_gpio";
typedef struct dedic_gpio_platform_t dedic_gpio_platform_t;
@@ -74,7 +76,7 @@ static esp_err_t dedic_gpio_build_platform(int core_id)
// prevent building platform concurrently
_lock_acquire(&s_platform_mutexlock[core_id]);
if (!s_platform[core_id]) {
s_platform[core_id] = calloc(1, sizeof(dedic_gpio_platform_t));
s_platform[core_id] = (dedic_gpio_platform_t *)heap_caps_calloc(1, sizeof(dedic_gpio_platform_t), DEDIC_GPIO_MEM_ALLOC_CAPS);
if (s_platform[core_id]) {
// initialize platform members
s_platform[core_id]->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
@@ -212,7 +214,7 @@ esp_err_t dedic_gpio_new_bundle(const dedic_gpio_bundle_config_t *config, dedic_
ESP_GOTO_ON_ERROR(dedic_gpio_build_platform(core_id), err, TAG, "build platform %d failed", core_id);
size_t bundle_size = sizeof(dedic_gpio_bundle_t) + config->array_size * sizeof(config->gpio_array[0]);
bundle = calloc(1, bundle_size);
bundle = (dedic_gpio_bundle_t *)heap_caps_calloc(1, bundle_size, DEDIC_GPIO_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(bundle, ESP_ERR_NO_MEM, err, TAG, "no mem for bundle");
// for performance reasons, we only search for continuous channels

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -32,6 +32,9 @@
#include "driver/rtc_io.h"
#include "soc/rtc_io_channel.h"
#include "driver/lp_io.h"
#if SOC_LP_GPIO_MATRIX_SUPPORTED
#include "soc/lp_gpio_pins.h"
#endif
#endif
#if I2C_USE_RETENTION_LINK
#include "esp_private/sleep_retention.h"
@@ -427,8 +430,8 @@ esp_err_t i2c_common_deinit_pins(i2c_bus_handle_t handle)
ESP_RETURN_ON_ERROR(rtc_gpio_deinit(handle->sda_num), TAG, "deinit rtc gpio failed");
ESP_RETURN_ON_ERROR(rtc_gpio_deinit(handle->scl_num), TAG, "deinit rtc gpio failed");
#if SOC_LP_GPIO_MATRIX_SUPPORTED
lp_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, i2c_periph_signal[port_id].scl_in_sig, 0);
lp_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, i2c_periph_signal[port_id].sda_in_sig, 0);
lp_gpio_connect_in_signal(LP_GPIO_MATRIX_CONST_ZERO_INPUT, i2c_periph_signal[port_id].scl_in_sig, 0);
lp_gpio_connect_in_signal(LP_GPIO_MATRIX_CONST_ZERO_INPUT, i2c_periph_signal[port_id].sda_in_sig, 0);
#endif
}
#endif

View File

@@ -34,7 +34,8 @@ extern "C" {
* @brief UART configuration parameters for uart_param_config function
*/
typedef struct {
int baud_rate; /*!< UART baud rate*/
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*/
@@ -225,7 +226,9 @@ esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode);
esp_err_t uart_get_sclk_freq(uart_sclk_t sclk, uint32_t* out_freq_hz);
/**
* @brief Set UART baud rate.
* @brief Set desired UART baud rate.
*
* Note that the actual baud rate set could have a slight deviation from the user-configured value due to rounding error.
*
* @param uart_num UART port number, the max port number is (UART_NUM_MAX -1).
* @param baudrate UART baud rate.
@@ -237,7 +240,9 @@ esp_err_t uart_get_sclk_freq(uart_sclk_t sclk, uint32_t* out_freq_hz);
esp_err_t uart_set_baudrate(uart_port_t uart_num, uint32_t baudrate);
/**
* @brief Get the UART baud rate configuration.
* @brief Get the actual UART baud rate.
*
* It returns the real UART rate set in the hardware. It could have a slight deviation from the user-configured baud rate.
*
* @param uart_num UART port number, the max port number is (UART_NUM_MAX -1).
* @param baudrate Pointer to accept value of UART baud rate

View File

@@ -36,6 +36,9 @@
#include "driver/rtc_io.h"
#include "hal/rtc_io_ll.h"
#include "driver/lp_io.h"
#if SOC_LP_GPIO_MATRIX_SUPPORTED
#include "soc/lp_gpio_pins.h"
#endif
#endif
#include "clk_ctrl_os.h"
#include "esp_pm.h"
@@ -109,6 +112,11 @@ static const char *UART_TAG = "uart";
.hal.dev = UART_LL_GET_HW(uart_num), \
INIT_CRIT_SECTION_LOCK_IN_STRUCT(spinlock) \
.hw_enabled = false, \
.tx_io_num = -1, \
.rx_io_num = -1, \
.rts_io_num = -1, \
.cts_io_num = -1, \
.io_reserved_mask = 0, \
}
typedef struct {
@@ -170,6 +178,11 @@ typedef struct {
uart_hal_context_t hal; /*!< UART hal context*/
DECLARE_CRIT_SECTION_LOCK_IN_STRUCT(spinlock)
bool hw_enabled;
int tx_io_num;
int rx_io_num;
int rts_io_num;
int cts_io_num;
uint64_t io_reserved_mask;
} uart_context_t;
static uart_obj_t *p_uart_obj[UART_NUM_MAX] = {0};
@@ -707,8 +720,72 @@ static bool uart_try_set_iomux_pin(uart_port_t uart_num, int io_num, uint32_t id
return true;
}
//internal signal can be output to multiple GPIO pads
//only one GPIO pad can connect with input signal
static void uart_release_pin(uart_port_t uart_num)
{
if (uart_num >= UART_NUM_MAX) {
return;
}
if (uart_context[uart_num].tx_io_num >= 0) {
gpio_output_disable(uart_context[uart_num].tx_io_num);
#if (SOC_UART_LP_NUM >= 1)
if (!(uart_num < SOC_UART_HP_NUM)) {
rtc_gpio_deinit(uart_context[uart_num].tx_io_num);
}
#endif
#if CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND || CONFIG_PM_SLP_DISABLE_GPIO
gpio_sleep_sel_en(uart_context[uart_num].tx_io_num); // re-enable the switch to the sleep configuration to save power consumption
#endif
}
if (uart_context[uart_num].rx_io_num >= 0) {
if (uart_num < SOC_UART_HP_NUM) {
esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), false);
}
#if (SOC_UART_LP_NUM >= 1)
else {
#if SOC_LP_GPIO_MATRIX_SUPPORTED
lp_gpio_connect_in_signal(LP_GPIO_MATRIX_CONST_ONE_INPUT, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), false);
#endif
rtc_gpio_deinit(uart_context[uart_num].rx_io_num);
}
#endif
#if CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND || CONFIG_PM_SLP_DISABLE_GPIO
gpio_sleep_sel_en(uart_context[uart_num].rx_io_num); // re-enable the switch to the sleep configuration to save power consumption
#endif
}
if (uart_context[uart_num].rts_io_num >= 0) {
gpio_output_disable(uart_context[uart_num].rts_io_num);
#if (SOC_UART_LP_NUM >= 1)
if (!(uart_num < SOC_UART_HP_NUM)) {
rtc_gpio_deinit(uart_context[uart_num].rts_io_num);
}
#endif
}
if (uart_context[uart_num].cts_io_num >= 0) {
if (uart_num < SOC_UART_HP_NUM) {
esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, UART_PERIPH_SIGNAL(uart_num, SOC_UART_CTS_PIN_IDX), false);
}
#if (SOC_UART_LP_NUM >= 1)
else {
#if SOC_LP_GPIO_MATRIX_SUPPORTED
lp_gpio_connect_in_signal(LP_GPIO_MATRIX_CONST_ZERO_INPUT, UART_PERIPH_SIGNAL(uart_num, SOC_UART_CTS_PIN_IDX), false);
#endif
rtc_gpio_deinit(uart_context[uart_num].cts_io_num);
}
#endif
}
esp_gpio_revoke(uart_context[uart_num].io_reserved_mask);
uart_context[uart_num].tx_io_num = -1;
uart_context[uart_num].rx_io_num = -1;
uart_context[uart_num].rts_io_num = -1;
uart_context[uart_num].cts_io_num = -1;
uart_context[uart_num].io_reserved_mask = 0;
}
esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num)
{
ESP_RETURN_ON_FALSE((uart_num >= 0), ESP_FAIL, UART_TAG, "uart_num error");
@@ -740,6 +817,9 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r
}
#endif
// First, release previously configured IOs if there is
uart_release_pin(uart_num);
// Potential IO reserved mask
uint64_t io_reserve_mask = 0;
io_reserve_mask |= (tx_io_num > 0 ? BIT64(tx_io_num) : 0);
@@ -752,42 +832,60 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r
bool tx_rx_same_io = (tx_io_num == rx_io_num);
/* In the following statements, if the io_num is negative, no need to configure anything. */
if (tx_io_num >= 0 && (tx_rx_same_io || !uart_try_set_iomux_pin(uart_num, tx_io_num, SOC_UART_TX_PIN_IDX))) {
if (uart_num < SOC_UART_HP_NUM) {
gpio_func_sel(tx_io_num, PIN_FUNC_GPIO);
esp_rom_gpio_connect_out_signal(tx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_TX_PIN_IDX), 0, 0);
// output enable is set inside esp_rom_gpio_connect_out_signal func after the signal is connected
// (output enabled too early may cause unnecessary level change at the pad)
}
#if SOC_LP_GPIO_MATRIX_SUPPORTED
else {
rtc_gpio_init(tx_io_num); // set as a LP_GPIO pin
lp_gpio_connect_out_signal(tx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_TX_PIN_IDX), 0, 0);
// output enable is set inside lp_gpio_connect_out_signal func after the signal is connected
}
if (tx_io_num >= 0) {
uart_context[uart_num].tx_io_num = tx_io_num;
#if CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND || CONFIG_PM_SLP_DISABLE_GPIO
// In such case, IOs are going to switch to sleep configuration (isolate) when entering sleep for power saving reason
// But TX IO in isolate state could write garbled data to the other end
// Therefore, we should disable the switch of the TX pin to sleep configuration
gpio_sleep_sel_dis(tx_io_num);
#endif
}
if (rx_io_num >= 0 && (tx_rx_same_io || !uart_try_set_iomux_pin(uart_num, rx_io_num, SOC_UART_RX_PIN_IDX))) {
io_reserve_mask &= ~BIT64(rx_io_num); // input IO via GPIO matrix does not need to be reserved
if (uart_num < SOC_UART_HP_NUM) {
gpio_input_enable(rx_io_num);
esp_rom_gpio_connect_in_signal(rx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), 0);
}
#if SOC_LP_GPIO_MATRIX_SUPPORTED
else {
rtc_gpio_mode_t mode = (tx_rx_same_io ? RTC_GPIO_MODE_INPUT_OUTPUT : RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_set_direction(rx_io_num, mode);
if (!tx_rx_same_io) { // set the same pin again as a LP_GPIO will overwrite connected out_signal, not desired, so skip
rtc_gpio_init(rx_io_num); // set as a LP_GPIO pin
if (tx_rx_same_io || !uart_try_set_iomux_pin(uart_num, tx_io_num, SOC_UART_TX_PIN_IDX)) {
if (uart_num < SOC_UART_HP_NUM) {
gpio_func_sel(tx_io_num, PIN_FUNC_GPIO);
esp_rom_gpio_connect_out_signal(tx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_TX_PIN_IDX), 0, 0);
// output enable is set inside esp_rom_gpio_connect_out_signal func after the signal is connected
// (output enabled too early may cause unnecessary level change at the pad)
}
#if SOC_LP_GPIO_MATRIX_SUPPORTED
else {
rtc_gpio_init(tx_io_num); // set as a LP_GPIO pin
lp_gpio_connect_out_signal(tx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_TX_PIN_IDX), 0, 0);
// output enable is set inside lp_gpio_connect_out_signal func after the signal is connected
}
lp_gpio_connect_in_signal(rx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), 0);
}
#endif
}
}
if (rts_io_num >= 0 && !uart_try_set_iomux_pin(uart_num, rts_io_num, SOC_UART_RTS_PIN_IDX)) {
if (rx_io_num >= 0) {
uart_context[uart_num].rx_io_num = rx_io_num;
#if CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND || CONFIG_PM_SLP_DISABLE_GPIO
// In such case, IOs are going to switch to sleep configuration (isolate) when entering sleep for power saving reason
// But RX IO in isolate state could receive garbled data into FIFO, which is not desired
// Therefore, we should disable the switch of the RX pin to sleep configuration
gpio_sleep_sel_dis(rx_io_num);
#endif
if (tx_rx_same_io || !uart_try_set_iomux_pin(uart_num, rx_io_num, SOC_UART_RX_PIN_IDX)) {
io_reserve_mask &= ~BIT64(rx_io_num); // input IO via GPIO matrix does not need to be reserved
if (uart_num < SOC_UART_HP_NUM) {
gpio_input_enable(rx_io_num);
esp_rom_gpio_connect_in_signal(rx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), 0);
}
#if SOC_LP_GPIO_MATRIX_SUPPORTED
else {
rtc_gpio_mode_t mode = (tx_rx_same_io ? RTC_GPIO_MODE_INPUT_OUTPUT : RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_set_direction(rx_io_num, mode);
if (!tx_rx_same_io) { // set the same pin again as a LP_GPIO will overwrite connected out_signal, not desired, so skip
rtc_gpio_init(rx_io_num); // set as a LP_GPIO pin
}
lp_gpio_connect_in_signal(rx_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), 0);
}
#endif
}
}
if (rts_io_num >= 0 && (uart_context[uart_num].rts_io_num = rts_io_num, !uart_try_set_iomux_pin(uart_num, rts_io_num, SOC_UART_RTS_PIN_IDX))) {
if (uart_num < SOC_UART_HP_NUM) {
gpio_func_sel(rts_io_num, PIN_FUNC_GPIO);
esp_rom_gpio_connect_out_signal(rts_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RTS_PIN_IDX), 0, 0);
@@ -802,7 +900,7 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r
#endif
}
if (cts_io_num >= 0 && !uart_try_set_iomux_pin(uart_num, cts_io_num, SOC_UART_CTS_PIN_IDX)) {
if (cts_io_num >= 0 && (uart_context[uart_num].cts_io_num = cts_io_num, !uart_try_set_iomux_pin(uart_num, cts_io_num, SOC_UART_CTS_PIN_IDX))) {
io_reserve_mask &= ~BIT64(cts_io_num); // input IO via GPIO matrix does not need to be reserved
if (uart_num < SOC_UART_HP_NUM) {
gpio_pullup_en(cts_io_num);
@@ -819,6 +917,7 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r
}
// IO reserve
uart_context[uart_num].io_reserved_mask = io_reserve_mask;
uint64_t old_busy_mask = esp_gpio_reserve(io_reserve_mask);
uint64_t conflict_mask = old_busy_mask & io_reserve_mask;
while (conflict_mask > 0) {
@@ -1801,6 +1900,9 @@ esp_err_t uart_driver_delete(uart_port_t uart_num)
ESP_LOGI(UART_TAG, "ALREADY NULL");
return ESP_OK;
}
uart_release_pin(uart_num);
esp_intr_free(p_uart_obj[uart_num]->intr_handle);
uart_disable_rx_intr(uart_num);
uart_disable_tx_intr(uart_num);

View File

@@ -880,7 +880,7 @@ static int uart_tcgetattr(int fd, struct termios *p)
}
{
uint32_t baudrate;
uint32_t baudrate = 0;
if (uart_get_baudrate(fd, &baudrate) != ESP_OK) {
errno = EINVAL;
return -1;

View File

@@ -563,13 +563,9 @@ static void IRAM_ATTR resume_timers(uint32_t sleep_flags) {
static void IRAM_ATTR flush_uarts(void)
{
for (int i = 0; i < SOC_UART_HP_NUM; ++i) {
#ifdef CONFIG_IDF_TARGET_ESP32
esp_rom_output_tx_wait_idle(i);
#else
if (uart_ll_is_enabled(i)) {
esp_rom_output_tx_wait_idle(i);
}
#endif
}
}
@@ -583,11 +579,9 @@ FORCE_INLINE_ATTR void suspend_uarts(void)
{
s_suspended_uarts_bmap = 0;
for (int i = 0; i < SOC_UART_HP_NUM; ++i) {
#ifndef CONFIG_IDF_TARGET_ESP32
if (!uart_ll_is_enabled(i)) {
continue;
}
#endif
uart_ll_force_xoff(i);
s_suspended_uarts_bmap |= BIT(i);
#if SOC_UART_SUPPORT_FSM_TX_WAIT_SEND

View File

@@ -107,12 +107,14 @@ static inline void uart_ll_reset_register(uart_port_t uart_num)
// ESP32C3 requires a workaround: enable core reset before enabling uart module clock to prevent uart output garbage value
switch (uart_num) {
case 0:
SYSTEM.perip_rst_en0.reg_uart_rst = 0;
UART0.clk_conf.rst_core = 1;
SYSTEM.perip_rst_en0.reg_uart_rst = 1;
SYSTEM.perip_rst_en0.reg_uart_rst = 0;
UART0.clk_conf.rst_core = 0;
break;
case 1:
SYSTEM.perip_rst_en0.reg_uart1_rst = 0;
UART1.clk_conf.rst_core = 1;
SYSTEM.perip_rst_en0.reg_uart1_rst = 1;
SYSTEM.perip_rst_en0.reg_uart1_rst = 0;

View File

@@ -112,18 +112,21 @@ static inline void uart_ll_reset_register(uart_port_t uart_num)
// ESP32S3 requires a workaround: enable core reset before enabling uart module clock to prevent uart output garbage value
switch (uart_num) {
case 0:
SYSTEM.perip_rst_en0.uart_rst = 0;
UART0.clk_conf.rst_core = 1;
SYSTEM.perip_rst_en0.uart_rst = 1;
SYSTEM.perip_rst_en0.uart_rst = 0;
UART0.clk_conf.rst_core = 0;
break;
case 1:
SYSTEM.perip_rst_en0.uart1_rst = 0;
UART1.clk_conf.rst_core = 1;
SYSTEM.perip_rst_en0.uart1_rst = 1;
SYSTEM.perip_rst_en0.uart1_rst = 0;
UART1.clk_conf.rst_core = 0;
break;
case 2:
SYSTEM.perip_rst_en1.uart2_rst = 0;
UART2.clk_conf.rst_core = 1;
SYSTEM.perip_rst_en1.uart2_rst = 1;
SYSTEM.perip_rst_en1.uart2_rst = 0;

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -264,7 +264,7 @@ static void parse_rmc(esp_gps_t *esp_gps)
}
break;
case 7: /* Process ground speed in unit m/s */
esp_gps->parent.speed = strtof(esp_gps->item_str, NULL) * 1.852;
esp_gps->parent.speed = strtof(esp_gps->item_str, NULL) * 0.514444;
break;
case 8: /* Process true course over ground */
esp_gps->parent.cog = strtof(esp_gps->item_str, NULL);
@@ -338,7 +338,7 @@ static void parse_vtg(esp_gps_t *esp_gps)
esp_gps->parent.variation = strtof(esp_gps->item_str, NULL);
break;
case 5:/* Process ground speed in unit m/s */
esp_gps->parent.speed = strtof(esp_gps->item_str, NULL) * 1.852;//knots to m/s
esp_gps->parent.speed = strtof(esp_gps->item_str, NULL) * 0.514444;//knots to m/s
break;
case 7:/* Process ground speed in unit m/s */
esp_gps->parent.speed = strtof(esp_gps->item_str, NULL) / 3.6;//km/h to m/s
@@ -702,7 +702,7 @@ nmea_parser_handle_t nmea_parser_init(const nmea_parser_config_t *config)
.task_name = NULL
};
if (esp_event_loop_create(&loop_args, &esp_gps->event_loop_hdl) != ESP_OK) {
ESP_LOGE(GPS_TAG, "create event loop faild");
ESP_LOGE(GPS_TAG, "create event loop failed");
goto err_eloop;
}
/* Create NMEA Parser task */