LibModbus源碼分析

LibModbus基本信息

GitHub: https://github.com/stephane/libmodbus.git
Branch: Master
Commit:docs: fix simple typo, reponse -> response
Version: 3.1.6

數(shù)據(jù)結(jié)構(gòu)

消息類型

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

  • MSG_INDICATION代表主設(shè)備發(fā)送的輪詢指令
  • MSG_CONFIRMATION代表從設(shè)備回復(fù)的響應(yīng)指令

協(xié)議解析狀態(tài)

/* 3 steps are used to parse the query */
typedef enum {
    _STEP_FUNCTION,
    _STEP_META,
    _STEP_DATA
} _step_t;

libmodbus根據(jù)狀態(tài)機(jī)模式讀取協(xié)議垦巴,三種狀態(tài)定義如下:

  • _STEP_FUNCTION 讀取操作碼
  • _STEP_META 讀取固定字段(寄存器等)
  • _STEP_DATA 讀取后續(xù)數(shù)據(jù)

modbus_t 結(jié)構(gòu)體成員大多數(shù)在_modbus_init_common函數(shù)中初始化

struct _modbus {
    /* Slave address */
    int slave;
    /* Socket or file descriptor */
    int s;
    int debug;
    int error_recovery;
    struct timeval response_timeout;
    struct timeval byte_timeout;
    struct timeval indication_timeout;
    const modbus_backend_t *backend;
    void *backend_data;
};
  • slaver 從機(jī)地址
  • s Socket文件描述符
  • debug 是否調(diào)試模式浊闪,默認(rèn)false
  • error_recovery 是否錯(cuò)誤恢復(fù)匣缘,默認(rèn)不恢復(fù)MODBUS_ERROR_RECOVERY_NONE
  • response_timeout master發(fā)送消息等待響應(yīng)的時(shí)長(zhǎng)近哟,在_modbus_init_common函數(shù)中被初始化為ctx->response_timeout.tv_usec = _RESPONSE_TIMEOUT;//(500000us == 500ms)
  • byte_timeout 協(xié)議接收過(guò)程中下一個(gè)字節(jié)的等待時(shí)長(zhǎng)岩瘦,默認(rèn)為_BYTE_TIMEOUT(500000us == 500ms)
  • indication_timeout 從設(shè)備接收MSG_INDICATION消息的等待時(shí)長(zhǎng)尝苇,默認(rèn)為0荣瑟,_modbus_receive_msg函數(shù)中有if (ctx->indication_timeout.tv_sec == 0 && ctx->indication_timeout.tv_usec == 0) { p_tv = NULL;...}征炼,select系統(tǒng)調(diào)用的timtout為NULL則阻塞,所以默認(rèn)從設(shè)備接收 MSG_INDICATION會(huì)進(jìn)入阻塞模式
  • backend modbus的核心操作函數(shù)集合
  • backend_data rtu/tcp/tcp_pi端口相關(guān)數(shù)據(jù)ctx->backend_data = (modbus_rtu_t *)malloc(sizeof(modbus_rtu_t));

modbus_backend_t

typedef struct _modbus_backend {
    unsigned int backend_type;
    unsigned int header_length;
    unsigned int checksum_length;
    unsigned int max_adu_length;
    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);
    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);
    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;

modbus_new_rtu函數(shù)中初始化了backend成員ctx->backend = &_modbus_rtu_backend;

const modbus_backend_t _modbus_rtu_backend = {
    _MODBUS_BACKEND_TYPE_RTU,
    _MODBUS_RTU_HEADER_LENGTH,
    _MODBUS_RTU_CHECKSUM_LENGTH,
    MODBUS_RTU_MAX_ADU_LENGTH,
    _modbus_set_slave,
    _modbus_rtu_build_request_basis,
    _modbus_rtu_build_response_basis,
    _modbus_rtu_prepare_response_tid,
    _modbus_rtu_send_msg_pre,
    _modbus_rtu_send,
    _modbus_rtu_receive,
    _modbus_rtu_recv,
    _modbus_rtu_check_integrity,
    _modbus_rtu_pre_check_confirmation,
    _modbus_rtu_connect,
    _modbus_rtu_close,
    _modbus_rtu_flush,
    _modbus_rtu_select,
    _modbus_rtu_free
};

