diff --git a/arch/xtensa/src/esp32s2/Kconfig b/arch/xtensa/src/esp32s2/Kconfig index 923c47c34db..625ea79ccdb 100644 --- a/arch/xtensa/src/esp32s2/Kconfig +++ b/arch/xtensa/src/esp32s2/Kconfig @@ -583,6 +583,31 @@ menu "UART Configuration" if ESP32S2_UART0 +config ESP32S2_UART0_RS485 + bool "RS-485 on UART0" + default n + ---help--- + Enable RS-485 interface on UART0. Your board config will have to + provide GPIO_UART0_RS485_DIR pin definition. + +config ESP32S2_UART0_RS485_DIR_PIN + int "UART0 RS-485 DIR pin" + default 25 + range 0 46 + depends on ESP32S2_UART0_RS485 + ---help--- + DIR pin for RS-485 on UART0. This pin will control the RS485 enable + TX of the RS485 transceiver. + +config ESP32S2_UART0_RS485_DIR_POLARITY + int "UART0 RS-485 DIR pin polarity" + default 1 + range 0 1 + depends on ESP32S2_UART0_RS485 + ---help--- + Polarity of DIR pin for RS-485 on UART0. Set to state on DIR pin which + enables TX (0 - low / nTXEN, 1 - high / TXEN). + config ESP32S2_UART0_TXPIN int "UART0 Tx Pin" default 43 @@ -609,6 +634,31 @@ endif # ESP32S2_UART0 if ESP32S2_UART1 +config ESP32S2_UART1_RS485 + bool "RS-485 on UART1" + default n + ---help--- + Enable RS-485 interface on UART1. Your board config will have to + provide GPIO_UART1_RS485_DIR pin definition. + +config ESP32S2_UART1_RS485_DIR_PIN + int "UART1 RS-485 DIR pin" + default 14 + range 0 46 + depends on ESP32S2_UART1_RS485 + ---help--- + DIR pin for RS-485 on UART1. This pin will control the RS485 enable + TX of the RS485 transceiver. + +config ESP32S2_UART1_RS485_DIR_POLARITY + int "UART1 RS-485 DIR pin polarity" + default 1 + range 0 1 + depends on ESP32S2_UART1_RS485 + ---help--- + Polarity of DIR pin for RS-485 on UART1. Set to state on DIR pin which + enables TX (0 - low / nTXEN, 1 - high / TXEN). + config ESP32S2_UART1_TXPIN int "UART1 Tx Pin" default 17 diff --git a/arch/xtensa/src/esp32s2/esp32s2_config.h b/arch/xtensa/src/esp32s2/esp32s2_config.h index c8097017b58..c57d4560df1 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_config.h +++ b/arch/xtensa/src/esp32s2/esp32s2_config.h @@ -43,6 +43,13 @@ # define HAVE_UART_DEVICE 1 #endif +/* Is RS-485 used? */ + +#if defined(CONFIG_ESP32S2_UART0_RS485) || \ + defined(CONFIG_ESP32S2_UART1_RS485) +# define HAVE_RS485 1 +#endif + /* Serial Console ***********************************************************/ /* Is there a serial console? There should be no more than one defined. It diff --git a/arch/xtensa/src/esp32s2/esp32s2_lowputc.c b/arch/xtensa/src/esp32s2/esp32s2_lowputc.c index dccd0506210..a5bb7b01c6b 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_lowputc.c +++ b/arch/xtensa/src/esp32s2/esp32s2_lowputc.c @@ -90,6 +90,14 @@ struct esp32s2_uart_s g_uart0_config = .oflow = false, /* output flow control (CTS) disabled */ #endif #endif +#ifdef CONFIG_ESP32S2_UART0_RS485 + .rs485_dir_gpio = CONFIG_ESP32S2_UART0_RS485_DIR_PIN, +#if (CONFIG_ESP32S2_UART0_RS485_DIR_POLARITY == 0) + .rs485_dir_polarity = false, +#else + .rs485_dir_polarity = true, +#endif +#endif }; #endif /* CONFIG_ESP32S2_UART0 */ @@ -129,6 +137,14 @@ struct esp32s2_uart_s g_uart1_config = .oflow = false, /* output flow control (CTS) disabled */ #endif #endif +#ifdef CONFIG_ESP32S2_UART1_RS485 + .rs485_dir_gpio = CONFIG_ESP32S2_UART1_RS485_DIR_PIN, +#if (CONFIG_ESP32S2_UART1_RS485_DIR_POLARITY == 0) + .rs485_dir_polarity = false, +#else + .rs485_dir_polarity = true, +#endif +#endif }; #endif /* CONFIG_ESP32S2_UART1 */ @@ -501,6 +517,25 @@ void esp32s2_lowputc_stop_length(const struct esp32s2_uart_s *priv) } } +/**************************************************************************** + * Name: esp32s2_lowputc_set_tx_idle_time + * + * Description: + * Set the idle time between transfers. + * + * Parameters: + * priv - Pointer to the private driver struct. + * time - Desired time interval between the transfers. + * + ****************************************************************************/ + +void esp32s2_lowputc_set_tx_idle_time(const struct esp32s2_uart_s *priv, + uint32_t time) +{ + modifyreg32(UART_IDLE_CONF_REG(priv->id), UART_TX_IDLE_NUM_M, + VALUE_TO_FIELD(time, UART_TX_IDLE_NUM)); +} + /**************************************************************************** * Name: esp32s2_lowputc_send_byte * @@ -716,6 +751,15 @@ void esp32s2_lowputc_config_pins(const struct esp32s2_uart_s *priv) esp32s2_gpio_matrix_in(priv->ctspin, priv->ctssig, 0); } #endif + +#ifdef HAVE_RS485 + if (priv->rs485_dir_gpio != 0) + { + esp32s2_configgpio(priv->rs485_dir_gpio, OUTPUT); + esp32s2_gpio_matrix_out(priv->rs485_dir_gpio, SIG_GPIO_OUT_IDX, 0, 0); + esp32s2_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity); + } +#endif } /**************************************************************************** diff --git a/arch/xtensa/src/esp32s2/esp32s2_lowputc.h b/arch/xtensa/src/esp32s2/esp32s2_lowputc.h index bd8723fb0eb..68260f42dc5 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_lowputc.h +++ b/arch/xtensa/src/esp32s2/esp32s2_lowputc.h @@ -102,6 +102,10 @@ struct esp32s2_uart_s uint8_t ctssig; /* CTS signal */ bool oflow; /* Output flow control (CTS) enabled */ #endif +#ifdef HAVE_RS485 + uint8_t rs485_dir_gpio; /* UART RS-485 DIR GPIO pin cfg */ + bool rs485_dir_polarity; /* UART RS-485 DIR TXEN polarity */ +#endif }; extern struct esp32s2_uart_s g_uart0_config; @@ -297,6 +301,21 @@ int esp32s2_lowputc_data_length(const struct esp32s2_uart_s *priv); void esp32s2_lowputc_stop_length(const struct esp32s2_uart_s *priv); +/**************************************************************************** + * Name: esp32s2_lowputc_set_tx_idle_time + * + * Description: + * Set the idle time between transfers. + * + * Parameters: + * priv - Pointer to the private driver struct. + * time - Desired time interval between the transfers. + * + ****************************************************************************/ + +void esp32s2_lowputc_set_tx_idle_time(const struct esp32s2_uart_s *priv, + uint32_t time); + /**************************************************************************** * Name: esp32s2_lowputc_send_byte * diff --git a/arch/xtensa/src/esp32s2/esp32s2_serial.c b/arch/xtensa/src/esp32s2/esp32s2_serial.c index d835a097bdf..9ab7067bf52 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_serial.c +++ b/arch/xtensa/src/esp32s2/esp32s2_serial.c @@ -45,6 +45,7 @@ #include "esp32s2_config.h" #include "esp32s2_irq.h" #include "esp32s2_lowputc.h" +#include "esp32s2_gpio.h" #include "hardware/esp32s2_uart.h" #include "hardware/esp32s2_system.h" @@ -237,6 +238,18 @@ static int uart_handler(int irq, void *context, void *arg) int_status = getreg32(UART_INT_ST_REG(priv->id)); +#ifdef HAVE_RS485 + if ((int_status & UART_TX_BRK_IDLE_DONE_INT_ST_M) != 0 && + esp32s2_txempty(dev)) + { + if (dev->xmit.tail == dev->xmit.head) + { + esp32s2_gpiowrite(priv->rs485_dir_gpio, + !priv->rs485_dir_polarity); + } + } +#endif + /* TX FIFO empty interrupt or UART TX done int */ if (int_status & tx_mask) @@ -369,6 +382,21 @@ static int esp32s2_setup(struct uart_dev_s *dev) esp32s2_lowputc_set_oflow(priv, false); } #endif +#ifdef HAVE_RS485 + + /* Configure the idle time between transfers */ + + if (priv->rs485_dir_gpio != 0) + { + esp32s2_lowputc_set_tx_idle_time(priv, 1); + } + else +#endif + { + /* No Tx idle interval */ + + esp32s2_lowputc_set_tx_idle_time(priv, 0); + } /* Reset FIFOs */ @@ -513,6 +541,18 @@ static void esp32s2_txint(struct uart_dev_s *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) + { + modifyreg32(UART_INT_ENA_REG(priv->id), + 0, UART_TX_BRK_IDLE_DONE_INT_ENA); + } +#endif + /* Set to receive an interrupt when the TX holding FIFO is empty or * a transmission is done. */ @@ -658,7 +698,16 @@ static bool esp32s2_txempty(struct uart_dev_s *dev) static void esp32s2_send(struct uart_dev_s *dev, int ch) { - esp32s2_lowputc_send_byte(dev->priv, ch); + struct esp32s2_uart_s *priv = dev->priv; + +#ifdef HAVE_RS485 + if (priv->rs485_dir_gpio != 0) + { + esp32s2_gpiowrite(priv->rs485_dir_gpio, priv->rs485_dir_polarity); + } +#endif + + esp32s2_lowputc_send_byte(priv, (char)ch); } /****************************************************************************