Files
esp-modbus/modbus/mb_controller/serial/mbc_serial_slave.c
T

305 lines
13 KiB
C

/*
* SPDX-FileCopyrightText: 2016-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// mbc_serial_slave.c
// Implementation of the Modbus controller serial slave
#include <sys/time.h> // for calculation of time stamp in milliseconds
#include "esp_log.h" // for log_write
#include "esp_modbus_common.h" // for common defines
#include "esp_modbus_slave.h" // for public slave interface types
#include "mbc_slave.h" // for private slave interface types
#include "mbc_serial_slave.h" // for serial slave implementation definitions
#include "mb_common.h" // for mb object types definition
#include "sdkconfig.h" // for KConfig values
#if (CONFIG_FMB_COMM_MODE_ASCII_EN || CONFIG_FMB_COMM_MODE_RTU_EN)
static const char *TAG = "mbc_serial.slave";
// Modbus task function
static void mbc_ser_slave_task(void *param)
{
mb_slave_options_t *mbs_opts = MB_SLAVE_GET_OPTS(param);
mbs_controller_iface_t *mbs_iface = MB_SLAVE_GET_IFACE(param);
// Main Modbus stack processing cycle
for (;;)
{
BaseType_t status = xEventGroupWaitBits(mbs_opts->event_group_handle,
(BaseType_t)(MB_EVENT_STACK_STARTED),
pdFALSE, // do not clear bits
pdFALSE,
portMAX_DELAY);
// Check if stack started then poll for data
if (status & MB_EVENT_STACK_STARTED)
{
(void)mbs_iface->mb_base->poll(mbs_iface->mb_base);
}
// esp_task_wdt_reset();
}
}
// Start Modbus controller start function
static esp_err_t mbc_serial_slave_start(void *ctx)
{
mb_slave_options_t *mbs_opts = MB_SLAVE_GET_OPTS(ctx);
mbs_controller_iface_t *mbs_iface = MB_SLAVE_GET_IFACE(ctx);
mb_err_enum_t status = MB_EIO;
status = mbs_iface->mb_base->enable(mbs_iface->mb_base);
MB_RETURN_ON_FALSE((status == MB_ENOERR), ESP_ERR_INVALID_STATE, TAG,
"mb stack enable fail, returned (0x%x).", (int)status);
// Set the mbcontroller start flag
EventBits_t flag = xEventGroupSetBits(mbs_opts->event_group_handle,
(EventBits_t)MB_EVENT_STACK_STARTED);
MB_RETURN_ON_FALSE((flag & MB_EVENT_STACK_STARTED),
ESP_ERR_INVALID_STATE, TAG, "mb stack start event set error.");
mbs_iface->mb_base->descr.parent = ctx;
mbs_iface->is_active = true;
return ESP_OK;
}
// Start Modbus controller stop function
static esp_err_t mbc_serial_slave_stop(void *ctx)
{
mb_slave_options_t *mbs_opts = MB_SLAVE_GET_OPTS(ctx);
mbs_controller_iface_t *mbs_iface = MB_SLAVE_GET_IFACE(ctx);
mb_err_enum_t status = MB_EIO;
// Clear the mbcontroller start flag
EventBits_t flag = xEventGroupClearBits(mbs_opts->event_group_handle,
(EventBits_t)MB_EVENT_STACK_STARTED);
MB_RETURN_ON_FALSE((flag & MB_EVENT_STACK_STARTED),
ESP_ERR_INVALID_STATE, TAG, "mb stack start event set error.");
status = mbs_iface->mb_base->disable(mbs_iface->mb_base);
MB_RETURN_ON_FALSE((status == MB_ENOERR), ESP_ERR_INVALID_STATE, TAG,
"mb stack disable fail, returned (0x%x).", (int)status);
mbs_iface->mb_base->descr.parent = NULL;
mbs_iface->is_active = false;
return ESP_OK;
}
// Blocking function to get event on parameter group change for application task
static mb_event_group_t mbc_serial_slave_check_event(void *ctx, mb_event_group_t group)
{
mb_slave_options_t *mbs_opts = MB_SLAVE_GET_OPTS(ctx);
MB_SLAVE_ASSERT(mbs_opts->event_group_handle);
BaseType_t status = xEventGroupWaitBits(mbs_opts->event_group_handle, (BaseType_t)group,
pdTRUE, pdFALSE, portMAX_DELAY);
return (mb_event_group_t)status;
}
// Function to get notification about parameter change from application task
static esp_err_t mbc_serial_slave_get_param_info(void *ctx, mb_param_info_t *reg_info, uint32_t timeout)
{
mb_slave_options_t *mbs_opts = MB_SLAVE_GET_OPTS(ctx);
esp_err_t err = ESP_ERR_TIMEOUT;
MB_RETURN_ON_FALSE((mbs_opts->notification_queue_handle),
ESP_ERR_INVALID_ARG, TAG, "mb queue handle is invalid.");
MB_RETURN_ON_FALSE((reg_info), ESP_ERR_INVALID_ARG, TAG, "mb register information is invalid.");
BaseType_t status = xQueueReceive(mbs_opts->notification_queue_handle,
reg_info, pdMS_TO_TICKS(timeout));
if (status == pdTRUE)
{
err = ESP_OK;
}
return err;
}
// Modbus controller delete function
static esp_err_t mbc_serial_slave_delete(void *ctx)
{
mbs_controller_iface_t *mbs_iface = MB_SLAVE_GET_IFACE(ctx);
mb_slave_options_t *mbs_opts = MB_SLAVE_GET_OPTS(ctx);
mb_err_enum_t mb_error = MB_ENOERR;
// Check the stack started bit
BaseType_t status = xEventGroupWaitBits(mbs_opts->event_group_handle,
(BaseType_t)(MB_EVENT_STACK_STARTED),
pdFALSE,
pdFALSE,
MB_CONTROLLER_NOTIFY_TIMEOUT);
if (mbs_iface->is_active || (status & MB_EVENT_STACK_STARTED))
{
ESP_LOGV(TAG, "mb stack is active, try to disable.");
if (mbc_serial_slave_stop(ctx) != ESP_OK) {
ESP_LOGE(TAG, "mb stack stop failure.");
}
}
mbs_iface->is_active = false;
vTaskDelete(mbs_opts->task_handle);
vEventGroupDelete(mbs_opts->event_group_handle);
vQueueDelete(mbs_opts->notification_queue_handle);
mbs_opts->notification_queue_handle = NULL;
mbs_opts->event_group_handle = NULL;
mbs_opts->task_handle = NULL;
mb_error = mbs_iface->mb_base->delete(mbs_iface->mb_base);
MB_RETURN_ON_FALSE((mb_error == MB_ENOERR), ESP_ERR_INVALID_STATE, TAG,
"mb stack close failure returned (0x%x).", (int)mb_error);
// free the controller will be performed in common slave object
return ESP_OK;
}
static void mbc_serial_slave_iface_free(void *ctx)
{
mbs_controller_iface_t *mbs_iface = (mbs_controller_iface_t *)(ctx);
if (mbs_iface)
{
if (mbs_iface->opts.task_handle)
{
vTaskDelete(mbs_iface->opts.task_handle);
mbs_iface->opts.task_handle = NULL;
}
if (mbs_iface->opts.event_group_handle)
{
vEventGroupDelete(mbs_iface->opts.event_group_handle);
mbs_iface->opts.event_group_handle = NULL;
}
if (mbs_iface->opts.notification_queue_handle)
{
vQueueDelete(mbs_iface->opts.notification_queue_handle);
}
free(mbs_iface); // free the memory allocated for interface
}
}
static esp_err_t mbc_serial_slave_controller_create(void **ctx)
{
MB_RETURN_ON_FALSE((ctx), ESP_ERR_INVALID_STATE, TAG,
"mb stack init interface fail.");
esp_err_t ret = ESP_ERR_INVALID_STATE;
mbs_controller_iface_t *mbs_controller_iface = malloc(sizeof(mbs_controller_iface_t));
MB_GOTO_ON_FALSE((mbs_controller_iface), ESP_ERR_NO_MEM, error,
TAG, "mb stack memory allocation fail.");
mb_slave_options_t *mbs_opts = &mbs_controller_iface->opts;
mbs_opts->port_type = MB_PORT_SERIAL_SLAVE; // set interface port type
// Initialization of active context of the Modbus controller
BaseType_t status = 0;
// Parameter change notification queue
mbs_opts->event_group_handle = xEventGroupCreate();
MB_GOTO_ON_FALSE((mbs_opts->event_group_handle), ESP_ERR_NO_MEM, error,
TAG, "mb event group error.");
// Parameter change notification queue
mbs_opts->notification_queue_handle = xQueueCreate(MB_CONTROLLER_NOTIFY_QUEUE_SIZE, sizeof(mb_param_info_t));
MB_GOTO_ON_FALSE((mbs_opts->notification_queue_handle), ESP_ERR_NO_MEM, error,
TAG, "mb notify queue creation error.");
// Create Modbus controller task
status = xTaskCreatePinnedToCore((void *)&mbc_ser_slave_task,
"mbc_ser_slave",
MB_CONTROLLER_STACK_SIZE,
mbs_controller_iface,
MB_CONTROLLER_PRIORITY,
&mbs_opts->task_handle,
MB_PORT_TASK_AFFINITY);
MB_GOTO_ON_FALSE((status == pdPASS), ESP_ERR_INVALID_STATE, error, TAG,
"mb controller task creation error");
MB_SLAVE_ASSERT(mbs_opts->task_handle); // The task is created but handle is incorrect
// Initialize interface function pointers
mbs_controller_iface->create = mbc_serial_slave_create;
mbs_controller_iface->delete = mbc_serial_slave_delete;
mbs_controller_iface->check_event = mbc_serial_slave_check_event;
mbs_controller_iface->get_param_info = mbc_serial_slave_get_param_info;
mbs_controller_iface->set_descriptor = NULL; // Use common set descriptor function
mbs_controller_iface->start = mbc_serial_slave_start;
mbs_controller_iface->stop = mbc_serial_slave_stop;
mbs_controller_iface->mb_base = NULL;
*ctx = mbs_controller_iface;
return ESP_OK;
error:
mbc_serial_slave_iface_free((void *)mbs_controller_iface);
return ret;
}
// Initialization of Modbus controller
esp_err_t mbc_serial_slave_create(mb_communication_info_t *config, void **ctx)
{
mbs_controller_iface_t *mbs_controller_iface = NULL;
MB_RETURN_ON_FALSE((ctx && config), ESP_ERR_INVALID_STATE, TAG,
"mb stack init interface fail.");
MB_RETURN_ON_FALSE((!*ctx), ESP_ERR_INVALID_STATE, TAG, "mb stack is not destroyed?");
mb_serial_opts_t *pcomm_info = &config->ser_opts;
// Check communication options
MB_RETURN_ON_FALSE(((pcomm_info->mode == MB_RTU) || (pcomm_info->mode == MB_ASCII)),
ESP_ERR_INVALID_ARG, TAG, "mb incorrect mode = (%u).",
(unsigned)pcomm_info->mode);
MB_RETURN_ON_FALSE((pcomm_info->port <= UART_NUM_MAX), ESP_ERR_INVALID_ARG, TAG,
"mb wrong port to set = (%u).", (unsigned)pcomm_info->port);
MB_RETURN_ON_FALSE((pcomm_info->parity <= UART_PARITY_ODD), ESP_ERR_INVALID_ARG, TAG,
"mb wrong parity option = (%u).", (unsigned)pcomm_info->parity);
MB_RETURN_ON_FALSE((pcomm_info->uid <= MB_ADDRESS_MAX),
ESP_ERR_INVALID_ARG, TAG, "mb wrong slave address = (0x%u).",
(unsigned)pcomm_info->uid);
esp_err_t ret = mbc_serial_slave_controller_create((void *)&mbs_controller_iface);
MB_GOTO_ON_FALSE((ret == ESP_OK), ESP_ERR_INVALID_STATE, error, TAG,
"mbc create returns (0x%x).", (int)ret);
mb_slave_options_t *mbs_opts = MB_SLAVE_GET_OPTS(mbs_controller_iface);
mbs_opts->port_type = MB_PORT_SERIAL_SLAVE;
mbs_opts->comm_opts = *config;
mb_err_enum_t err = MB_ENOERR;
void *pinst = (void *)mbs_controller_iface;
// Initialize Modbus stack using mbcontroller parameters
if (pcomm_info->mode == MB_RTU)
{
#if (CONFIG_FMB_COMM_MODE_RTU_EN)
err = mbs_rtu_create(pcomm_info, &pinst);
#else
ESP_LOGE(TAG, "RTU mode is not enabled in the configuration.");
ret = ESP_ERR_NOT_SUPPORTED;
goto error;
#endif
}
else if (pcomm_info->mode == MB_ASCII)
{
#if (CONFIG_FMB_COMM_MODE_ASCII_EN)
err = mbs_ascii_create(pcomm_info, &pinst);
#else
ESP_LOGE(TAG, "ASCII mode is not enabled in the configuration.");
ret = ESP_ERR_NOT_SUPPORTED;
goto error;
#endif // CONFIG_FMB_COMM_MODE_ASCII_EN
}
MB_GOTO_ON_FALSE((err == MB_ENOERR), ESP_ERR_INVALID_STATE, error, TAG,
"mbs create returns (0x%x).", (int)err);
mbs_controller_iface->mb_base = (mb_base_t *)pinst;
// Configure Modbus read/write callbacks for the base modbus object
const mb_rw_callbacks_t rw_cbs = {
.reg_input_cb = mbc_reg_input_slave_cb,
.reg_holding_cb = mbc_reg_holding_slave_cb,
.reg_coils_cb = mbc_reg_coils_slave_cb,
.reg_discrete_cb = mbc_reg_discrete_slave_cb
};
mbs_controller_iface->mb_base->rw_cbs = rw_cbs;
mbs_controller_iface->is_active = false;
*ctx = (void *)mbs_controller_iface;
return ESP_OK;
error:
if (mbs_controller_iface) {
if (mbs_controller_iface->mb_base) {
mbs_controller_iface->mb_base->delete (mbs_controller_iface->mb_base);
mbs_controller_iface->mb_base = NULL;
}
mbc_serial_slave_iface_free((void *)mbs_controller_iface);
*ctx = NULL;
}
return ret;
}
#endif // #if (CONFIG_FMB_COMM_MODE_ASCII_EN || CONFIG_FMB_COMM_MODE_RTU_EN)