數(shù)據(jù)接收

Modbus Slaver接收函數(shù)

static int _modbus_rtu_receive(modbus_t *ctx, uint8_t *req)
{
    int rc;
    modbus_rtu_t *ctx_rtu = ctx->backend_data;

    if (ctx_rtu->confirmation_to_ignore) {
        _modbus_receive_msg(ctx, req, MSG_CONFIRMATION);
        /* Ignore errors and reset the flag */
        ctx_rtu->confirmation_to_ignore = FALSE;
        rc = 0;
        if (ctx->debug) {
            printf("Confirmation to ignore\n");
        }
    } else {
        rc = _modbus_receive_msg(ctx, req, MSG_INDICATION);
        if (rc == 0) {
            /* The next expected message is a confirmation to ignore */
            ctx_rtu->confirmation_to_ignore = TRUE;
        }
    }
    return rc;
}

_modbus_rtu_receive最終調(diào)用了_modbus_receive_msg函數(shù)吼鱼,該函數(shù)有個(gè)msg_type_t類型參數(shù)蓬豁,代表當(dāng)前讀取的消息是MSG_INDICATION or MSG_CONFIRMATION

confirmation_to_ignore在初始化時(shí)默認(rèn)為false菇肃,因此地粪,_modbus_rtu_receive函數(shù)大多數(shù)時(shí)候是在執(zhí)行_modbus_receive_msg(ctx, req, MSG_INDICATION);指令,即接收MSG_INDICATION消息琐谤,接收成功蟆技,下一步將ctx_rtu->confirmation_to_ignore = TRUE,開始執(zhí)行_modbus_receive_msg(ctx, req, MSG_CONFIRMATION),接收MSG_CONFIRMATION消息,不管是否成功都重新將ctx_rtu->confirmation_to_ignore = FALSE,又開始接收MSG_INDICATION消息斗忌。


Modbus Master讀取寄存器函數(shù)

/* Reads the data from a remove device and put that data into an array */
static int read_registers(modbus_t *ctx, int function, int addr, int nb,
                          uint16_t *dest)
{
    int rc;
    int req_length;
    uint8_t req[_MIN_REQ_LENGTH];
    uint8_t rsp[MAX_MESSAGE_LENGTH];

    if (nb > MODBUS_MAX_READ_REGISTERS) {
        if (ctx->debug) {
            fprintf(stderr,
                    "ERROR Too many registers requested (%d > %d)\n",
                    nb, MODBUS_MAX_READ_REGISTERS);
        }
        errno = EMBMDATA;
        return -1;
    }

    req_length = ctx->backend->build_request_basis(ctx, function, addr, nb, req);

    rc = send_msg(ctx, req, req_length);
    if (rc > 0) {
        int offset;
        int i;

        rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);
        if (rc == -1)
            return -1;

        rc = check_confirmation(ctx, req, rsp, rc);
        if (rc == -1)
            return -1;

        offset = ctx->backend->header_length;

        for (i = 0; i < rc; i++) {
            /* shift reg hi_byte to temp OR with lo_byte */
            dest[i] = (rsp[offset + 2 + (i << 1)] << 8) |
                rsp[offset + 3 + (i << 1)];
        }
    }

    return rc;
}

read_registers 發(fā)送寄存器讀取指令rc = send_msg(ctx, req, req_length);质礼,然后通過(guò)rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION);讀取從機(jī)的響應(yīng),函數(shù)直接調(diào)用了_modbus_receive_msg()飞蹂,并且以MSG_CONFIRMATION作為消息類型几苍,代表讀取一條Modbus響應(yīng)協(xié)議.


_modbus_receive_msg接收函數(shù)

