modbus: add control for DE/nRE RS-485 transceiver signals

Add support to control DE/nRE RS-485 transceiver signals
over GPIO pins. Useful if the UART controller does not
support RS-485 mode.

Signed-off-by: Johann Fischer <johann.fischer@nordicsemi.no>
This commit is contained in:
Johann Fischer 2020-12-22 16:49:57 +01:00 committed by Carles Cufí
commit c96aeff785
3 changed files with 134 additions and 12 deletions

View file

@ -6,3 +6,22 @@ description: Modbus over serial line device
compatible: "zephyr,modbus-serial"
include: uart-device.yaml
properties:
de-gpios:
type: phandle-array
required: false
description: Driver enable pin.
Driver enable pin (DE) of the RS-485 transceiver.
If connected directly the MCU pin should be configured
as active high.
re-gpios:
type: phandle-array
required: false
description: Receiver enable pin.
Receiver enable pin (nRE) of the RS-485 transceiver.
If connected directly the MCU pin should be configured
as active low.

View file

@ -29,12 +29,69 @@ LOG_MODULE_REGISTER(mb_rtu, CONFIG_MODBUS_RTU_LOG_LEVEL);
#define DT_DRV_COMPAT zephyr_modbus_serial
#define MODBUS_DT_GET_DEV(port) {.dev_name = DT_INST_BUS_LABEL(port),},
#define MB_RTU_DEFINE_GPIO_CFG(n, d) \
static struct mb_rtu_gpio_config d##_cfg_##n = { \
.name = DT_INST_GPIO_LABEL(n, d), \
.pin = DT_INST_GPIO_PIN(n, d), \
.flags = DT_INST_GPIO_FLAGS(n, d), \
};
#define MB_RTU_DEFINE_GPIO_CFGS(n) \
COND_CODE_1(DT_INST_NODE_HAS_PROP(n, de_gpios), \
(MB_RTU_DEFINE_GPIO_CFG(n, de_gpios)), ()) \
COND_CODE_1(DT_INST_NODE_HAS_PROP(n, re_gpios), \
(MB_RTU_DEFINE_GPIO_CFG(n, re_gpios)), ())
DT_INST_FOREACH_STATUS_OKAY(MB_RTU_DEFINE_GPIO_CFGS)
#define MB_RTU_ASSIGN_GPIO_CFG(n, d) \
COND_CODE_1(DT_INST_NODE_HAS_PROP(n, d), \
(&d##_cfg_##n), (NULL))
#define MODBUS_DT_GET_DEV(n) { \
.dev_name = DT_INST_BUS_LABEL(n), \
.de = MB_RTU_ASSIGN_GPIO_CFG(n, de_gpios), \
.re = MB_RTU_ASSIGN_GPIO_CFG(n, re_gpios), \
},
static struct mb_rtu_context mb_ctx_tbl[] = {
DT_INST_FOREACH_STATUS_OKAY(MODBUS_DT_GET_DEV)
};
static void mb_tx_enable(struct mb_rtu_context *ctx)
{
if (ctx->de != NULL) {
gpio_pin_set(ctx->de->dev, ctx->de->pin, 1);
}
uart_irq_tx_enable(ctx->dev);
}
static void mb_tx_disable(struct mb_rtu_context *ctx)
{
uart_irq_tx_disable(ctx->dev);
if (ctx->de != NULL) {
gpio_pin_set(ctx->de->dev, ctx->de->pin, 0);
}
}
static void mb_rx_enable(struct mb_rtu_context *ctx)
{
if (ctx->re != NULL) {
gpio_pin_set(ctx->re->dev, ctx->re->pin, 1);
}
uart_irq_rx_enable(ctx->dev);
}
static void mb_rx_disable(struct mb_rtu_context *ctx)
{
uart_irq_rx_disable(ctx->dev);
if (ctx->re != NULL) {
gpio_pin_set(ctx->re->dev, ctx->re->pin, 0);
}
}
#ifdef CONFIG_MODBUS_RTU_ASCII_MODE
/* The function calculates an 8-bit Longitudinal Redundancy Check. */
static uint8_t mb_ascii_get_lrc(uint8_t *src, size_t length)
@ -192,8 +249,8 @@ static void mb_tx_ascii_frame(struct mb_rtu_context *ctx)
ctx->uart_buf_ptr = &ctx->uart_buf[0];
LOG_DBG("Start frame transmission");
uart_irq_rx_disable(ctx->dev);
uart_irq_tx_enable(ctx->dev);
mb_rx_disable(ctx);
mb_tx_enable(ctx);
}
#else
static int mb_rx_ascii_frame(struct mb_rtu_context *ctx)
@ -300,8 +357,8 @@ static void mb_tx_rtu_frame(struct mb_rtu_context *ctx)
LOG_HEXDUMP_DBG(ctx->uart_buf, ctx->uart_buf_ctr, "uart_buf");
LOG_DBG("Start frame transmission");
uart_irq_rx_disable(ctx->dev);
uart_irq_tx_enable(ctx->dev);
mb_rx_disable(ctx);
mb_tx_enable(ctx);
}
void mb_tx_frame(struct mb_rtu_context *ctx)
@ -372,8 +429,8 @@ static void mb_cb_handler_tx(struct mb_rtu_context *ctx)
} else {
/* Disable transmission */
ctx->uart_buf_ptr = &ctx->uart_buf[0];
uart_irq_tx_disable(ctx->dev);
uart_irq_rx_enable(ctx->dev);
mb_tx_disable(ctx);
mb_rx_enable(ctx);
}
}
@ -408,7 +465,7 @@ static void mb_rx_handler(struct k_work *item)
return;
}
uart_irq_rx_disable(ctx->dev);
mb_rx_disable(ctx);
if (IS_ENABLED(CONFIG_MODBUS_RTU_ASCII_MODE) &&
(ctx->ascii_mode == true)) {
@ -422,7 +479,7 @@ static void mb_rx_handler(struct k_work *item)
} else if (IS_ENABLED(CONFIG_MODBUS_RTU_SERVER)) {
if (mbs_rx_handler(ctx) == false) {
/* Server does not send response, re-enable RX */
uart_irq_rx_enable(ctx->dev);
mb_rx_enable(ctx);
}
}
}
@ -487,7 +544,7 @@ static int mb_configure_uart(struct mb_rtu_context *ctx,
}
uart_irq_callback_user_data_set(ctx->dev, mb_uart_cb_handler, ctx);
uart_irq_rx_enable(ctx->dev);
mb_rx_enable(ctx);
if (baudrate <= 38400) {
ctx->rtu_timeout = (numof_bits * if_delay_max) / baudrate;
@ -519,6 +576,36 @@ struct mb_rtu_context *mb_get_context(const uint8_t iface)
return ctx;
}
static int mb_configure_gpio(struct mb_rtu_context *ctx)
{
if (ctx->de != NULL) {
ctx->de->dev = device_get_binding(ctx->de->name);
if (ctx->de->dev == NULL) {
return -ENODEV;
}
if (gpio_pin_configure(ctx->de->dev, ctx->de->pin,
GPIO_OUTPUT_INACTIVE | ctx->de->flags)) {
return -EIO;
}
}
if (ctx->re != NULL) {
ctx->re->dev = device_get_binding(ctx->re->name);
if (ctx->re->dev == NULL) {
return -ENODEV;
}
if (gpio_pin_configure(ctx->re->dev, ctx->re->pin,
GPIO_OUTPUT_INACTIVE | ctx->re->flags)) {
return -EIO;
}
}
return 0;
}
static struct mb_rtu_context *mb_cfg_iface(const uint8_t iface,
const uint8_t node_addr,
const uint32_t baud,
@ -565,6 +652,10 @@ static struct mb_rtu_context *mb_cfg_iface(const uint8_t iface,
mbs_reset_statistics(ctx);
}
if (mb_configure_gpio(ctx) != 0) {
return NULL;
}
if (mb_configure_uart(ctx, baud, parity) != 0) {
LOG_ERR("Failed to configure UART");
return NULL;
@ -638,8 +729,8 @@ int mb_rtu_disable_iface(const uint8_t iface)
ctx = &mb_ctx_tbl[iface];
uart_irq_tx_disable(ctx->dev);
uart_irq_rx_disable(ctx->dev);
mb_tx_disable(ctx);
mb_rx_disable(ctx);
k_timer_stop(&ctx->rtu_timer);
ctx->rxwait_to = 0;

View file

@ -23,6 +23,7 @@
#define ZEPHYR_INCLUDE_MODBUS_RTU_INTERNAL_H_
#include <zephyr.h>
#include <drivers/gpio.h>
#include <modbus/modbus_rtu.h>
#ifdef CONFIG_MODBUS_RTU_FP_EXTENSIONS
@ -78,6 +79,13 @@ struct mb_rtu_frame {
uint16_t crc;
};
struct mb_rtu_gpio_config {
const char *name;
const struct device *dev;
gpio_pin_t pin;
gpio_dt_flags_t flags;
};
#define MB_RTU_STATE_CONFIGURED 0
struct mb_rtu_context {
@ -99,6 +107,10 @@ struct mb_rtu_context {
atomic_t state;
/* Pointer to current position in buffer */
uint8_t *uart_buf_ptr;
/* Pointer to driver enable (DE) pin config */
struct mb_rtu_gpio_config *de;
/* Pointer to receiver enable (nRE) pin config */
struct mb_rtu_gpio_config *re;
/* Client's mutually exclusive access */
struct k_mutex iface_lock;