fix(uart): allow same pin for tx and rx in uart_set_pin

Also add IO reserve to uart driver

Closes https://github.com/espressif/esp-idf/issues/14787
This commit is contained in:
Song Ruo Jing
2024-11-07 11:51:16 +08:00
parent bb9a2658a9
commit e8f0299557
4 changed files with 100 additions and 30 deletions

View File

@@ -27,6 +27,7 @@
#include "driver/uart_select.h"
#include "esp_private/esp_clk_tree_common.h"
#include "esp_private/gpio.h"
#include "esp_private/esp_gpio_reserve.h"
#include "esp_private/uart_share_hw_ctrl.h"
#include "esp_clk_tree.h"
#include "sdkconfig.h"
@@ -746,8 +747,19 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r
}
#endif
// Potential IO reserved mask
uint64_t io_reserve_mask = 0;
io_reserve_mask |= (tx_io_num > 0 ? BIT64(tx_io_num) : 0);
io_reserve_mask |= (rx_io_num > 0 ? BIT64(rx_io_num) : 0);
io_reserve_mask |= (rts_io_num > 0 ? BIT64(rts_io_num) : 0);
io_reserve_mask |= (cts_io_num > 0 ? BIT64(cts_io_num) : 0);
// 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 && !uart_try_set_iomux_pin(uart_num, tx_io_num, SOC_UART_TX_PIN_IDX)) {
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);
@@ -756,27 +768,27 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r
}
#if SOC_LP_GPIO_MATRIX_SUPPORTED
else {
rtc_gpio_init(tx_io_num);
rtc_gpio_iomux_func_sel(tx_io_num, RTCIO_LL_PIN_FUNC);
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 && !uart_try_set_iomux_pin(uart_num, rx_io_num, SOC_UART_RX_PIN_IDX)) {
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_func_sel(rx_io_num, PIN_FUNC_GPIO);
gpio_set_pull_mode(rx_io_num, GPIO_PULLUP_ONLY); // This does not consider that RX signal can be read inverted by configuring the hardware (i.e. idle is at low level). However, it is only a weak pullup, the TX at the other end can always drive the line.
gpio_set_direction(rx_io_num, GPIO_MODE_INPUT);
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_set_direction(rx_io_num, RTC_GPIO_MODE_INPUT_ONLY);
rtc_gpio_init(rx_io_num);
rtc_gpio_iomux_func_sel(rx_io_num, RTCIO_LL_PIN_FUNC);
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);
}
@@ -791,8 +803,7 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r
}
#if SOC_LP_GPIO_MATRIX_SUPPORTED
else {
rtc_gpio_init(rts_io_num);
rtc_gpio_iomux_func_sel(rts_io_num, RTCIO_LL_PIN_FUNC);
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
}
@@ -800,22 +811,31 @@ esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int r
}
if (cts_io_num >= 0 && !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_func_sel(cts_io_num, PIN_FUNC_GPIO);
gpio_set_pull_mode(cts_io_num, GPIO_PULLUP_ONLY);
gpio_set_direction(cts_io_num, GPIO_MODE_INPUT);
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);
rtc_gpio_iomux_func_sel(cts_io_num, RTCIO_LL_PIN_FUNC);
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
}
// IO reserve
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) {
uint8_t pos = __builtin_ctzll(conflict_mask);
conflict_mask &= ~(1ULL << pos);
ESP_LOGW(UART_TAG, "GPIO %d is not usable, maybe used by others", pos);
}
return ESP_OK;
}