int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type)
{
    int rc;
    fd_set rset;
    struct timeval tv;
    struct timeval *p_tv;
    int length_to_read;
    int msg_length = 0;
    _step_t step;

    if (ctx->debug) {
        if (msg_type == MSG_INDICATION) {
            printf("Waiting for an indication...\n");
        } else {
            printf("Waiting for a confirmation...\n");
        }
    }

    /* Add a file descriptor to the set */
    FD_ZERO(&rset);
    FD_SET(ctx->s, &rset);

    /* We need to analyse the message step by step.  At the first step, we want
     * to reach the function code because all packets contain this
     * information. */
    step = _STEP_FUNCTION;
    length_to_read = ctx->backend->header_length + 1;

    if (msg_type == MSG_INDICATION) {
        /* Wait for a message, we don't know when the message will be
         * received */
        if (ctx->indication_timeout.tv_sec == 0 && ctx->indication_timeout.tv_usec == 0) {
            /* By default, the indication timeout isn't set */
            p_tv = NULL;
        } else {
            /* Wait for an indication (name of a received request by a server, see schema) */
            tv.tv_sec = ctx->indication_timeout.tv_sec;
            tv.tv_usec = ctx->indication_timeout.tv_usec;
            p_tv = &tv;
        }
    } else {
        tv.tv_sec = ctx->response_timeout.tv_sec;
        tv.tv_usec = ctx->response_timeout.tv_usec;
        p_tv = &tv;
    }

    while (length_to_read != 0) {
        rc = ctx->backend->select(ctx, &rset, p_tv, length_to_read);
        if (rc == -1) {
            _error_print(ctx, "select");
            if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) {
                int saved_errno = errno;

                if (errno == ETIMEDOUT) {
                    _sleep_response_timeout(ctx);
                    modbus_flush(ctx);
                } else if (errno == EBADF) {
                    modbus_close(ctx);
                    modbus_connect(ctx);
                }
                errno = saved_errno;
            }
            return -1;
        }

        rc = ctx->backend->recv(ctx, msg + msg_length, length_to_read);
        if (rc == 0) {
            errno = ECONNRESET;
            rc = -1;
        }

        if (rc == -1) {
            _error_print(ctx, "read");
            if ((ctx->error_recovery & MODBUS_ERROR_RECOVERY_LINK) &&
                (errno == ECONNRESET || errno == ECONNREFUSED ||
                 errno == EBADF)) {
                int saved_errno = errno;
                modbus_close(ctx);
                modbus_connect(ctx);
                /* Could be removed by previous calls */
                errno = saved_errno;
            }
            return -1;
        }

        /* Display the hex code of each character received */
        if (ctx->debug) {
            int i;
            for (i=0; i < rc; i++)
                printf("<%.2X>", msg[msg_length + i]);
        }

        /* Sums bytes received */
        msg_length += rc;
        /* Computes remaining bytes */
        length_to_read -= rc;

        if (length_to_read == 0) {
            switch (step) {
            case _STEP_FUNCTION:
                /* Function code position */
                length_to_read = compute_meta_length_after_function(
                    msg[ctx->backend->header_length],
                    msg_type);
                if (length_to_read != 0) {
                    step = _STEP_META;
                    break;
                } /* else switches straight to the next step */
            case _STEP_META:
                length_to_read = compute_data_length_after_meta(
                    ctx, msg, msg_type);
                if ((msg_length + length_to_read) > (int)ctx->backend->max_adu_length) {
                    errno = EMBBADDATA;
                    _error_print(ctx, "too many data");
                    return -1;
                }
                step = _STEP_DATA;
                break;
            default:
                break;
            }
        }

        if (length_to_read > 0 &&
            (ctx->byte_timeout.tv_sec > 0 || ctx->byte_timeout.tv_usec > 0)) {
            /* If there is no character in the buffer, the allowed timeout
               interval between two consecutive bytes is defined by
               byte_timeout */
            tv.tv_sec = ctx->byte_timeout.tv_sec;
            tv.tv_usec = ctx->byte_timeout.tv_usec;
            p_tv = &tv;
        }
        /* else timeout isn't set again, the full response must be read before
           expiration of response timeout (for CONFIRMATION only) */
    }

    if (ctx->debug)
        printf("\n");

    return ctx->backend->check_integrity(ctx, msg, msg_length);
}

本函數(shù)根據(jù)msg_type_t msg_type參數(shù)及modbus_t *ctx上下文嘗試讀取并解析一條modbus協(xié)議,基本流程如下:

