Skip to content

Commit b27ab80

Browse files
Anouar Raddaouikartben
authored andcommitted
modbus: support UART ASYNC API in modbus serial
Reason for this extension are CRC mismatches happening due to RX data loss when using the interrupt driven API at higher speeds and/or high CPU load. It is recommended to use the ASYNC API along with hardware byte counting requiring a timer instance and one PPI channel to achieve a robust communication at high baudrates on nRF platforms. Signed-off-by: Anouar Raddaoui <[email protected]>
1 parent c77e5a6 commit b27ab80

File tree

2 files changed

+86
-13
lines changed

2 files changed

+86
-13
lines changed

subsys/modbus/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ config MODBUS_SERIAL
4848
help
4949
Enable Modbus over serial line support.
5050

51+
config MODBUS_SERIAL_ASYNC_API
52+
depends on MODBUS_SERIAL && UART_ASYNC_API
53+
bool "Modbus over serial line UART ASYNC API support"
54+
help
55+
Use UART ASYNC API for Modbus over serial line.
56+
5157
config MODBUS_ASCII_MODE
5258
depends on MODBUS_SERIAL
5359
bool "Modbus transmission mode ASCII"

subsys/modbus/modbus_serial.c

Lines changed: 80 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,26 @@ static void modbus_serial_tx_on(struct modbus_context *ctx)
3737
gpio_pin_set(cfg->de->port, cfg->de->pin, 1);
3838
}
3939

40-
uart_irq_tx_enable(cfg->dev);
40+
if (IS_ENABLED(CONFIG_MODBUS_SERIAL_ASYNC_API)) {
41+
int err;
42+
43+
err = uart_tx(cfg->dev, cfg->uart_buf, cfg->uart_buf_ctr, SYS_FOREVER_US);
44+
if (err) {
45+
LOG_ERR("uart_tx failed with %d", err);
46+
}
47+
} else {
48+
uart_irq_tx_enable(cfg->dev);
49+
}
4150
}
4251

4352
static void modbus_serial_tx_off(struct modbus_context *ctx)
4453
{
4554
struct modbus_serial_config *cfg = ctx->cfg;
4655

47-
uart_irq_tx_disable(cfg->dev);
56+
if (!IS_ENABLED(CONFIG_MODBUS_SERIAL_ASYNC_API)) {
57+
uart_irq_tx_disable(cfg->dev);
58+
}
59+
4860
if (cfg->de != NULL) {
4961
gpio_pin_set(cfg->de->port, cfg->de->pin, 0);
5062
}
@@ -70,14 +82,29 @@ static void modbus_serial_rx_on(struct modbus_context *ctx)
7082
}
7183

7284
atomic_set_bit(&ctx->state, MODBUS_STATE_RX_ENABLED);
73-
uart_irq_rx_enable(cfg->dev);
85+
if (IS_ENABLED(CONFIG_MODBUS_SERIAL_ASYNC_API)) {
86+
int err;
87+
88+
err = uart_rx_enable(cfg->dev, cfg->uart_buf,
89+
sizeof(cfg->uart_buf), cfg->rtu_timeout);
90+
if (err) {
91+
LOG_ERR("uart_rx_enable failed with %d", err);
92+
}
93+
} else {
94+
uart_irq_rx_enable(cfg->dev);
95+
}
7496
}
7597

7698
static void modbus_serial_rx_off(struct modbus_context *ctx)
7799
{
78100
struct modbus_serial_config *cfg = ctx->cfg;
79101

80-
uart_irq_rx_disable(cfg->dev);
102+
if (IS_ENABLED(CONFIG_MODBUS_SERIAL_ASYNC_API)) {
103+
uart_rx_disable(cfg->dev);
104+
} else {
105+
uart_irq_rx_disable(cfg->dev);
106+
}
107+
81108
atomic_clear_bit(&ctx->state, MODBUS_STATE_RX_ENABLED);
82109

83110
if (cfg->re != NULL) {
@@ -424,6 +451,44 @@ static void uart_cb_handler(const struct device *dev, void *app_data)
424451
}
425452
}
426453

454+
static void uart_cb_async_handler(const struct device *dev, struct uart_event *evt, void *app_data)
455+
{
456+
struct modbus_context *ctx = (struct modbus_context *)app_data;
457+
struct modbus_serial_config *cfg;
458+
459+
if (ctx == NULL) {
460+
LOG_ERR("Modbus hardware is not properly initialized");
461+
return;
462+
}
463+
464+
cfg = ctx->cfg;
465+
466+
switch (evt->type) {
467+
case UART_TX_DONE:
468+
cfg->uart_buf_ctr = 0;
469+
modbus_serial_tx_off(ctx);
470+
modbus_serial_rx_on(ctx);
471+
break;
472+
case UART_RX_RDY:
473+
cfg->uart_buf_ctr = evt->data.rx.len;
474+
k_work_submit(&ctx->server_work);
475+
break;
476+
case UART_TX_ABORTED:
477+
__fallthrough;
478+
case UART_RX_STOPPED:
479+
__fallthrough;
480+
case UART_RX_BUF_REQUEST:
481+
__fallthrough;
482+
case UART_RX_BUF_RELEASED:
483+
__fallthrough;
484+
case UART_RX_DISABLED:
485+
break;
486+
default:
487+
LOG_WRN("Unhandled UART event type: %d", evt->type);
488+
break;
489+
}
490+
}
491+
427492
/* This function is called when the RTU framing timer expires. */
428493
static void rtu_tmr_handler(struct k_timer *t_id)
429494
{
@@ -621,18 +686,20 @@ int modbus_serial_init(struct modbus_context *ctx,
621686
cfg->uart_buf_ctr = 0;
622687
cfg->uart_buf_ptr = &cfg->uart_buf[0];
623688

624-
err = uart_irq_callback_user_data_set(cfg->dev, uart_cb_handler, ctx);
625-
if (err != 0) {
626-
return err;
627-
};
628-
629-
k_timer_init(&cfg->rtu_timer, rtu_tmr_handler, NULL);
630-
k_timer_user_data_set(&cfg->rtu_timer, ctx);
689+
if (IS_ENABLED(CONFIG_MODBUS_SERIAL_ASYNC_API)) {
690+
err = uart_callback_set(cfg->dev, uart_cb_async_handler, ctx);
691+
} else {
692+
err = uart_irq_callback_user_data_set(cfg->dev, uart_cb_handler, ctx);
693+
if (!err) {
694+
k_timer_init(&cfg->rtu_timer, rtu_tmr_handler, NULL);
695+
k_timer_user_data_set(&cfg->rtu_timer, ctx);
696+
modbus_serial_rx_on(ctx);
697+
}
698+
}
631699

632-
modbus_serial_rx_on(ctx);
633700
LOG_INF("RTU timeout %u us", cfg->rtu_timeout);
634701

635-
return 0;
702+
return err;
636703
}
637704

638705
void modbus_serial_disable(struct modbus_context *ctx)

0 commit comments

Comments
 (0)