Modbus-RTU協議代碼
下面是RTU協議代碼,讀寫寄存器數據組包,校驗,解析
modbus_common.h文件:
/**************************************************************************
** 名稱: modbus_common.h頭文件
* 日期:
* 作者:
* 描述:
* 修改記錄:
***************************************************************************/
#ifndef __MODBUS_COMMON_H__
#define __MODBUS_COMMON_H__
#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif /* __cplusplus */
#include <stdarg.h>
#include <stdio.h>
/**************************************************************************
** 宏定義
**************************************************************************/
/* 簡化類型定義 */
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 <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <netinet/in.h>
/**************************************************************************
** 宏定義
**************************************************************************/
#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文件:
/**************************************************************************
** 名稱: modbus_rtu.c文件
* 日期:
* 作者:
* 描述:
* 1.讀線圈數據(開關量-- 0x01) -- 按位進行讀
* 2.讀離散型量(開關量-- 0x02) -- 按位進行讀
*
*
* 修改記錄:
***************************************************************************/
#include "modbus_rtu.h"
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#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頭文件:
/**************************************************************************
** 名稱: 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
---------------------
作者:chensufei24
來源:CSDN
原文:https://blog.csdn.net/chensufei24/article/details/82995720
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

浙公網安備 33010602011771號