嵌入式Libmodbus源码分析-类型和结构体

Posted 沧海一笑-dj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了嵌入式Libmodbus源码分析-类型和结构体相关的知识,希望对你有一定的参考价值。

00. 目录

01. 常见类型声明

便于理解和跨平台使用。

stdint.h内容如下:

//
// stdint.h
//
//      Copyright (c) Microsoft Corporation. All rights reserved.
//
// The C Standard Library <stdint.h> header.
//
#pragma once
#define _STDINT

#include <vcruntime.h>

#if _VCRT_COMPILER_PREPROCESSOR

#pragma warning(push)
#pragma warning(disable: _VCRUNTIME_DISABLED_WARNINGS)

typedef signed char        int8_t;
typedef short              int16_t;
typedef int                int32_t;
typedef long long          int64_t;
typedef unsigned char      uint8_t;
typedef unsigned short     uint16_t;
typedef unsigned int       uint32_t;
typedef unsigned long long uint64_t;

typedef signed char        int_least8_t;
typedef short              int_least16_t;
typedef int                int_least32_t;
typedef long long          int_least64_t;
typedef unsigned char      uint_least8_t;
typedef unsigned short     uint_least16_t;
typedef unsigned int       uint_least32_t;
typedef unsigned long long uint_least64_t;

typedef signed char        int_fast8_t;
typedef int                int_fast16_t;
typedef int                int_fast32_t;
typedef long long          int_fast64_t;
typedef unsigned char      uint_fast8_t;
typedef unsigned int       uint_fast16_t;
typedef unsigned int       uint_fast32_t;
typedef unsigned long long uint_fast64_t;

typedef long long          intmax_t;
typedef unsigned long long uintmax_t;

// These macros must exactly match those in the Windows SDK's intsafe.h.
#define INT8_MIN         (-127i8 - 1)
#define INT16_MIN        (-32767i16 - 1)
#define INT32_MIN        (-2147483647i32 - 1)
#define INT64_MIN        (-9223372036854775807i64 - 1)
#define INT8_MAX         127i8
#define INT16_MAX        32767i16
#define INT32_MAX        2147483647i32
#define INT64_MAX        9223372036854775807i64
#define UINT8_MAX        0xffui8
#define UINT16_MAX       0xffffui16
#define UINT32_MAX       0xffffffffui32
#define UINT64_MAX       0xffffffffffffffffui64

#define INT_LEAST8_MIN   INT8_MIN
#define INT_LEAST16_MIN  INT16_MIN
#define INT_LEAST32_MIN  INT32_MIN
#define INT_LEAST64_MIN  INT64_MIN
#define INT_LEAST8_MAX   INT8_MAX
#define INT_LEAST16_MAX  INT16_MAX
#define INT_LEAST32_MAX  INT32_MAX
#define INT_LEAST64_MAX  INT64_MAX
#define UINT_LEAST8_MAX  UINT8_MAX
#define UINT_LEAST16_MAX UINT16_MAX
#define UINT_LEAST32_MAX UINT32_MAX
#define UINT_LEAST64_MAX UINT64_MAX

#define INT_FAST8_MIN    INT8_MIN
#define INT_FAST16_MIN   INT32_MIN
#define INT_FAST32_MIN   INT32_MIN
#define INT_FAST64_MIN   INT64_MIN
#define INT_FAST8_MAX    INT8_MAX
#define INT_FAST16_MAX   INT32_MAX
#define INT_FAST32_MAX   INT32_MAX
#define INT_FAST64_MAX   INT64_MAX
#define UINT_FAST8_MAX   UINT8_MAX
#define UINT_FAST16_MAX  UINT32_MAX
#define UINT_FAST32_MAX  UINT32_MAX
#define UINT_FAST64_MAX  UINT64_MAX

#ifdef _WIN64
    #define INTPTR_MIN   INT64_MIN
    #define INTPTR_MAX   INT64_MAX
    #define UINTPTR_MAX  UINT64_MAX
#else
    #define INTPTR_MIN   INT32_MIN
    #define INTPTR_MAX   INT32_MAX
    #define UINTPTR_MAX  UINT32_MAX
#endif

#define INTMAX_MIN       INT64_MIN
#define INTMAX_MAX       INT64_MAX
#define UINTMAX_MAX      UINT64_MAX

#define PTRDIFF_MIN      INTPTR_MIN
#define PTRDIFF_MAX      INTPTR_MAX

#ifndef SIZE_MAX
    // SIZE_MAX definition must match exactly with limits.h for modules support.
    #ifdef _WIN64
        #define SIZE_MAX 0xffffffffffffffffui64
    #else
        #define SIZE_MAX 0xffffffffui32
    #endif
#endif

#define SIG_ATOMIC_MIN   INT32_MIN
#define SIG_ATOMIC_MAX   INT32_MAX

#define WCHAR_MIN        0x0000
#define WCHAR_MAX        0xffff

#define WINT_MIN         0x0000
#define WINT_MAX         0xffff

#define INT8_C(x)    (x)
#define INT16_C(x)   (x)
#define INT32_C(x)   (x)
#define INT64_C(x)   (x ## LL)

#define UINT8_C(x)   (x)
#define UINT16_C(x)  (x)
#define UINT32_C(x)  (x ## U)
#define UINT64_C(x)  (x ## ULL)

#define INTMAX_C(x)  INT64_C(x)
#define UINTMAX_C(x) UINT64_C(x)

#pragma warning(pop) // _VCRUNTIME_DISABLED_WARNINGS

#endif // _VCRT_COMPILER_PREPROCESSOR

02. 常量声明

在modbus.h文件中,通过宏定义libmodbus库目前支持的所有Modbus功能码

/* 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

在modbus.h文件中定义了最大可读/可写线圈数量,最大可读/可写寄存器数量。

//广播地址
#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

错误码常量

/* 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)  //响应与查询地址不匹配

03. _modbus结构体声明

在文件modbus-private.h中定义了_modbus结构体,具体定义如下:

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模式下特殊配置数据
};

04. modbus_backend_t结构体声明

modbus_backend_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;

05. modbus_mapping_t结构体声明

modbus_mapping_t声明在modbus.h文件中,其声明如下:

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;

06. 附录

libmodbus源码下载:https://github.com/stephane/libmodbus

以上是关于嵌入式Libmodbus源码分析-类型和结构体的主要内容,如果未能解决你的问题,请参考以下文章

嵌入式Libmodbus源码分析-RTU相关函数分析

嵌入式Libmodbus源码分析-TCP相关函数分析

嵌入式Libmodbus源码分析-常用接口函数分析

嵌入式Libmodbus下载和编译详解

Golang Cond源码分析

Linux 内核实时调度类 ⑤ ( 实时调度类 rt_sched_class 源码分析 | 结构体字段及函数指针分析 )