image.png

  • 讀取過(guò)程Linux主要采用select系統(tǒng)調(diào)用陈哑,win封裝了win32_ser_select函數(shù)
  • 解析過(guò)程采用狀態(tài)機(jī)模型進(jìn)行解析妻坝,參考文章開頭數(shù)據(jù)結(jié)構(gòu)“協(xié)議解析狀態(tài)”

這里拿幾個(gè)MSG_INDICATION協(xié)議舉例,MSG_CONFIRMATION消息同理:

  • [01 03 00 14 00 02 84 0F] 讀保持寄存器惊窖,從0014開始刽宪,讀取0002個(gè)寄存器
  • [01 06 00 14 01 99 08 34] 寫單個(gè)保持寄存器,地址為0014界酒,寫入數(shù)據(jù)為:01 99
  • [01 10 00 14 00 02 04 AA BB CC DD 37 F4] 寫多個(gè)保持寄存器圣拄,從0014開始,寫入0002個(gè)寄存器毁欣,共04字節(jié)庇谆,寫入數(shù)據(jù)為:AA BB CC DD

解析過(guò)程中,根據(jù)階段計(jì)算應(yīng)該讀取的長(zhǎng)度

  • _STEP_FUNCTION階段: length_to_read固定為2個(gè)字節(jié)
    03協(xié)議:length_to_read = 2
    06協(xié)議:length_to_read = 2
    10協(xié)議:length_to_read = 2
  • _STEP_META階段: 具體參考compute_meta_length_after_function函數(shù)
    03協(xié)議:length_to_read = 4
    06協(xié)議:length_to_read = 4
    10協(xié)議:length_to_read = 5
  • _STEP_DATA階段:具體參考compute_data_length_after_meta函數(shù)
    03協(xié)議:length_to_read = 0
    06協(xié)議:length_to_read = 0
    10協(xié)議:length_to_read = 5

寄存器

數(shù)據(jù)結(jié)構(gòu)

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共有四種寄存器

  • COILS 線圈 每一個(gè)bit對(duì)應(yīng)一個(gè)信號(hào)的開關(guān)狀態(tài)岳掐,單位是bit
  • Discrete Inputs 離散量輸入 相當(dāng)于線圈寄存器的只讀模式,單位是bit
  • Input Registers 輸入寄存器 和下面的保持寄存器類似饭耳,單位是U16
  • Holding Registers 保持寄存器 單位不再是bit而是兩個(gè)byte串述,單位是U16
    結(jié)構(gòu)體成員
  • nb_bits/start/bits/tab_bits 對(duì)應(yīng) 線圈的數(shù)量/起始地址/內(nèi)存空間
  • nb_input_bits/start_input_bits/tab_input_bits 對(duì)應(yīng) 離散輸入的數(shù)量/起始地址/內(nèi)存空間
  • nb_input_registers/start_input_registers/tab_input_registers 對(duì)應(yīng)輸入寄存器的數(shù)量/起始地址/內(nèi)存空間
  • nb_registers/start_registers/tab_registers 對(duì)應(yīng)保持寄存器的數(shù)量/起始地址/內(nèi)存空間

分配空間

modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits,
                                     int nb_registers, int nb_input_registers)
{
    return modbus_mapping_new_start_address(
        0, nb_bits, 0, nb_input_bits, 0, nb_registers, 0, nb_input_registers);
}

modbus_mapping_new調(diào)用了modbus_mapping_new_start_address函數(shù),將四種寄存器的起始地址都設(shè)定為0.

