Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions drivers/serial/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ config UART_USE_RUNTIME_CONFIGURE
Say y if unsure. Disable this to reduce footprint for
applications that do not require runtime UART configuration.

config SERIAL_SUPPORT_RS485
bool
help
This is an option to be enabled by individual serial driver
to signal that the driver and hardware supports RS485.

config UART_ASYNC_API
bool "Asynchronous UART API"
depends on SERIAL_SUPPORT_ASYNC
Expand Down
1 change: 1 addition & 0 deletions drivers/serial/Kconfig.stm32
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ config UART_STM32
default y
depends on DT_HAS_ST_STM32_UART_ENABLED
select SERIAL_HAS_DRIVER
select SERIAL_SUPPORT_RS485
select SERIAL_SUPPORT_INTERRUPT
# the ASYNC implementation requires a DMA controller
select SERIAL_SUPPORT_ASYNC \
Expand Down
109 changes: 88 additions & 21 deletions drivers/serial/uart_stm32.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
* Copyright (c) 2016 Open-RnD Sp. z o.o.
* Copyright (c) 2016 Linaro Limited.
* Copyright (c) 2024 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*/

Expand Down Expand Up @@ -66,6 +65,21 @@ LOG_MODULE_REGISTER(uart_stm32, CONFIG_UART_LOG_LEVEL);
#define HAS_DRIVER_ENABLE 0
#endif

/* On platforms without HW DE support, SW DE is the only option.
* On platforms with HW DE support, SW DE is still available as fallback.
* To minimize SW footprint, only compile SW DE on platforms that need it.
*/
#if !HAS_DRIVER_ENABLE
#define UART_STM32_NEEDS_SW_DE 1
#else
#define UART_STM32_NEEDS_SW_DE 0
#endif



/* Helper macro to check if SW DE might be needed */
#define UART_STM32_SW_DE_NEEDED(config) ((config)->de_enable && (config)->de_pin.port != NULL)

/* Helper for checking if we can use hardware receive timeouts
* instead of the work queue on a given UART
*/
Expand Down Expand Up @@ -112,6 +126,23 @@ uint32_t lpuartdiv_calc(const uint64_t clock_rate, const uint32_t baud_rate)
#define STM32_ASYNC_STATUS_TIMEOUT (DMA_STATUS_BLOCK + 1)
#endif

/* The RS485 DE management is now handled based on de_enable and de_pin fields:
* - If de_enable is false, no DE control is performed.
* - If de_enable is true and de_pin is not specified (port is NULL), the hardware
* method is assumed (HW control via driver enable functions).
* - If de_enable is true and de_pin is defined, a software (SW) method is used.
*/

#if UART_STM32_NEEDS_SW_DE || HAS_DRIVER_ENABLE
static void rs485_de_time_expire_callback(struct k_timer *timer)
{
const struct uart_stm32_config *config = k_timer_user_data_get(timer);

/* SW method: deassert DE signal */
gpio_pin_set(config->de_pin.port, config->de_pin.pin, !config->de_invert);
}
#endif

