From 4794ba564a1a1e4d63434cba3947a59ed707fb11 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Sat, 21 Jun 2025 16:49:56 -0300 Subject: [PATCH 1/6] feat(uart): fixes pin attach for any IDF 5.x --- cores/esp32/esp32-hal-uart.c | 131 +++++++++++++++++++++++++++++++++-- 1 file changed, 126 insertions(+), 5 deletions(-) diff --git a/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index 3c9e6bf178b..5e8ba6c1988 100644 --- a/cores/esp32/esp32-hal-uart.c +++ b/cores/esp32/esp32-hal-uart.c @@ -295,6 +295,125 @@ static bool _uartDetachBus_RTS(void *busptr) { return _uartDetachPins(bus->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, bus->_rtsPin); } +static bool _uartTrySetIomuxPin(uart_port_t uart_num, int io_num, uint32_t idx) { + // Store a pointer to the default pin, to optimize access to its fields. + const uart_periph_sig_t *upin = &uart_periph_signal[uart_num].pins[idx]; + + // In theory, if default_gpio is -1, iomux_func should also be -1, but let's be safe and test both. + if (upin->iomux_func == -1 || upin->default_gpio == -1 || upin->default_gpio != io_num) { + return false; + } + + // Assign the correct funct to the GPIO. + assert(upin->iomux_func != -1); + if (uart_num < SOC_UART_HP_NUM) { + gpio_iomux_out(io_num, upin->iomux_func, false); + // If the pin is input, we also have to redirect the signal, in order to bypasse the GPIO matrix. + if (upin->input) { + gpio_iomux_in(io_num, upin->signal); + } + } +#if (SOC_UART_LP_NUM >= 1) && (SOC_RTCIO_PIN_COUNT >= 1) + else { + if (upin->input) { + rtc_gpio_set_direction(io_num, RTC_GPIO_MODE_INPUT_ONLY); + } else { + rtc_gpio_set_direction(io_num, RTC_GPIO_MODE_OUTPUT_ONLY); + } + rtc_gpio_init(io_num); + rtc_gpio_iomux_func_sel(io_num, upin->iomux_func); + } +#endif + return true; +} + +static esp_err_t _uartInternalSetPin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num) { + // Since an IO cannot route peripheral signals via IOMUX and GPIO matrix at the same time, + // if tx and rx share the same IO, both signals need to be route to IOs through GPIO matrix + 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) { +#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 (tx_rx_same_io || !_uartTrySetIomuxPin(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 + } +#endif + } + } + + if (rx_io_num >= 0) { +#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 || !_uartTrySetIomuxPin(uart_num, rx_io_num, SOC_UART_RX_PIN_IDX)) { + 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 && !_uartTrySetIomuxPin(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); + // output enable is set inside esp_rom_gpio_connect_out_signal func after the signal is connected + } +#if SOC_LP_GPIO_MATRIX_SUPPORTED + else { + rtc_gpio_init(rts_io_num); // set as a LP_GPIO pin + lp_gpio_connect_out_signal(rts_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RTS_PIN_IDX), 0, 0); + // output enable is set inside lp_gpio_connect_out_signal func after the signal is connected + } +#endif + } + + if (cts_io_num >= 0 && !_uartTrySetIomuxPin(uart_num, cts_io_num, SOC_UART_CTS_PIN_IDX)) { + if (uart_num < SOC_UART_HP_NUM) { + gpio_pullup_en(cts_io_num); + gpio_input_enable(cts_io_num); + esp_rom_gpio_connect_in_signal(cts_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_CTS_PIN_IDX), 0); + } +#if SOC_LP_GPIO_MATRIX_SUPPORTED + else { + rtc_gpio_set_direction(cts_io_num, RTC_GPIO_MODE_INPUT_ONLY); + rtc_gpio_init(cts_io_num); // set as a LP_GPIO pin + lp_gpio_connect_in_signal(cts_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_CTS_PIN_IDX), 0); + } +#endif + } + return ESP_OK; +} + // Attach function for UART // connects the IO Pad, set Paripheral Manager and internal UART structure data static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin) { @@ -307,7 +426,7 @@ static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t //log_v("attaching UART%d pins: prev,new RX(%d,%d) TX(%d,%d) CTS(%d,%d) RTS(%d,%d)", uart_num, // uart->_rxPin, rxPin, uart->_txPin, txPin, uart->_ctsPin, ctsPin, uart->_rtsPin, rtsPin); vTaskDelay(10); - // IDF uart_set_pin() checks if the pin is used within LP UART and if it is a valid RTC IO pin + // IDF _uartInternalSetPin() checks if the pin is used within LP UART and if it is a valid RTC IO pin // No need for Arduino Layer to check it again bool retCode = true; if (rxPin >= 0) { @@ -316,7 +435,7 @@ static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t perimanClearPinBus(rxPin); } // connect RX Pad - bool ret = ESP_OK == uart_set_pin(uart->num, UART_PIN_NO_CHANGE, rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + bool ret = ESP_OK == _uartInternalSetPin(uart->num, UART_PIN_NO_CHANGE, rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); #if SOC_UART_LP_NUM >= 1 if (ret && uart_num >= SOC_UART_HP_NUM) { // it is a LP UART NUM ret &= lp_uart_config_io(uart->num, rxPin, RTC_GPIO_MODE_INPUT_ONLY, SOC_UART_RX_PIN_IDX); @@ -339,7 +458,7 @@ static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t perimanClearPinBus(txPin); } // connect TX Pad - bool ret = ESP_OK == uart_set_pin(uart->num, txPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + bool ret = ESP_OK == _uartInternalSetPin(uart->num, txPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); #if SOC_UART_LP_NUM >= 1 if (ret && uart_num >= SOC_UART_HP_NUM) { // it is a LP UART NUM ret &= lp_uart_config_io(uart->num, txPin, RTC_GPIO_MODE_OUTPUT_ONLY, SOC_UART_TX_PIN_IDX); @@ -362,7 +481,7 @@ static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t perimanClearPinBus(ctsPin); } // connect CTS Pad - bool ret = ESP_OK == uart_set_pin(uart->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, ctsPin); + bool ret = ESP_OK == _uartInternalSetPin(uart->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, ctsPin); #if SOC_UART_LP_NUM >= 1 if (ret && uart_num >= SOC_UART_HP_NUM) { // it is a LP UART NUM ret &= lp_uart_config_io(uart->num, ctsPin, RTC_GPIO_MODE_INPUT_ONLY, SOC_UART_CTS_PIN_IDX); @@ -385,7 +504,7 @@ static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t perimanClearPinBus(rtsPin); } // connect RTS Pad - bool ret = ESP_OK == uart_set_pin(uart->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, rtsPin, UART_PIN_NO_CHANGE); + bool ret = ESP_OK == _uartInternalSetPin(uart->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, rtsPin, UART_PIN_NO_CHANGE); #if SOC_UART_LP_NUM >= 1 if (ret && uart_num >= SOC_UART_HP_NUM) { // it is a LP UART NUM ret &= lp_uart_config_io(uart->num, rtsPin, RTC_GPIO_MODE_OUTPUT_ONLY, SOC_UART_RTS_PIN_IDX); @@ -1398,11 +1517,13 @@ void uart_internal_loopback(uint8_t uartNum, int8_t rxPin) { log_e("UART%d is not supported for loopback or RX pin %d is invalid.", uartNum, rxPin); return; } +#if 0 // leave this code here for future reference and need // forces rxPin to use GPIO Matrix and setup the pin to receive UART TX Signal - IDF 5.4.1 Change with uart_release_pin() gpio_func_sel((gpio_num_t)rxPin, PIN_FUNC_GPIO); gpio_pullup_en((gpio_num_t)rxPin); gpio_input_enable((gpio_num_t)rxPin); esp_rom_gpio_connect_in_signal(rxPin, uart_periph_signal[uartNum].pins[SOC_UART_RX_PIN_IDX].signal, false); +#endif esp_rom_gpio_connect_out_signal(rxPin, uart_periph_signal[uartNum].pins[SOC_UART_TX_PIN_IDX].signal, false, false); } From bf51428e55972c8800133ca7478e059d0d13dffd Mon Sep 17 00:00:00 2001 From: Sugar Glider Date: Sat, 21 Jun 2025 16:59:58 -0300 Subject: [PATCH 2/6] fix(uart): commentary typo error Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- cores/esp32/esp32-hal-uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index 5e8ba6c1988..67f64bd90dc 100644 --- a/cores/esp32/esp32-hal-uart.c +++ b/cores/esp32/esp32-hal-uart.c @@ -304,7 +304,7 @@ static bool _uartTrySetIomuxPin(uart_port_t uart_num, int io_num, uint32_t idx) return false; } - // Assign the correct funct to the GPIO. + // Assign the correct function to the GPIO. assert(upin->iomux_func != -1); if (uart_num < SOC_UART_HP_NUM) { gpio_iomux_out(io_num, upin->iomux_func, false); From ade185b08b1cff271114e86c0df25b47e9ff1474 Mon Sep 17 00:00:00 2001 From: Sugar Glider Date: Sat, 21 Jun 2025 17:00:12 -0300 Subject: [PATCH 3/6] fix(uart): commentary typo error Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- cores/esp32/esp32-hal-uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index 67f64bd90dc..3f82b2f4f77 100644 --- a/cores/esp32/esp32-hal-uart.c +++ b/cores/esp32/esp32-hal-uart.c @@ -308,7 +308,7 @@ static bool _uartTrySetIomuxPin(uart_port_t uart_num, int io_num, uint32_t idx) assert(upin->iomux_func != -1); if (uart_num < SOC_UART_HP_NUM) { gpio_iomux_out(io_num, upin->iomux_func, false); - // If the pin is input, we also have to redirect the signal, in order to bypasse the GPIO matrix. + // If the pin is input, we also have to redirect the signal, in order to bypass the GPIO matrix. if (upin->input) { gpio_iomux_in(io_num, upin->signal); } From cdeb4d944419f43ec27dcd3312647eb570671af2 Mon Sep 17 00:00:00 2001 From: Sugar Glider Date: Sat, 21 Jun 2025 17:00:22 -0300 Subject: [PATCH 4/6] fix(uart): commentary typo error Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- cores/esp32/esp32-hal-uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index 3f82b2f4f77..c83ec3fb3b7 100644 --- a/cores/esp32/esp32-hal-uart.c +++ b/cores/esp32/esp32-hal-uart.c @@ -329,7 +329,7 @@ static bool _uartTrySetIomuxPin(uart_port_t uart_num, int io_num, uint32_t idx) static esp_err_t _uartInternalSetPin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num) { // Since an IO cannot route peripheral signals via IOMUX and GPIO matrix at the same time, - // if tx and rx share the same IO, both signals need to be route to IOs through GPIO matrix + // if tx and rx share the same IO, both signals need to be routed to IOs through GPIO matrix 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. */ From 2e7bae93610ae84c5a79cfcf7cba664cc432476a Mon Sep 17 00:00:00 2001 From: Sugar Glider Date: Sat, 21 Jun 2025 17:33:57 -0300 Subject: [PATCH 5/6] fix(uart): commentary style fix --- cores/esp32/esp32-hal-uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index c83ec3fb3b7..e121a4a0b9c 100644 --- a/cores/esp32/esp32-hal-uart.c +++ b/cores/esp32/esp32-hal-uart.c @@ -332,7 +332,7 @@ static esp_err_t _uartInternalSetPin(uart_port_t uart_num, int tx_io_num, int rx // if tx and rx share the same IO, both signals need to be routed to IOs through GPIO matrix 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. */ + // In the following statements, if the io_num is negative, no need to configure anything. if (tx_io_num >= 0) { #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 From c54a8a847f8f500183bbc5abca769709f990c7a6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 16:00:36 +0000 Subject: [PATCH 6/6] ci(pre-commit): Apply automatic fixes --- cores/esp32/esp32-hal-uart.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index e121a4a0b9c..2163e9b5f42 100644 --- a/cores/esp32/esp32-hal-uart.c +++ b/cores/esp32/esp32-hal-uart.c @@ -349,7 +349,7 @@ static esp_err_t _uartInternalSetPin(uart_port_t uart_num, int tx_io_num, int rx } #if SOC_LP_GPIO_MATRIX_SUPPORTED else { - rtc_gpio_init(tx_io_num); // set as a LP_GPIO pin + 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 } @@ -368,13 +368,13 @@ static esp_err_t _uartInternalSetPin(uart_port_t uart_num, int tx_io_num, int rx 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) { // 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); } @@ -390,14 +390,14 @@ static esp_err_t _uartInternalSetPin(uart_port_t uart_num, int tx_io_num, int rx } #if SOC_LP_GPIO_MATRIX_SUPPORTED else { - rtc_gpio_init(rts_io_num); // set as a LP_GPIO pin + rtc_gpio_init(rts_io_num); // set as a LP_GPIO pin lp_gpio_connect_out_signal(rts_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RTS_PIN_IDX), 0, 0); // output enable is set inside lp_gpio_connect_out_signal func after the signal is connected } #endif } - if (cts_io_num >= 0 && !_uartTrySetIomuxPin(uart_num, cts_io_num, SOC_UART_CTS_PIN_IDX)) { + if (cts_io_num >= 0 && !_uartTrySetIomuxPin(uart_num, cts_io_num, SOC_UART_CTS_PIN_IDX)) { if (uart_num < SOC_UART_HP_NUM) { gpio_pullup_en(cts_io_num); gpio_input_enable(cts_io_num); @@ -406,7 +406,7 @@ static esp_err_t _uartInternalSetPin(uart_port_t uart_num, int tx_io_num, int rx #if SOC_LP_GPIO_MATRIX_SUPPORTED else { rtc_gpio_set_direction(cts_io_num, RTC_GPIO_MODE_INPUT_ONLY); - rtc_gpio_init(cts_io_num); // set as a LP_GPIO pin + rtc_gpio_init(cts_io_num); // set as a LP_GPIO pin lp_gpio_connect_in_signal(cts_io_num, UART_PERIPH_SIGNAL(uart_num, SOC_UART_CTS_PIN_IDX), 0); } #endif @@ -1517,7 +1517,7 @@ void uart_internal_loopback(uint8_t uartNum, int8_t rxPin) { log_e("UART%d is not supported for loopback or RX pin %d is invalid.", uartNum, rxPin); return; } -#if 0 // leave this code here for future reference and need +#if 0 // leave this code here for future reference and need // forces rxPin to use GPIO Matrix and setup the pin to receive UART TX Signal - IDF 5.4.1 Change with uart_release_pin() gpio_func_sel((gpio_num_t)rxPin, PIN_FUNC_GPIO); gpio_pullup_en((gpio_num_t)rxPin);