stm32开发之threadx+netxduo(结合 modbus 编写tcp接口程序)

news2025/6/22 9:17:22

前言

  1. 本篇结合freemodbus源码程序进行移植,驱动实现的接口为modbus tcp
  2. 需要知道threadx的 事件标志组、信号量、线程相关的知识
  3. 需要知道netxduo tcp方面的api和创建流程方面的知识

freemodbus程序源码

  1. 本次使用的源码来自于rt-thread软件包里面的,可以参考之前的博客:rt-thread之通讯协议modbus软件包的使用记录(lwip+modbus组合)本篇重点在threadx系列
  2. freemodbus框架核心在于事件驱动

移植接口文件的编写

port.c文件

/*
 * FreeModbus Libary: RT-Thread Port
 * Copyright (C) 2013 Armink <armink.ztl@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * File: $Id: port.c,v 1.60 2015/02/01 9:18:05 Armink $
 */

/* ----------------------- System includes --------------------------------*/

/* ----------------------- Modbus includes ----------------------------------*/
#include "port.h"

/* ----------------------- Variables ----------------------------------------*/

static TX_SEMAPHORE lock;
static int is_inited = 0;

/* ----------------------- Start implementation -----------------------------*/
void EnterCriticalSection(void) {
    uint err;
    if (!is_inited) {
        err = tx_semaphore_create(&lock, "fmb_lock", 1);

        if (err != TX_SUCCESS) {
            tx_log("Freemodbus Critical init failed!\r\n");
        }
        is_inited = 1;
    }
    tx_semaphore_get(&lock, TX_WAIT_FOREVER);
}

void ExitCriticalSection(void) {
    tx_semaphore_put(&lock);
}


portevent.c文件

/*
 * FreeModbus Libary: RT-Thread Port
 * Copyright (C) 2013 Armink <armink.ztl@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * File: $Id: portevent.c,v 1.60 2013/08/13 15:07:05 Armink $
 */

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- Variables ----------------------------------------*/
static TX_EVENT_FLAGS_GROUP xSlaveOsEvent;

/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortEventInit(void) {
    UINT stat = tx_event_flags_create(&xSlaveOsEvent, "slave event");
    if (stat) {
        tx_log("tx_event_flags_create error:%d\r\n", stat);
        return FALSE;
    }
    return TRUE;
}

BOOL
xMBPortEventPost(eMBEventType eEvent) {
    tx_event_flags_set(&xSlaveOsEvent, eEvent, TX_OR);
    return TRUE;
}

BOOL
xMBPortEventGet(eMBEventType *eEvent) {
    uint32_t recvedEvent;
    /* waiting forever OS event */
    tx_event_flags_get(&xSlaveOsEvent,
                       EV_READY | EV_FRAME_RECEIVED | EV_EXECUTE | EV_FRAME_SENT,
                       TX_OR_CLEAR, &recvedEvent, TX_WAIT_FOREVER);
    switch (recvedEvent) {
        case EV_READY:
            *eEvent = EV_READY;
            break;
        case EV_FRAME_RECEIVED:
            *eEvent = EV_FRAME_RECEIVED;
            break;
        case EV_EXECUTE:
            *eEvent = EV_EXECUTE;
            break;
        case EV_FRAME_SENT:
            *eEvent = EV_FRAME_SENT;
            break;
    }
    return TRUE;
}

porttcp.c文件

/*
 * FreeModbus Libary: RT-Thread Port
 * Copyright (C) 2019 flybreak <guozhanxin@rt-thread.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * File: $Id: portserial.c,v 1.60 2019/07/11 17:04:32 flybreak $
 */

#include "port.h"

#ifdef PKG_MODBUS_SLAVE_TCP
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"



/* ----------------------- Defines  -----------------------------------------*/
#define MB_TCP_DEFAULT_PORT     502
#define MB_TCP_BUF_SIZE     ( 256 + 7 )

extern UINT mb_tcp_slave_response_data(NX_TCP_SOCKET *socket_ptr, void *data, uint16_t len);

static NX_TCP_SOCKET *cur_socket = NX_NULL; /*保存当前连接的客户端socket 指针,方便后期mb线程响应数据*/
static TX_SEMAPHORE mb_semaphore;/*线程之间的同步通信信号量*/
/* ----------------------- Static variables ---------------------------------*/
static UCHAR prvvTCPBuf[MB_TCP_BUF_SIZE];
static USHORT prvvTCPLength;

