arch/risc-v/espressif: Add RS485 support for esp32[c3|c6|h2]

Add RS485 support for Risc-V based Espressif devices

Signed-off-by: Eren Terzioglu <eren.terzioglu@espressif.com>
This commit is contained in:
Eren Terzioglu 2025-03-26 13:59:48 +01:00 committed by Xiang Xiao
parent 4d70fa74e8
commit 929c6313ff
5 changed files with 134 additions and 4 deletions

View File

@ -1090,7 +1090,7 @@ config ESPRESSIF_WIFI_RX_BA_WIN
Set the size of WiFi Block Ack RX window. Generally a bigger value means higher throughput and better
compatibility but more memory. Most of time we should NOT change the default value unless special
reason, e.g. test the maximum UDP RX throughput with iperf etc. For iperf test in shieldbox, the
recommended value is 9~12. If PSRAM is used and WiFi memory is prefered to allocat in PSRAM first,
recommended value is 9~12. If PSRAM is used and WiFi memory is preferred to allocate in PSRAM first,
the default and minimum value should be 16 to achieve better throughput and compatibility with both
stations and APs.
@ -1193,6 +1193,31 @@ menu "UART Configuration"
if ESPRESSIF_UART0
config ESPRESSIF_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 ESPRESSIF_UART0_RS485_DIR_PIN
int "UART0 RS-485 DIR pin"
default 10
range 0 46
depends on ESPRESSIF_UART0_RS485
---help---
DIR pin for RS-485 on UART0. This pin will control the RS485 enable
TX of the RS485 transceiver.
config ESPRESSIF_UART0_RS485_DIR_POLARITY
int "UART0 RS-485 DIR pin polarity"
default 1
range 0 1
depends on ESPRESSIF_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 ESPRESSIF_UART0_TXPIN
int "UART0 TX Pin"
default 21 if ESPRESSIF_ESP32C3
@ -1235,6 +1260,31 @@ endif # ESPRESSIF_UART0
if ESPRESSIF_UART1
config ESPRESSIF_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 ESPRESSIF_UART1_RS485_DIR_PIN
int "UART1 RS-485 DIR pin"
default 4
range 0 46
depends on ESPRESSIF_UART1_RS485
---help---
DIR pin for RS-485 on UART1. This pin will control the RS485 enable
TX of the RS485 transceiver.
config ESPRESSIF_UART1_RS485_DIR_POLARITY
int "UART1 RS-485 DIR pin polarity"
default 1
range 0 1
depends on ESPRESSIF_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 ESPRESSIF_UART1_TXPIN
int "UART1 TX Pin"
default 8 if ESPRESSIF_ESP32C3 || ESPRESSIF_ESP32C6 || ESPRESSIF_ESP32H2

View File

@ -44,6 +44,13 @@
# define HAVE_UART_DEVICE 1 /* Flag to indicate a UART has been selected */
#endif
/* Is RS-485 used? */
#if defined(CONFIG_ESPRESSIF_UART0_RS485) || \
defined(CONFIG_ESPRESSIF_UART1_RS485)
# define HAVE_RS485 1
#endif
/* Serial Console ***********************************************************/
/* Is there a serial console? There should be no more than one defined. It

View File

@ -101,6 +101,14 @@ struct esp_uart_s g_uart0_config =
#else
.oflow = false, /* output flow control (CTS) disabled */
#endif
#endif
#ifdef CONFIG_ESPRESSIF_UART0_RS485
.rs485_dir_gpio = CONFIG_ESPRESSIF_UART0_RS485_DIR_PIN,
#if (CONFIG_ESPRESSIF_UART0_RS485_DIR_POLARITY == 0)
.rs485_dir_polarity = false,
#else
.rs485_dir_polarity = true,
#endif
#endif
.hal = &g_uart0_hal,
.lock = SP_UNLOCKED
@ -147,6 +155,14 @@ struct esp_uart_s g_uart1_config =
#else
.oflow = false, /* output flow control (CTS) disabled */
#endif
#endif
#ifdef CONFIG_ESPRESSIF_UART1_RS485
.rs485_dir_gpio = CONFIG_ESPRESSIF_UART1_RS485_DIR_PIN,
#if (CONFIG_ESPRESSIF_UART1_RS485_DIR_POLARITY == 0)
.rs485_dir_polarity = false,
#else
.rs485_dir_polarity = true,
#endif
#endif
.hal = &g_uart1_hal,
.lock = SP_UNLOCKED
@ -289,6 +305,15 @@ void esp_lowputc_config_pins(const struct esp_uart_s *priv)
esp_gpio_matrix_in(priv->ctspin, priv->ctssig, 0);
}
#endif
#ifdef HAVE_RS485
if (priv->rs485_dir_gpio != 0)
{
esp_configgpio(priv->rs485_dir_gpio, OUTPUT);
esp_gpio_matrix_out(priv->rs485_dir_gpio, SIG_GPIO_OUT_IDX, 0, 0);
esp_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity);
}
#endif
}
/****************************************************************************

View File

@ -75,6 +75,10 @@ struct esp_uart_s
uint8_t ctspin; /* CTS pin number */
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
uart_hal_context_t *hal; /* HAL context */
spinlock_t lock; /* Spinlock */