/* Allocates 4 arrays to store bits, input bits, registers and inputs
   registers. The pointers are stored in modbus_mapping structure.

   The modbus_mapping_new_start_address() function shall return the new allocated
   structure if successful. Otherwise it shall return NULL and set errno to
   ENOMEM. */
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_mapping_t *mb_mapping;

    mb_mapping = (modbus_mapping_t *)malloc(sizeof(modbus_mapping_t));
    if (mb_mapping == NULL) {
        return NULL;
    }

    /* 0X */
    mb_mapping->nb_bits = nb_bits;
    mb_mapping->start_bits = start_bits;
    if (nb_bits == 0) {
        mb_mapping->tab_bits = NULL;
    } else {
        /* Negative number raises a POSIX error */
        mb_mapping->tab_bits =
            (uint8_t *) malloc(nb_bits * sizeof(uint8_t));
        if (mb_mapping->tab_bits == NULL) {
            free(mb_mapping);
            return NULL;
        }
        memset(mb_mapping->tab_bits, 0, nb_bits * sizeof(uint8_t));
    }

    /* 1X */
    mb_mapping->nb_input_bits = nb_input_bits;
    mb_mapping->start_input_bits = start_input_bits;
    if (nb_input_bits == 0) {
        mb_mapping->tab_input_bits = NULL;
    } else {
        mb_mapping->tab_input_bits =
            (uint8_t *) malloc(nb_input_bits * sizeof(uint8_t));
        if (mb_mapping->tab_input_bits == NULL) {
            free(mb_mapping->tab_bits);
            free(mb_mapping);
            return NULL;
        }
        memset(mb_mapping->tab_input_bits, 0, nb_input_bits * sizeof(uint8_t));
    }

    /* 4X */
    mb_mapping->nb_registers = nb_registers;
    mb_mapping->start_registers = start_registers;
    if (nb_registers == 0) {
        mb_mapping->tab_registers = NULL;
    } else {
        mb_mapping->tab_registers =
            (uint16_t *) malloc(nb_registers * sizeof(uint16_t));
        if (mb_mapping->tab_registers == NULL) {
            free(mb_mapping->tab_input_bits);
            free(mb_mapping->tab_bits);
            free(mb_mapping);
            return NULL;
        }
        memset(mb_mapping->tab_registers, 0, nb_registers * sizeof(uint16_t));
    }

    /* 3X */
    mb_mapping->nb_input_registers = nb_input_registers;
    mb_mapping->start_input_registers = start_input_registers;
    if (nb_input_registers == 0) {
        mb_mapping->tab_input_registers = NULL;
    } else {
        mb_mapping->tab_input_registers =
            (uint16_t *) malloc(nb_input_registers * sizeof(uint16_t));
        if (mb_mapping->tab_input_registers == NULL) {
            free(mb_mapping->tab_registers);
            free(mb_mapping->tab_input_bits);
            free(mb_mapping->tab_bits);
            free(mb_mapping);
            return NULL;
        }
        memset(mb_mapping->tab_input_registers, 0,
               nb_input_registers * sizeof(uint16_t));
    }

    return mb_mapping;
}

modbus_mapping_new_start_address函數(shù)按照給定的起始地址和數(shù)量分配寄存器空間寞肖。

讀寫寄存器

