天天看點

【嵌入式】Libmodbus源碼分析(一)-類型和結構體

00. 目錄

文章目錄

00. 目錄01. 常見類型聲明02. 常量聲明03. _modbus結構體聲明04. modbus_backend_t結構體聲明05. modbus_mapping_t結構體聲明06. 附錄

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. 附錄