/**
 * @brief 接收到客户端连接数据处理
 * @param socket_ptr
 * @return
 */
UINT receive_socket_handle(NX_TCP_SOCKET *socket_ptr) {
    cur_socket = socket_ptr;
    UINT status;
    NX_PACKET *packet_ptr;

    continue_rec:
    /* Receive a TCP message from the socket.  */
    status = nx_tcp_socket_receive(socket_ptr, &packet_ptr, 30 * NX_IP_PERIODIC_RATE);
    /* Check for error.  */
    switch (status) {
        case NX_SUCCESS: {
            tx_log(" data packet size:%d\r\n", packet_ptr->nx_packet_length);

            for (int i = 0; i < packet_ptr->nx_packet_length; ++i) {

                tx_log("%#x ", packet_ptr->nx_packet_prepend_ptr[i]);

            }
            tx_log("\r\n");

            /*数据拷贝*/
            prvvTCPLength = packet_ptr->nx_packet_length;
            memcpy(prvvTCPBuf, packet_ptr->nx_packet_prepend_ptr, packet_ptr->nx_packet_length);
            /*通知mb线程*/
            xMBPortEventPost(EV_FRAME_RECEIVED);
            /*处理完成之后释放数据包,这里需要我们自己进行释放*/
            nx_packet_release(packet_ptr);
            /*等待mb线程响应*/
            status = tx_semaphore_get(&mb_semaphore, NX_IP_PERIODIC_RATE);
            if (status) {
                tx_log(" tx_semaphore_get err:%d\r\n", status);
                break;/*直接退出接收,拒绝客户端发送的数据*/
            }
            goto continue_rec; /*继续接收客户端*/
        }
        case NX_NO_PACKET: { /*客户端连接,但没有发送数据,这里可以做超时断开客户端连接操作*/
            tx_log("nx_tcp_socket_receive NX_NO_PACKET\r\n");
            goto continue_rec; /*继续接收客户端*/
        }
        case NX_NOT_CONNECTED: {
            tx_log("client disconnected\r\n");
            break;
        }
        default: {
            tx_log("nx_tcp_socket_receive status:%d\r\n", status);
            break;
        }
    }
    return NX_SUCCESS;
}


extern UINT nx_mb_slave_tcp_thread_create(uint16_t listen_port);

BOOL
xMBTCPPortInit(USHORT usTCPPort) {

    if (usTCPPort == 0)
        usTCPPort = MB_TCP_DEFAULT_PORT;

    tx_semaphore_create(&mb_semaphore, "mb data semaphore", 0);
    return nx_mb_slave_tcp_thread_create(usTCPPort) == NX_SUCCESS ? TRUE : FALSE;
}

void
vMBTCPPortClose(void) {
}

void
vMBTCPPortDisable(void) {
}

BOOL
xMBTCPPortGetRequest(UCHAR **ppucMBTCPFrame, USHORT *usTCPLength) {
    *ppucMBTCPFrame = &prvvTCPBuf[0];
    *usTCPLength = prvvTCPLength;

    return TRUE;
}

BOOL
xMBTCPPortSendResponse(const UCHAR *pucMBTCPFrame, USHORT usTCPLength) {

    if (cur_socket) {
        UINT stat = mb_tcp_slave_response_data(cur_socket,
                                               (void *) pucMBTCPFrame,
                                               usTCPLength);
        tx_semaphore_put(&mb_semaphore);
        return stat == NX_SUCCESS ? TRUE : FALSE; /*转换成modbus 相关的类型,尽管两者宏定义都一样,建议执行此操作*/
    }
    return FALSE;
}

#endif

tcp 服务端线程(由mb 主服务线程在初始化的时候创建)

/*
 * Copyright (c) 2024-2024,shchl
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2024-4-10     shchl   first version
 */
#include "includes.h"

#define     TCP_LOCAL_IP               IP_ADDRESS(192, 168, 199, 216)
#define     TCP_LOCAL_SUB_MASK         IP_ADDRESS(255,255,255,0)
/*
*******************************************************************************************************
*                               外部引入变量
*******************************************************************************************************
*/
VOID mb_tcp_urgent_data_callback(NX_TCP_SOCKET *socket_ptr);