#ifdef CONFIG_PM
static void uart_stm32_pm_policy_state_lock_get_unconditional(void)
{
Expand Down Expand Up @@ -622,14 +653,13 @@ static int uart_stm32_configure(const struct device *dev,
}

/* Driver supports only RTS/CTS and RS485 flow control */
if (!(cfg->flow_ctrl == UART_CFG_FLOW_CTRL_NONE
|| (cfg->flow_ctrl == UART_CFG_FLOW_CTRL_RTS_CTS &&
IS_UART_HWFLOW_INSTANCE(usart))
if (!(cfg->flow_ctrl == UART_CFG_FLOW_CTRL_NONE ||
(cfg->flow_ctrl == UART_CFG_FLOW_CTRL_RTS_CTS && IS_UART_HWFLOW_INSTANCE(usart))
#if HAS_DRIVER_ENABLE
|| (cfg->flow_ctrl == UART_CFG_FLOW_CTRL_RS485 &&
IS_UART_DRIVER_ENABLE_INSTANCE(usart))
||
(cfg->flow_ctrl == UART_CFG_FLOW_CTRL_RS485 && IS_UART_DRIVER_ENABLE_INSTANCE(usart))
#endif
)) {
)) {
return -ENOTSUP;
}

Expand Down Expand Up @@ -1006,6 +1036,14 @@ static void uart_stm32_irq_tx_enable(const struct device *dev)
unsigned int key;
#endif

/* RS485 DE management: SW method when de_pin is specified */
#if UART_STM32_NEEDS_SW_DE || HAS_DRIVER_ENABLE
if (config->de_enable && config->de_pin.port != NULL) {
/* SW method: set DE active */
gpio_pin_set(config->de_pin.port, config->de_pin.pin, config->de_invert);
}
#endif

#ifdef CONFIG_PM
key = irq_lock();
data->tx_poll_stream_on = false;
Expand All @@ -1022,8 +1060,24 @@ static void uart_stm32_irq_tx_enable(const struct device *dev)
static void uart_stm32_irq_tx_disable(const struct device *dev)
{
const struct uart_stm32_config *config = dev->config;
#ifdef CONFIG_PM
struct uart_stm32_data *data = dev->data;

/* RS485 DE management: SW deassertion when de_pin is specified */
#if UART_STM32_NEEDS_SW_DE || HAS_DRIVER_ENABLE
if (config->de_enable && config->de_pin.port != NULL) {
/* SW method: handle DE deassertion */
if (config->de_deassert_time_us) {
k_timer_start(&data->rs485_timer,
K_USEC(config->de_deassert_time_us),
K_NO_WAIT);
} else {
gpio_pin_set(config->de_pin.port, config->de_pin.pin,
!config->de_invert);
}
}
#endif

#ifdef CONFIG_PM
unsigned int key;

key = irq_lock();
Expand All @@ -1034,9 +1088,6 @@ static void uart_stm32_irq_tx_disable(const struct device *dev)
#ifdef CONFIG_PM
data->tx_int_stream_on = false;
uart_stm32_pm_policy_state_lock_put(dev);
#endif

#ifdef CONFIG_PM
irq_unlock(key);
#endif
}
Expand Down Expand Up @@ -2115,7 +2166,7 @@ static int uart_stm32_async_rx_buf_rsp_u16(const struct device *dev, uint16_t *b

#endif /* CONFIG_UART_ASYNC_API */

static DEVICE_API(uart, uart_stm32_driver_api) = {
static const struct uart_driver_api uart_stm32_driver_api = {
.poll_in = uart_stm32_poll_in,
.poll_out = uart_stm32_poll_out,
#ifdef CONFIG_UART_WIDE_DATA
Expand Down Expand Up @@ -2250,7 +2301,12 @@ static int uart_stm32_registers_configure(const struct device *dev)
return -EINVAL;
}

uart_stm32_set_driver_enable(dev, true);
/* If hardware DE control is desired, de_pin is not set.
* Otherwise, SW method will be used.
*/
if (config->de_pin.port == NULL) {
uart_stm32_set_driver_enable(dev, true);
}
LL_USART_SetDEAssertionTime(usart, config->de_assert_time);
LL_USART_SetDEDeassertionTime(usart, config->de_deassert_time);

Expand Down Expand Up @@ -2323,6 +2379,7 @@ static int uart_stm32_registers_configure(const struct device *dev)
static int uart_stm32_init(const struct device *dev)
{
const struct uart_stm32_config *config = dev->config;
struct uart_stm32_data *data = dev->data;
int err;

err = uart_stm32_clocks_enable(dev);
Expand All @@ -2341,9 +2398,18 @@ static int uart_stm32_init(const struct device *dev)
return err;
}

#if defined(CONFIG_PM) || \
defined(CONFIG_UART_INTERRUPT_DRIVEN) || \
defined(CONFIG_UART_ASYNC_API)
/* Init SW DE timer and pin for RS485 mode when needed */
#if UART_STM32_NEEDS_SW_DE || HAS_DRIVER_ENABLE
if (config->de_enable && config->de_pin.port != NULL) {
/* SW method: configure DE pin and initialize timer */
gpio_pin_set(config->de_pin.port, config->de_pin.pin,
config->de_invert);
k_timer_init(&data->rs485_timer, rs485_de_time_expire_callback, NULL);
k_timer_user_data_set(&data->rs485_timer, (void *)config);
}
#endif

#if defined(CONFIG_PM) || defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_UART_ASYNC_API)
config->irq_config_func(dev);
#endif /* CONFIG_PM || CONFIG_UART_INTERRUPT_DRIVEN || CONFIG_UART_ASYNC_API */

Expand Down Expand Up @@ -2637,11 +2703,12 @@ static const struct uart_stm32_config uart_stm32_cfg_##index = { \
.tx_rx_swap = DT_INST_PROP(index, tx_rx_swap), \
.rx_invert = DT_INST_PROP(index, rx_invert), \
.tx_invert = DT_INST_PROP(index, tx_invert), \
.de_enable = DT_INST_PROP(index, de_enable), \
.de_assert_time = DT_INST_PROP(index, de_assert_time), \
.de_deassert_time = DT_INST_PROP(index, de_deassert_time), \
.de_invert = DT_INST_PROP(index, de_invert), \
.fifo_enable = DT_INST_PROP(index, fifo_enable), \
.de_enable = DT_INST_PROP(index, rs485_enabled), \
.de_assert_time_us = DT_INST_PROP(index, rs485_assertion_time_de_us), \
.de_deassert_time_us = DT_INST_PROP(index, rs485_deassertion_time_de_us), \
.de_invert = DT_INST_PROP(index, rs485_de_active_low), \
.de_pin = GPIO_DT_SPEC_INST_GET_OR(index, rs485_de_gpios, {0}), \
.fifo_enable = DT_INST_PROP(index, fifo_enable), \
STM32_UART_IRQ_HANDLER_FUNC(index) \
STM32_UART_PM_WAKEUP(index) \
}; \
Expand Down
12 changes: 11 additions & 1 deletion drivers/serial/uart_stm32.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/reset.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/drivers/gpio.h>

#include <stm32_ll_usart.h>

Expand Down Expand Up @@ -48,6 +49,11 @@ struct uart_stm32_config {
bool fifo_enable;
/* pin muxing */
const struct pinctrl_dev_config *pcfg;
struct gpio_dt_spec de_pin;
/* de signal assertion time in nanoseconds */
uint32_t de_assert_time_us;
/* de signal deassertion time in nanoseconds */
uint32_t de_deassert_time_us;
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_UART_ASYNC_API) || \
defined(CONFIG_PM)
uart_irq_config_func_t irq_config_func;
Expand Down Expand Up @@ -89,7 +95,6 @@ struct uart_stm32_data {
uart_irq_callback_user_data_t user_cb;
void *user_data;
#endif

#ifdef CONFIG_UART_ASYNC_API
const struct device *uart_dev;
uart_callback_t async_cb;
Expand All @@ -105,6 +110,11 @@ struct uart_stm32_data {
bool pm_policy_state_on;
bool rx_woken;
#endif
/* SW DE timer - present when SW DE support may be needed */
#if !defined(USART_CR3_DEM) || defined(USART_CR3_DEM)
struct k_timer rs485_timer;
#endif

};

#endif /* ZEPHYR_DRIVERS_SERIAL_UART_STM32_H_ */
29 changes: 1 addition & 28 deletions dts/bindings/serial/st,stm32-uart-base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ include:
property-blocklist:
- clock-frequency
- name: pinctrl-device.yaml
- name: uart-rs485-controller.yaml
- name: reset-device.yaml
- name: uart-controller-pin-inversion.yaml

Expand Down Expand Up @@ -68,34 +69,6 @@ properties:
the core from stop mode(s).
Valid range: 0 - 31

de-enable:
type: boolean
description: |
Enable activating an external transeiver through the DE pin which must also be configured
using pinctrl.

de-assert-time:
type: int
default: 0
description: |
Defines the time between the activation of the DE signal and the beginning of the start bit.
It is expressed in 16th of a bit time.
Valid range: 0 - 31

de-deassert-time:
type: int
default: 0
description: |
Defines the time between the end of the stop bit and the deactivation of the DE signal.
It is expressed in 16th of a bit time.
Valid range: 0 - 31

de-invert:
type: boolean
description: |
Invert the binary logic of the de pin. When enabled, physical logic levels are inverted and
we use 1=Low, 0=High instead of 1=High, 0=Low.

fifo-enable:
type: boolean
description: |
Expand Down
36 changes: 36 additions & 0 deletions dts/bindings/serial/uart-rs485-controller.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Common fields for uart-rs485 controllers

include: uart-controller.yaml

properties:
rs485-enabled:
type: boolean
description: Enable rs485 mode
rs485-de-gpios:
type: phandle-array
description: Output for control RS485 driver
rs485-full-duplex-mode:
type: boolean
description: Enable rs485 in full duplex mode
rs485-de-active-low:
type: boolean
description: Driver enable active low polarity
rs485-re-active-high:
type: boolean
description: Receiver enable active high polarity
rs485-assertion-time-de-us:
type: int
default: 0
description: Driver enable assertion time in nanoseconds
rs485-deassertion-time-de-us:
type: int
default: 0
description: Driver enable deassertion time in nanoseconds
rs485-assertion-time-re-us:
type: int
default: 0
description: Receiver enable assertion time in nanoseconds
rs485-deassertion-time-re-us:
type: int
default: 0
description: Receiver enable deassertion time in nanoseconds
1 change: 1 addition & 0 deletions include/zephyr/drivers/uart.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ enum uart_line_ctrl {
UART_LINE_CTRL_DTR = BIT(2), /**< Data Terminal Ready (DTR) */
UART_LINE_CTRL_DCD = BIT(3), /**< Data Carrier Detect (DCD) */
UART_LINE_CTRL_DSR = BIT(4), /**< Data Set Ready (DSR) */
UART_LINE_CTRL_RS485 = BIT(5), /**< Enable/disable rs485 mode */
};

/**
Expand Down
7 changes: 7 additions & 0 deletions samples/drivers/uart/uart_rs485/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(rs485_api_sample)

target_sources(app PRIVATE src/main.c)
Loading
Loading