3232#include "driver/gpio.h"
3333#include "hal/gpio_hal.h"
3434#include "esp_rom_gpio.h"
35+ #include "esp_private/gpio.h"
3536
3637#include "driver/rtc_io.h"
3738#include "driver/lp_io.h"
38- #include "soc/uart_periph .h"
39+ #include "soc/uart_pins .h"
3940#include "esp_private/uart_share_hw_ctrl.h"
4041
4142static int s_uart_debug_nr = 0 ; // UART number for debug output
@@ -294,6 +295,128 @@ static bool _uartDetachBus_RTS(void *busptr) {
294295 return _uartDetachPins (bus -> num , UART_PIN_NO_CHANGE , UART_PIN_NO_CHANGE , UART_PIN_NO_CHANGE , bus -> _rtsPin );
295296}
296297
298+ static bool _uartTrySetIomuxPin (uart_port_t uart_num , int io_num , uint32_t idx ) {
299+ // Store a pointer to the default pin, to optimize access to its fields.
300+ const uart_periph_sig_t * upin = & uart_periph_signal [uart_num ].pins [idx ];
301+
302+ // In theory, if default_gpio is -1, iomux_func should also be -1, but let's be safe and test both.
303+ if (upin -> iomux_func == -1 || upin -> default_gpio == -1 || upin -> default_gpio != io_num ) {
304+ return false;
305+ }
306+
307+ // Assign the correct function to the GPIO.
308+ if (upin -> iomux_func == -1 ) {
309+ log_e ("IO#%d has bad IOMUX internal information. Switching to GPIO Matrix UART function." , io_num );
310+ return false;
311+ }
312+ if (uart_num < SOC_UART_HP_NUM ) {
313+ gpio_iomux_out (io_num , upin -> iomux_func , false);
314+ // If the pin is input, we also have to redirect the signal, in order to bypass the GPIO matrix.
315+ if (upin -> input ) {
316+ gpio_iomux_in (io_num , upin -> signal );
317+ }
318+ }
319+ #if (SOC_UART_LP_NUM >= 1 ) && (SOC_RTCIO_PIN_COUNT >= 1 )
320+ else {
321+ if (upin -> input ) {
322+ rtc_gpio_set_direction (io_num , RTC_GPIO_MODE_INPUT_ONLY );
323+ } else {
324+ rtc_gpio_set_direction (io_num , RTC_GPIO_MODE_OUTPUT_ONLY );
325+ }
326+ rtc_gpio_init (io_num );
327+ rtc_gpio_iomux_func_sel (io_num , upin -> iomux_func );
328+ }
329+ #endif
330+ return true;
331+ }
332+
333+ 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 ) {
334+ // Since an IO cannot route peripheral signals via IOMUX and GPIO matrix at the same time,
335+ // if tx and rx share the same IO, both signals need to be routed to IOs through GPIO matrix
336+ bool tx_rx_same_io = (tx_io_num == rx_io_num );
337+
338+ // In the following statements, if the io_num is negative, no need to configure anything.
339+ if (tx_io_num >= 0 ) {
340+ #if CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND || CONFIG_PM_SLP_DISABLE_GPIO
341+ // In such case, IOs are going to switch to sleep configuration (isolate) when entering sleep for power saving reason
342+ // But TX IO in isolate state could write garbled data to the other end
343+ // Therefore, we should disable the switch of the TX pin to sleep configuration
344+ gpio_sleep_sel_dis (tx_io_num );
345+ #endif
346+ if (tx_rx_same_io || !_uartTrySetIomuxPin (uart_num , tx_io_num , SOC_UART_TX_PIN_IDX )) {
347+ if (uart_num < SOC_UART_HP_NUM ) {
348+ gpio_func_sel (tx_io_num , PIN_FUNC_GPIO );
349+ esp_rom_gpio_connect_out_signal (tx_io_num , UART_PERIPH_SIGNAL (uart_num , SOC_UART_TX_PIN_IDX ), 0 , 0 );
350+ // output enable is set inside esp_rom_gpio_connect_out_signal func after the signal is connected
351+ // (output enabled too early may cause unnecessary level change at the pad)
352+ }
353+ #if SOC_LP_GPIO_MATRIX_SUPPORTED
354+ else {
355+ rtc_gpio_init (tx_io_num ); // set as a LP_GPIO pin
356+ lp_gpio_connect_out_signal (tx_io_num , UART_PERIPH_SIGNAL (uart_num , SOC_UART_TX_PIN_IDX ), 0 , 0 );
357+ // output enable is set inside lp_gpio_connect_out_signal func after the signal is connected
358+ }
359+ #endif
360+ }
361+ }
362+
363+ if (rx_io_num >= 0 ) {
364+ #if CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND || CONFIG_PM_SLP_DISABLE_GPIO
365+ // In such case, IOs are going to switch to sleep configuration (isolate) when entering sleep for power saving reason
366+ // But RX IO in isolate state could receive garbled data into FIFO, which is not desired
367+ // Therefore, we should disable the switch of the RX pin to sleep configuration
368+ gpio_sleep_sel_dis (rx_io_num );
369+ #endif
370+ if (tx_rx_same_io || !_uartTrySetIomuxPin (uart_num , rx_io_num , SOC_UART_RX_PIN_IDX )) {
371+ if (uart_num < SOC_UART_HP_NUM ) {
372+ gpio_input_enable (rx_io_num );
373+ esp_rom_gpio_connect_in_signal (rx_io_num , UART_PERIPH_SIGNAL (uart_num , SOC_UART_RX_PIN_IDX ), 0 );
374+ }
375+ #if SOC_LP_GPIO_MATRIX_SUPPORTED
376+ else {
377+ rtc_gpio_mode_t mode = (tx_rx_same_io ? RTC_GPIO_MODE_INPUT_OUTPUT : RTC_GPIO_MODE_INPUT_ONLY );
378+ rtc_gpio_set_direction (rx_io_num , mode );
379+ if (!tx_rx_same_io ) { // set the same pin again as a LP_GPIO will overwrite connected out_signal, not desired, so skip
380+ rtc_gpio_init (rx_io_num ); // set as a LP_GPIO pin
381+ }
382+ lp_gpio_connect_in_signal (rx_io_num , UART_PERIPH_SIGNAL (uart_num , SOC_UART_RX_PIN_IDX ), 0 );
383+ }
384+ #endif
385+ }
386+ }
387+
388+ if (rts_io_num >= 0 && !_uartTrySetIomuxPin (uart_num , rts_io_num , SOC_UART_RTS_PIN_IDX )) {
389+ if (uart_num < SOC_UART_HP_NUM ) {
390+ gpio_func_sel (rts_io_num , PIN_FUNC_GPIO );
391+ esp_rom_gpio_connect_out_signal (rts_io_num , UART_PERIPH_SIGNAL (uart_num , SOC_UART_RTS_PIN_IDX ), 0 , 0 );
392+ // output enable is set inside esp_rom_gpio_connect_out_signal func after the signal is connected
393+ }
394+ #if SOC_LP_GPIO_MATRIX_SUPPORTED
395+ else {
396+ rtc_gpio_init (rts_io_num ); // set as a LP_GPIO pin
397+ lp_gpio_connect_out_signal (rts_io_num , UART_PERIPH_SIGNAL (uart_num , SOC_UART_RTS_PIN_IDX ), 0 , 0 );
398+ // output enable is set inside lp_gpio_connect_out_signal func after the signal is connected
399+ }
400+ #endif
401+ }
402+
403+ if (cts_io_num >= 0 && !_uartTrySetIomuxPin (uart_num , cts_io_num , SOC_UART_CTS_PIN_IDX )) {
404+ if (uart_num < SOC_UART_HP_NUM ) {
405+ gpio_pullup_en (cts_io_num );
406+ gpio_input_enable (cts_io_num );
407+ esp_rom_gpio_connect_in_signal (cts_io_num , UART_PERIPH_SIGNAL (uart_num , SOC_UART_CTS_PIN_IDX ), 0 );
408+ }
409+ #if SOC_LP_GPIO_MATRIX_SUPPORTED
410+ else {
411+ rtc_gpio_set_direction (cts_io_num , RTC_GPIO_MODE_INPUT_ONLY );
412+ rtc_gpio_init (cts_io_num ); // set as a LP_GPIO pin
413+ lp_gpio_connect_in_signal (cts_io_num , UART_PERIPH_SIGNAL (uart_num , SOC_UART_CTS_PIN_IDX ), 0 );
414+ }
415+ #endif
416+ }
417+ return ESP_OK ;
418+ }
419+
297420// Attach function for UART
298421// connects the IO Pad, set Paripheral Manager and internal UART structure data
299422static bool _uartAttachPins (uint8_t uart_num , int8_t rxPin , int8_t txPin , int8_t ctsPin , int8_t rtsPin ) {
@@ -306,7 +429,7 @@ static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t
306429 //log_v("attaching UART%d pins: prev,new RX(%d,%d) TX(%d,%d) CTS(%d,%d) RTS(%d,%d)", uart_num,
307430 // uart->_rxPin, rxPin, uart->_txPin, txPin, uart->_ctsPin, ctsPin, uart->_rtsPin, rtsPin); vTaskDelay(10);
308431
309- // IDF uart_set_pin () checks if the pin is used within LP UART and if it is a valid RTC IO pin
432+ // IDF _uartInternalSetPin () checks if the pin is used within LP UART and if it is a valid RTC IO pin
310433 // No need for Arduino Layer to check it again
311434 bool retCode = true;
312435 if (rxPin >= 0 ) {
@@ -315,7 +438,7 @@ static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t
315438 perimanClearPinBus (rxPin );
316439 }
317440 // connect RX Pad
318- bool ret = ESP_OK == uart_set_pin (uart -> num , UART_PIN_NO_CHANGE , rxPin , UART_PIN_NO_CHANGE , UART_PIN_NO_CHANGE );
441+ bool ret = ESP_OK == _uartInternalSetPin (uart -> num , UART_PIN_NO_CHANGE , rxPin , UART_PIN_NO_CHANGE , UART_PIN_NO_CHANGE );
319442#if SOC_UART_LP_NUM >= 1
320443 if (ret && uart_num >= SOC_UART_HP_NUM ) { // it is a LP UART NUM
321444 ret &= lp_uart_config_io (uart -> num , rxPin , RTC_GPIO_MODE_INPUT_ONLY , SOC_UART_RX_PIN_IDX );
@@ -338,7 +461,7 @@ static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t
338461 perimanClearPinBus (txPin );
339462 }
340463 // connect TX Pad
341- bool ret = ESP_OK == uart_set_pin (uart -> num , txPin , UART_PIN_NO_CHANGE , UART_PIN_NO_CHANGE , UART_PIN_NO_CHANGE );
464+ bool ret = ESP_OK == _uartInternalSetPin (uart -> num , txPin , UART_PIN_NO_CHANGE , UART_PIN_NO_CHANGE , UART_PIN_NO_CHANGE );
342465#if SOC_UART_LP_NUM >= 1
343466 if (ret && uart_num >= SOC_UART_HP_NUM ) { // it is a LP UART NUM
344467 ret &= lp_uart_config_io (uart -> num , txPin , RTC_GPIO_MODE_OUTPUT_ONLY , SOC_UART_TX_PIN_IDX );
@@ -361,7 +484,7 @@ static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t
361484 perimanClearPinBus (ctsPin );
362485 }
363486 // connect CTS Pad
364- bool ret = ESP_OK == uart_set_pin (uart -> num , UART_PIN_NO_CHANGE , UART_PIN_NO_CHANGE , UART_PIN_NO_CHANGE , ctsPin );
487+ bool ret = ESP_OK == _uartInternalSetPin (uart -> num , UART_PIN_NO_CHANGE , UART_PIN_NO_CHANGE , UART_PIN_NO_CHANGE , ctsPin );
365488#if SOC_UART_LP_NUM >= 1
366489 if (ret && uart_num >= SOC_UART_HP_NUM ) { // it is a LP UART NUM
367490 ret &= lp_uart_config_io (uart -> num , ctsPin , RTC_GPIO_MODE_INPUT_ONLY , SOC_UART_CTS_PIN_IDX );
@@ -384,7 +507,7 @@ static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t
384507 perimanClearPinBus (rtsPin );
385508 }
386509 // connect RTS Pad
387- bool ret = ESP_OK == uart_set_pin (uart -> num , UART_PIN_NO_CHANGE , UART_PIN_NO_CHANGE , rtsPin , UART_PIN_NO_CHANGE );
510+ bool ret = ESP_OK == _uartInternalSetPin (uart -> num , UART_PIN_NO_CHANGE , UART_PIN_NO_CHANGE , rtsPin , UART_PIN_NO_CHANGE );
388511#if SOC_UART_LP_NUM >= 1
389512 if (ret && uart_num >= SOC_UART_HP_NUM ) { // it is a LP UART NUM
390513 ret &= lp_uart_config_io (uart -> num , rtsPin , RTC_GPIO_MODE_OUTPUT_ONLY , SOC_UART_RTS_PIN_IDX );
@@ -1383,39 +1506,9 @@ unsigned long uartDetectBaudrate(uart_t *uart) {
13831506}
13841507
13851508/*
1386- These functions are for testing purpose only and can be used in Arduino Sketches
1387- Those are used in the UART examples
1388- */
1389-
1390- /*
1391- This is intended to make an internal loopback connection using IOMUX
1392- The function uart_internal_loopback() shall be used right after Arduino Serial.begin(...)
1393- This code "replaces" the physical wiring for connecting TX <--> RX in a loopback
1394- */
1395-
1396- // gets the right TX or RX SIGNAL, based on the UART number from gpio_sig_map.h
1397- #ifdef CONFIG_IDF_TARGET_ESP32P4
1398- #define UART_TX_SIGNAL (uartNumber ) \
1399- (uartNumber == UART_NUM_0 \
1400- ? UART0_TXD_PAD_OUT_IDX \
1401- : (uartNumber == UART_NUM_1 \
1402- ? UART1_TXD_PAD_OUT_IDX \
1403- : (uartNumber == UART_NUM_2 ? UART2_TXD_PAD_OUT_IDX : (uartNumber == UART_NUM_3 ? UART3_TXD_PAD_OUT_IDX : UART4_TXD_PAD_OUT_IDX))))
1404- #define UART_RX_SIGNAL (uartNumber ) \
1405- (uartNumber == UART_NUM_0 \
1406- ? UART0_RXD_PAD_IN_IDX \
1407- : (uartNumber == UART_NUM_1 \
1408- ? UART1_RXD_PAD_IN_IDX \
1409- : (uartNumber == UART_NUM_2 ? UART2_RXD_PAD_IN_IDX : (uartNumber == UART_NUM_3 ? UART3_RXD_PAD_IN_IDX : UART4_RXD_PAD_IN_IDX))))
1410- #else
1411- #if SOC_UART_HP_NUM > 2
1412- #define UART_TX_SIGNAL (uartNumber ) (uartNumber == UART_NUM_0 ? U0TXD_OUT_IDX : (uartNumber == UART_NUM_1 ? U1TXD_OUT_IDX : U2TXD_OUT_IDX))
1413- #define UART_RX_SIGNAL (uartNumber ) (uartNumber == UART_NUM_0 ? U0RXD_IN_IDX : (uartNumber == UART_NUM_1 ? U1RXD_IN_IDX : U2RXD_IN_IDX))
1414- #else
1415- #define UART_TX_SIGNAL (uartNumber ) (uartNumber == UART_NUM_0 ? U0TXD_OUT_IDX : U1TXD_OUT_IDX)
1416- #define UART_RX_SIGNAL (uartNumber ) (uartNumber == UART_NUM_0 ? U0RXD_IN_IDX : U1RXD_IN_IDX)
1417- #endif
1418- #endif // ifdef CONFIG_IDF_TARGET_ESP32P4
1509+ * These functions are for testing purposes only and can be used in Arduino Sketches.
1510+ * They are utilized in the UART examples and CI.
1511+ */
14191512
14201513/*
14211514 This function internally binds defined UARTs TX signal with defined RX pin of any UART (same or different).
@@ -1427,7 +1520,14 @@ void uart_internal_loopback(uint8_t uartNum, int8_t rxPin) {
14271520 log_e ("UART%d is not supported for loopback or RX pin %d is invalid." , uartNum , rxPin );
14281521 return ;
14291522 }
1430- esp_rom_gpio_connect_out_signal (rxPin , UART_TX_SIGNAL (uartNum ), false, false);
1523+ #if 0 // leave this code here for future reference and need
1524+ // forces rxPin to use GPIO Matrix and setup the pin to receive UART TX Signal - IDF 5.4.1 Change with uart_release_pin()
1525+ gpio_func_sel ((gpio_num_t )rxPin , PIN_FUNC_GPIO );
1526+ gpio_pullup_en ((gpio_num_t )rxPin );
1527+ gpio_input_enable ((gpio_num_t )rxPin );
1528+ esp_rom_gpio_connect_in_signal (rxPin , uart_periph_signal [uartNum ].pins [SOC_UART_RX_PIN_IDX ].signal , false);
1529+ #endif
1530+ esp_rom_gpio_connect_out_signal (rxPin , uart_periph_signal [uartNum ].pins [SOC_UART_TX_PIN_IDX ].signal , false, false);
14311531}
14321532
14331533/*
0 commit comments