VOID mb_tcp_disconnect_callback(NX_TCP_SOCKET *socket_ptr);

extern UINT receive_socket_handle(NX_TCP_SOCKET *socket);

/*
*******************************************************************************************************
*                               变量
*******************************************************************************************************
*/
TX_THREAD mb_tcp_slave_thread;
NX_IP mb_tcp_slave_ip;
/*
*********************************************************************************************************
*                                       静态全局变量
*********************************************************************************************************
*/
static NX_PACKET_POOL mb_slave_pool;
static NX_TCP_SOCKET mb_slave_socket;

/*
*********************************************************************************************************
*                                      函数声明
*********************************************************************************************************
*/
UINT mb_tcp_slave_response_data(NX_TCP_SOCKET *socket_ptr, void *data, uint16_t len);


/*
*********************************************************************************************************
*                                      外部函数
*********************************************************************************************************
*/
static void mb_tcp_slave_entry(ULONG input);
/*
*********************************************************************************************************
*                                      内部函数
*********************************************************************************************************
*/



/**
 * @brief nx modbus tcp 线程创建
 * @param listen_port  监听端口
 * @return
 */
UINT nx_mb_slave_tcp_thread_create(uint16_t listen_port) {
#define MB_SLAVE_PRIORITY 8
#define     PACKET_SIZE              1536
#define     POOL_SIZE                   ((sizeof(NX_PACKET) + PACKET_SIZE) * 16) /*数据包池大小(16个缓冲区)*/
    UINT status;
    /* 创建 packet 内存池(可以理解为分配一个大数组).  */
    status = nx_packet_pool_create(&mb_slave_pool,
                                   "mb_pool",
                                   PACKET_SIZE,
                                   app_malloc(POOL_SIZE),
                                   POOL_SIZE);

    if (status) return NX_NOT_CREATED;

    /* 创建一个ip 实列. 内部会创建一个对应的线程 */
    status = nx_ip_create(&mb_tcp_slave_ip, "ip instance",
                          TCP_LOCAL_IP, TCP_LOCAL_SUB_MASK,
                          &mb_slave_pool,
                          nx_stm32_eth_driver,
                          app_malloc(2048), 2048, 7);
    if (status) return status;

    /* 开启 地址解析协议 并分配缓冲区. */
    status = nx_arp_enable(&mb_tcp_slave_ip, app_malloc(1024), 1024);
    if (status && status != NX_ALREADY_ENABLED) return status;
    /*开启 icmp 协议, 能通过ping 命令去检查ip */
    status = nxd_icmp_enable(&mb_tcp_slave_ip);
    if (status && status != NX_ALREADY_ENABLED) return status;
    /* 开启 tcp 处理 */
    status = nx_tcp_enable(&mb_tcp_slave_ip);
    if (status && status != NX_ALREADY_ENABLED) return status;

    /* 创建server 线程.  */
    tx_thread_create(&mb_tcp_slave_thread,
                     "mb_tcp_slave thread",
                     mb_tcp_slave_entry, listen_port,
                     app_malloc(4096),
                     4096,
                     MB_SLAVE_PRIORITY,
                     MB_SLAVE_PRIORITY,
                     TX_NO_TIME_SLICE,
                     TX_AUTO_START);

    return NX_SUCCESS;
}


/**
 * @brief
 * @param listen_port 参数为监听的端口
 */
