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:
parent
92b11e2645
commit
c96aeff785
3 changed files with 134 additions and 12 deletions
|
@ -6,3 +6,22 @@ description: Modbus over serial line device
|
||||||
compatible: "zephyr,modbus-serial"
|
compatible: "zephyr,modbus-serial"
|
||||||
|
|
||||||
include: uart-device.yaml
|
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.
|
||||||
|
|
|
@ -29,12 +29,69 @@ LOG_MODULE_REGISTER(mb_rtu, CONFIG_MODBUS_RTU_LOG_LEVEL);
|
||||||
|
|
||||||
#define DT_DRV_COMPAT zephyr_modbus_serial
|
#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[] = {
|
static struct mb_rtu_context mb_ctx_tbl[] = {
|
||||||
DT_INST_FOREACH_STATUS_OKAY(MODBUS_DT_GET_DEV)
|
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
|
#ifdef CONFIG_MODBUS_RTU_ASCII_MODE
|
||||||
/* The function calculates an 8-bit Longitudinal Redundancy Check. */
|
/* The function calculates an 8-bit Longitudinal Redundancy Check. */
|
||||||
static uint8_t mb_ascii_get_lrc(uint8_t *src, size_t length)
|
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];
|
ctx->uart_buf_ptr = &ctx->uart_buf[0];
|
||||||
|
|
||||||
LOG_DBG("Start frame transmission");
|
LOG_DBG("Start frame transmission");
|
||||||
uart_irq_rx_disable(ctx->dev);
|
mb_rx_disable(ctx);
|
||||||
uart_irq_tx_enable(ctx->dev);
|
mb_tx_enable(ctx);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static int mb_rx_ascii_frame(struct mb_rtu_context *ctx)
|
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_HEXDUMP_DBG(ctx->uart_buf, ctx->uart_buf_ctr, "uart_buf");
|
||||||
LOG_DBG("Start frame transmission");
|
LOG_DBG("Start frame transmission");
|
||||||
uart_irq_rx_disable(ctx->dev);
|
mb_rx_disable(ctx);
|
||||||
uart_irq_tx_enable(ctx->dev);
|
mb_tx_enable(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mb_tx_frame(struct mb_rtu_context *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 {
|
} else {
|
||||||
/* Disable transmission */
|
/* Disable transmission */
|
||||||
ctx->uart_buf_ptr = &ctx->uart_buf[0];
|
ctx->uart_buf_ptr = &ctx->uart_buf[0];
|
||||||
uart_irq_tx_disable(ctx->dev);
|
mb_tx_disable(ctx);
|
||||||
uart_irq_rx_enable(ctx->dev);
|
mb_rx_enable(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,7 +465,7 @@ static void mb_rx_handler(struct k_work *item)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uart_irq_rx_disable(ctx->dev);
|
mb_rx_disable(ctx);
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_MODBUS_RTU_ASCII_MODE) &&
|
if (IS_ENABLED(CONFIG_MODBUS_RTU_ASCII_MODE) &&
|
||||||
(ctx->ascii_mode == true)) {
|
(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)) {
|
} else if (IS_ENABLED(CONFIG_MODBUS_RTU_SERVER)) {
|
||||||
if (mbs_rx_handler(ctx) == false) {
|
if (mbs_rx_handler(ctx) == false) {
|
||||||
/* Server does not send response, re-enable RX */
|
/* 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_callback_user_data_set(ctx->dev, mb_uart_cb_handler, ctx);
|
||||||
uart_irq_rx_enable(ctx->dev);
|
mb_rx_enable(ctx);
|
||||||
|
|
||||||
if (baudrate <= 38400) {
|
if (baudrate <= 38400) {
|
||||||
ctx->rtu_timeout = (numof_bits * if_delay_max) / baudrate;
|
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;
|
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,
|
static struct mb_rtu_context *mb_cfg_iface(const uint8_t iface,
|
||||||
const uint8_t node_addr,
|
const uint8_t node_addr,
|
||||||
const uint32_t baud,
|
const uint32_t baud,
|
||||||
|
@ -565,6 +652,10 @@ static struct mb_rtu_context *mb_cfg_iface(const uint8_t iface,
|
||||||
mbs_reset_statistics(ctx);
|
mbs_reset_statistics(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mb_configure_gpio(ctx) != 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (mb_configure_uart(ctx, baud, parity) != 0) {
|
if (mb_configure_uart(ctx, baud, parity) != 0) {
|
||||||
LOG_ERR("Failed to configure UART");
|
LOG_ERR("Failed to configure UART");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -638,8 +729,8 @@ int mb_rtu_disable_iface(const uint8_t iface)
|
||||||
|
|
||||||
ctx = &mb_ctx_tbl[iface];
|
ctx = &mb_ctx_tbl[iface];
|
||||||
|
|
||||||
uart_irq_tx_disable(ctx->dev);
|
mb_tx_disable(ctx);
|
||||||
uart_irq_rx_disable(ctx->dev);
|
mb_rx_disable(ctx);
|
||||||
k_timer_stop(&ctx->rtu_timer);
|
k_timer_stop(&ctx->rtu_timer);
|
||||||
|
|
||||||
ctx->rxwait_to = 0;
|
ctx->rxwait_to = 0;
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#define ZEPHYR_INCLUDE_MODBUS_RTU_INTERNAL_H_
|
#define ZEPHYR_INCLUDE_MODBUS_RTU_INTERNAL_H_
|
||||||
|
|
||||||
#include <zephyr.h>
|
#include <zephyr.h>
|
||||||
|
#include <drivers/gpio.h>
|
||||||
#include <modbus/modbus_rtu.h>
|
#include <modbus/modbus_rtu.h>
|
||||||
|
|
||||||
#ifdef CONFIG_MODBUS_RTU_FP_EXTENSIONS
|
#ifdef CONFIG_MODBUS_RTU_FP_EXTENSIONS
|
||||||
|
@ -78,6 +79,13 @@ struct mb_rtu_frame {
|
||||||
uint16_t crc;
|
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
|
#define MB_RTU_STATE_CONFIGURED 0
|
||||||
|
|
||||||
struct mb_rtu_context {
|
struct mb_rtu_context {
|
||||||
|
@ -99,6 +107,10 @@ struct mb_rtu_context {
|
||||||
atomic_t state;
|
atomic_t state;
|
||||||
/* Pointer to current position in buffer */
|
/* Pointer to current position in buffer */
|
||||||
uint8_t *uart_buf_ptr;
|
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 */
|
/* Client's mutually exclusive access */
|
||||||
struct k_mutex iface_lock;
|
struct k_mutex iface_lock;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue