Commit 4ff616b6 authored by Johann Fischer's avatar Johann Fischer Committed by Carles Cufi
Browse files

modbus: rework interface configuration



Use commot parameter structure to configure server
or client interfaces.

Signed-off-by: default avatarJohann Fischer <johann.fischer@nordicsemi.no>
parent 55204d50
Loading
Loading
Loading
Loading
+64 −28
Original line number Diff line number Diff line
/*
 * Copyright (c) 2020 PHYTEC Messtechnik GmbH
 * Copyright (c) 2021 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */
@@ -347,44 +348,79 @@ struct modbus_user_callbacks {
int modbus_iface_get_by_name(const char *iface_name);

/**
 * @brief Configure Modbus Interface as server
 *
 * @param iface      Modbus interface index
 * @param unit_id    Modbus unit ID of the server
 * @param baud       Baudrate of the serial line
 * @param parity     UART's parity setting:
 * @brief Modbus interface mode
 */
enum modbus_mode {
	/** Modbus over serial line RTU mode */
	MODBUS_MODE_RTU,
	/** Modbus over serial line ASCII mode */
	MODBUS_MODE_ASCII,
};

/**
 * @brief Modbus serial line parameter
 */
struct modbus_serial_param {
	/** Baudrate of the serial line */
	uint32_t baud;
	/** parity UART's parity setting:
	 *    UART_CFG_PARITY_NONE,
	 *    UART_CFG_PARITY_EVEN,
	 *    UART_CFG_PARITY_ODD
 * @param cb         Pointer to the User Callback structure
 * @param ascii_mode Enable ASCII Transfer Mode
	 */
	enum uart_config_parity parity;
};

/**
 * @brief Modbus server parameter
 */
struct modbus_server_param {
	/** Pointer to the User Callback structure */
	struct modbus_user_callbacks *user_cb;
	/** Modbus unit ID of the server */
	uint8_t unit_id;
};

/**
 * @brief User parameter structure to configure Modbus interfase
 *        as client or server.
 */
struct modbus_iface_param {
	/** Mode of the interface */
	enum modbus_mode mode;
	union {
		struct modbus_server_param server;
		/** Amount of time client will wait for
		 *  a response from the server.
		 */
		uint32_t rx_timeout;
	};
	union {
		/** Serial support parameter of the interface */
		struct modbus_serial_param serial;
	};
};

/**
 * @brief Configure Modbus Interface as raw ADU server
 *
 * @param iface      Modbus RTU interface index
 * @param param      Configuration parameter of the server interface
 *
 * @retval           0 If the function was successful
 */
int modbus_init_server(const uint8_t iface, const uint8_t unit_id,
		       const uint32_t baud, enum uart_config_parity parity,
		       struct modbus_user_callbacks *const cb,
		       bool ascii_mode);
int modbus_init_server(const int iface, struct modbus_iface_param param);

/**
 * @brief Configure Modbus Interface as client
 * @brief Configure Modbus Interface as raw ADU client
 *
 * @param iface      Modbus interface index
 * @param baud       Baudrate of the serial line
 * @param parity     UART's parity setting:
 *                       UART_CFG_PARITY_NONE,
 *                       UART_CFG_PARITY_EVEN,
 *                       UART_CFG_PARITY_ODD
 * @param rx_timeout Amount of time client will wait for a response
 *                   from the server.
 * @param ascii_mode Enable ASCII Transfer Mode
 * @param iface      Modbus RTU interface index
 * @param param      Configuration parameter of the client interface
 *
 * @retval           0 If the function was successful
 */
int modbus_init_client(const uint8_t iface,
		       const uint32_t baud, enum uart_config_parity parity,
		       uint32_t rx_timeout,
		       bool ascii_mode);
int modbus_init_client(const int iface, struct modbus_iface_param param);

/**
 * @brief Disable Modbus Interface
 *
+22 −13
Original line number Diff line number Diff line
@@ -12,15 +12,24 @@
#include <logging/log.h>
LOG_MODULE_REGISTER(mbc_sample, LOG_LEVEL_INF);

#define RTU_IFACE	0
static int client_iface;

const static struct modbus_iface_param client_param = {
	.mode = MODBUS_MODE_RTU,
	.rx_timeout = 50000,
	.serial = {
		.baud = 19200,
		.parity = UART_CFG_PARITY_NONE,
	},
};

static int init_modbus_client(void)
{
	const uint32_t mb_rtu_br = 19200;
	const uint32_t rsp_timeout = 50000;
	const char iface_name[] = {DT_PROP(DT_INST(0, zephyr_modbus_serial), label)};

	client_iface = modbus_iface_get_by_name(iface_name);

	return modbus_init_client(RTU_IFACE, mb_rtu_br, UART_CFG_PARITY_NONE,
				  rsp_timeout, false);
	return modbus_init_client(client_iface, client_param);
}

void main(void)
@@ -38,14 +47,14 @@ void main(void)
		return;
	}

	err = modbus_write_holding_regs(RTU_IFACE, node, 0, holding_reg,
	err = modbus_write_holding_regs(client_iface, node, 0, holding_reg,
					ARRAY_SIZE(holding_reg));
	if (err != 0) {
		LOG_ERR("FC16 failed");
		return;
	}

	err = modbus_read_holding_regs(RTU_IFACE, node, 0, holding_reg,
	err = modbus_read_holding_regs(client_iface, node, 0, holding_reg,
				       ARRAY_SIZE(holding_reg));
	if (err != 0) {
		LOG_ERR("FC03 failed with %d", err);
@@ -58,7 +67,7 @@ void main(void)
	while (true) {
		uint16_t addr = 0;

		err = modbus_read_coils(RTU_IFACE, node, 0, coil, coil_qty);
		err = modbus_read_coils(client_iface, node, 0, coil, coil_qty);
		if (err != 0) {
			LOG_ERR("FC01 failed with %d", err);
			return;
@@ -66,28 +75,28 @@ void main(void)

		LOG_INF("Coils state 0x%02x", coil[0]);

		err = modbus_write_coil(RTU_IFACE, node, addr++, true);
		err = modbus_write_coil(client_iface, node, addr++, true);
		if (err != 0) {
			LOG_ERR("FC05 failed with %d", err);
			return;
		}

		k_msleep(sleep);
		err = modbus_write_coil(RTU_IFACE, node, addr++, true);
		err = modbus_write_coil(client_iface, node, addr++, true);
		if (err != 0) {
			LOG_ERR("FC05 failed with %d", err);
			return;
		}

		k_msleep(sleep);
		err = modbus_write_coil(RTU_IFACE, node, addr++, true);
		err = modbus_write_coil(client_iface, node, addr++, true);
		if (err != 0) {
			LOG_ERR("FC05 failed with %d", err);
			return;
		}

		k_msleep(sleep);
		err = modbus_read_coils(RTU_IFACE, node, 0, coil, coil_qty);
		err = modbus_read_coils(client_iface, node, 0, coil, coil_qty);
		if (err != 0) {
			LOG_ERR("FC01 failed with %d", err);
			return;
@@ -96,7 +105,7 @@ void main(void)
		LOG_INF("Coils state 0x%02x", coil[0]);

		coil[0] = 0;
		err = modbus_write_coils(RTU_IFACE, node, 0, coil, coil_qty);
		err = modbus_write_coils(client_iface, node, 0, coil, coil_qty);
		if (err != 0) {
			LOG_ERR("FC15 failed with %d", err);
			return;
+13 −2
Original line number Diff line number Diff line
@@ -100,6 +100,18 @@ static struct modbus_user_callbacks mbs_cbs = {
	.holding_reg_wr = holding_reg_wr,
};

const static struct modbus_iface_param server_param = {
	.mode = MODBUS_MODE_RTU,
	.server = {
		.user_cb = &mbs_cbs,
		.unit_id = 1,
	},
	.serial = {
		.baud = 19200,
		.parity = UART_CFG_PARITY_NONE,
	},
};

static int init_modbus_server(void)
{
	const uint32_t mb_rtu_br = 19200;
@@ -113,8 +125,7 @@ static int init_modbus_server(void)
		return iface;
	}

	return modbus_init_server(iface, 1, mb_rtu_br, UART_CFG_PARITY_NONE,
				  &mbs_cbs, false);
	return modbus_init_server(iface, server_param);
}

void main(void)
+91 −75
Original line number Diff line number Diff line
@@ -149,13 +149,18 @@ struct modbus_context *modbus_get_context(const uint8_t iface)
	return ctx;
}

static struct modbus_context *mb_cfg_iface(const uint8_t iface,
					   const uint8_t unit_id,
					   const uint32_t baud,
					   const enum uart_config_parity parity,
					   const uint32_t rx_timeout,
					   const bool client,
					   const bool ascii_mode)
int modbus_iface_get_by_name(const char *iface_name)
{
	for (int i = 0; i < ARRAY_SIZE(mb_ctx_tbl); i++) {
		if (strcmp(iface_name, mb_ctx_tbl[i].iface_name) == 0) {
			return i;
		}
	}

	return -ENODEV;
}

static struct modbus_context *modbus_init_iface(const uint8_t iface)
{
	struct modbus_context *ctx;

@@ -171,118 +176,129 @@ static struct modbus_context *mb_cfg_iface(const uint8_t iface,
		return NULL;
	}

	if ((client == true) &&
	    !IS_ENABLED(CONFIG_MODBUS_CLIENT)) {
		LOG_ERR("Modbus client support is not enabled");
		ctx->client = false;
		return NULL;
	}

	ctx->rxwait_to = rx_timeout;
	ctx->unit_id = unit_id;
	ctx->client = client;
	ctx->mbs_user_cb = NULL;
	k_mutex_init(&ctx->iface_lock);

	k_sem_init(&ctx->client_wait_sem, 0, 1);
	k_work_init(&ctx->server_work, modbus_rx_handler);

	if (IS_ENABLED(CONFIG_MODBUS_FC08_DIAGNOSTIC)) {
		modbus_reset_stats(ctx);
	}

	switch (ctx->mode) {
	case MODBUS_MODE_RTU:
	case MODBUS_MODE_ASCII:
		if (IS_ENABLED(CONFIG_MODBUS_SERIAL) &&
		    modbus_serial_init(ctx, baud, parity, ascii_mode) != 0) {
			LOG_ERR("Failed to init MODBUS over serial line");
			return NULL;
		}
		break;
	default:
		LOG_ERR("Unknown MODBUS mode");
		return NULL;
	}

	LOG_DBG("Modbus interface %s initialized", ctx->iface_name);

	return ctx;
}

int modbus_init_server(const uint8_t iface, const uint8_t unit_id,
		       const uint32_t baud, const enum uart_config_parity parity,
		       struct modbus_user_callbacks *const cb,
		       const bool ascii_mode)
int modbus_init_server(const int iface, struct modbus_iface_param param)
{
	struct modbus_context *ctx;
	struct modbus_context *ctx = NULL;
	int rc = 0;

	if (!IS_ENABLED(CONFIG_MODBUS_SERVER)) {
		LOG_ERR("Modbus server support is not enabled");
		return -ENOTSUP;
		rc = -ENOTSUP;
		goto init_server_error;
	}

	if (cb == NULL) {
	if (param.server.user_cb == NULL) {
		LOG_ERR("User callbacks should be available");
		return -EINVAL;
		rc = -EINVAL;
		goto init_server_error;
	}

	ctx = mb_cfg_iface(iface, unit_id, baud,
			   parity, 0, false, ascii_mode);

	ctx = modbus_init_iface(iface);
	if (ctx == NULL) {
		return -EINVAL;
		rc = -EINVAL;
		goto init_server_error;
	}

	ctx->mbs_user_cb = cb;

	return 0;
	switch (param.mode) {
	case MODBUS_MODE_RTU:
	case MODBUS_MODE_ASCII:
		if (IS_ENABLED(CONFIG_MODBUS_SERIAL) &&
		    modbus_serial_init(ctx, param) != 0) {
			LOG_ERR("Failed to init MODBUS over serial line");
			rc = -EINVAL;
			goto init_server_error;
		}
		break;
	default:
		LOG_ERR("Unknown MODBUS mode");
		rc = -ENOTSUP;
		goto init_server_error;
	}

int modbus_iface_get_by_name(const char *iface_name)
{
	for (int i = 0; i < ARRAY_SIZE(mb_ctx_tbl); i++) {
		if (strcmp(iface_name, mb_ctx_tbl[i].iface_name) == 0) {
			return i;
	ctx->client = false;
	ctx->unit_id = param.server.unit_id;
	ctx->mbs_user_cb = param.server.user_cb;
	if (IS_ENABLED(CONFIG_MODBUS_FC08_DIAGNOSTIC)) {
		modbus_reset_stats(ctx);
	}

	LOG_DBG("Modbus interface %s initialized", ctx->iface_name);

	return 0;

init_server_error:
	if (ctx != NULL) {
		atomic_clear_bit(&ctx->state, MODBUS_STATE_CONFIGURED);
	}

	return -ENODEV;
	return rc;
}

int modbus_init_client(const uint8_t iface,
		       const uint32_t baud, const enum uart_config_parity parity,
		       const uint32_t rx_timeout,
		       const bool ascii_mode)
int modbus_init_client(const int iface, struct modbus_iface_param param)
{
	struct modbus_context *ctx;
	struct modbus_context *ctx = NULL;
	int rc = 0;

	if (!IS_ENABLED(CONFIG_MODBUS_CLIENT)) {
		LOG_ERR("Modbus client support is not enabled");
		return -ENOTSUP;
		rc = -ENOTSUP;
		goto init_client_error;
	}

	ctx = mb_cfg_iface(iface, 0, baud,
			   parity, rx_timeout, true, ascii_mode);

	ctx = modbus_init_iface(iface);
	if (ctx == NULL) {
		return -EINVAL;
		rc = -EINVAL;
		goto init_client_error;
	}

	switch (param.mode) {
	case MODBUS_MODE_RTU:
	case MODBUS_MODE_ASCII:
		if (IS_ENABLED(CONFIG_MODBUS_SERIAL) &&
		    modbus_serial_init(ctx, param) != 0) {
			LOG_ERR("Failed to init MODBUS over serial line");
			rc = -EINVAL;
			goto init_client_error;
		}
		break;
	default:
		LOG_ERR("Unknown MODBUS mode");
		rc = -ENOTSUP;
		goto init_client_error;
	}

	ctx->client = true;
	ctx->unit_id = 0;
	ctx->mbs_user_cb = NULL;
	ctx->rxwait_to = param.rx_timeout;

	return 0;

init_client_error:
	if (ctx != NULL) {
		atomic_clear_bit(&ctx->state, MODBUS_STATE_CONFIGURED);
	}

	return rc;
}

int modbus_disable(const uint8_t iface)
{
	struct modbus_context *ctx;

	if (iface >= ARRAY_SIZE(mb_ctx_tbl)) {
		LOG_ERR("Interface %u not available", iface);
	ctx = modbus_get_context(iface);
	if (ctx == NULL) {
		LOG_ERR("Interface %u not initialized", iface);
		return -EINVAL;
	}

	ctx = &mb_ctx_tbl[iface];

	switch (ctx->mode) {
	case MODBUS_MODE_RTU:
	case MODBUS_MODE_ASCII:
@@ -300,7 +316,7 @@ int modbus_disable(const uint8_t iface)
	ctx->mbs_user_cb = NULL;
	atomic_clear_bit(&ctx->state, MODBUS_STATE_CONFIGURED);

	LOG_INF("Disable Modbus interface");
	LOG_INF("Modbus interface %u disabled", iface);

	return 0;
}
+2 −14
Original line number Diff line number Diff line
@@ -87,11 +87,6 @@ struct mb_rtu_gpio_config {
	gpio_dt_flags_t flags;
};

enum modbus_mode {
	MODBUS_MODE_RTU,
	MODBUS_MODE_ASCII,
};

struct modbus_serial_config {
	/* UART device name */
	const char *dev_name;
@@ -244,19 +239,12 @@ int modbus_serial_tx_adu(struct modbus_context *ctx);
 * @brief Initialize serial line support.
 *
 * @param ctx        Modbus interface context
 * @param baudrate   Baudrate of the serial line
 * @param parity     UART's parity setting:
 *                       UART_CFG_PARITY_NONE,
 *                       UART_CFG_PARITY_EVEN,
 *                       UART_CFG_PARITY_ODD
 * @param ascii_mode Enable ASCII Transfer Mode
 * @param param      Configuration parameter of the interface
 *
 * @retval           0 If the function was successful.
 */
int modbus_serial_init(struct modbus_context *ctx,
		       uint32_t baudrate,
		       enum uart_config_parity parity,
		       const bool ascii_mode);
		       struct modbus_iface_param param);

/**
 * @brief Disable serial line support.
Loading