static void mb_tcp_slave_entry(ULONG listen_port) {
    UINT status;
    ULONG actual_status;
    /* 确保 IP 实例已初始化。  */
    do {
        /* 等待 1 秒钟,让 内部 IP 线程完成其初始化。. */
        status = nx_ip_status_check(&mb_tcp_slave_ip,
                                    NX_IP_INITIALIZE_DONE,
                                    &actual_status,
                                    NX_IP_PERIODIC_RATE);
        if (status != NX_SUCCESS) {
            tx_thread_sleep(NX_IP_PERIODIC_RATE);
        }
    } while (status != NX_SUCCESS);
    /* 创建socket */
    status = nx_tcp_socket_create(&mb_tcp_slave_ip,
                                  &mb_slave_socket,
                                  "mb_slave_socket",
                                  NX_IP_NORMAL, /* IP服务类型 */
                                  NX_FRAGMENT_OKAY,/* 使能IP分段 */
                                  NX_IP_TIME_TO_LIVE, /*默认数据包生存时间*/
                                  PACKET_SIZE, /*这个参数对应到后面发送的数据包是否会进行分包处理*/
                                  mb_tcp_urgent_data_callback, /* 用于在接收流中检测到紧急数据时调用的回调函数 */
                                  mb_tcp_disconnect_callback  /* TCP Socket另一端发出断开连接时调用的回调函数 */
    );

    /*  监听新的链接。 */
    status = nx_tcp_server_socket_listen(&mb_tcp_slave_ip, /* IP实例控制块 */
                                         listen_port, /* 端口 */
                                         &mb_slave_socket,/* TCP Socket控制块 */
                                         1,/* 可以监听的连接数 */
                                         NX_NULL  /* 监听接收到连接函数 */
    );

    while (TX_LOOP_FOREVER) {

        status = nx_tcp_server_socket_accept(&mb_slave_socket, NX_WAIT_FOREVER);

        if (status) {
            tx_log("nx_tcp_server_socket_accept error:%d\r\n", status);
            continue;
        }



        /*处理接收到的客户端连接*/
        receive_socket_handle(&mb_slave_socket);
        /* 断开服务器套接字。  */
        status = nx_tcp_socket_disconnect(&mb_slave_socket, NX_IP_PERIODIC_RATE);
        if (status) {
        }

        /* 解除Socket和服务器端口的绑定 */
        status = nx_tcp_server_socket_unaccept(&mb_slave_socket);
        /* 重新监听 */
        status = nx_tcp_server_socket_relisten(
                &mb_tcp_slave_ip, listen_port, &mb_slave_socket);


    }
}

VOID mb_tcp_urgent_data_callback(NX_TCP_SOCKET *socket_ptr) {
    TX_PARAMETER_NOT_USED(socket_ptr);

}

VOID mb_tcp_disconnect_callback(NX_TCP_SOCKET *socket_ptr) {
    TX_PARAMETER_NOT_USED(socket_ptr);

}

/**
 * @brief 响应数据发送
 * @param data
 * @param len
 * @return
 */
UINT mb_tcp_slave_response_data(NX_TCP_SOCKET *socket_ptr, void *data, uint16_t len) {
    static NX_PACKET *mb_packet;

    UINT status = nx_packet_allocate(&mb_slave_pool,
                                     &mb_packet,
                                     NX_TCP_PACKET,
                                     NX_WAIT_FOREVER);

    if (status) return NX_NOT_CREATED;
    nx_packet_data_append(mb_packet,
                          data, len,
                          &mb_slave_pool,
                          TX_WAIT_FOREVER);

    /* Send the packet out!  */
    status = nx_tcp_socket_send(socket_ptr, mb_packet, NX_IP_PERIODIC_RATE);

    if (status) {
        nx_packet_release(mb_packet);
    }

    return status;
}

mb主服务线程

/*
 * Copyright (c) 2024-2024,shchl
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2024-4-11     shchl   first version
 */
#include "app_task_def.h"
#include "mb.h"

#define APP_TASK_MB_SLAVE_ENABLE 1
#if APP_TASK_MB_SLAVE_ENABLE

#define MB_POLL_CYCLE_MS 1
static TX_THREAD mb_thread;

static void mb_thread_entry(ULONG input);
/**
 * @brief 创建mb 主服务线程
 * @return 
 */
int app_task_mb_slave_thread_create() {


    tx_thread_create(
            &mb_thread, "mb thread",
            mb_thread_entry, 0,
            app_malloc(4096), 4096,
           5, 5,
            TX_NO_TIME_SLICE,
            TX_AUTO_START
    );


    return NX_SUCCESS;
}

NET_X_APP_EXPORT(app_task_mb_slave_thread_create); /*自动组件初始化*/

static void mb_thread_entry(ULONG input) {

    eMBTCPInit(0); /*使用默认端口502*/
    eMBEnable();
    while (1) {
        eMBPoll();
        tx_thread_sleep(MB_POLL_CYCLE_MS);
    }

}

#endif

应用数据对接的源文件

