嵌入式Libmodbus源码分析-modbus相关函数分析
Posted 沧海一笑-dj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了嵌入式Libmodbus源码分析-modbus相关函数分析相关的知识,希望对你有一定的参考价值。
00. 目录
01. modbus-private.h文件
libmodbus内部使用的结构和函数的声明
/*
* Copyright © 2010-2012 Stéphane Raimbault <stephane.raimbault@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef MODBUS_PRIVATE_H
#define MODBUS_PRIVATE_H
#ifndef _MSC_VER
# include <stdint.h>
# include <sys/time.h>
#else
# include "stdint.h"
# include <time.h>
typedef int ssize_t;
#endif
#include <sys/types.h>
#include <config.h>
#include "modbus.h"
MODBUS_BEGIN_DECLS
/* It's not really the minimal length (the real one is report slave ID
* in RTU (4 bytes)) but it's a convenient size to use in RTU or TCP
* communications to read many values or write a single one.
* Maximum between :
* - HEADER_LENGTH_TCP (7) + function (1) + address (2) + number (2)
* - HEADER_LENGTH_RTU (1) + function (1) + address (2) + number (2) + CRC (2)
*/
#define _MIN_REQ_LENGTH 12
#define _REPORT_SLAVE_ID 180
#define _MODBUS_EXCEPTION_RSP_LENGTH 5
/* Timeouts in microsecond (0.5 s) */
#define _RESPONSE_TIMEOUT 500000
#define _BYTE_TIMEOUT 500000
typedef enum {
_MODBUS_BACKEND_TYPE_RTU=0,
_MODBUS_BACKEND_TYPE_TCP
} modbus_backend_type_t;
/*
* ---------- Request Indication ----------
* | Client | ---------------------->| Server |
* ---------- Confirmation Response ----------
*/
typedef enum {
/* Request message on the server side */
MSG_INDICATION,
/* Request message on the client side */
MSG_CONFIRMATION
} msg_type_t;
/* This structure reduces the number of params in functions and so
* optimizes the speed of execution (~ 37%). */
typedef struct _sft {
int slave;
int function;
int t_id;
} sft_t;
typedef struct _modbus_backend {
unsigned int backend_type; //modbus_backend_type_t类型
unsigned int header_length; //HBMP长度
unsigned int checksum_length; //错误校验字段长度
unsigned int max_adu_length; //ADU最大长度
int (*set_slave) (modbus_t *ctx, int slave); //设置从站设备地址
//构造查询报文的基本通信帧
int (*build_request_basis) (modbus_t *ctx, int function, int addr,
int nb, uint8_t *req);
//构造响应报文的基本通信帧
int (*build_response_basis) (sft_t *sft, uint8_t *rsp);
//构造响应报文TID参数
int (*prepare_response_tid) (const uint8_t *req, int *req_length);
//发送报文前的预处理
int (*send_msg_pre) (uint8_t *req, int req_length);
//发送报文
ssize_t (*send) (modbus_t *ctx, const uint8_t *req, int req_length);
//接收报文
int (*receive) (modbus_t *ctx, uint8_t *req);
//接收报文 该函数被receive函数调用
ssize_t (*recv) (modbus_t *ctx, uint8_t *rsp, int rsp_length);
//用于数据完整性检查
int (*check_integrity) (modbus_t *ctx, uint8_t *msg,
const int msg_length);
//确认响应报文的帧头是否一致
int (*pre_check_confirmation) (modbus_t *ctx, const uint8_t *req,
const uint8_t *rsp, int rsp_length);
//建立连接
int (*connect) (modbus_t *ctx);
//关闭连接
void (*close) (modbus_t *ctx);
//清空缓冲区
int (*flush) (modbus_t *ctx);
//用于设置超时并读取通信事件,以检测是否存在待接收数据
int (*select) (modbus_t *ctx, fd_set *rset, struct timeval *tv, int msg_length);
//释放内存
void (*free) (modbus_t *ctx);
} modbus_backend_t;
struct _modbus {
/* Slave address */
int slave; //从站设备地址
/* Socket or file descriptor */
int s; //TCP模式下为套接字 RTU模式下为串口句柄
int debug; //是否启用debug模式
int error_recovery; //错误恢复模式
struct timeval response_timeout; //响应超时设置
struct timeval byte_timeout; //字节超时设置
struct timeval indication_timeout; //请求超时设置
//包含一系列通用函数指针
const modbus_backend_t *backend;
void *backend_data; //TCP模式下特殊配置数据 RTU模式下特殊配置数据
};
void _modbus_init_common(modbus_t *ctx);
void _error_print(modbus_t *ctx, const char *context);
int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type);
#ifndef HAVE_STRLCPY
size_t strlcpy(char *dest, const char *src, size_t dest_size);
#endif
MODBUS_END_DECLS
#endif /* MODBUS_PRIVATE_H */
02. modbus.h文件
libmodbus对外开放的API接口
/*
* Copyright © 2001-2013 Stéphane Raimbault <stephane.raimbault@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef MODBUS_H
#define MODBUS_H
/* Add this for macros that defined unix flavor */
#if (defined(__unix__) || defined(unix)) && !defined(USG)
#include <sys/param.h>
#endif
#ifndef _MSC_VER
#include <stdint.h>
#else
#include "stdint.h"
#endif
#include "modbus-version.h"
#if defined(_MSC_VER)
# if defined(DLLBUILD)
/* define DLLBUILD when building the DLL */
# define MODBUS_API __declspec(dllexport)
# else
# define MODBUS_API __declspec(dllimport)
# endif
#else
# define MODBUS_API
#endif
#ifdef __cplusplus
# define MODBUS_BEGIN_DECLS extern "C" {
# define MODBUS_END_DECLS }
#else
# define MODBUS_BEGIN_DECLS
# define MODBUS_END_DECLS
#endif
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 function codes */ //功能码
#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
#define MODBUS_FC_REPORT_SLAVE_ID 0x11
#define MODBUS_FC_MASK_WRITE_REGISTER 0x16
#define MODBUS_FC_WRITE_AND_READ_REGISTERS 0x17
//广播地址
#define MODBUS_BROADCAST_ADDRESS 0
/* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 1 page 12)
* Quantity of Coils to read (2 bytes): 1 to 2000 (0x7D0)
* (chapter 6 section 11 page 29)
* Quantity of Coils to write (2 bytes): 1 to 1968 (0x7B0)
*/
#define MODBUS_MAX_READ_BITS 2000
#define MODBUS_MAX_WRITE_BITS 1968
/* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 3 page 15)
* Quantity of Registers to read (2 bytes): 1 to 125 (0x7D)
* (chapter 6 section 12 page 31)
* Quantity of Registers to write (2 bytes) 1 to 123 (0x7B)
* (chapter 6 section 17 page 38)
* Quantity of Registers to write in R/W registers (2 bytes) 1 to 121 (0x79)
*/
#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
/* The size of the MODBUS PDU is limited by the size constraint inherited from
* the first MODBUS implementation on Serial Line network (max. RS485 ADU = 256
* bytes). Therefore, MODBUS PDU for serial line communication = 256 - Server
* address (1 byte) - CRC (2 bytes) = 253 bytes.
*/
#define MODBUS_MAX_PDU_LENGTH 253
/* Consequently:
* - RTU MODBUS ADU = 253 bytes + Server address (1 byte) + CRC (2 bytes) = 256
* bytes.
* - TCP MODBUS ADU = 253 bytes + MBAP (7 bytes) = 260 bytes.
* so the maximum of both backend in 260 bytes. This size can used to allocate
* an array of bytes to store responses and it will be compatible with the two
* backends.
*/
#define MODBUS_MAX_ADU_LENGTH 260
/* Random number to avoid errno conflicts */
#define MODBUS_ENOBASE 112345678
/* Protocol exceptions */
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, //ACK异常
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
};
#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)
/* Native libmodbus error codes */
#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;
typedef struct _modbus modbus_t;
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;
typedef enum
{
MODBUS_ERROR_RECOVERY_NONE = 0, //不恢复
MODBUS_ERROR_RECOVERY_LINK = (1<<1), //链接层恢复
MODBUS_ERROR_RECOVERY_PROTOCOL = (1<<2) //协议层恢复
} modbus_error_recovery_mode;
//设置从站地址
MODBUS_API int modbus_set_slave(modbus_t* ctx, int slave);
//获取从站地址
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或者串口句柄
MODBUS_API int modbus_set_socket(modbus_t *ctx, int s);
//获取当前socket或者窗口句柄
MODBUS_API int modbus_get_socket(modbus_t *ctx);
//获取响应超时
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);
//获取连续字节之间的超时时间
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);
//获取服务端等待客户端请求超时时间
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);
//获取报文头长度
MODBUS_API int modbus_get_header_length(modbus_t *ctx);
//用于主站设备与从站设备建立连接
MODBUS_API int modbus_connect(modbus_t *ctx);
//关闭连接
MODBUS_API void modbus_close(modbus_t *ctx);
//释放内存
MODBUS_API void modbus_free(modbus_t *ctx);
//刷新缓冲区
MODBUS_API int modbus_flush(modbus_t *ctx);
//是否设置为debug模式
MODBUS_API int modbus_set_debug(modbus_t *ctx, int flag);
//获取当前错误信息
MODBUS_API const char *modbus_strerror(int errnum);
//-------------------------------------------------------------------------------
//读取线圈或者离散量输出状态(功能码 0x1)
MODBUS_API int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
//读取离散量输入值(功能码 0x2)
MODBUS_API int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest);
//读取保持寄存器(功能码 0x3)
MODBUS_API int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
//读取输入寄存器(功能码 0x4)
MODBUS_API int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
//写单个线圈或者单个离散量(功能码 0x5)
MODBUS_API int modbus_write_bit(modbus_t *ctx, int coil_addr, int status);
//写单个保持寄存器(功能码 0x6)
MODBUS_API int modbus_write_register(modbus_t *ctx, int reg_addr, const uint16_t value);
//写多个线圈(功能码 0xF)
MODBUS_API int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data);
//写多个保持寄存器(功能码 0x10)
MODBUS_API int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data);
MODBUS_API int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask);
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(功能码 0x11)
MODBUS_API int modbus_report_slave_id(modbus_t *ctx, int max_dest, uint8_t *dest);
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);
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_API int modbus_send_raw_request(modbus_t *ctx, const uint8_t *raw_req, int raw_req_length);
MODBUS_API int modbus_receive(modbus_t *ctx, uint8_t *req);
MODBUS_API int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp);
MODBUS_API int modbus_reply(modbus_t *ctx, const uint8_t *req,
int req_length, modbus_mapping_t *mb_mapping);
MODBUS_API int modbus_reply_exception(modbus_t *ctx, const uint8_t *req,
unsigned int exception_code);
/**
* UTILS FUNCTIONS
**/
//获取高字节
#define MODBUS_GET_HIGH_BYTE(data) (((data) >> 8) & 0xFF)
//获取低字节
#define MODBUS_GET_LOW_BYTE(data) ((data) & 0xFF)
#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])
#define MODBUS_GET_INT32_FROM_INT16(tab_int16, index) \\
(((int32_t)tab_int16[(index) ] << 16) | \\
(int32_t)tab_int16[(index) + 1])
#define MODBUS_GET_INT16_FROM_INT8(tab_int8, index) \\
(((int16_t)tab_int8[(index) ] << 8) | \\
(int16_t)tab_int8[(index) + 1])
#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)
#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)
#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)
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);
MODBUS_API float modbus_get_float(const uint16_t *src);
MODBUS_API float modbus_get_float_abcd(const uint16_t *src);
MODBUS_API float modbus_get_float_dcba(const uint16_t *src);
MODBUS_API float modbus_get_float_badc(const uint16_t *src);
MODBUS_API float modbus_get_float_cdab(const uint16_t *src);
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);
#include "modbus-tcp.h"
#include "modbus-rtu.h"
MODBUS_END_DECLS
#endif /* MODBUS_H */
03. modbus.c文件
modbus.c 核心文件,实现Modbus协议层,定义通用的Modbus消息发送和接收函数、各功能码对应的函数
/*
* Copyright © 2001-2011 Stéphane Raimbault <stephane.raimbault@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This library implements the Modbus protocol.
* http://libmodbus.org/
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#以上是关于嵌入式Libmodbus源码分析-modbus相关函数分析的主要内容,如果未能解决你的问题,请参考以下文章