From 7db47cafb2b02087af9b600305c369f5c1373382 Mon Sep 17 00:00:00 2001 From: Andrew Sund Date: Mon, 2 Jun 2025 17:47:28 -0700 Subject: [PATCH] feat(uart): Add function to invert hardware UART Tx line Simply clone existing Rx functionality for Tx. Allow granular control over both lines. Avoid overloading HardwareSerial::begin() to change the bool invert parameter to a bitmask type. Add an untested implementation for ESP32C6, ESP32H2, ESP32P4 that references the different register naming on those chips. --- cores/esp32/HardwareSerial.cpp | 4 ++++ cores/esp32/HardwareSerial.h | 1 + cores/esp32/esp32-hal-uart.c | 42 ++++++++++++++++++++++++++++++---- cores/esp32/esp32-hal-uart.h | 1 + tests/validation/uart/uart.ino | 8 +++++++ 5 files changed, 52 insertions(+), 4 deletions(-) diff --git a/cores/esp32/HardwareSerial.cpp b/cores/esp32/HardwareSerial.cpp index 6d762da21fb..42acdb742b7 100644 --- a/cores/esp32/HardwareSerial.cpp +++ b/cores/esp32/HardwareSerial.cpp @@ -578,6 +578,10 @@ void HardwareSerial::setRxInvert(bool invert) { uartSetRxInvert(_uart, invert); } +void HardwareSerial::setTxInvert(bool invert) { + uartSetTxInvert(_uart, invert); +} + // negative Pin value will keep it unmodified // can be called after or before begin() bool HardwareSerial::setPins(int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin) { diff --git a/cores/esp32/HardwareSerial.h b/cores/esp32/HardwareSerial.h index e974f112701..c6dc917706d 100644 --- a/cores/esp32/HardwareSerial.h +++ b/cores/esp32/HardwareSerial.h @@ -349,6 +349,7 @@ class HardwareSerial : public Stream { void setDebugOutput(bool); void setRxInvert(bool); + void setTxInvert(bool); // Negative Pin Number will keep it unmodified, thus this function can set individual pins // setPins() can be called after or before begin() diff --git a/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index 5311aff4f37..5d9c3497651 100644 --- a/cores/esp32/esp32-hal-uart.c +++ b/cores/esp32/esp32-hal-uart.c @@ -827,18 +827,23 @@ void uartSetRxInvert(uart_t *uart, bool invert) { if (uart == NULL) { return; } -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 // POTENTIAL ISSUE :: original code only set/reset rxd_inv bit // IDF or LL set/reset the whole inv_mask! // if (invert) // ESP_ERROR_CHECK(uart_set_line_inverse(uart->num, UART_SIGNAL_RXD_INV)); // else // ESP_ERROR_CHECK(uart_set_line_inverse(uart->num, UART_SIGNAL_INV_DISABLE)); - log_e("uartSetRxInvert is not supported in ESP32C6, ESP32H2 and ESP32P4"); -#else // this implementation is better over IDF API because it only affects RXD - // this is supported in ESP32, ESP32-S2 and ESP32-C3 uart_dev_t *hw = UART_LL_GET_HW(uart->num); + +#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 + if (invert) { + hw->conf0_sync.rxd_inv = 1; + } else { + hw->conf0_sync.rxd_inv = 0; + } +#else + // this is supported in ESP32, ESP32-S2 and ESP32-C3 if (invert) { hw->conf0.rxd_inv = 1; } else { @@ -847,6 +852,35 @@ void uartSetRxInvert(uart_t *uart, bool invert) { #endif } +void uartSetTxInvert(uart_t *uart, bool invert) { + if (uart == NULL) { + return; + } + // POTENTIAL ISSUE :: original code only set/reset txd_inv bit + // IDF or LL set/reset the whole inv_mask! + // if (invert) + // ESP_ERROR_CHECK(uart_set_line_inverse(uart->num, UART_SIGNAL_TXD_INV)); + // else + // ESP_ERROR_CHECK(uart_set_line_inverse(uart->num, UART_SIGNAL_INV_DISABLE)); + // this implementation is better over IDF API because it only affects TXD + uart_dev_t *hw = UART_LL_GET_HW(uart->num); + +#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 + if (invert) { + hw->conf0_sync.txd_inv = 1; + } else { + hw->conf0_sync.txd_inv = 0; + } +#else + // this is supported in ESP32, ESP32-S2 and ESP32-C3 + if (invert) { + hw->conf0.txd_inv = 1; + } else { + hw->conf0.txd_inv = 0; + } +#endif +} + uint32_t uartAvailable(uart_t *uart) { if (uart == NULL) { diff --git a/cores/esp32/esp32-hal-uart.h b/cores/esp32/esp32-hal-uart.h index 41b005aa151..4f23ab629f5 100644 --- a/cores/esp32/esp32-hal-uart.h +++ b/cores/esp32/esp32-hal-uart.h @@ -62,6 +62,7 @@ bool uartSetBaudRate(uart_t *uart, uint32_t baud_rate); uint32_t uartGetBaudRate(uart_t *uart); void uartSetRxInvert(uart_t *uart, bool invert); +void uartSetTxInvert(uart_t *uart, bool invert); bool uartSetRxTimeout(uart_t *uart, uint8_t numSymbTimeout); bool uartSetRxFIFOFull(uart_t *uart, uint8_t numBytesFIFOFull); void uartSetFastReading(uart_t *uart); diff --git a/tests/validation/uart/uart.ino b/tests/validation/uart/uart.ino index 794fc9affc2..18da797659b 100644 --- a/tests/validation/uart/uart.ino +++ b/tests/validation/uart/uart.ino @@ -276,6 +276,10 @@ void enabled_uart_calls_test(void) { Serial1.setRxInvert(true); Serial1.setRxInvert(false); + log_d("Checking if Serial 1 TX can be inverted while running"); + Serial1.setTxInvert(true); + Serial1.setTxInvert(false); + Serial.println("Enabled UART calls test successful"); } @@ -351,6 +355,10 @@ void disabled_uart_calls_test(void) { Serial1.setRxInvert(true); Serial1.setRxInvert(false); + log_d("Checking if Serial 1 TX can be inverted when stopped"); + Serial1.setTxInvert(true); + Serial1.setTxInvert(false); + Serial.println("Disabled UART calls test successful"); }