下面是RTU协议代码,读写寄存器数据组包,校验,解析
/**************************************************************************
** 名称: modbus_common.h头文件
* 日期:
* 作者:
* 描述:
* 修改记录:
***************************************************************************/
#ifndef __MODBUS_COMMON_H__
#define __MODBUS_COMMON_H__
#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif /* __cplusplus */
#include
#include
/**************************************************************************
** 宏定义
**************************************************************************/
/* 简化类型定义 */
typedef unsigned char uint8_t;
typedef signed char int8_t;
typedef unsigned short uint16_t;
typedef signed short int16_t;
typedef unsigned int uint32_t;
typedef signed int int32_t;
typedef unsigned long long int uint64_t;
typedef signed long long int int64_t;
#define M_MODBUS_OK (0) /* 成功 */
#define M_MODBUS_ERR (-1) /* 失败 */
#ifndef bool
typedef unsigned char bool;
#endif
#ifndef false
#define false (uint8_t)(0)
#endif
#ifndef true
#define true (uint8_t)(1)
#endif
#define DEBUG (1)
/* 打印重定义 */
#ifdef DEBUG
#define M_MODBUS_LOG_DEBUG(format,...) printf("[%s][%d] debug: "format"\n", __func__, __LINE__, ##__VA_ARGS__)
#define M_MODBUS_LOG_WARN(format,...) printf("[%s][%d] warn: "format"\n", __func__, __LINE__, ##__VA_ARGS__)
#define M_MODBUS_LOG_ERROR(format,...) printf("[%s][%d] error: "format"\n", __func__, __LINE__, ##__VA_ARGS__)
#define M_MODBUS_LOG_INFO(format,...) printf("[%s][%d] info: "format"\n", __func__, __LINE__, ##__VA_ARGS__)
#define M_MODBUS_TRACE_IN() printf("[%s][%s][%d] trace in\n", __FILE__, __func__, __LINE__)
#define M_MODBUS_TRACE_OUT() printf("[%s][%s][%d] trace out\n", __FILE__, __func__, __LINE__)
#else
#define M_MODBUS_LOG_DEBUG(format,...)
#define M_MODBUS_LOG_WARN(format,...)
#define M_MODBUS_LOG_ERROR(format,...)
#define M_MODBUS_LOG_INFO(format,...)
#define M_MODBUS_TRACE_IN()
#define M_MODBUS_TRACE_OUT()
#endif
/**************************************************************************
** 结构体声明
**************************************************************************/
/* 寄存器错误码应答 */
typedef enum _MODBUS_ERROR_CODE_E
{
E_CODE_NO_ERR = 0,
E_CODE_ILLEGAL_FUNC_ERR, /* 非法功能错误 */
E_CODE_ILLEGAL_REG_ADDR_ERR, /* 非法寄存器数据地址 */
E_CODE_ILLEGAL_REG_VAL_ERR, /* 非法寄存器数据值 */
E_CODE_SLAVER_FAULT_ERR, /* 从设备故障 */
E_CODE_DEALING_CONFIRM_ERR, /* 正在确认 */
E_CODE_OTHER_ERR, /* 其他错误 */
} MODBUS_ERROR_CODE_E;
/* 寄存器类型 */
typedef enum _MODBUS_FUNC_CODE_TYPE_E
{
E_FUNC_CODE_READ_COILS = 0x01, /* 读线圈状态 */
E_FUNC_CODE_READ_DISCRETE_INPUTS = 0x02, /* 读离散输入状态 */
E_FUNC_CODE_READ_HOLDING_REGISTERS = 0x03, /* 读保持寄存器 */
E_FUNC_CODE_READ_INPUT_REGISTERS = 0x04, /* 读输入寄存器 */
E_FUNC_CODE_WRITE_SINGLE_COIL = 0x05, /* 写单个线圈 */
E_FUNC_CODE_WRITE_SINGLE_REGISTER = 0x06, /* 写单个保持寄存器 */
E_FUNC_CODE_READ_EXCEPTION_STATUS = 0x07, /* 读异常状态 */
E_FUNC_CODE_WRITE_MULTIPLE_COILS = 0x0F, /* 写多个线圈 */
E_FUNC_CODE_WRITE_MULTIPLE_REGISTERS = 0x10, /* 写多个保持寄存器 */
E_FUNC_CODE_REPORT_SLAVE_ID = 0x11, /* 报告从机标识 */
} MODBUS_FUNC_CODE_TYPE_E;
/* 支持三种协议方式, modbus ASCII, RTU, RTU_TCP */
typedef enum _MODBUS_PROTOCOL_TYPE_E
{
E_START_PROTOCOL_TYPE = 0,
E_ASCII_PROTOCOL_TYPE,
E_RTU_PROTOCOL_TYPE,
E_RTU_TCP_PROTOCOL_TYPE,
} MODBUS_PROTOCOL_TYPE_E;
/* 波特率 */
typedef enum _MODBUS_BAUD_E
{
E_BAUD_2400BPS = 2400,
E_BAUD_4800BPS = 4800,
E_BAUD_9600BPS = 9600,
E_BAUD_14400BPS = 14400,
E_BAUD_19200BPS = 19200,
E_BAUD_28800BPS = 28800,
E_BAUD_38400BPS = 38400,
E_BAUD_57600BPS = 56700,
E_BAUD_115200BPS = 115200,
E_BAUD_128000BPS = 128000,
E_BAUD_256000BPS = 256000,
} MODBUS_BAUD_E;
/* 数据位 */
typedef enum _MODBUS_DATA_BIT_E
{
E_DATA_4BITS = 4,
E_DATA_5BITS = 5,
E_DATA_6BITS = 6,
E_DATA_7BITS = 7,
E_DATA_8BITS = 8,
} MODBUS_DATA_BIT_E;
/* 停止位 */
typedef enum _MODBUS_STOP_BIT_E
{
E_STOP_1V0BIT = 0,
E_STOP_1V5BITS,
E_STOP_2V0BITS,
} MODBUS_STOP_BIT_E;
/* 校验符 */
typedef enum _MODBUS_CHECK_E
{
E_CHECK_NONE = 0,
E_CHECK_EVEN,
E_CHECK_ODD,
E_CHECK_MARK,
E_CHECK_SPACK,
} MODBUS_CHECK_E;
/* modbus的驱动口的相关参数 */
typedef struct _modbus_com_params_st
{
char *device; /* 硬件设备 */
int32_t baud; /* 波特率 */
uint8_t data_bit; /* 数据位 */
uint8_t stop_bit; /* 停止位 */
char parity; /* 校验符 */
} modbus_com_params_st;
/**************************************************************************
** 函数声明
**************************************************************************/
/**************************************************************************
* 函 数: void modbus_log_hex_print(uint8_t *data, uint32_t datalen)
* 描 述: 打印hex数据
* 入 参: uint8_t *data : 需要打印的数据流
uint32_t datalen : 数据长度
* 出 参: void
* 返回值: void
**************************************************************************/
void modbus_log_hex_print(uint8_t *data, uint32_t datalen);
/**************************************************************************
* 函 数: int32_t modbus_calc_crc(uint8_t *data, uint32_t datalen, uint16_t *crc)
* 描 述: 计算crc校验函数
* 入 参: uint8_t *data : 计算crc校验的数据
uint32_t datalen : 计算crc校验的数据长度
* 出 参: uint16_t *crc : 计算crc校验值
* 返回值: int32_t : M_MODBUS_OK - 成功
M_MODBUS_ERR - 失败
**************************************************************************/
int32_t modbus_calc_crc(uint8_t *data, uint32_t datalen, uint16_t *crc);
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */
#endif
## modbus_common.c文件:
/**************************************************************************
** 名称: modbus_common.c文件
* 日期:
* 作者:
* 描述:
* 修改记录:
***************************************************************************/
#include "modbus_common.h"
#include
#include
#include
#include
/**************************************************************************
** 宏定义
**************************************************************************/
#define M_MODBUS_POLY_VALUE (0x1021) /* 定义的多项式值 */
#define M_MODBUS_CRC_SEED_VALUE (0xFFFF) /* 预置值 */
/**************************************************************************
** 结构体声明
**************************************************************************/
/**************************************************************************
** 函数声明
**************************************************************************/
/**************************************************************************
** 函数定义
**************************************************************************/
/**************************************************************************
* 函 数: void modbus_log_hex_print(uint8_t *data, uint32_t datalen)
* 描 述: 打印hex数据
* 举 例: 输入data: 0x12 0x34 0x56 0x00 0x78 0x90; datalen: 6
打印结果: "hex_data[6]: 12 34 56 00 78 90"
* 入 参: uint8_t *data : 需要打印的数据流
uint32_t datalen : 数据长度
* 出 参: void
* 返回值: void
**************************************************************************/
void modbus_log_hex_print(uint8_t *data, uint32_t datalen)
{
uint32_t index = 0;
uint32_t temp_data_len = datalen * 3 + 1;
uint32_t templen = 0;
char *temp_data = NULL;
if ((NULL == data) || (0 == datalen))
{
M_MODBUS_LOG_ERROR("print hex data log failed, input param is error.");
return;
}
temp_data = (char
*)malloc(temp_data_len);
if (NULL == temp_data)
{
M_MODBUS_LOG_ERROR("print hex data log, malloc data[%d] space failed.", temp_data_len);
return;
}
(void)memset(temp_data, 0, temp_data_len);
for (index = 0; index < datalen; index++)
{
templen += sprintf(&temp_data[templen], "%02x ", data[index]);
}
printf("hex_data[%d]: %s\n", datalen, temp_data);
free(temp_data);
temp_data = NULL;
}
/**************************************************************************
* 函 数: int32_t modbus_calc_crc(uint8_t *data, uint32_t datalen, uint16_t *crc)
* 描 述: 计算crc校验函数
* 举 例: 输入data: 0x12 0x23 0x46 0x99; datalen = 4;
计算得到两个字节的crc校验码: XXXX
* 入 参: uint8_t *data : 计算crc校验的数据
uint32_t datalen : 计算crc校验的数据长度
* 出 参: uint16_t *crc : 计算crc校验值
* 返回值: int32_t : M_MODBUS_OK - 成功
M_MODBUS_ERR - 失败
**************************************************************************/
int32_t modbus_calc_crc(uint8_t *data, uint32_t datalen, uint16_t *crc)
{
uint16_t crc_val = M_MODBUS_CRC_SEED_VALUE;
uint32_t i = 0;
uint32_t j = 0;
if ((NULL == data) || (0 == datalen))
{
M_MODBUS_LOG_ERROR("input param is error");
return M_MODBUS_ERR;
}
for (i = datalen; i > 0; i--)
{
crc_val = crc_val ^ (*data++ << 8);
for (j = 0; j < 8; j++)
{
if (crc_val & 0x8000)
{
crc_val = (crc_val << 1) ^ M_MODBUS_POLY_VALUE;
}
else
{
crc_val <<= 1;
}
}
}
*crc = crc_val;
return M_MODBUS_OK;
}
/**************************************************************************
** 名称: modbus_rtu.c文件
* 日期:
* 作者:
* 描述:
* 1.读线圈数据(开关量-- 0x01) -- 按位进行读
* 2.读离散型量(开关量-- 0x02) -- 按位进行读
*
*
* 修改记录:
***************************************************************************/
#include "modbus_rtu.h"
#include
#include
#include
#include
#include "winsock2.h"
/**************************************************************************
** 宏定义
**************************************************************************/
#define M_MODBUS_MAX_REGISTER_CNT (127) /* 读写寄存器最大个数 */
/**************************************************************************
** 结构体声明
**************************************************************************/
/**************************************************************************
** 全局变量声明
**************************************************************************/
/**************************************************************************
** 函数声明
**************************************************************************/
/*************************************************************************
* 函 数: int32_t modbus_rtu_pack_read_reg(modbus_read_reg_info_st *reg_info, uint8_t *data, uint32_t *datalen)
* 描 述: 按Modbus协议,组包读寄存器
* 举 例: 按照以下协议组包公共部分
typedef struct _modbus_rtu_read_reg_st
{
uint8_t slave_addr;
uint8_t func_code;
uint16_t register_addr;
uint16_t register_cnt;
uint16_t crc;
} __attribute__((packed)) modbus_rtu_read_reg_st;
根据输入的寄存器信息,组包数据部分
* 入 参: modbus_read_reg_info_st *reg_info : 输入读写寄存器信息
* 出 参: uint8_t *data : 按modbus协议组包的数据流
uint32_t *datalen : 数据流长度
* 返回值: int32_t : M_MODBUS_OK - 成功
M_MODBUS_ERR - 失败
**************************************************************************/
int32_t modbus_rtu_pack_read_reg
(
modbus_read_reg_info_st *reg_info,
uint8_t *data,
uint32_t *datalen
)
{
int32_t ret = M_MODBUS_ERR;
modbus_rtu_read_reg_st read_reg;
M_MODBUS_TRACE_IN();
do
{
/* 入参校验 */
if ((NULL == reg_info) || (NULL == data) || (NULL == datalen))
{
M_MODBUS_LOG_ERROR("input param is error, null point");
break;
}
/* 按modbus协议组包 */
(void)memset(&read_reg, 0, sizeof(read_reg));
read_reg.slave_addr = reg_info->slave_addr;
read_reg.func_code = (uint8_t)reg_info->func_code;
/* 组包寄存器地址 */
read_reg.register_addr = reg_info->register_addr;
/* 寄存器数目 */
if ((0 == reg_info->register_cnt) || (M_MODBUS_MAX_REGISTER_CNT < reg_info->register_cnt))
{
M_MODBUS_LOG_ERROR("register count[%d] is over out of range", reg_info->register_cnt);
break;
}
read_reg.register_cnt = reg_info->register_cnt;
/* 大小端转换 */
read_reg.register_addr = htons(read_reg.register_addr);
read_reg.register_cnt = htons(read_reg.register_cnt);
/* crc校验 */
ret = modbus_calc_crc((uint8_t *)&read_reg, sizeof(read_reg) - sizeof(uint16_t), &read_reg.crc);
if (M_MODBUS_OK != ret)
{
M_MODBUS_LOG_ERROR("calc crc is failed, ret = %d", ret);
break;
}
(void)memcpy(data, (uint8_t *)&read_reg, sizeof(modbus_rtu_read_reg_st));
*datalen = sizeof(modbus_rtu_read_reg_st);
/* 打印组包数据 */
modbus_log_hex_print(data, *datalen);
M_MODBUS_LOG_DEBUG("package read status register data ok");
ret = M_MODBUS_OK;
}
while (0);
M_MODBUS_TRACE_OUT();
return ret;
}
/**************************************************************************
* 函 数: int32_t modbus_rtu_unpack_read_status_reg(modbus_read_reg_info_st *reg_info,
modbus_rtu_ack_data_rw_st *data_ack, modbus_rtu_reg_data_val_st *data_val)
* 描 述: 按Modbus协议,解析读取状态寄存器的响应数据
* 举 例: 按照以下协议组包进行解析,输出正常响应的数据值
typedef struct _modbus_rtu_ack_data_rw_st
{
uint8_t slave_addr;
uint8_t func_code;
uint8_t datalen;
uint8_t *data;
} __attribute__((packed)) modbus_rtu_ack_data_rw_st;
* 入 参: modbus_read_reg_info_st *reg_info : 输入读写寄存器信息
modbus_rtu_ack_data_rw_st *data_ack : 接收的状态寄存器的信息
* 出 参: modbus_rtu_reg_data_val_st *data_val : 输出读保存/状态寄存器的数据值信息
* 返回值: int32_t : M_MODBUS_OK - 成功
M_MODBUS_ERR - 失败
**************************************************************************/
static int32_t modbus_rtu_unpack_read_status_reg
(
modbus_read_reg_info_st *reg_info,
modbus_rtu_ack_data_rw_st *data_ack,
modbus_rtu_reg_data_val_st *data_val
)
{
int32_t ret = M_MODBUS_ERR;
M_MODBUS_TRACE_IN();
do
{
if ((NULL == reg_info) || (NULL == data_ack) || (NULL == data_val))
{
M_MODBUS_LOG_ERROR("input param is error, null point");
break;
}
/* 读状态寄存器 */
if (reg_info->register_cnt != (uint16_t)data_ack->datalen)
{
M_MODBUS_LOG_ERROR("read status register num[%x]
not equal recv datalen[%x]",
reg_info->register_cnt, data_ack->datalen);
break;
}
/* 保存到结构体中 */
data_val->read_reg_type = E_MODBUS_RTU_READ_STATUS_REG;
data_val->start_register_addr = reg_info->register_addr;
data_val->read_register_cnt = reg_info->register_cnt / 8 + (reg_info->register_cnt % 8) ? 1 : 0;
data_val->reg_data.status_reg_value = (uint8_t *)malloc(data_val->read_register_cnt);
if (NULL == data_val->reg_data.status_reg_value)
{
M_MODBUS_LOG_ERROR("malloc read status register[%d] space is failed", data_val->read_register_cnt);
break;
}
(void)memset(data_val->reg_data.status_reg_value, 0, data_val->read_register_cnt);
(void)memcpy(data_val->reg_data.status_reg_value, data_ack->data, data_val->read_register_cnt);
M_MODBUS_LOG_DEBUG("recv read status register data ok");
ret = M_MODBUS_OK;
}
while (0);
M_MODBUS_TRACE_OUT();
return ret;
}
/**************************************************************************
* 函 数: int32_t modbus_rtu_unpack_read_storage_reg(modbus_read_reg_info_st *reg_info,
modbus_rtu_ack_data_rw_st *data_ack, modbus_rtu_reg_data_val_st *data_val)
* 描 述: 按Modbus协议,解析读取存储寄存器的响应数据
* 举 例: 按照以下协议组包进行解析,输出正常响应的数据值
typedef struct _modbus_rtu_ack_data_rw_st
{
uint8_t slave_addr;
uint8_t func_code;
uint8_t datalen;
uint8_t *data;
} __attribute__((packed)) modbus_rtu_ack_data_rw_st;
* 入 参: modbus_read_reg_info_st *reg_info : 输入读写寄存器信息
modbus_rtu_ack_data_rw_st *data_ack : 接收的状态寄存器的信息
* 出 参: modbus_rtu_reg_data_val_st *data_val : 输出读保存/状态寄存器的数据值信息
* 返回值: int32_t : M_MODBUS_OK - 成功
M_MODBUS_ERR - 失败
**************************************************************************/
static int32_t modbus_rtu_unpack_read_storage_reg
(
modbus_read_reg_info_st *reg_info,
modbus_rtu_ack_data_rw_st *data_ack,
modbus_rtu_reg_data_val_st *data_val
)
{
uint16_t index = 0;
int32_t ret = M_MODBUS_ERR;
M_MODBUS_TRACE_IN();
do
{
if ((NULL == reg_info) || (NULL == data_ack) || (NULL == data_val))
{
M_MODBUS_LOG_ERROR("input param is error, null point");
break;
}
/* 读保存寄存器 */
if ((reg_info->register_cnt * 2) != (uint16_t)data_ack->datalen)
{
M_MODBUS_LOG_ERROR("read register num[%x *2] not equal recv datalen[%x]", reg_info->register_cnt, data_ack->datalen);
break;
}
/* 解析获取到结构体中 */
data_val->read_reg_type = E_MODBUS_RTU_READ_STORAGE_REG;
data_val->start_register_addr = reg_info->register_addr;
data_val->read_register_cnt = data_ack->datalen / 2;
data_val->reg_data.storage_reg_value = (uint16_t *)malloc(data_val->read_register_cnt);
if (NULL == data_val->reg_data.storage_reg_value)
{
M_MODBUS_LOG_ERROR("malloc read storage register[%d] space is failed", data_val->read_register_cnt);
break;
}
(void)memset(data_val->reg_data.storage_reg_value, 0, data_val->read_register_cnt);
for (index = 0; index < reg_info->register_cnt; index++)
{
data_val->reg_data.storage_reg_value[index] = data_ack->data[index * 2];
data_val->reg_data.storage_reg_value[index] <<= 8;
data_val->reg_data.storage_reg_value[index] |= data_ack->data[index * 2 + 1];
}
M_MODBUS_LOG_DEBUG("recv read storage register data ok");
ret = M_MODBUS_OK;
}
while (0);
M_MODBUS_TRACE_OUT();
return ret;
}
/**************************************************************************
* 函 数: int32_t modbus_rtu_unpack_read_register(modbus_read_reg_info_st *reg_info,
uint8_t *data, uint32_t datalen, uint16_t *pOutBuf)
* 描 述: 按Modbus协议,解析响应的数据
* 举 例: 按照以下协议组包进行解析,输出正常响应的数据值
typedef struct _modbus_rtu_ack_data_rw_st
{
uint8_t slave_addr;
uint8_t func_code;
uint8_t datalen;
uint8_t *data;
} __attribute__((packed)) modbus_rtu_ack_data_rw_st;
* 入 参: modbus_read_reg_info_st *reg_info : 输入读写寄存器信息
uint8_t *data : 按modbus协议组包的数据流
uint32_t datalen : 数据流长度
* 出 参: modbus_rtu_reg_data_val_st *data_val : 输出读保存/状态寄存器的数据值信息,
在调用后,成功获取完寄存器的数据后,需要释放申请的寄存器的值
* 返回值: int32_t : M_MODBUS_OK - 成功
M_MODBUS_ERR - 失败
**************************************************************************/
int32_t modbus_rtu_unpack_read_register
(
modbus_read_reg_info_st *reg_info,
uint8_t *data,
uint32_t datalen,
modbus_rtu_reg_data_val_st *data_val
)
{
int32_t ret = M_MODBUS_ERR;
uint16_t crc_val = 0;
uint16_t recv_crc_val = 0;
modbus_rtu_ack_data_rw_st *data_ack = NULL;
modbus_rtu_fail_st *fail_info = NULL;
MODBUS_ERROR_CODE_E err_code = E_CODE_NO_ERR;
M_MODBUS_TRACE_IN();
do
{
/* 入参校验 */
if ((NULL == reg_info) || (NULL == data) || (NULL == data_val) || (3 > datalen))
{
M_MODBUS_LOG_ERROR("input param is error, null point");
break;
}
data_ack = (modbus_rtu_ack_data_rw_st *)data;
/* 从地址校验 */
if (reg_info->slave_addr != data_ack->slave_addr)
{
M_MODBUS_LOG_ERROR("pack read_reg[%x] not equal recv read_reg[%x]",
reg_info->slave_addr, data_ack->slave_addr);
break;
}
/* CRC校验 */
ret = modbus_calc_crc(data, datalen - 2, &crc_val);
if (M_MODBUS_OK != ret)
{
M_MODBUS_LOG_ERROR("recv read_reg ack calc crc failed");
break;
}
recv_crc_val = (uint16_t)(((uint16_t)data[datalen - 2] << 8) | (uint16_t)data[datalen - 1]);
if (crc_val != recv_crc_val)
{
M_MODBUS_LOG_ERROR("recv read_reg(%x) not equal calc crc(%x)", recv_crc_val, crc_val);
ret = M_MODBUS_ERR;
break;
}
/* 错误功能码解析 */
if ((uint8_t)reg_info->func_code != data_ack->func_code)
{
if (((uint8_t)reg_info->func_code | 0x80) == data_ack->func_code)
{
fail_info = (modbus_rtu_fail_st *)data;
/* 返回的是错误码 */
switch (fail_info->except_code)
{
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
{
err_code = (MODBUS_ERROR_CODE_E)fail_info->except_code;
break;
}
default:
{
err_code = E_CODE_OTHER_ERR;
break;
}
}
}
else
{
err_code = E_CODE_OTHER_ERR;
}
M_MODBUS_LOG_WARN("read register data, func code[%x] is fail ack value[%x]",
data_ack->func_code, fail_info->except_code);
ret = M_MODBUS_OK;
break;
}
switch (reg_info->func_code)
{
case E_FUNC_CODE_READ_COILS:
case E_FUNC_CODE_READ_DISCRETE_INPUTS:
{
/* 读状态寄存器 */
ret = modbus_rtu_unpack_read_status_reg(reg_info, data_ack, data_val);
break;
}
case E_FUNC_CODE_READ_HOLDING_REGISTERS:
case E_FUNC_CODE_READ_INPUT_REGISTERS:
{
/* 读存储寄存器 */
ret = modbus_rtu_unpack_read_storage_reg(reg_info, data_ack, data_val);
break;
}
default:
{
/* 其他 */
M_MODBUS_LOG_WARN("not recv read register data, func code[%x]", reg_info->func_code);
ret = M_MODBUS_ERR;
break;
}
}
}
while (0);
M_MODBUS_TRACE_OUT();
return ret;
}
/**************************************************************************
* 函 数: int32_t modbus_rtu_pack_write_reg(modbus_rtu_write_reg_info_st *reg_info,
uint8_t *data, uint32_t *datalen)
* 描 述: 按Modbus协议,组包写寄存器的值
* 举 例:
对于单个状态/存储寄存器来说,组包格式,按字节的方式:
发送->slave_addr(1) + func(1) + reg_addr(2) + reg_val(2) + crc(2)
应答->slave_addr(1) + func(1) + reg_addr(2) + reg_val(2) + crc(2)
对于多个状态寄存器来说,组包格式,按bit的方式:
发送->slave_addr(1) + func(1) + reg_addr(2) + reg_cnt(2) + reg_val_len(1) + reg_val(n) + crc(2)
应答->slave_addr(1) + func(1) + reg_addr(2) + reg_cnt(2) + reg_val_len(1) + crcc(2)
对于多个存储寄存器来说,组包格式,按字节的方式:
发送->slave_addr(1) + func(1) + reg_addr(2) + reg_cnt(2) + reg_val_len(1) + reg_val(n) + crc(2)
应答->slave_addr(1) + func(1) + reg_addr(2) + reg_cnt(2) + crcc(2)
* 入 参: modbus_register_info_st *reg_info : 输入写寄存器信息
* 出 参: uint8_t *data : 按modbus协议组包的数据流
uint32_t *datalen : 数据流长度
* 返回值: int32_t : M_MODBUS_OK - 成功
M_MODBUS_ERR - 失败
**************************************************************************/
int32_t modbus_rtu_pack_write_reg
(
modbus_rtu_write_reg_info_st *reg_info,
uint8_t *data,
uint32_t *datalen
)
{
int32_t ret = M_MODBUS_ERR;
uint16_t crc_val = 0;
uint8_t index = 0;
uint8_t templen = 0;
uint8_t register_val_len = 0;
M_MODBUS_TRACE_IN();
do
{
/* 入参校验 */
if ((NULL == reg_info) || (NULL == data) || (NULL == datalen))
{
M_MODBUS_LOG_ERROR("input param is error, null point");
break;
}
/* 寄存器个数为0,直接退出 */
if (0x00 == reg_info->register_cnt)
{
M_MODBUS_LOG_ERROR("input param write register count is 0x00, exit.");
break;
}
/* 按modbus协议组包 */
data[templen++] = reg_info->slave_addr;
data[templen++] = (uint8_t)reg_info->func_code;
/* 组包寄存器地址 */
data[templen++] = (uint8_t)((reg_info->register_addr >> 8) & 0xff);
data[templen++] = (uint8_t)(reg_info->register_addr & 0xff);
if (0x01 == reg_info->register_cnt)
{
/* 单个状态/保存寄存器 */
data[templen++] = (uint8_t)((reg_info->reg_val.register_val[0] >> 8) & 0xff);
data[templen++] = (uint8_t)(reg_info->reg_val.register_val[0] & 0xff);
}
else
{
/* 写寄存器个数 */
data[templen++] = 0x00;
data[templen++] = reg_info->register_cnt;
if (E_FUNC_CODE_WRITE_MULTIPLE_COILS == reg_info->func_code)
{
/* 写多个线圈的字节数长度 */
register_val_len = reg_info->register_cnt / 8 + (reg_info->register_cnt % 8 ? 1 : 0);
data[templen++] = register_val_len;
/* 多个线圈状态值 */
(void)memcpy(&data[templen], reg_info->reg_val.multiple_status_reg_val, register_val_len);
templen += register_val_len;
}
else if (E_FUNC_CODE_WRITE_MULTIPLE_REGISTERS == reg_info->func_code)
{
/* 写多个寄存器字节长度 */
data[templen++] = reg_info->register_cnt * 2;
for (index = 0; index < reg_info->register_cnt; index++)
{
data[templen++] = (uint8_t)((reg_info->reg_val.register_val[index] >> 8) & 0xff);
data[templen++] = (uint8_t)(reg_info->reg_val.register_val[index] & 0xff);
}
}
else
{
M_MODBUS_LOG_ERROR("write multiple register, func code[%d] error", reg_info->func_code);
break;
}
}
/* crc校验 */
ret = modbus_calc_crc(data, templen, &crc_val);
if (M_MODBUS_OK != ret)
{
M_MODBUS_LOG_ERROR("calc crc is failed, ret = %d", ret);
break;
}
data[templen++] = (uint8_t)((crc_val >> 8) & 0xff);
data[templen++] = (uint8_t)(crc_val & 0xff);
*datalen = templen;
/* 打印组包数据 */
modbus_log_hex_print(data, *datalen);
M_MODBUS_LOG_DEBUG("package write register data ok");
}
while (0);
M_MODBUS_TRACE_OUT();
return ret;
}
/**************************************************************************
* 函 数: int32_t modbus_rtu_unpack_write_signal_reg(modbus_read_reg_info_st *reg_info,
uint8_t *data, uint32_t datalen)
* 描 述: 按Modbus协议,解析读取单个状态/存储寄存器的响应数据
* 举 例: 对于单个状态/存储寄存器来说,组包格式,按字节的方式:
发送->slave_addr(1) + func(1) + reg_addr(2) + reg_val(2) + crc(2)
应答->slave_addr(1) + func(1) + reg_addr(2) + reg_val(2) + crc(2)
* 入 参: modbus_read_reg_info_st *reg_info : 输入读写寄存器信息
uint8_t *data : 按modbus协议接收的数据流
uint32_t datalen : 数据流长度
* 出 参: void
* 返回值: int32_t : M_MODBUS_OK - 成功
M_MODBUS_ERR - 失败
**************************************************************************/
static int32_t modbus_rtu_unpack_write_signal_reg
(
modbus_rtu_write_reg_info_st *reg_info,
uint8_t *data,
uint32_t datalen
)
{
int32_t ret = M_MODBUS_ERR;
modbus_rtu_ack_write_signal_reg_st *write_signal_reg = NULL;
M_MODBUS_TRACE_IN();
do
{
/* 入参判断 */
if ((NULL == reg_info) || (NULL == data) || (3 >= datalen))
{
M_MODBUS_LOG_ERROR("input param is error, null point");
break;
}
write_signal_reg = (modbus_rtu_ack_write_signal_reg_st *)data;
/* 寄存器地址和寄存器值的校验 */
write_signal_reg->register_addr = ntohs(write_signal_reg->register_addr);
write_signal_reg->register_data = ntohs(write_signal_reg->register_data);
if (reg_info->register_addr != write_signal_reg->register_addr)
{
M_MODBUS_LOG_ERROR("recv write_signal_reg addr(%x) != recv write signal addr(%x)",
reg_info->register_addr, write_signal_reg->register_addr);
break;
}
if (reg_info->reg_val.register_val[0] != write_signal_reg->register_data)
{
M_MODBUS_LOG_ERROR("recv write_signal_reg val(%x) != recv write signal val(%x)",
reg_info->reg_val.register_val[0], write_signal_reg->register_data);
break;
}
M_MODBUS_LOG_DEBUG("recv write signal register data ok");
ret = M_MODBUS_OK;
break;
}
while (0);
M_MODBUS_TRACE_OUT();
return ret;
}
/**************************************************************************
* 函 数: int32_t modbus_rtu_unpack_write_multiple_state_reg(
modbus_read_reg_info_st *reg_info, uint8_t *data, uint32_t datalen)
* 描 述: 按Modbus协议,解析读取多个状态寄存器的响应数据
* 举 例: 对于多个状态寄存器来说,组包格式,按bit的方式:
发送->slave_addr(1) + func(1) + reg_addr(2) + reg_cnt(2) + reg_val_len(1) + reg_val(n) + crc(2)
应答->slave_addr(1) + func(1) + reg_addr(2) + reg_cnt(2) + reg_val_len(1) + crcc(2)
* 入 参: modbus_read_reg_info_st *reg_info : 输入读写寄存器信息
uint8_t *data : 按modbus协议接收的数据流
uint32_t datalen : 数据流长度
* 出 参: void
* 返回值: int32_t : M_MODBUS_OK - 成功
M_MODBUS_ERR - 失败
**************************************************************************/
static int32_t modbus_rtu_unpack_write_multiple_state_reg
(
modbus_rtu_write_reg_info_st *reg_info,
uint8_t *data,
uint32_t datalen
)
{
int32_t ret = M_MODBUS_ERR;
uint8_t write_data_len = 0;
modbus_rtu_ack_write_multiple_state_reg_st *write_state_reg = NULL;
M_MODBUS_TRACE_IN();
do
{
/* 入参判断 */
if ((NULL == reg_info) || (NULL == data) || (3 >= datalen))
{
M_MODBUS_LOG_ERROR("input param is error, null point");
break;
}
write_state_reg = (modbus_rtu_ack_write_multiple_state_reg_st *)data;
/* 寄存器地址和寄存器个数,寄存器长度进行校验*/
write_state_reg->register_addr = ntohs(write_state_reg->register_addr);
write_state_reg->register_cnt = ntohs(write_state_reg->register_cnt);
if (reg_info->register_addr != write_state_reg->register_addr)
{
M_MODBUS_LOG_ERROR("write_multiple_status_reg addr(%x) != recv write multiple_status addr(%x)",
reg_info->register_addr, write_state_reg->register_addr);
break;
}
if ((uint16_t)reg_info->register_cnt != write_state_reg->register_cnt)
{
M_MODBUS_LOG_ERROR("write_multiple_status_cnt(%x) != recv write multiple_status cnt(%x)",
reg_info->register_cnt, write_state_reg->register_cnt);
break;
}
write_data_len = reg_info->register_cnt / 8 + (reg_info->register_cnt % 8 ? 1 : 0);
if (write_data_len != write_state_reg->register_data_len)
{
M_MODBUS_LOG_ERROR("write_multiple_status_data_len(%x) != recv write multiple_status data len(%x)",
write_data_len, write_state_reg->register_data_len);
break;
}
M_MODBUS_LOG_DEBUG("recv write multiple_status data ok");
ret = M_MODBUS_OK;
}
while (0);
M_MODBUS_TRACE_OUT();
return ret;
}
/**************************************************************************
* 函 数: int32_t modbus_rtu_unpack_write_multiple_storage_reg(
modbus_read_reg_info_st *reg_info, uint8_t *data, uint32_t datalen)
* 描 述: 按Modbus协议,解析读取多个存储寄存器的响应数据
* 举 例: 对于多个存储寄存器来说,组包格式,按字节的方式:
发送->slave_addr(1) + func(1) + reg_addr(2) + reg_cnt(2) + reg_val_len(1) + reg_val(n) + crc(2)
应答->slave_addr(1) + func(1) + reg_addr(2) + reg_cnt(2) + crcc(2)
* 入 参: modbus_read_reg_info_st *reg_info : 输入读写寄存器信息
uint8_t *data : 按modbus协议接收的数据流
uint32_t datalen : 数据流长度
* 出 参: void
* 返回值: int32_t : M_MODBUS_OK - 成功
M_MODBUS_ERR - 失败
**************************************************************************/
static int32_t modbus_rtu_unpack_write_multiple_storage_reg
(
modbus_rtu_write_reg_info_st *reg_info,
uint8_t *data,
uint32_t datalen
)
{
int32_t ret = M_MODBUS_ERR;
modbus_rtu_ack_write_multiple_storage_reg_st *write_storage_reg = NULL;
M_MODBUS_TRACE_IN();
do
{
/* 入参判断 */
if ((NULL == reg_info) || (NULL == data) || (3 >= datalen))
{
M_MODBUS_LOG_ERROR("input param is error, null point");
break;
}
write_storage_reg = (modbus_rtu_ack_write_multiple_storage_reg_st *)data;
/* 寄存器地址和寄存器个数,寄存器长度进行校验*/
write_storage_reg->register_addr = ntohs(write_storage_reg->register_addr);
write_storage_reg->register_cnt = ntohs(write_storage_reg->register_cnt);
if (reg_info->register_addr != write_storage_reg->register_addr)
{
M_MODBUS_LOG_ERROR("write_multiple_storage_reg addr(%x) != recv write multiple_storage addr(%x)",
reg_info->register_addr, write_storage_reg->register_addr);
break;
}
if ((uint16_t)reg_info->register_cnt != write_storage_reg->register_cnt)
{
M_MODBUS_LOG_ERROR("write_multiple_storage_cnt(%x) != recv write multiple_storage cnt(%x)",
reg_info->register_cnt, write_storage_reg->register_cnt);
break;
}
M_MODBUS_LOG_DEBUG("recv write multiple_storage data ok");
ret = M_MODBUS_OK;
}
while (0);
M_MODBUS_TRACE_OUT();
return ret;
}
/**************************************************************************
* 函 数: int32_t modbus_rtu_unpack_write_reg(modbus_read_reg_info_st *reg_info,
uint8_t *data, uint32_t datalen)
* 描 述: 按Modbus协议,解析读取寄存器的响应数据
* 举 例: 对于单个状态/存储寄存器来说,组包格式,按字节的方式:
发送->slave_addr(1) + func(1) + reg_addr(2) + reg_val(2) + crc(2)
应答->slave_addr(1) + func(1) + reg_addr(2) + reg_val(2) + crc(2)
对于多个状态寄存器来说,组包格式,按bit的方式:
发送->slave_addr(1) + func(1) + reg_addr(2) + reg_cnt(2) + reg_val_len(1) + reg_val(n) + crc(2)
应答->slave_addr(1) + func(1) + reg_addr(2) + reg_cnt(2) + reg_val_len(1) + crcc(2)
对于多个存储寄存器来说,组包格式,按字节的方式:
发送->slave_addr(1) + func(1) + reg_addr(2) + reg_cnt(2) + reg_val_len(1) + reg_val(n) + crc(2)
应答->slave_addr(1) + func(1) + reg_addr(2) + reg_cnt(2) + crcc(2)
* 入 参: modbus_read_reg_info_st *reg_info : 输入读写寄存器信息
uint8_t *data : 按modbus协议接收的数据流
uint32_t datalen : 数据流长度
* 出 参: void
* 返回值: int32_t : M_MODBUS_OK - 成功
M_MODBUS_ERR - 失败
**************************************************************************/
static int32_t modbus_rtu_unpack_write_reg
(
modbus_rtu_write_reg_info_st *reg_info,
uint8_t *data,
uint32_t datalen
)
{
int32_t ret = M_MODBUS_ERR;
uint16_t crc_val = 0;
uint16_t recv_crc_val = 0;
MODBUS_ERROR_CODE_E err_code = E_CODE_NO_ERR;
modbus_rtu_fail_st *fail_info = NULL;
modbus_rtu_ack_data_rw_st *data_ack = NULL;
M_MODBUS_TRACE_IN();
do
{
/* 入参判断 */
if ((NULL == reg_info) || (NULL == data) || (3 >= datalen))
{
M_MODBUS_LOG_ERROR("input param is error, null point");
break;
}
data_ack = (modbus_rtu_ack_data_rw_st *)data;
/* 地址是否一致 */
if (reg_info->slave_addr != data_ack->slave_addr)
{
M_MODBUS_LOG_ERROR("write register slave addr[%x] != recv data ack slave addr[%x]",
reg_info->slave_addr, data_ack->slave_addr);
break;
}
/* crc校验 */
ret = modbus_calc_crc(data, datalen - 2, &crc_val);
if (M_MODBUS_OK != ret)
{
M_MODBUS_LOG_ERROR("recv write_signal_reg ack calc crc failed");
break;
}
recv_crc_val = (uint16_t)(((uint16_t)data[datalen - 2] << 8) | (uint16_t)data[datalen - 1]);
if (crc_val != recv_crc_val)
{
M_MODBUS_LOG_ERROR("recv write_signal_reg_crc(%x) not equal calc crc(%x)", recv_crc_val, crc_val);
ret = M_MODBUS_ERR;
break;
}
/* 错误功能码解析 */
if ((uint8_t)reg_info->func_code != data_ack->func_code)
{
if (((uint8_t)reg_info->func_code | 0x80) == data_ack->func_code)
{
fail_info = (modbus_rtu_fail_st *)data;
/* 返回的是错误码 */
switch (fail_info->except_code)
{
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
{
err_code = (MODBUS_ERROR_CODE_E)fail_info->except_code;
break;
}
default:
{
err_code = E_CODE_OTHER_ERR;
break;
}
}
}
else
{
err_code = E_CODE_OTHER_ERR;
}
M_MODBUS_LOG_WARN("write signal register data, func code[%x] is fail ack value[%x]",
reg_info->func_code, data_ack->func_code);
ret = M_MODBUS_ERR;
break;
}
switch (reg_info->func_code)
{
case E_FUNC_CODE_WRITE_SINGLE_COIL:
case E_FUNC_CODE_WRITE_SINGLE_REGISTER:
{
ret = modbus_rtu_unpack_write_signal_reg(reg_info, data, datalen);
}
case E_FUNC_CODE_WRITE_MULTIPLE_COILS:
{
ret = modbus_rtu_unpack_write_multiple_state_reg(reg_info, data, datalen);
}
case E_FUNC_CODE_WRITE_MULTIPLE_REGISTERS:
{
ret = modbus_rtu_unpack_write_multiple_storage_reg(reg_info, data, datalen);
}
default:
{
/* 其他 */
M_MODBUS_LOG_WARN("not recv write register data, func code[%x]", reg_info->func_code);
ret = M_MODBUS_ERR;
}
}
}
while (0);
M_MODBUS_TRACE_OUT();
return ret;
}
/**************************************************************************
* 函 数: int32_t modbus_rtu_init(modbus_com_params_st *com_param)
* 描 述: 初始化modbus rtu 通信口参数
* 入 参: modbus_com_params_st *com_param : 通信口参数信息
* 出 参: void
* 返回值: int32_t : M_MODBUS_OK - 成功
M_MODBUS_ERR - 失败
**************************************************************************/
int32_t modbus_rtu_init(modbus_com_params_st *com_param)
{
int32_t ret = M_MODBUS_ERR;
M_MODBUS_TRACE_IN();
do
{
if (NULL == com_param)
{
M_MODBUS_LOG_ERROR("input param is error, null point");
break;
}
/* 参数初始化 */
(void)memset(&g_modbus_com, 0, sizeof(g_modbus_com));
if (strlen(com_param->device) > 0)
{
(void)memcpy(g_modbus_com.device, com_param->device, strlen(com_param->device));
}
/* 波特率 */
if ((E_BAUD_2400BPS > com_param->baud) || (E_BAUD_256000BPS < com_param->baud))
{
g_modbus_com.baud = E_BAUD_9600BPS;
}
else
{
g_modbus_com.baud = com_param->baud;
}
/* 数据位 */
if ((E_DATA_4BITS > com_param->data_bit) || (E_DATA_8BITS < com_param->data_bit))
{
g_modbus_com.data_bit = E_DATA_8BITS;
}
else
{
g_modbus_com.data_bit = com_param->data_bit;
}
/* 停止位 */
g_modbus_com.stop_bit = com_param->stop_bit;
g_modbus_com.parity = com_param->parity;
}
while (0);
M_MODBUS_TRACE_OUT();
return ret;
}
/**************************************************************************
** 名称: modbus_rtu.h头文件
* 日期:
* 作者:
* 描述:
* 修改记录:
***************************************************************************/
#ifndef __MODBUS_RTU_H__
#define __MODBUS_RTU_H__
#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif /* __cplusplus */
#include "modbus_common.h"
/**************************************************************************
** 宏定义
**************************************************************************/
/**************************************************************************
** 结构体声明
**************************************************************************/
/* 返回失败功能码应答结构体 */
typedef struct _modbus_rtu_fail_st
{
uint8_t slave_addr;
uint8_t err_func_code;
uint8_t except_code;
uint16_t crc16;
} __attribute__((packed)) modbus_rtu_fail_st;
/* modbus rtu 读写寄存器响应协议结构 */
typedef struct _modbus_rtu_ack_data_rw_st
{
uint8_t slave_addr; /* 从设备地址 */
uint8_t func_code; /* 功能码 */
uint8_t datalen; /* 响应数长的 */
uint8_t *data; /* 响应数据 */
} __attribute__((packed)) modbus_rtu_ack_data_rw_st;
/****************************************************************************
** 读寄存器结构体
*****************************************************************************/
/* 读从机寄存器的结构体(支持1-126) */
typedef struct _modbus_read_reg_info_st
{
uint8_t slave_addr; /* 从设备地址 */
MODBUS_FUNC_CODE_TYPE_E func_code; /* 功能码 */
uint16_t register_addr; /* 寄存器首地址 */
uint16_t register_cnt; /* 寄存器数目 */
} modbus_read_reg_info_st;
/* modbus rtu 读寄存器协议结构 */
typedef struct _modbus_rtu_read_reg_st
{
uint8_t slave_addr; /* 从设备地址 */
uint8_t func_code; /* 功能码 */
uint16_t register_addr; /* 读寄存器地址 */
uint16_t register_cnt; /* 寄存器数目 */
uint16_t crc; /* crc校验 */
} __attribute__((packed)) modbus_rtu_read_reg_st;
/* 用于区分读状态寄存器,还是读保存寄存器 */
typedef enum _MODBUS_RTU_READ_REG_TYPE_E
{
E_MODBUS_RTU_READ_STATUS_REG = 0,
E_MODBUS_RTU_READ_STORAGE_REG,
} MODBUS_RTU_READ_REG_TYPE_E;
/* 用于保存读状态寄存器和保存寄存器的数据值信息 */
typedef union _modbus_rtu_reg_data_u
{
uint16_t *storage_reg_value;
uint8_t *status_reg_value;
} modbus_rtu_reg_data_u;
typedef struct _modbus_rtu_reg_data_val_st
{
MODBUS_RTU_READ_REG_TYPE_E read_reg_type; /* 状态寄存器,还是存储寄存器 */
uint16_t start_register_addr; /* 寄存器起始地址 */
uint8_t read_register_cnt; /* 寄存器个数 */
modbus_rtu_reg_data_u reg_data; /* 保存的寄存器数据值 */
} modbus_rtu_reg_data_val_st;
/****************************************************************************
** 写寄存器结构体
****************************************************************************/
/* 用于写寄存器的值 */
typedef union _modbus_rtu_write_reg_val_st
{
uint16_t *register_val; /* 写寄存器的值 */
uint8_t *multiple_status_reg_val; /* 多个状态寄存器的值 */
} modbus_rtu_write_reg_val_st;
/* 用于写状态/存储寄存器 */
typedef struct _modbus_rtu_write_reg_info_st
{
uint8_t slave_addr; /* 从设备地址 */
MODBUS_FUNC_CODE_TYPE_E func_code; /* 功能码 */
uint16_t register_addr; /* 寄存器首地址 */
uint8_t register_cnt; /* 写寄存器个数 */
modbus_rtu_write_reg_val_st reg_val; /* 寄存器的值 */
} modbus_rtu_write_reg_info_st;
/* 解析写单个寄存器 */
typedef struct _modbus_rtu_ack_write_signal_reg_st
{
uint8_t slave_addr; /* 从设备地址 */
uint8_t func_code; /* 功能码 */
uint16_t register_addr; /* 寄存器首地址 */
uint16_t register_data; /* 写寄存器的值 */
uint16_t crc; /* crc校验 */
} __attribute__((packed)) modbus_rtu_ack_write_signal_reg_st;
/* 解析写多个状态寄存器应答结构 */
typedef struct _modbus_rtu_ack_write_multiple_status_reg_st
{
uint8_t slave_addr; /* 从设备地址 */
uint8_t func_code; /* 功能码 */
uint16_t register_addr; /* 寄存器首地址 */
uint16_t register_cnt; /* 寄存器个数 */
uint8_t register_data_len; /* 寄存器数据值长度 */
uint16_t crc; /* crc校验 */
} __attribute__((packed)) modbus_rtu_ack_write_multiple_state_reg_st;
/* 解析写多个状态寄存器应答结构 */
typedef struct _modbus_rtu_ack_write_multiple_storage_reg_st
{
uint8_t slave_addr; /* 从设备地址 */
uint8_t func_code; /* 功能码 */
uint16_t register_addr; /* 寄存器首地址 */
uint16_t register_cnt; /* 寄存器个数 */
uint16_t crc; /* crc校验 */
} __attribute__((packed)) modbus_rtu_ack_write_multiple_storage_reg_st;
/**************************************************************************
** 函数声明
**************************************************************************/
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */
#endif