int modbus_reply(modbus_t *ctx, const uint8_t *req,
                 int req_length, modbus_mapping_t *mb_mapping)
{
    //...省略
    /* Data are flushed on illegal number of values errors. */
    switch (function) {
    case MODBUS_FC_READ_COILS:
    case MODBUS_FC_READ_DISCRETE_INPUTS: {
        //...省略
    }
        break;
    case MODBUS_FC_READ_HOLDING_REGISTERS:
    case MODBUS_FC_READ_INPUT_REGISTERS: {
        //...省略
    }
        break;
    case MODBUS_FC_WRITE_SINGLE_COIL: {
        //...省略
    }
        break;
    case MODBUS_FC_WRITE_SINGLE_REGISTER: {
        int mapping_address = address - mb_mapping->start_registers;

        if (mapping_address < 0 || mapping_address >= mb_mapping->nb_registers) {
            rsp_length = response_exception(
                ctx, &sft,
                MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
                "Illegal data address 0x%0X in write_register\n",
                address);
        } else {
            int data = (req[offset + 3] << 8) + req[offset + 4];

            mb_mapping->tab_registers[mapping_address] = data;
            memcpy(rsp, req, req_length);
            rsp_length = req_length;
        }
    }
        break;
    case MODBUS_FC_WRITE_MULTIPLE_COILS: {
        //...省略
    }
        break;
    case MODBUS_FC_WRITE_MULTIPLE_REGISTERS: {
        int nb = (req[offset + 3] << 8) + req[offset + 4];
        int nb_bytes = req[offset + 5];
        int mapping_address = address - mb_mapping->start_registers;

        if (nb < 1 || MODBUS_MAX_WRITE_REGISTERS < nb || nb_bytes != nb * 2) {
            rsp_length = response_exception(
                ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE,
                "Illegal number of values %d in write_registers (max %d)\n",
                nb, MODBUS_MAX_WRITE_REGISTERS);
        } else if (mapping_address < 0 ||
                   (mapping_address + nb) > mb_mapping->nb_registers) {
            rsp_length = response_exception(
                ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
                "Illegal data address 0x%0X in write_registers\n",
                mapping_address < 0 ? address : address + nb);
        } else {
            int i, j;
            for (i = mapping_address, j = 6; i < mapping_address + nb; i++, j += 2) {
                /* 6 and 7 = first value */
                mb_mapping->tab_registers[i] =
                    (req[offset + j] << 8) + req[offset + j + 1];
            }

            rsp_length = ctx->backend->build_response_basis(&sft, rsp);
            /* 4 to copy the address (2) and the no. of registers */
            memcpy(rsp + rsp_length, req + rsp_length, 4);
            rsp_length += 4;
        }
    }
        break;
    case MODBUS_FC_REPORT_SLAVE_ID: {
        //...省略
    }
        break;
    case MODBUS_FC_READ_EXCEPTION_STATUS:
        //...省略
        break;
    case MODBUS_FC_MASK_WRITE_REGISTER: {
        int mapping_address = address - mb_mapping->start_registers;

        if (mapping_address < 0 || mapping_address >= mb_mapping->nb_registers) {
            rsp_length = response_exception(
                ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE,
                "Illegal data address 0x%0X in write_register\n",
                address);
        } else {
            uint16_t data = mb_mapping->tab_registers[mapping_address];
            uint16_t and = (req[offset + 3] << 8) + req[offset + 4];
            uint16_t or = (req[offset + 5] << 8) + req[offset + 6];

            data = (data & and) | (or & (~and));
            mb_mapping->tab_registers[mapping_address] = data;
            memcpy(rsp, req, req_length);
            rsp_length = req_length;
        }
    }
        break;
    case MODBUS_FC_WRITE_AND_READ_REGISTERS: {
        //...省略
    }
        break;

    default:
        rsp_length = response_exception(
            ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_FUNCTION, rsp, TRUE,
            "Unknown Modbus function code: 0x%0X\n", function);
        break;
    }

    /* Suppress any responses when the request was a broadcast */
    return (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU &&
            slave == MODBUS_BROADCAST_ADDRESS) ? 0 : send_msg(ctx, rsp, rsp_length);
}

modbus_reply函數(shù)根據(jù)收到的消息纲酗,對(duì)寄存器進(jìn)行操作,并向master回復(fù)消息.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末新蟆,一起剝皮案震驚了整個(gè)濱河市觅赊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌琼稻,老刑警劉巖吮螺,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異帕翻,居然都是意外死亡规脸,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門熊咽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人闹丐,你說(shuō)我怎么就攤上這事横殴。” “怎么了卿拴?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵衫仑,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我堕花,道長(zhǎng)文狱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任岛蚤,我火速辦了婚禮贱勃,結(jié)果婚禮上盔腔,老公的妹妹穿的比我還像新娘。我一直安慰自己苏研,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布腮郊。 她就那樣靜靜地躺著摹蘑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪轧飞。 梳的紋絲不亂的頭發(fā)上衅鹿,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天撒踪,我揣著相機(jī)與錄音,去河邊找鬼大渤。 笑死制妄,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的兼犯。 我是一名探鬼主播忍捡,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼切黔!你這毒婦竟也來(lái)了砸脊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤纬霞,失蹤者是張志新(化名)和其女友劉穎凌埂,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诗芜,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瞳抓,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了伏恐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片孩哑。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖翠桦,靈堂內(nèi)的尸體忽然破棺而出横蜒,到底是詐尸還是另有隱情,我是刑警寧澤销凑,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布丛晌,位于F島的核電站,受9級(jí)特大地震影響斗幼,放射性物質(zhì)發(fā)生泄漏澎蛛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一蜕窿、第九天 我趴在偏房一處隱蔽的房頂上張望谋逻。 院中可真熱鬧,春花似錦桐经、人聲如沸斤贰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)荧恍。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間送巡,已是汗流浹背摹菠。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留骗爆,地道東北人次氨。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像摘投,于是被迫代替她去往敵國(guó)和親煮寡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容