View File

@ -48,6 +48,7 @@
#include "esp_config.h"
#include "esp_irq.h"
#include "esp_lowputc.h"
#include "esp_gpio.h"
#ifdef CONFIG_ESPRESSIF_USBSERIAL
# include "esp_usbserial.h"
@ -273,6 +274,19 @@ static int uart_handler(int irq, void *context, void *arg)
uint32_t rx_mask = UART_INTR_RXFIFO_TOUT | UART_INTR_RXFIFO_FULL;
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))
{
uart_hal_clr_intsts_mask(priv->hal, UART_INTR_TX_BRK_IDLE);
if (dev->xmit.tail == dev->xmit.head)
{
esp_gpiowrite(priv->rs485_dir_gpio,
!priv->rs485_dir_polarity);
}
}
#endif
/* Tx fifo empty interrupt or UART tx done int */
if ((int_status & tx_mask) != 0)
@ -437,6 +451,17 @@ static int esp_setup(uart_dev_t *dev)
uart_hal_set_hw_flow_ctrl(priv->hal, flow_ctrl, rx_thrs);
#endif /* CONFIG_SERIAL_IFLOWCONTROL || CONFIG_SERIAL_OFLOWCONTROL */
#ifdef HAVE_RS485
/* Configure the idle time between transfers */
if (priv->rs485_dir_gpio != 0)
{
uart_hal_set_tx_idle_num(priv->hal, 1);
}
else
#endif
/* Clear FIFOs */
uart_hal_rxfifo_rst(priv->hal);
@ -575,6 +600,16 @@ 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
*/
@ -711,7 +746,16 @@ static bool esp_txempty(uart_dev_t *dev)
static void esp_send(uart_dev_t *dev, int ch)
{
esp_lowputc_send_byte(dev->priv, ch);
struct esp_uart_s *priv = dev->priv;
#ifdef HAVE_RS485
if (priv->rs485_dir_gpio != 0)
{
esp_gpiowrite(priv->rs485_dir_gpio, priv->rs485_dir_polarity);
}
#endif
esp_lowputc_send_byte(priv, ch);
}
/****************************************************************************
@ -1058,7 +1102,7 @@ static bool esp_rxflowcontrol(uart_dev_t *dev, unsigned int nbuffered,
*
* Description:
* Performs the low level UART initialization early in debug so that the
* serial console will be available during bootup. This must be called
* serial console will be available during boot-up. This must be called
* before riscv_serialinit.
* NOTE: This function depends on GPIO pin configuration performed in
* in up_consoleinit() and main clock initialization performed in
@ -1089,7 +1133,7 @@ void riscv_earlyserialinit(void)
#endif
/* Configure console in early step.
* Setup for other serials will be perfomed when the serial driver is
* Setup for other serials will be performed when the serial driver is
* open.
*/