/*
 * FreeModbus Libary: user callback functions and buffer define in slave mode
 * Copyright (C) 2013 Armink <armink.ztl@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * File: $Id: user_mb_app.c,v 1.60 2013/11/23 11:49:05 Armink $
 */
#include "user_mb_app.h"

/*------------------------Slave mode use these variables----------------------*/
//Slave mode:DiscreteInputs variables
USHORT   usSDiscInStart                               = S_DISCRETE_INPUT_START;
#if S_DISCRETE_INPUT_NDISCRETES%8
UCHAR    ucSDiscInBuf[S_DISCRETE_INPUT_NDISCRETES/8+1];
#else
UCHAR    ucSDiscInBuf[S_DISCRETE_INPUT_NDISCRETES/8]  ;
#endif
//Slave mode:Coils variables
USHORT   usSCoilStart                                 = S_COIL_START;
#if S_COIL_NCOILS%8
UCHAR    ucSCoilBuf[S_COIL_NCOILS/8+1]                ;
#else
UCHAR    ucSCoilBuf[S_COIL_NCOILS/8]                  ;
#endif
//Slave mode:InputRegister variables
USHORT   usSRegInStart                                = S_REG_INPUT_START;
USHORT   usSRegInBuf[S_REG_INPUT_NREGS]               ;
//Slave mode:HoldingRegister variables
USHORT   usSRegHoldStart                              = S_REG_HOLDING_START;
USHORT   usSRegHoldBuf[S_REG_HOLDING_NREGS]           ;

/**
 * Modbus slave input register callback function.
 *
 * @param pucRegBuffer input register buffer
 * @param usAddress input register address
 * @param usNRegs input register number
 *
 * @return result
 */
