前言
- 本篇结合freemodbus源码程序进行移植,驱动实现的接口为modbus tcp
- 需要知道threadx的 事件标志组、信号量、线程相关的知识
- 需要知道netxduo tcp方面的api和创建流程方面的知识
freemodbus程序源码
- 本次使用的源码来自于rt-thread软件包里面的,可以参考之前的博客:rt-thread之通讯协议modbus软件包的使用记录(lwip+modbus组合)本篇重点在threadx系列
- 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