Merge branch 'bugfix/release_console_uart_pins' into 'master'

fix(console): release default console UART pins if console is switched in bootloader

Closes IDFGH-15851

See merge request espressif/esp-idf!40578
This commit is contained in:
Song Ruo Jing
2025-07-30 19:27:47 +08:00
10 changed files with 61 additions and 55 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
*/
@@ -8,9 +8,8 @@
#include "bootloader_console.h"
#include "soc/soc_caps.h"
#include "soc/uart_periph.h"
#include "soc/uart_channel.h"
#include "soc/uart_pins.h"
#include "soc/io_mux_reg.h"
#include "soc/gpio_periph.h"
#include "soc/gpio_sig_map.h"
#include "soc/rtc.h"
#include "hal/gpio_ll.h"
@@ -27,9 +26,20 @@
#include "esp_rom_sys.h"
#include "esp_rom_caps.h"
static void __attribute__((unused)) release_default_console_io(void)
{
// Default console is UART0 with TX and RX on their IOMUX pins
gpio_ll_output_disable(&GPIO, U0TXD_GPIO_NUM);
esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, UART_PERIPH_SIGNAL(UART_NUM_0, SOC_UART_RX_PIN_IDX), 0);
}
#ifdef CONFIG_ESP_CONSOLE_NONE
void bootloader_console_init(void)
{
// Wait for UART FIFO to be empty.
esp_rom_output_tx_wait_idle(0);
release_default_console_io();
esp_rom_install_channel_putc(1, NULL);
esp_rom_install_channel_putc(2, NULL);
}
@@ -49,8 +59,8 @@ void bootloader_console_init(void)
#if CONFIG_ESP_CONSOLE_UART_CUSTOM
// Some constants to make the following code less upper-case
const int uart_tx_gpio = (CONFIG_ESP_CONSOLE_UART_TX_GPIO >= 0) ? CONFIG_ESP_CONSOLE_UART_TX_GPIO : UART_NUM_0_TXD_DIRECT_GPIO_NUM;
const int uart_rx_gpio = (CONFIG_ESP_CONSOLE_UART_RX_GPIO >= 0) ? CONFIG_ESP_CONSOLE_UART_RX_GPIO : UART_NUM_0_RXD_DIRECT_GPIO_NUM;
const int uart_tx_gpio = (CONFIG_ESP_CONSOLE_UART_TX_GPIO >= 0) ? CONFIG_ESP_CONSOLE_UART_TX_GPIO : U0TXD_GPIO_NUM;
const int uart_rx_gpio = (CONFIG_ESP_CONSOLE_UART_RX_GPIO >= 0) ? CONFIG_ESP_CONSOLE_UART_RX_GPIO : U0RXD_GPIO_NUM;
// Switch to the new UART (this just changes UART number used for esp_rom_printf in ROM code).
esp_rom_output_set_as_console(uart_num);
@@ -58,11 +68,9 @@ void bootloader_console_init(void)
// If console is attached to UART1 or if non-default pins are used,
// need to reconfigure pins using GPIO matrix
if (uart_num != 0 ||
uart_tx_gpio != UART_NUM_0_TXD_DIRECT_GPIO_NUM ||
uart_rx_gpio != UART_NUM_0_RXD_DIRECT_GPIO_NUM) {
// Change default UART pins back to GPIOs
gpio_ll_func_sel(&GPIO, UART_NUM_0_RXD_DIRECT_GPIO_NUM, PIN_FUNC_GPIO);
gpio_ll_func_sel(&GPIO, UART_NUM_0_TXD_DIRECT_GPIO_NUM, PIN_FUNC_GPIO);
uart_tx_gpio != U0TXD_GPIO_NUM ||
uart_rx_gpio != U0RXD_GPIO_NUM) {
release_default_console_io();
// Route GPIO signals to/from pins
const uint32_t tx_idx = UART_PERIPH_SIGNAL(uart_num, SOC_UART_TX_PIN_IDX);
const uint32_t rx_idx = UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX);
@@ -102,6 +110,10 @@ static char s_usb_cdc_buf[ESP_ROM_CDC_ACM_WORK_BUF_MIN];
void bootloader_console_init(void)
{
// Wait for UART FIFO to be empty.
esp_rom_output_tx_wait_idle(0);
release_default_console_io();
#ifdef CONFIG_IDF_TARGET_ESP32S2
/* ESP32-S2 specific patch to set the correct serial number in the descriptor.
* Later chips don't need this.
@@ -121,6 +133,10 @@ void bootloader_console_init(void)
#ifdef CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
void bootloader_console_init(void)
{
// Wait for UART FIFO to be empty.
esp_rom_output_tx_wait_idle(0);
release_default_console_io();
esp_rom_output_switch_buffer(ESP_ROM_USB_SERIAL_DEVICE_NUM);
/* Switch console channel to avoid output on UART and allow */

View File

@@ -12,7 +12,7 @@
#include "driver/gpio_etm.h"
#include "driver/gpio.h"
TEST_CASE("gpio_etm_self_trigger", "[etm]")
TEST_CASE("gpio_etm_self_trigger", "[gpio_etm]")
{
// GPIO any edge ---> EMT channel ---> GPIO toggle
const uint32_t input_gpio = 0;
@@ -91,7 +91,7 @@ TEST_CASE("gpio_etm_self_trigger", "[etm]")
TEST_ESP_OK(esp_etm_del_channel(etm_channel_a));
}
TEST_CASE("gpio_etm_self_trigger_multi_action", "[etm]")
TEST_CASE("gpio_etm_self_trigger_multi_action", "[gpio_etm]")
{
// GPIO 0 pos edge event ---> GPIO 1 set level task
// GPIO 2 pos edge event ---> GPIO 1 clear level task

View File

@@ -3,6 +3,7 @@
import pytest
from pytest_embedded_idf import IdfDut
from pytest_embedded_idf.utils import idf_parametrize
from pytest_embedded_idf.utils import soc_filtered_targets
CONFIGS = [
'iram_safe',
@@ -14,7 +15,7 @@ CONFIGS = [
@pytest.mark.parametrize('config', CONFIGS, indirect=True)
@idf_parametrize(
'target',
['esp32c2', 'esp32c3', 'esp32c5', 'esp32c6', 'esp32c61', 'esp32h2', 'esp32s2', 'esp32s3', 'esp32p4'],
soc_filtered_targets('SOC_GPIO_SUPPORT_PIN_GLITCH_FILTER == 1 or SOC_GPIO_FLEX_GLITCH_FILTER_NUM > 0'),
indirect=['target'],
)
def test_gpio_filter(dut: IdfDut) -> None:
@@ -25,8 +26,19 @@ def test_gpio_filter(dut: IdfDut) -> None:
@pytest.mark.parametrize('config', CONFIGS, indirect=True)
@idf_parametrize(
'target',
['esp32c2', 'esp32c3', 'esp32c5', 'esp32c6', 'esp32c61', 'esp32h2', 'esp32s2', 'esp32s3', 'esp32p4'],
soc_filtered_targets('SOC_DEDICATED_GPIO_SUPPORTED == 1'),
indirect=['target'],
)
def test_dedic_gpio(dut: IdfDut) -> None:
dut.run_all_single_board_cases(group='dedic_gpio')
@pytest.mark.generic
@pytest.mark.parametrize('config', CONFIGS, indirect=True)
@idf_parametrize(
'target',
soc_filtered_targets('SOC_GPIO_SUPPORT_ETM == 1'),
indirect=['target'],
)
def test_gpio_etm(dut: IdfDut) -> None:
dut.run_all_single_board_cases(group='gpio_etm')

View File

@@ -22,7 +22,7 @@
#include "hal/gpio_hal.h"
#include "hal/rtc_io_hal.h"
#include "soc/rtc_io_periph.h"
#include "soc/uart_channel.h"
#include "soc/uart_pins.h"
#include "hal/rtc_hal.h"
@@ -93,11 +93,11 @@ void esp_sleep_enable_gpio_switch(bool enable)
if (GPIO_IS_VALID_GPIO(gpio_num)) {
#if CONFIG_ESP_CONSOLE_UART
#if CONFIG_ESP_CONSOLE_UART_CUSTOM
const int uart_tx_gpio = (CONFIG_ESP_CONSOLE_UART_TX_GPIO >= 0) ? CONFIG_ESP_CONSOLE_UART_TX_GPIO : UART_NUM_0_TXD_DIRECT_GPIO_NUM;
const int uart_rx_gpio = (CONFIG_ESP_CONSOLE_UART_RX_GPIO >= 0) ? CONFIG_ESP_CONSOLE_UART_RX_GPIO : UART_NUM_0_RXD_DIRECT_GPIO_NUM;
const int uart_tx_gpio = (CONFIG_ESP_CONSOLE_UART_TX_GPIO >= 0) ? CONFIG_ESP_CONSOLE_UART_TX_GPIO : U0TXD_GPIO_NUM;
const int uart_rx_gpio = (CONFIG_ESP_CONSOLE_UART_RX_GPIO >= 0) ? CONFIG_ESP_CONSOLE_UART_RX_GPIO : U0RXD_GPIO_NUM;
if ((gpio_num == uart_tx_gpio) || (gpio_num == uart_rx_gpio)) {
#else
if ((gpio_num == UART_NUM_0_TXD_DIRECT_GPIO_NUM) || (gpio_num == UART_NUM_0_RXD_DIRECT_GPIO_NUM)) {
if ((gpio_num == U0TXD_GPIO_NUM) || (gpio_num == U0RXD_GPIO_NUM)) {
#endif
gpio_sleep_sel_dis(gpio_num);
continue;

View File

@@ -98,6 +98,7 @@
#include "hal/cache_ll.h"
#include "hal/efuse_ll.h"
#include "hal/uart_ll.h"
#include "soc/uart_pins.h"
#include "hal/cpu_utility_ll.h"
#include "soc/periph_defs.h"
#include "esp_cpu.h"
@@ -761,6 +762,13 @@ NOINLINE_ATTR static void system_early_init(const soc_reset_reason_t *rst_reas)
_uart_ll_set_baudrate(UART_LL_GET_HW(CONFIG_ESP_CONSOLE_ROM_SERIAL_PORT_NUM), CONFIG_ESP_CONSOLE_UART_BAUDRATE, clock_hz);
#endif
int console_uart_tx_pin = U0TXD_GPIO_NUM;
int console_uart_rx_pin = U0RXD_GPIO_NUM;
#if CONFIG_ESP_CONSOLE_UART_CUSTOM
console_uart_tx_pin = (CONFIG_ESP_CONSOLE_UART_TX_GPIO >= 0) ? CONFIG_ESP_CONSOLE_UART_TX_GPIO : U0TXD_GPIO_NUM;
console_uart_rx_pin = (CONFIG_ESP_CONSOLE_UART_RX_GPIO >= 0) ? CONFIG_ESP_CONSOLE_UART_RX_GPIO : U0RXD_GPIO_NUM;
#endif
ESP_EARLY_LOGI(TAG, "GPIO %d and %d are used as console UART I/O pins", console_uart_rx_pin, console_uart_tx_pin);
#endif
#if SOC_DEEP_SLEEP_SUPPORTED

View File

@@ -15,7 +15,7 @@
#include "esp_private/uart_share_hw_ctrl.h"
#include "driver/uart.h"
#include "soc/uart_channel.h"
#include "soc/uart_pins.h"
#include <fcntl.h>
#include <unistd.h>
@@ -41,7 +41,7 @@ static void console_none_print(void)
ESP_ERROR_CHECK(uart_driver_install(CONSOLE_UART_NUM, 256, 0, 0, NULL, 0));
ESP_ERROR_CHECK(uart_param_config(CONSOLE_UART_NUM, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(CONSOLE_UART_NUM, UART_NUM_0_TXD_DIRECT_GPIO_NUM, UART_NUM_0_RXD_DIRECT_GPIO_NUM, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
ESP_ERROR_CHECK(uart_set_pin(CONSOLE_UART_NUM, U0TXD_GPIO_NUM, U0RXD_GPIO_NUM, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
// Configure a temporary buffer for the incoming data
uint8_t data[] = "This message will be printed even with CONFIG_ESP_CONSOLE_NONE\r\n";

View File

@@ -8,11 +8,5 @@
#pragma once
//UART channels
#define UART_GPIO16_DIRECT_CHANNEL UART_NUM_0
#define UART_NUM_0_TXD_DIRECT_GPIO_NUM 16
#define UART_GPIO15_DIRECT_CHANNEL UART_NUM_0
#define UART_NUM_0_RXD_DIRECT_GPIO_NUM 15
#define UART_TXD_GPIO16_DIRECT_CHANNEL UART_GPIO16_DIRECT_CHANNEL
#define UART_RXD_GPIO15_DIRECT_CHANNEL UART_GPIO15_DIRECT_CHANNEL

View File

@@ -4,15 +4,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
// This file defines GPIO lookup macros for available UART IO_MUX pins on ESP32C6.
// This file defines GPIO lookup macros for available UART IO_MUX pins on ESP32H4.
#pragma once
//UART channels
#define UART_GPIO16_DIRECT_CHANNEL UART_NUM_0
#define UART_NUM_0_TXD_DIRECT_GPIO_NUM 16
#define UART_GPIO17_DIRECT_CHANNEL UART_NUM_0
#define UART_NUM_0_RXD_DIRECT_GPIO_NUM 17
#define UART_TXD_GPIO16_DIRECT_CHANNEL UART_GPIO16_DIRECT_CHANNEL
#define UART_RXD_GPIO17_DIRECT_CHANNEL UART_GPIO17_DIRECT_CHANNEL
#define UART_NUM_0_TXD_DIRECT_GPIO_NUM 24
#define UART_NUM_0_RXD_DIRECT_GPIO_NUM 23

View File

@@ -441,15 +441,6 @@ API Reference
GPIO Lookup Macros
^^^^^^^^^^^^^^^^^^
The UART peripherals have dedicated IO_MUX pins to which they are connected directly. However, signals can also be routed to other pins using the less direct GPIO matrix. To use direct routes, you need to know which pin is a dedicated IO_MUX pin for a UART channel. GPIO Lookup Macros simplify the process of finding and assigning IO_MUX pins. You choose a macro based on either the IO_MUX pin number, or a required UART channel name, and the macro returns the matching counterpart for you. See some examples below.
.. note::
These macros are useful if you need very high UART baud rates (over 40 MHz), which means you will have to use IO_MUX pins only. In other cases, these macros can be ignored, and you can use the GPIO Matrix as it allows you to configure any GPIO pin for any UART function.
1. :c:macro:`UART_NUM_2_TXD_DIRECT_GPIO_NUM` returns the IO_MUX pin number of UART channel 2 TXD pin (pin 17)
2. :c:macro:`UART_GPIO19_DIRECT_CHANNEL` returns the UART number of GPIO 19 when connected to the UART peripheral via IO_MUX (this is UART_NUM_0)
3. :c:macro:`UART_CTS_GPIO19_DIRECT_CHANNEL` returns the UART number of GPIO 19 when used as the UART CTS pin via IO_MUX (this is UART_NUM_0). It is similar to the above macro but specifies the pin function which is also part of the IO_MUX assignment.
Some UART ports have dedicated IO_MUX pins to which they are connected directly. These can be useful if you need very high UART baud rates, which means you will have to use IO_MUX pins only. In other cases, any GPIO pin can be used for UART communication by routing the signals through the GPIO matrix. If the UART port has dedicated IO_MUX pins, :c:macro:`UART_NUM_x_TXD_DIRECT_GPIO_NUM` and :c:macro:`UART_NUM_x_RXD_DIRECT_GPIO_NUM` can be used to find the corresponding IO_MUX pin numbers.
.. include-build-file:: inc/uart_channel.inc

View File

@@ -441,15 +441,6 @@ API 参考
GPIO 查找宏指令
^^^^^^^^^^^^^^^^^^^^^^^^^^^
UART 外设有供直接连接的专用 IO_MUX 管脚,但也可用非直接的 GPIO 矩阵将信号配置到其他管脚。如要直接连接,需要知道哪一管脚为 UART 通道的专用 IO_MUX 管脚。GPIO 查找宏简化了查找和分配 IO_MUX 管脚的过程,可根据 IO_MUX 管脚编号或所需 UART 通道名称选择一个宏,该宏将返回匹配的对应项。请查看下列示例
.. note::
如需较高的 UART 波特率(超过 40 MHz即仅使用 IO_MUX 管脚时,可以使用此类宏。在其他情况下可以忽略这些宏,并使用 GPIO 矩阵为 UART 功能配置任一 GPIO 管脚。
1. :c:macro:`UART_NUM_2_TXD_DIRECT_GPIO_NUM` 返回 UART 通道 2 TXD 管脚的 IO_MUX 管脚编号(管脚 17
2. :c:macro:`UART_GPIO19_DIRECT_CHANNEL` 在通过 IO_MUX 连接到 UART 外设时返回 GPIO 19 的 UART 编号(即 UART_NUM_0
3. GPIO 19 在通过 IO_MUX 用作 UART CTS 管脚时,:c:macro:`UART_CTS_GPIO19_DIRECT_CHANNEL` 将返回 GPIO 19 的 UART 编号(即 UART_NUM_0。该宏类似于上述宏但指定了管脚功能这也是 IO_MUX 分配的一部分。
一些 UART 外设有供直接连接的专用 IO_MUX 管脚。这些管脚可用于需要极高 UART 波特率的场景,即仅可使用 IO_MUX 管脚。在其他情况下,任一 GPIO 管脚都可用于 UART 通信,只需将信号通过 GPIO 矩阵路由即可。当特定的 UART 外设有专用 IO_MUX 管脚时,:c:macro:`UART_NUM_x_TXD_DIRECT_GPIO_NUM`:c:macro:`UART_NUM_x_RXD_DIRECT_GPIO_NUM` 可用于查找对应的 IO_MUX 管脚编号
.. include-build-file:: inc/uart_channel.inc