eMBErrorCode eMBRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    USHORT          iRegIndex;
    USHORT *        pusRegInputBuf;
    USHORT          REG_INPUT_START;
    USHORT          REG_INPUT_NREGS;
    USHORT          usRegInStart;

    pusRegInputBuf = usSRegInBuf;
    REG_INPUT_START = S_REG_INPUT_START;
    REG_INPUT_NREGS = S_REG_INPUT_NREGS;
    usRegInStart = usSRegInStart;

    /* it already plus one in modbus function method. */
    usAddress--;

    if ((usAddress >= REG_INPUT_START)
            && (usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS))
    {
        iRegIndex = usAddress - usRegInStart;
        while (usNRegs > 0)
        {
            *pucRegBuffer++ = (UCHAR) (pusRegInputBuf[iRegIndex] >> 8);
            *pucRegBuffer++ = (UCHAR) (pusRegInputBuf[iRegIndex] & 0xFF);
            iRegIndex++;
            usNRegs--;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;
}

/**
 * Modbus slave holding register callback function.
 *
 * @param pucRegBuffer holding register buffer
 * @param usAddress holding register address
 * @param usNRegs holding register number
 * @param eMode read or write
 *
 * @return result
 */
eMBErrorCode eMBRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress,
        USHORT usNRegs, eMBRegisterMode eMode)
{
    eMBErrorCode    eStatus = MB_ENOERR;
    USHORT          iRegIndex;
    USHORT *        pusRegHoldingBuf;
    USHORT          REG_HOLDING_START;
    USHORT          REG_HOLDING_NREGS;
    USHORT          usRegHoldStart;

    pusRegHoldingBuf = usSRegHoldBuf;
    REG_HOLDING_START = S_REG_HOLDING_START;
    REG_HOLDING_NREGS = S_REG_HOLDING_NREGS;
    usRegHoldStart = usSRegHoldStart;

    /* it already plus one in modbus function method. */
    usAddress--;

    if ((usAddress >= REG_HOLDING_START)
            && (usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS))
    {
        iRegIndex = usAddress - usRegHoldStart;
        switch (eMode)
        {
        /* read current register values from the protocol stack. */
        case MB_REG_READ:
            while (usNRegs > 0)
            {
                *pucRegBuffer++ = (UCHAR) (pusRegHoldingBuf[iRegIndex] >> 8);
                *pucRegBuffer++ = (UCHAR) (pusRegHoldingBuf[iRegIndex] & 0xFF);
                iRegIndex++;
                usNRegs--;
            }
            break;

        /* write current register values with new values from the protocol stack. */
        case MB_REG_WRITE:
            while (usNRegs > 0)
            {
                pusRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
                pusRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
                iRegIndex++;
                usNRegs--;
            }
            break;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }
    return eStatus;
}

/**
 * Modbus slave coils callback function.
 *
 * @param pucRegBuffer coils buffer
 * @param usAddress coils address
 * @param usNCoils coils number
 * @param eMode read or write
 *
 * @return result
 */
eMBErrorCode eMBRegCoilsCB(UCHAR * pucRegBuffer, USHORT usAddress,
        USHORT usNCoils, eMBRegisterMode eMode)
{
    eMBErrorCode    eStatus = MB_ENOERR;
    USHORT          iRegIndex , iRegBitIndex , iNReg;
    UCHAR *         pucCoilBuf;
    USHORT          COIL_START;
    USHORT          COIL_NCOILS;
    USHORT          usCoilStart;
    iNReg =  usNCoils / 8 + 1;

    pucCoilBuf = ucSCoilBuf;
    COIL_START = S_COIL_START;
    COIL_NCOILS = S_COIL_NCOILS;
    usCoilStart = usSCoilStart;

    /* it already plus one in modbus function method. */
    usAddress--;

    if( ( usAddress >= COIL_START ) &&
        ( usAddress + usNCoils <= COIL_START + COIL_NCOILS ) )
    {
        iRegIndex = (USHORT) (usAddress - usCoilStart) / 8;
        iRegBitIndex = (USHORT) (usAddress - usCoilStart) % 8;
        switch ( eMode )
        {
        /* read current coil values from the protocol stack. */
        case MB_REG_READ:
            while (iNReg > 0)
            {
                *pucRegBuffer++ = xMBUtilGetBits(&pucCoilBuf[iRegIndex++],
                        iRegBitIndex, 8);
                iNReg--;
            }
            pucRegBuffer--;
            /* last coils */
            usNCoils = usNCoils % 8;
            /* filling zero to high bit */
            *pucRegBuffer = *pucRegBuffer << (8 - usNCoils);
            *pucRegBuffer = *pucRegBuffer >> (8 - usNCoils);
            break;

            /* write current coil values with new values from the protocol stack. */
        case MB_REG_WRITE:
            while (iNReg > 1)
            {
                xMBUtilSetBits(&pucCoilBuf[iRegIndex++], iRegBitIndex, 8,
                        *pucRegBuffer++);
                iNReg--;
            }
            /* last coils */
            usNCoils = usNCoils % 8;
            /* xMBUtilSetBits has bug when ucNBits is zero */
            if (usNCoils != 0)
            {
                xMBUtilSetBits(&pucCoilBuf[iRegIndex++], iRegBitIndex, usNCoils,
                        *pucRegBuffer++);
            }
            break;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }
    return eStatus;
}

/**
 * Modbus slave discrete callback function.
 *
 * @param pucRegBuffer discrete buffer
 * @param usAddress discrete address
 * @param usNDiscrete discrete number
 *
 * @return result
 */
eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    USHORT          iRegIndex , iRegBitIndex , iNReg;
    UCHAR *         pucDiscreteInputBuf;
    USHORT          DISCRETE_INPUT_START;
    USHORT          DISCRETE_INPUT_NDISCRETES;
    USHORT          usDiscreteInputStart;
    iNReg =  usNDiscrete / 8 + 1;

    pucDiscreteInputBuf = ucSDiscInBuf;
    DISCRETE_INPUT_START = S_DISCRETE_INPUT_START;
    DISCRETE_INPUT_NDISCRETES = S_DISCRETE_INPUT_NDISCRETES;
    usDiscreteInputStart = usSDiscInStart;

    /* it already plus one in modbus function method. */
    usAddress--;

    if ((usAddress >= DISCRETE_INPUT_START)
            && (usAddress + usNDiscrete    <= DISCRETE_INPUT_START + DISCRETE_INPUT_NDISCRETES))
    {
        iRegIndex = (USHORT) (usAddress - usDiscreteInputStart) / 8;
        iRegBitIndex = (USHORT) (usAddress - usDiscreteInputStart) % 8;

        while (iNReg > 0)
        {
            *pucRegBuffer++ = xMBUtilGetBits(&pucDiscreteInputBuf[iRegIndex++],
                    iRegBitIndex, 8);
            iNReg--;
        }
        pucRegBuffer--;
        /* last discrete */
        usNDiscrete = usNDiscrete % 8;
        /* filling zero to high bit */
        *pucRegBuffer = *pucRegBuffer << (8 - usNDiscrete);
        *pucRegBuffer = *pucRegBuffer >> (8 - usNDiscrete);
    }
    else
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;
}


测试结果

测试指令提供

00 84 00 00 00 06 01 03 00 00 00 0A

工具测试

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1584727.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux查看网络连接数

目录 netstat top netstat 1.netstat查看当前主机上网络连接信息&#xff0c;端口号&#xff0c;pid,程序名等等 #直接查看 netstat -anp #一般使用的时候&#xff0c;可能要筛选 #比如8080端口是否被占用 netstat -anp | grep 8080 #minio服务占用了那些端口 netstat -anp …

掌握判断IPv4地址是否正确的方法

在数字通信和互联网领域中&#xff0c;IPv4地址作为标识网络设备的核心元素&#xff0c;其正确性至关重要。一个有效的IPv4地址能够确保设备在网络中的正常通信和交互&#xff0c;而错误的IPv4地址则可能导致连接失败、通信中断甚至网络安全问题。因此&#xff0c;掌握判断IPv4…

【docker】之linux写shell脚本备份线上数据库(备份为dump文件)

目录 1. SH文件1.1 SH文件示例1.2 文件解释1.3 .sh文件执行 2. 备份线上数据库的.sh文件2.1 文件命令解析 3. 命令执行4. 线下dump文件的恢复与备份 环境&#xff1a;linux容器&#xff1a;docker 1. SH文件 SH文件通常指的是 Shell 脚本文件&#xff0c;文件后缀名为.sh&…

Spring Boot统一功能处理(一)

本篇主要介绍Spring Boot的统一功能处理中的拦截器。 目录 一、拦截器的基本使用 二、拦截器实操 三、浅尝源码 初始化DispatcherServerlet 处理请求&#xff08;doDispatch) 四、适配器模式 一、拦截器的基本使用 在一般的学校或者社区门口&#xff0c;通常会安排几个…

【C++学习】C++11新特性(第一节)

文章目录 ♫一.文章前言♫二.C11新特性♫一.统一的列表初始化♫二.std::initializer_list♫三.声明♫四.decltype关键字♫五.nullptr♫六.新增加容器---静态数组array、forward_list以及unordered系列♫6.1unordered_map与unoredered_set♫6.2array♫6.3 forward_list&#xff…

Leetcode算法训练日记 | day18

一、找树左下角的值 1.题目 Leetcode&#xff1a;第 513 题 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1: 输入: root [2,1,3] 输出: 1示例 2: 输入: [1,2,3,4,null,5,6,null,null,7] 输出:…

nandgame中的Memory(内存操作):栈、堆、静态区

Push Memory Push Memory 将内存中的值push到栈内堆栈顶部的值是一个内存地址。从堆栈中弹出地址。获取内存地址的当前内容&#xff0c;并将其推送到堆栈上。POP_A //堆栈顶部的值是一个内存地址。从堆栈中弹出地址。 D *A //获取内存地址的当前内容 PUSH_D //将其推送到…

JVM性能调优——运行时参数

文章目录 1、JVM参数选项类型1.1、标准参数选项1.2、非标准参数选项1.3、非稳定参数选项 2、添加JVM参数的方式3、常用JVM参数选项4、通过Java代码获取JVM参数5、小结 熟悉JVM参数对于系统调优是非常重要的。比如一个高流量的延迟的电子交易平台&#xff0c;它要求的响应时间都…

ROS机器人未知环境自主探索功能包explore_lite最全源码详细解析(五)

本系列文章主要针对ROS机器人常使用的未知环境自主探索功能包explore_lite展开全源码的详细解析&#xff0c;并进行概括总结。 本系列文章共包含六篇文章&#xff0c;前五篇文章主要介绍explore_lite功能包中 explore.cpp、costmap_tools.h、frontier_search.cpp、costmap_clie…

MySQL:关于数据库的一些练习题

文章目录 前面的内容已经把数据库的一些必要知识已经储备好了&#xff0c;因此下面就对于这些语句进行一些练习&#xff1a; 批量插入数据 insert into actor values (1, PENELOPE, GUINESS, 2006-02-15 12:34:33), (2, NICK, WAHLBERG, 2006-02-15 12:34:33);SQL202 找出所有…

C/S医学检验LIS实验室信息管理系统源码 医院LIS源码

LIS系统即实验室信息管理系统。LIS系统能实现临床检验信息化&#xff0c;检验科信息管理自动化。其主要功能是将检验科的实验仪器传出的检验数据经数据分析后&#xff0c;自动生成打印报告&#xff0c;通过网络存储在数据库中&#xff0c;使医生能够通过医生工作站方便、及时地…

一起学习python——基础篇(14)

今天讲一下python的json解析方式。 上一篇文章讲述了将传参数据转换为json格式的数据传给后台&#xff0c;如果后端返回的json格式数据&#xff0c;我们该如何解析呢&#xff1f; 例子一&#xff1a;简单的json数据格式 如果后端返回的json数据如下&#xff0c; { "na…

【leetcode面试经典150题】34.有效的数独(C++)

【leetcode面试经典150题】专栏系列将为准备暑期实习生以及秋招的同学们提高在面试时的经典面试算法题的思路和想法。本专栏将以一题多解和精简算法思路为主&#xff0c;题解使用C语言。&#xff08;若有使用其他语言的同学也可了解题解思路&#xff0c;本质上语法内容一致&…

Docker Compose 一键安装

文章目录 一、场景说明二、脚本职责三、参数说明四、操作示例五、注意事项 一、场景说明 本自动化脚本旨在为提高研发、测试、运维快速部署应用环境而编写。 脚本遵循拿来即用的原则快速完成 CentOS 系统各应用环境部署工作。 统一研发、测试、生产环境的部署模式、部署结构、…

【C++学习】C++11新特性(第二节)—— 右值引用与移动语义超详解

文章目录 文章简介二.右值引用1.什么是左值&#xff0c;什么是右值&#xff1f;什么是左值引用&#xff0c;什么是右值引用&#xff1f;2.左值引用与右值引用比较 三.右值引用使用场景和意义1.左值引用的使用场景&#xff1a;2.左值引用的短板&#xff1a;3.右值引用与移动构造…

2024大模型落地应用案例集(免费下载)

【1】扫码关注本公众号 【2】私信发送 2024大模型落地应用案例集 【3】获取本方案PDF下载链接&#xff0c;直接下载即可。

【Linux】环境下OpenSSH升级到 OpenSSH_9.6P1(图文教程)

漏洞描述 OpenSSH&#xff08;OpenBSD Secure Shell&#xff09;是加拿大OpenBSD计划组的一套用于安全访问远程计算机的连接工具。该工具是SSH协议的开源实现&#xff0c;支持对所有的传输进行加密&#xff0c;可有效阻止窃听、连接劫持以及其他网络级的攻击。OpenSSH 9.6之前…

深入浅出Golang image库:编写高效的图像处理代码

深入浅出Golang image库&#xff1a;编写高效的图像处理代码 引言image库概览图像处理基础概念image库的主要组成和功能image接口图像格式的支持color模型 结论 图像的基本操作创建图像新图像的创建从文件加载图像 图像的保存与导出图像的颜色和像素处理绘制基本形状和文字 高级…

无网络连接 请检查你的网络设置 然后重试 [2604] 彻底解决方案

错误提示&#xff1a;无网络连接 请检查你的网络设置 然后重试 [2604] 彻底解决方案如下&#xff1a; 方案一&#xff1a; 打开Internet Explorer浏览器&#xff0c;选择"工具 > Internet 选项"。 在Internet属性窗口下&#xff0c;点击高级下滑到安全模块分类…

[漏洞复现]D-Link未授权RCE漏洞复现(CVE-2024-3273)

声明&#xff1a;亲爱的读者&#xff0c;我们诚挚地提醒您&#xff0c;Aniya网络安全的技术文章仅供个人研究学习参考。任何因传播或利用本实验室提供的信息而造成的直接或间接后果及损失&#xff0c;均由使用者自行承担责任。Aniya网络安全及作者对此概不负责。如有侵权&#…