RTU/release/inc/myModbus.h

735 lines
28 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* myModbus.h — libmodbus 统一 API 头文件
*
* 本文件整合了 libmodbus 的全部公开接口,包括 RTU 和 TCP 后端。
* 用户只需包含这一个头文件即可使用 libmodbus 的所有功能。
*
* 原始代码版权 © Stéphane Raimbault <stephane.raimbault@gmail.com>
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* 库版本3.1.12
* 官网http://libmodbus.org
*/
#ifndef MY_MODBUS_H
#define MY_MODBUS_H
// clang-format off
/* 添加此宏用于定义了 Unix 风格的系统 */
#if (defined(__unix__) || defined(unix)) && !defined(USG)
# include <sys/param.h>
#endif
#include <stdint.h>
/* ================================================================
* 版本信息宏
*
* 版本号格式LIBMODBUS_VERSION_MAJOR.MINOR.MICRO
* 当前版本3.1.12
* ================================================================ */
#define LIBMODBUS_VERSION_MAJOR 3
#define LIBMODBUS_VERSION_MINOR 1
#define LIBMODBUS_VERSION_MICRO 12
/* 完整版本号字符串 */
#define LIBMODBUS_VERSION 3.1.12
#define LIBMODBUS_VERSION_STRING "3.1.12"
/* 数值编码的版本号,例如 v1.2.3 编码为 0x010203 */
#define LIBMODBUS_VERSION_HEX \
((LIBMODBUS_VERSION_MAJOR << 16) | (LIBMODBUS_VERSION_MINOR << 8) | \
(LIBMODBUS_VERSION_MICRO << 0))
/* 版本检查宏:当库版本大于等于指定版本时返回真
* @param major 主版本号
* @param minor 次版本号
* @param micro 修订版本号
*/
#define LIBMODBUS_VERSION_CHECK(major, minor, micro) \
(LIBMODBUS_VERSION_MAJOR > (major) || \
(LIBMODBUS_VERSION_MAJOR == (major) && LIBMODBUS_VERSION_MINOR > (minor)) || \
(LIBMODBUS_VERSION_MAJOR == (major) && LIBMODBUS_VERSION_MINOR == (minor) && \
LIBMODBUS_VERSION_MICRO >= (micro)))
#define MODBUS_API
#ifdef __cplusplus
# define MODBUS_BEGIN_DECLS extern "C" {
# define MODBUS_END_DECLS }
#else
# define MODBUS_BEGIN_DECLS
# define MODBUS_END_DECLS
#endif
// clang-format on
MODBUS_BEGIN_DECLS
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef OFF
#define OFF 0
#endif
#ifndef ON
#define ON 1
#endif
/* ================================================================
* 一、Modbus 功能码定义
* ================================================================ */
/* 读取线圈状态 */
#define MODBUS_FC_READ_COILS 0x01
/* 读取离散输入状态 */
#define MODBUS_FC_READ_DISCRETE_INPUTS 0x02
/* 读取保持寄存器 */
#define MODBUS_FC_READ_HOLDING_REGISTERS 0x03
/* 读取输入寄存器 */
#define MODBUS_FC_READ_INPUT_REGISTERS 0x04
/* 写单个线圈 */
#define MODBUS_FC_WRITE_SINGLE_COIL 0x05
/* 写单个寄存器 */
#define MODBUS_FC_WRITE_SINGLE_REGISTER 0x06
/* 读取异常状态 */
#define MODBUS_FC_READ_EXCEPTION_STATUS 0x07
/* 写多个线圈 */
#define MODBUS_FC_WRITE_MULTIPLE_COILS 0x0F
/* 写多个寄存器 */
#define MODBUS_FC_WRITE_MULTIPLE_REGISTERS 0x10
/* 报告从站ID */
#define MODBUS_FC_REPORT_SLAVE_ID 0x11
/* 掩码写寄存器 */
#define MODBUS_FC_MASK_WRITE_REGISTER 0x16
/* 读写多个寄存器 */
#define MODBUS_FC_WRITE_AND_READ_REGISTERS 0x17
/* 广播地址(仅 RTU 模式有效) */
#define MODBUS_BROADCAST_ADDRESS 0
/* ================================================================
* 二、数据量限制
*
* 参考 Modbus_Application_Protocol_V1_1b.pdf
* ================================================================ */
/* 位操作限制:
* 单次读取最多 2000 个位(线圈/离散输入)
* 单次写入最多 1968 个位(线圈)
*/
#define MODBUS_MAX_READ_BITS 2000
#define MODBUS_MAX_WRITE_BITS 1968
/* 寄存器操作限制:
* 单次读取最多 125 个寄存器
* 单次写入最多 123 个寄存器
* 读写操作中写入最多 121 个、读取最多 125 个寄存器
*/
#define MODBUS_MAX_READ_REGISTERS 125
#define MODBUS_MAX_WRITE_REGISTERS 123
#define MODBUS_MAX_WR_WRITE_REGISTERS 121
#define MODBUS_MAX_WR_READ_REGISTERS 125
/* ================================================================
* 三、ADU/PDU 长度定义
*
* Modbus PDU 的大小受限于最早 Modbus 实现的尺寸约束:
* 最大 RS485 ADU = 256 字节。
* 因此串行线路通信的 Modbus PDU = 256 - 从站地址(1) - CRC(2) = 253 字节。
*
* 由此可得各后端最大 ADU
* RTU: 253 + 从站地址(1) + CRC(2) = 256 字节
* TCP: 253 + MBAP头(7) = 260 字节
* 统一取 260 字节保证兼容所有后端。
* ================================================================ */
#define MODBUS_MAX_PDU_LENGTH 253
#define MODBUS_MAX_ADU_LENGTH 260
/* 用一个任意数作为 errno 基准值,避免与系统 errno 冲突 */
#define MODBUS_ENOBASE 112345678
/* ================================================================
* 四、Modbus 协议异常码
*
* 当服务端无法处理请求时,在功能码上设置 0x80 并返回异常码。
* ================================================================ */
enum {
MODBUS_EXCEPTION_ILLEGAL_FUNCTION = 0x01, /* 非法功能码 */
MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, /* 非法数据地址 */
MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, /* 非法数据值 */
MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE, /* 从站/服务端故障 */
MODBUS_EXCEPTION_ACKNOWLEDGE, /* 确认(处理中) */
MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY, /* 从站/服务端忙 */
MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE, /* 否定确认 */
MODBUS_EXCEPTION_MEMORY_PARITY, /* 存储器奇偶校验错误 */
MODBUS_EXCEPTION_NOT_DEFINED, /* 未定义 */
MODBUS_EXCEPTION_GATEWAY_PATH, /* 网关路径不可用 */
MODBUS_EXCEPTION_GATEWAY_TARGET, /* 网关目标设备无响应 */
MODBUS_EXCEPTION_MAX /* 异常码上限 */
};
/* 异常 errno 数值:基值 + 异常码 */
#define EMBXILFUN (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_FUNCTION)
#define EMBXILADD (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS)
#define EMBXILVAL (MODBUS_ENOBASE + MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE)
#define EMBXSFAIL (MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE)
#define EMBXACK (MODBUS_ENOBASE + MODBUS_EXCEPTION_ACKNOWLEDGE)
#define EMBXSBUSY (MODBUS_ENOBASE + MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY)
#define EMBXNACK (MODBUS_ENOBASE + MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE)
#define EMBXMEMPAR (MODBUS_ENOBASE + MODBUS_EXCEPTION_MEMORY_PARITY)
#define EMBXGPATH (MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_PATH)
#define EMBXGTAR (MODBUS_ENOBASE + MODBUS_EXCEPTION_GATEWAY_TARGET)
/* libmodbus 本地错误码(非协议定义) */
#define EMBBADCRC (EMBXGTAR + 1) /* CRC 校验错误 */
#define EMBBADDATA (EMBXGTAR + 2) /* 无效数据 */
#define EMBBADEXC (EMBXGTAR + 3) /* 无效异常码 */
#define EMBUNKEXC (EMBXGTAR + 4) /* 未知异常码 */
#define EMBMDATA (EMBXGTAR + 5) /* 数据过多 */
#define EMBBADSLAVE (EMBXGTAR + 6) /* 响应并非来自请求的从站 */
/* ================================================================
* 五、版本信息全局变量
* ================================================================ */
extern const unsigned int libmodbus_version_major;
extern const unsigned int libmodbus_version_minor;
extern const unsigned int libmodbus_version_micro;
/* ================================================================
* 六、核心数据类型
* ================================================================ */
/* Modbus 上下文(不透明类型,所有 API 操作的基础句柄) */
typedef struct _modbus modbus_t;
/*!
* Modbus 数据映射表(服务端数据模型)
*
* 四种数据区与 Modbus 协议的对应关系:
* Coils (0X) — tab_bits — 可读写位
* Discrete Inputs (1X) — tab_input_bits — 只读位
* Holding Registers (4X) — tab_registers — 可读写 16 位寄存器
* Input Registers (3X) — tab_input_registers — 只读 16 位寄存器
*
* tab_xxx 数组中的内存布局采用处理器字节序。
* 接收 Modbus 数据时会自动转换为处理器字节序,参见 read_registers()。
*/
typedef struct _modbus_mapping_t {
int nb_bits; /* 线圈数量 */
int start_bits; /* 线圈起始地址 */
int nb_input_bits; /* 离散输入数量 */
int start_input_bits; /* 离散输入起始地址 */
int nb_input_registers; /* 输入寄存器数量 */
int start_input_registers; /* 输入寄存器起始地址 */
int nb_registers; /* 保持寄存器数量 */
int start_registers; /* 保持寄存器起始地址 */
uint8_t *tab_bits; /* 线圈数据数组 */
uint8_t *tab_input_bits; /* 离散输入数据数组 */
uint16_t *tab_input_registers; /* 输入寄存器数据数组 */
uint16_t *tab_registers; /* 保持寄存器数据数组 */
} modbus_mapping_t;
/*!
* 错误恢复模式(可按位组合)
*
* MODBUS_ERROR_RECOVERY_LINK — 链路级恢复send() 失败时自动关闭连接、
* 等待超时后重连
* MODBUS_ERROR_RECOVERY_PROTOCOL — 协议级恢复:收到无效回复时自动休眠刷新
*/
typedef enum {
MODBUS_ERROR_RECOVERY_NONE = 0,
MODBUS_ERROR_RECOVERY_LINK = (1 << 1),
MODBUS_ERROR_RECOVERY_PROTOCOL = (1 << 2)
} modbus_error_recovery_mode;
/*!
* Quirks 行为开关(用于启用以非标准方式工作的设备的兼容模式)
*
* MODBUS_QUIRK_MAX_SLAVE — 允许从站地址扩展到 255而非标准 247
* MODBUS_QUIRK_REPLY_TO_BROADCAST — 允许服务器回复广播请求
*/
typedef enum {
MODBUS_QUIRK_NONE = 0,
MODBUS_QUIRK_MAX_SLAVE = (1 << 1),
MODBUS_QUIRK_REPLY_TO_BROADCAST = (1 << 2),
MODBUS_QUIRK_ALL = 0xFF
} modbus_quirks;
/* ================================================================
* 七、通用 APIRTU 和 TCP 均适用)
* ================================================================ */
/* ---- 从站地址 ---- */
/* 设置从站地址RTU: 0~247, TCP: 默认 0xFF
* @param ctx Modbus 上下文
* @param slave 从站地址
* @return 成功返回 0失败返回 -1 并设置 errno
*/
MODBUS_API int modbus_set_slave(modbus_t *ctx, int slave);
/* 获取当前从站地址
* @param ctx Modbus 上下文
* @return 从站地址,失败返回 -1
*/
MODBUS_API int modbus_get_slave(modbus_t *ctx);
/* ---- 错误恢复 ---- */
MODBUS_API int modbus_set_error_recovery(modbus_t *ctx,
modbus_error_recovery_mode error_recovery);
/* ---- Socket 描述符 ---- */
/* 设置自定义 socket 文件描述符
* @param ctx Modbus 上下文
* @param s 文件描述符
*/
MODBUS_API int modbus_set_socket(modbus_t *ctx, int s);
/* 获取当前 socket 文件描述符 */
MODBUS_API int modbus_get_socket(modbus_t *ctx);
/* ---- 超时配置 ---- */
/* 获取/设置响应超时(客户端等待服务端响应的最长时间)
* @param to_sec 秒
* @param to_usec 微秒0~999999
* 默认500000 微秒0.5 秒)
*/
MODBUS_API int
modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
MODBUS_API int
modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
/* 获取/设置字节间超时(接收时两个连续字节间的最大间隔)
* 可设置为 0 禁用字节间超时检查
*/
MODBUS_API int
modbus_get_byte_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
MODBUS_API int modbus_set_byte_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
/* 获取/设置指示超时(服务端等待客户端请求的最大时间)
* 默认为 0无限等待/阻塞模式)
*/
MODBUS_API int
modbus_get_indication_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec);
MODBUS_API int
modbus_set_indication_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec);
/* 获取帧头长度RTU=1, TCP=7 */
MODBUS_API int modbus_get_header_length(modbus_t *ctx);
/* ---- 连接管理 ---- */
/* 建立 Modbus 连接
* TCP: 连接指定 IP 和端口
* RTU: 打开串口并配置参数
*/
MODBUS_API int modbus_connect(modbus_t *ctx);
/* 关闭连接TCP: 关闭 socket, RTU: 关闭串口并恢复设置) */
MODBUS_API void modbus_close(modbus_t *ctx);
/* 释放 Modbus 上下文占用的所有内存 */
MODBUS_API void modbus_free(modbus_t *ctx);
/* 清空收发缓冲区 */
MODBUS_API int modbus_flush(modbus_t *ctx);
/* 开启/关闭调试输出(将通信数据打印到 stderr/stdout */
MODBUS_API int modbus_set_debug(modbus_t *ctx, int flag);
/* ---- 错误信息 ---- */
/* 将 errno 错误码转换为可读的字符串描述 */
MODBUS_API const char *modbus_strerror(int errnum);
/* ================================================================
* 八、客户端读写 API
*
* 以下函数用于 Modbus 主站Master/Client向从站发起读写请求。
* 成功时返回读取/写入的数据个数,失败时返回 -1 并设置 errno。
* ================================================================ */
/* ---- 位操作 ---- */
/* 读取线圈状态 (FC 0x01)
* @param addr 起始地址
* @param nb 数量1~2000
* @param dest 输出缓冲区(每个元素为 TRUE 或 FALSE
* @return 成功返回 nb失败返回 -1
*/
MODBUS_API int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
/* 读取离散输入状态 (FC 0x02) */
MODBUS_API int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
/* 读取保持寄存器 (FC 0x03)
* @param addr 起始地址
* @param nb 数量1~125
* @param dest 输出缓冲区
* @return 成功返回 nb失败返回 -1
*/
MODBUS_API int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
/* 读取输入寄存器 (FC 0x04) */
MODBUS_API int
modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
/* 写单个线圈 (FC 0x05)
* @param coil_addr 线圈地址
* @param status ON(1) 或 OFF(0)
* @return 成功返回 1失败返回 -1
*/
MODBUS_API int modbus_write_bit(modbus_t *ctx, int coil_addr, int status);
/* 写单个寄存器 (FC 0x06) */
MODBUS_API int modbus_write_register(modbus_t *ctx, int reg_addr, const uint16_t value);
/* 写多个线圈 (FC 0x0F)
* @param addr 起始地址
* @param nb 数量1~1968
* @param data 数据源数组(每个元素为 TRUE 或 FALSE
* @return 成功返回 nb失败返回 -1
*/
MODBUS_API int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data);
/* 写多个寄存器 (FC 0x10)
* @param addr 起始地址
* @param nb 数量1~123
* @param data 数据源数组
* @return 成功返回 nb失败返回 -1
*/
MODBUS_API int
modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data);
/* 掩码写寄存器 (FC 0x16) — 原子读-修改-写操作
* 操作result = (current & and_mask) | (or_mask & ~and_mask)
*/
MODBUS_API int
modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask);
/* 写并读多个寄存器 (FC 0x17) — 在单次请求中完成写入和读取
* @param write_addr 写入起始地址
* @param write_nb 写入数量1~121
* @param src 写入数据源
* @param read_addr 读取起始地址
* @param read_nb 读取数量1~125
* @param dest 读取数据目标
* @return 成功返回 read_nb失败返回 -1
*/
MODBUS_API int modbus_write_and_read_registers(modbus_t *ctx,
int write_addr,
int write_nb,
const uint16_t *src,
int read_addr,
int read_nb,
uint16_t *dest);
/* 读取从站 ID 信息 (FC 0x11) */
MODBUS_API int modbus_report_slave_id(modbus_t *ctx, int max_dest, uint8_t *dest);
/* ================================================================
* 九、服务端 API
*
* 以下函数用于 Modbus 从站Slave/Server处理请求和生成响应。
* ================================================================ */
/* ---- 数据映射管理 ---- */
/* 创建数据映射(指定起始地址,推荐使用,灵活)
* @param start_bits 线圈起始地址
* @param nb_bits 线圈数量
* @param start_input_bits 离散输入起始地址
* @param nb_input_bits 离散输入数量
* @param start_registers 保持寄存器起始地址
* @param nb_registers 保持寄存器数量
* @param start_input_registers 输入寄存器起始地址
* @param nb_input_registers 输入寄存器数量
* @return 成功返回映射指针,失败返回 NULL
*/
MODBUS_API modbus_mapping_t *
modbus_mapping_new_start_address(unsigned int start_bits,
unsigned int nb_bits,
unsigned int start_input_bits,
unsigned int nb_input_bits,
unsigned int start_registers,
unsigned int nb_registers,
unsigned int start_input_registers,
unsigned int nb_input_registers);
/* 创建数据映射(起始地址均为 0 */
MODBUS_API modbus_mapping_t *modbus_mapping_new(int nb_bits,
int nb_input_bits,
int nb_registers,
int nb_input_registers);
/* 释放数据映射占用的内存 */
MODBUS_API void modbus_mapping_free(modbus_mapping_t *mb_mapping);
/* ---- 原始请求发送 ---- */
/* 发送原始 Modbus 请求帧(不含后端帧头/校验,库会自动添加)
* @param ctx Modbus 上下文
* @param raw_req 原始请求数据(格式:[slave][function][data...]
* @param raw_req_length 请求长度
* @return 成功返回已发送字节数,失败返回 -1
*/
MODBUS_API int
modbus_send_raw_request(modbus_t *ctx, const uint8_t *raw_req, int raw_req_length);
/* 同上,但可指定事务 IDTCP 模式下有意义) */
MODBUS_API int modbus_send_raw_request_tid(modbus_t *ctx,
const uint8_t *raw_req,
int raw_req_length,
int tid);
/* ---- 请求接收 ---- */
/* 接收 Modbus 请求(服务端使用)
* @param ctx Modbus 上下文
* @param req 接收缓冲区
* @return >0 请求长度, 0 请求已过滤, -1 错误
*/
MODBUS_API int modbus_receive(modbus_t *ctx, uint8_t *req);
/* 接收确认响应(客户端使用)
* @param ctx Modbus 上下文
* @param rsp 接收缓冲区
* @return >0 响应长度, -1 错误
*/
MODBUS_API int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp);
/* ---- 请求处理 ---- */
/* 自动解析请求并根据数据映射生成响应
* @param ctx Modbus 上下文
* @param req 请求数据
* @param req_length 请求长度
* @param mb_mapping 数据映射表
* @return 成功返回响应长度,失败返回 -1
*/
MODBUS_API int modbus_reply(modbus_t *ctx,
const uint8_t *req,
int req_length,
modbus_mapping_t *mb_mapping);
/* 手动返回异常响应
* @param ctx Modbus 上下文
* @param req 原始请求数据
* @param exception_code 异常码MODBUS_EXCEPTION_xxx
* @return 成功返回响应长度,失败返回 -1
*/
MODBUS_API int
modbus_reply_exception(modbus_t *ctx, const uint8_t *req, unsigned int exception_code);
/* ---- Quirks 管理 ---- */
/* 启用 quirks 特性(可按位组合) */
MODBUS_API int modbus_enable_quirks(modbus_t *ctx, unsigned int quirks_mask);
/* 禁用 quirks 特性 */
MODBUS_API int modbus_disable_quirks(modbus_t *ctx, unsigned int quirks_mask);
/* ================================================================
* 十、数据工具函数
* ================================================================ */
/* ---- 字节/位转换宏 ---- */
#define MODBUS_GET_HIGH_BYTE(data) (((data) >> 8) & 0xFF)
#define MODBUS_GET_LOW_BYTE(data) ((data) & 0xFF)
/* 从 4 个连续的 16 位寄存器中组合出 64 位整数 */
#define MODBUS_GET_INT64_FROM_INT16(tab_int16, index) \
(((int64_t) tab_int16[(index)] << 48) | ((int64_t) tab_int16[(index) + 1] << 32) | \
((int64_t) tab_int16[(index) + 2] << 16) | (int64_t) tab_int16[(index) + 3])
/* 从 2 个连续的 16 位寄存器中组合出 32 位整数 */
#define MODBUS_GET_INT32_FROM_INT16(tab_int16, index) \
(((int32_t) tab_int16[(index)] << 16) | (int32_t) tab_int16[(index) + 1])
/* 从 2 个连续的 8 位值中组合出 16 位整数 */
#define MODBUS_GET_INT16_FROM_INT8(tab_int8, index) \
(((int16_t) tab_int8[(index)] << 8) | (int16_t) tab_int8[(index) + 1])
/* 将 16 位整数拆分写入 2 个连续的 8 位值 */
#define MODBUS_SET_INT16_TO_INT8(tab_int8, index, value) \
do { \
((int8_t *) (tab_int8))[(index)] = (int8_t) ((value) >> 8); \
((int8_t *) (tab_int8))[(index) + 1] = (int8_t) (value); \
} while (0)
/* 将 32 位整数拆分写入 2 个连续的 16 位寄存器 */
#define MODBUS_SET_INT32_TO_INT16(tab_int16, index, value) \
do { \
((int16_t *) (tab_int16))[(index)] = (int16_t) ((value) >> 16); \
((int16_t *) (tab_int16))[(index) + 1] = (int16_t) (value); \
} while (0)
/* 将 64 位整数拆分写入 4 个连续的 16 位寄存器 */
#define MODBUS_SET_INT64_TO_INT16(tab_int16, index, value) \
do { \
((int16_t *) (tab_int16))[(index)] = (int16_t) ((value) >> 48); \
((int16_t *) (tab_int16))[(index) + 1] = (int16_t) ((value) >> 32); \
((int16_t *) (tab_int16))[(index) + 2] = (int16_t) ((value) >> 16); \
((int16_t *) (tab_int16))[(index) + 3] = (int16_t) (value); \
} while (0)
/* ---- 位数组工具函数 ---- */
/* 从单字节设置 8 个位 */
MODBUS_API void modbus_set_bits_from_byte(uint8_t *dest, int idx, const uint8_t value);
/* 从字节数组设置多个位 */
MODBUS_API void modbus_set_bits_from_bytes(uint8_t *dest,
int idx,
unsigned int nb_bits,
const uint8_t *tab_byte);
/* 从位数组获取一个字节 */
MODBUS_API uint8_t modbus_get_byte_from_bits(const uint8_t *src,
int idx,
unsigned int nb_bits);
/* ---- 浮点数转换IEEE 754 单精度 ↔ 2 个 Modbus 寄存器) ---- */
/* 已弃用:默认使用 CDAB字交换字节序 */
MODBUS_API float modbus_get_float(const uint16_t *src);
/* ABCD 字节序(大端):高位字节在前 */
MODBUS_API float modbus_get_float_abcd(const uint16_t *src);
/* DCBA 字节序(小端):低位字节在前 */
MODBUS_API float modbus_get_float_dcba(const uint16_t *src);
/* BADC 字节序:字内字节交换 */
MODBUS_API float modbus_get_float_badc(const uint16_t *src);
/* CDAB 字节序:字间交换 */
MODBUS_API float modbus_get_float_cdab(const uint16_t *src);
/* 已弃用:默认使用 CDAB字交换字节序 */
MODBUS_API void modbus_set_float(float f, uint16_t *dest);
MODBUS_API void modbus_set_float_abcd(float f, uint16_t *dest);
MODBUS_API void modbus_set_float_dcba(float f, uint16_t *dest);
MODBUS_API void modbus_set_float_badc(float f, uint16_t *dest);
MODBUS_API void modbus_set_float_cdab(float f, uint16_t *dest);
/* ================================================================
* 十一、RTU 后端 API串口通信
*
* 仅 RTU 后端可用TCP 后端调用将返回错误。
* ================================================================ */
/* RS232 / RS485 ADU = 253 + 从站地址(1) + CRC(2) = 256 字节 */
#define MODBUS_RTU_MAX_ADU_LENGTH 256
/* 创建 RTU 上下文
* @param device 串口设备路径(如 "/dev/ttyUSB0"
* @param baud 波特率9600, 115200 等)
* @param parity 校验位:'N' 无校验, 'E' 偶校验, 'O' 奇校验
* @param data_bit 数据位5, 6, 7, 8
* @param stop_bit 停止位1 或 2
* @return 成功返回 Modbus 上下文指针,失败返回 NULL
*/
MODBUS_API modbus_t *
modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit);
/* RS232/RS485 模式切换
* MODBUS_RTU_RS232 — RS232 模式
* MODBUS_RTU_RS485 — RS485 模式
*/
#define MODBUS_RTU_RS232 0
#define MODBUS_RTU_RS485 1
/* 设置/获取串行通信模式RS232 或 RS485
* 注意:需要平台支持 TIOCSRS485 ioctl
*/
MODBUS_API int modbus_rtu_set_serial_mode(modbus_t *ctx, int mode);
MODBUS_API int modbus_rtu_get_serial_mode(modbus_t *ctx);
/* ---- RTS 引脚控制(半双工 RS485 自动收发切换) ---- */
/* RTS 控制模式 */
#define MODBUS_RTU_RTS_NONE 0 /* 不使用 RTS */
#define MODBUS_RTU_RTS_UP 1 /* 发送前置 RTS 为高 */
#define MODBUS_RTU_RTS_DOWN 2 /* 发送前置 RTS 为低 */
/* 设置/获取 RTS 控制模式 */
MODBUS_API int modbus_rtu_set_rts(modbus_t *ctx, int mode);
MODBUS_API int modbus_rtu_get_rts(modbus_t *ctx);
/* 设置自定义 RTS 回调函数
* @param set_rts 回调函数指针void callback(modbus_t *ctx, int on)
*/
MODBUS_API int modbus_rtu_set_custom_rts(modbus_t *ctx,
void (*set_rts)(modbus_t *ctx, int on));
/* 设置/获取 RTS 切换延时(微秒)
* 在发送前 RTS 信号有效后等待的延时,以及发送后 RTS 释放前的延时。
* 默认值根据波特率自动计算。
*/
MODBUS_API int modbus_rtu_set_rts_delay(modbus_t *ctx, int us);
MODBUS_API int modbus_rtu_get_rts_delay(modbus_t *ctx);
/* ================================================================
* 十二、TCP 后端 API以太网通信
* ================================================================ */
/* Modbus TCP 标准端口IANA 注册) */
#define MODBUS_TCP_DEFAULT_PORT 502
/* TCP 模式下默认的单元标识符(表示由 TCP/IP 层寻址) */
#define MODBUS_TCP_SLAVE 0xFF
/* TCP MODBUS ADU = 253 字节数据 + MBAP 报文头(7) = 260 字节 */
#define MODBUS_TCP_MAX_ADU_LENGTH 260
/* ---- TCP IPv4 接口 ---- */
/* 创建 TCP 上下文IPv4
* @param ip_address 服务器 IP 地址(如 "192.168.1.100"
* @param port 端口号(默认 502
* @return 成功返回 Modbus 上下文指针,失败返回 NULL
*/
MODBUS_API modbus_t *modbus_new_tcp(const char *ip_address, int port);
/* TCP 服务端监听
* @param ctx Modbus 上下文
* @param nb_connection 最大等待连接数
* @return 成功返回监听 socket 描述符,失败返回 -1
*/
MODBUS_API int modbus_tcp_listen(modbus_t *ctx, int nb_connection);
/* TCP 服务端接受客户端连接
* @param ctx Modbus 上下文
* @param s 监听 socket 描述符(由 modbus_tcp_listen 返回)
* @return 成功返回客户端 socket失败返回 -1
*/
MODBUS_API int modbus_tcp_accept(modbus_t *ctx, int *s);
/* ---- TCP PI 接口(协议无关,同时支持 IPv4/IPv6 ---- */
/* 创建 TCP PI 上下文
* @param node 主机名或 IP 地址NULL 表示监听所有地址)
* @param service 端口号或服务名(如 "502"
* @return 成功返回 Modbus 上下文指针,失败返回 NULL
*/
MODBUS_API modbus_t *modbus_new_tcp_pi(const char *node, const char *service);
/* TCP PI 服务端监听 */
MODBUS_API int modbus_tcp_pi_listen(modbus_t *ctx, int nb_connection);
/* TCP PI 服务端接受客户端连接 */
MODBUS_API int modbus_tcp_pi_accept(modbus_t *ctx, int *s);
MODBUS_END_DECLS
#endif /* MY_MODBUS_H */