This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit d0a86138cef19b9c572552fb3d147d951625220d Author: Erik Englund <[email protected]> AuthorDate: Mon Sep 28 22:10:22 2026 +0200 arch/risc-v/espressif: Release RS-485 DIR on TX_DONE. In RS-485 mode the DIR (DE) pin was only released on TX_BRK_IDLE_DONE. That interrupt is part of the UART break feature (UART_TXD_BRK), which this driver never enables, so it does not fire after normal data: DIR stayed asserted after the first transmit and the port never received again. TX_DONE is the right event, but the upper half calls txint(false) as soon as its software buffer is empty, while the last bytes are still in the FIFO (see #15888), so it has to outlive txint(false): * txint(false) keeps TX_DONE enabled on an RS-485 port. * On TX_DONE with the software buffer and TX FIFO empty, the handler waits (bounded) for the transmitter to go idle, releases DIR and disables TX_DONE. * txempty() uses uart_hal_is_tx_idle() (FIFO count and FSM state). The raw TXFIFO_EMPTY bit only means the FIFO is below its empty threshold, so tcdrain() could return with data still in the FIFO. Same approach as the ESP32-S3 fix in #20389. Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Erik Englund <[email protected]> --- arch/risc-v/src/common/espressif/esp_serial.c | 66 +++++++++++++++++---------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/arch/risc-v/src/common/espressif/esp_serial.c b/arch/risc-v/src/common/espressif/esp_serial.c index 84003b92e6f..c9f5c57c8e4 100644 --- a/arch/risc-v/src/common/espressif/esp_serial.c +++ b/arch/risc-v/src/common/espressif/esp_serial.c @@ -350,15 +350,33 @@ static int uart_handler(int irq, void *context, void *arg) uint32_t int_status = uart_hal_get_intsts_mask(priv->hal); #ifdef HAVE_RS485 - if ((int_status & UART_INTR_TX_BRK_IDLE) != 0 && - esp_txempty(dev)) + /* Release the RS-485 driver once the frame has physically left the + * transmitter. TX_DONE stays enabled while DIR is asserted (see + * esp_txint()): the upper half disables TX interrupts as soon as its + * software buffer drains, while the last bytes are still in the FIFO. + * TX_BRK_IDLE is not usable here: it belongs to the break feature + * (UART_TXD_BRK), which this driver never enables. + */ + + if (priv->rs485_dir_gpio != 0 && + (int_status & UART_INTR_TX_DONE) != 0 && + dev->xmit.tail == dev->xmit.head && + uart_hal_get_txfifo_len(priv->hal) == SOC_UART_FIFO_LEN) { - uart_hal_clr_intsts_mask(priv->hal, UART_INTR_TX_BRK_IDLE); - if (dev->xmit.tail == dev->xmit.head) + /* TX_DONE can precede the transmitter FSM returning to idle by the + * tail of the last stop bit; wait for it (bounded) so the stop bit + * is not clipped. + */ + + int i; + + for (i = 0; i < 1000 && !esp_txempty(dev); i++) { - esp_gpiowrite(priv->rs485_dir_gpio, - !priv->rs485_dir_polarity); + up_udelay(1); } + + esp_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity); + uart_hal_disable_intr_mask(priv->hal, UART_INTR_TX_DONE); } #endif @@ -734,16 +752,6 @@ static void esp_txint(uart_dev_t *dev, bool enable) if (enable) { - /* After all bytes physically transmitted in the RS485 bus - * the TX_BRK_IDLE will indicate we can disable the TX pin. - */ -#ifdef HAVE_RS485 - if (priv->rs485_dir_gpio != 0) - { - uart_hal_ena_intr_mask(priv->hal, UART_INTR_TX_BRK_IDLE); - } - -#endif /* Set to receive an interrupt when the TX holding register register * is empty */ @@ -754,9 +762,21 @@ static void esp_txint(uart_dev_t *dev, bool enable) } else { - /* Disable the TX interrupt */ + /* Disable the TX interrupt. In RS-485 mode keep TX_DONE enabled: + * uart_handler() needs it to release DIR once the FIFO has drained, + * and disables it itself when it does. + */ - uart_hal_disable_intr_mask(priv->hal, ints_mask); +#ifdef HAVE_RS485 + if (priv->rs485_dir_gpio != 0) + { + uart_hal_disable_intr_mask(priv->hal, UART_INTR_TXFIFO_EMPTY); + } + else +#endif + { + uart_hal_disable_intr_mask(priv->hal, ints_mask); + } } } @@ -860,11 +880,11 @@ static bool esp_txempty(uart_dev_t *dev) { struct esp_uart_s *priv = dev->priv; -#if defined(CONFIG_ARCH_CHIP_ESP32P4) - return priv->hal->dev->int_raw.txfifo_empty_int_raw != 0; -#else - return priv->hal->dev->int_raw.txfifo_empty != 0; -#endif + /* FIFO drained and the transmitter FSM idle. The raw TXFIFO_EMPTY bit + * is not usable: it only means the FIFO is below its empty threshold. + */ + + return uart_hal_is_tx_idle(priv->hal); } /****************************************************************************
