您的位置:首页 > 编程语言 > C语言/C++

VC++实现的ModBus-RTU主机接口函数(采用回调方式)

2016-11-04 16:50 417 查看
计算机上面使用Modbus读取传感器或相关设备还是比较常用的,之前写的Modbus-RTU协议将串口封装到了协议栈内,使用的时候遇到短板了,比如我最新需要使用TCP来读取Modbus设备,就不好用了,通过回调函数可以很简单的解决这个问题。

//modbus-rtu.c

/*************************************************************************************************************
* 文件名:		MODBUS_RTU.c
* 功能:			MODBUS_RTU通信协议层
* 作者:			cp1300@139.com
* 创建时间:		2014-03-24
* 最后修改时间:	2016-11-04
* 详细:			MODBUS RTU通信协议层
2016-03-21:增加防止接收数据过短导致异常
2016-11-04:增加回调接口,将数据收发接口使用回调函数
*************************************************************************************************************/
#include "StdAfx.h"
#include "MODBUS_RTU.h"
#include "windows.h"

using namespace System;

/*************************************************************************************************************************
* 函数		:	bool MODBUS_RTU::ReadMultRegPack(BYTE *pPackBuff, DWORD *pPackLen, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum, char **pError)
* 功能		:	主机读取从机指定多个连续寄存器数据打包
* 参数		:	pPackBuff:打包缓冲区,pPackLen:打包的数据长度;RegType:读取的寄存器类型;SlaveAddr:从机地址;RegAddr:需读取的寄存器地址;RegNum:寄存器数量;pError:返回错误说明
* 返回		:	true:成功;false:错误
* 依赖		:	底层通信驱动
* 作者		:	cp1300@139.com
* 时间		:	2014-03-24
* 最后修改时间	:	2016-11-04
* 说明		: 	数据打包,不涉及发送
*************************************************************************************************************************/
bool MODBUS_RTU::ReadMultRegPack(BYTE *pPackBuff, DWORD *pPackLen, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum, char **pError)
{
MRTU_READ_FRAME *pFrame;		//发送数据帧格式
u16 crc16;

if (pPackBuff == nullptr)		//缓冲区无效
{
*pPackLen = 0;
if (pError != nullptr) *pError = "缓冲区无效!";
return false;	//句柄无效
}

pFrame = (MRTU_READ_FRAME *)pPackBuff;
//数据结构填充
pFrame->addr = SlaveAddr;						//从机地址
pFrame->fun = (u8)RegType;						//功能码,读取
pFrame->StartReg = SWAP16(RegAddr);				//寄存器起始地址
if ((RegNum > 127) || (RegNum == 0))
{
if (pError != nullptr) *pError = "一次读取的寄存器数量超出范围!";
return false;								//寄存器数量错误
}
pFrame->RegNum = SWAP16(RegNum);				//需要读取的寄存器数量
crc16 = usMBCRC16(pPackBuff, 6);				//计算CRC16
pFrame->CRC16 = crc16;							//crc16
*pPackLen = 6+2;

if (pError != nullptr) *pError = "打包成功!";
return true;
}

/*************************************************************************************************************************
* 函数		:	MRTU_ERROR  MODBUS_RTU::ReadMultRegUnpack(BYTE *pPackBuff, DWORD PackLen, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[], char **pError)
* 功能		:	主机读取从机指定多个连续寄存器数据解包
* 参数		:	pPackBuff:数据包缓冲区;PackLen:数据包大小;SlaveAddr:从机地址;RegAddr:需读取的寄存器地址;RegNum:寄存器数量;pRegData:返回寄存器的值,至少为RegNum的2倍
返回的寄存器的值按照循序存放在pRegData中
* 返回		:	MRTU_ERROR:通信状态
* 依赖		:	底层通信驱动
* 作者		:	cp1300@139.com
* 时间		:	2014-03-24
* 最后修改时间	:	2016-11-04
* 说明		: 	数据包解析
*************************************************************************************************************************/
MRTU_ERROR  MODBUS_RTU::ReadMultRegUnpack(BYTE *pPackBuff, DWORD PackLen, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[], char **pError)
{
MRTU_RETURN_FRAME *pReFrame;	//返回数据帧格式
MRTU_UNU_FRAME	*pUnuFrame;		//返回的异常数据帧格式
u16 crc16;
u16 i;

if (pPackBuff == nullptr)		//缓冲区无效
{
if (pError != nullptr) *pError = "缓冲区无效!";
return MRTU_HANDLE_ERROR;	//句柄无效
}

if (PackLen < 3)
{
if (pError != nullptr) *pError = "返回数据长度过短!";
return MRTU_LEN_ERROR;	//返回数据长度错误
}

pReFrame = (MRTU_RETURN_FRAME *)pPackBuff;
//检查地址
if (pReFrame->addr != SlaveAddr)
{
if (pError != nullptr) *pError = "返回的从机地址错误!";
return MRTU_ADDR_ERROR;
}
//对接受的数据进行CRC校验
crc16 = usMBCRC16(pPackBuff, PackLen - 2);//计算CRC16
if ((pPackBuff[PackLen - 1] != (crc16 >> 8)) || (pPackBuff[PackLen - 2] != (crc16 & 0xff)))
{
if (pError != nullptr) *pError = "CRC校验错误";
return MRTU_CRC_ERROR;				//返回CRC校验错误
}

//返回的功能码不一致
if (pReFrame->fun != (u8)RegType)
{
pUnuFrame = (MRTU_UNU_FRAME *)pPackBuff;				//异常数据帧
if (pUnuFrame->ErrorFun == ((u8)RegType | 0x80))		//返回有异常
{
switch (pUnuFrame->unu)
{
case 1: //异常码1
{
if (pError != nullptr) *pError = "返回异常码1!";
return MRTU_UNUS1_ERROR;
}
case 2: //异常码2
{
if (pError != nullptr) *pError = "返回异常码2!";
return MRTU_UNUS2_ERROR;
}
case 3: //异常码3
{
if (pError != nullptr) *pError = "返回异常码3!";
return MRTU_UNUS3_ERROR;
}
case 4: //异常码4
{
if (pError != nullptr) *pError = "返回异常码4!";
return MRTU_UNUS4_ERROR;
}
case 5://异常码5
{
if (pError != nullptr) *pError = "返回异常码5!";
return MRTU_UNUS5_ERROR;
}
case 6://异常码6
{
if (pError != nullptr) *pError = "返回异常码6!";
return MRTU_UNUS6_ERROR;
}
default:
{
if (pError != nullptr) *pError = "返回未知异常码!";
return MRTU_OTHER_ERROR;
}
}
}
else
{
if (pError != nullptr) *pError = "返回功能码错误!";
return MRTU_FUNR_ERROR;
}
}
//判断数据长度
if (pReFrame->DataLen != (RegNum * 2))
{
if (pError != nullptr) *pError = "返回数据长度错误,长度小于需要读取的寄存器数量x2!";
return MRTU_LEN_ERROR;				//返回数据长度错误
}
//获取返回的寄存器的值
for (i = 0; i < RegNum; i++)
{
pRegData[i] = pReFrame->DataBuff[i * 2];
pRegData[i] <<= 8;
pRegData[i] |= pReFrame->DataBuff[i * 2 + 1];
}

if (pError != nullptr) *pError = "读取成功!";
return MRTU_OK;						//返回成功
}

/*************************************************************************************************************************
* 函数		:	bool WriteOnetRegPack(BYTE *pPackBuff, DWORD *pPackLen, u8 SlaveAddr, u16 RegAddr, u16 RegData, char **pError)
* 功能		:	主机写从机一个指定寄存器数据打包
* 参数		:	pPackBuff:打包缓冲区,pPackLen:打包的数据长度;SlaveAddr:从机地址;RegAddr:写寄存器地址;RegData:寄存器的值;pError:错误提示
* 返回		:	true:成功;false:错误
* 依赖		:	底层通信驱动
* 作者		:	cp1300@139.com
* 时间		:	2014-03-24
* 最后修改时间	:	2016-11-04
* 说明		: 	数据打包,不涉及发送
*************************************************************************************************************************/
bool MODBUS_RTU::WriteOnetRegPack(BYTE *pPackBuff, DWORD *pPackLen, u8 SlaveAddr, u16 RegAddr, u16 RegData, char **pError)
{
MRTU_WRITE_FRAME *pFrame;//发送数据帧格式
u16 crc16;

if (pPackBuff == nullptr)		//缓冲区无效
{
*pPackLen = 0;
if (pError != nullptr) *pError = "缓冲区无效!";
return false;	//句柄无效
}

pFrame = (MRTU_WRITE_FRAME *)pPackBuff;
//数据结构填充
pFrame->addr = SlaveAddr;						//从机地址
pFrame->fun = (u8)MRTU_FUN_WRITE;				//功能码,预置单个寄存器
pFrame->StartReg = SWAP16(RegAddr);				//寄存器起始地址
pFrame->RegData = SWAP16(RegData);				//写入寄存器内容
pFrame->crc16 = usMBCRC16(pPackBuff, 6);		//计算CRC16
*pPackLen = 6+2;

if (pError != nullptr) *pError = "打包成功!";
return true;
}

/*************************************************************************************************************************
* 函数		:	MRTU_ERROR MODBUS_RTU::WriteOneRegUnpack(BYTE *pPackBuff, DWORD PackLen, u8 SlaveAddr, u16 RegAddr, u16 RegData, char **pError)
* 功能		:	主机写从机一个指定寄存器数据解包
* 参数		:	pPackBuff:打包缓冲区,pPackLen:打包的数据长度;SlaveAddr:从机地址;RegAddr:写寄存器地址;RegData:寄存器的值;RegData:需要写入的值;pError:错误说明
* 返回		:	MRTU_ERROR:通信状态
* 依赖		:	底层通信驱动
* 作者		:	cp1300@139.com
* 时间		:	2014-03-24
* 最后修改时间	:	2016-11-04
* 说明		: 	数据包解包
*************************************************************************************************************************/
MRTU_ERROR MODBUS_RTU::WriteOneRegUnpack(BYTE *pPackBuff, DWORD PackLen, u8 SlaveAddr, u16 RegAddr, u16 RegData, char **pError)
{
MRTU_WRITE_FRAME *pReFrame;//发送数据帧格式
MRTU_UNU_FRAME	*pUnuFrame;			//返回的异常数据帧格式
u16 crc16;

if (pPackBuff == nullptr)		//缓冲区无效
{
if (pError != nullptr) *pError = "缓冲区无效!";
return MRTU_HANDLE_ERROR;	//句柄无效
}
if (PackLen < 3)
{
if (pError != nullptr) *pError = "返回数据长度过短!";
return MRTU_LEN_ERROR;	//返回数据长度错误
}

pReFrame = (MRTU_WRITE_FRAME *)pPackBuff;
//检查地址
if (pReFrame->addr != SlaveAddr)
{
if (pError != nullptr) *pError = "返回的从机地址错误!";
return MRTU_ADDR_ERROR;
}
//对接受的数据进行CRC校验
crc16 = usMBCRC16(pPackBuff, PackLen - 2);//计算CRC16
if ((pPackBuff[PackLen - 1] != (crc16 >> 8)) || (pPackBuff[PackLen - 2] != (crc16 & 0xff)))
{
if (pError != nullptr) *pError = "CRC校验错误";
return MRTU_CRC_ERROR;				//返回CRC校验错误
}
//返回的功能码不一致
if (pReFrame->fun != (u8)MRTU_FUN_WRITE)
{
pUnuFrame = (MRTU_UNU_FRAME *)pPackBuff;		//异常数据帧
if (pUnuFrame->ErrorFun == ((u8)MRTU_FUN_WRITE | 0x80))//返回有异常
{
switch (pUnuFrame->unu)
{
case 1: //异常码1
{
if (pError != nullptr) *pError = "返回异常码1!";
return MRTU_UNUS1_ERROR;
}
case 2: //异常码2
{
if (pError != nullptr) *pError = "返回异常码2!";
return MRTU_UNUS2_ERROR;
}
case 3: //异常码3
{
if (pError != nullptr) *pError = "返回异常码3!";
return MRTU_UNUS3_ERROR;
}
case 4: //异常码4
{
if (pError != nullptr) *pError = "返回异常码4!";
return MRTU_UNUS4_ERROR;
}
case 5://异常码5
{
if (pError != nullptr) *pError = "返回异常码5!";
return MRTU_UNUS5_ERROR;
}
case 6://异常码6
{
if (pError != nullptr) *pError = "返回异常码6!";
return MRTU_UNUS6_ERROR;
}
default:
{
if (pError != nullptr) *pError = "返回未知异常码!";
return MRTU_OTHER_ERROR;
}
}
}
else
{
if (pError != nullptr) *pError = "返回功能码错误!";
return MRTU_FUNR_ERROR;
}
}
//判断数据是否写入
if (SWAP16(pReFrame->StartReg) != RegAddr)	//返回的寄存器地址不一致
{
if (pError != nullptr) *pError = "返回寄存器地址错误!";
return MRTU_REG_ERROR;					//返回寄存器错误
}
if (SWAP16(pReFrame->RegData) != RegData)
{
if (pError != nullptr) *pError = "数据写入错误,没有写入成功!";
return MRTU_WRITE_ERROR;				//写入数据错误
}

if (pError != nullptr) *pError = "写入成功!";
return MRTU_OK;								//返回成功
}

/*************************************************************************************************************************
* 函数		:	MRTU_ERROR MODBUS_RTU::WriteMultRegPack(BYTE *pPackBuff, DWORD *pPackLen, u8 SlaveAddr, u16 RegAddr, u16 pRegData[], u8 RegNum, , char **pError)
* 功能		:	主机写从机多个指定寄存器数据打包
* 参数		:	pPackBuff:打包缓冲区,pPackLen:打包的数据长度;SlaveAddr:从机地址;RegAddr:写寄存器地址;pRegData:需要写入的寄存器的值;RegNum:寄存器数量;pError:错误说明
* 返回		:	true:成功;false:错误
* 依赖		:	底层通信驱动
* 作者		:	cp1300@139.com
* 时间		:	2014-03-24
* 最后修改时间	:	2016-11-04
* 说明		: 	写多个寄存器数据打包
写入寄存器的值按照循序排列,使用小端格式,大小必须为RegNum*2
最大只能一次写入不超过127个寄存器
*************************************************************************************************************************/
bool MODBUS_RTU::WriteMultRegPack(BYTE *pPackBuff, DWORD *pPackLen, u8 SlaveAddr, u16 RegAddr, u16 pRegData[], u8 RegNum, char **pError)
{
MRTU_WRITE_MULT_FRAME *pFrame;					//发送数据帧格式
DWORD i;
WORD crc16;

if (pPackBuff == nullptr)		//缓冲区无效
{
*pPackLen = 0;
if (pError != nullptr) *pError = "缓冲区无效!";
return false;	//句柄无效
}
pFrame = (MRTU_WRITE_MULT_FRAME *)pPackBuff;
//数据结构填充
pFrame->addr = SlaveAddr;						//从机地址
pFrame->fun = (u8)MRTU_FUN_MWRITE;				//功能码,预置多个寄存器
pFrame->StartReg = SWAP16(RegAddr);				//寄存器起始地址
if ((RegNum > 127) || (RegNum == 0))
{
*pPackLen = 0;
if (pError != nullptr) *pError = "一次写入寄存器数量过多!";
return FALSE;	//寄存器数量错误
}

pFrame->RegNum = SWAP16(RegNum);				//写入寄存器数量
pFrame->DataLen = 2 * RegNum;						//数据长度
//循环写入数据
for (i = 0; i < RegNum; i++)
{
pFrame->DataBuff[2 * i] = pRegData[i] >> 8;		//高位
pFrame->DataBuff[2 * i + 1] = pRegData[i] & 0xff;	//低位
}
crc16 = usMBCRC16(pPackBuff, 7 + pFrame->DataLen);	//计算CRC16,高低位对调过
pFrame->DataBuff[pFrame->DataLen] = crc16 & 0xff;	//高位
pFrame->DataBuff[pFrame->DataLen + 1] = crc16 >> 8;	//低位
*pPackLen = 7 + pFrame->DataLen + 2;

if (pError != nullptr) *pError = "打包成功!";
return true;
}

/*************************************************************************************************************************
* 函数		:	MRTU_ERROR MODBUS_RTU::WriteMultRegUnpack(BYTE *pPackBuff, DWORD PackLen, u8 SlaveAddr, u16 RegAddr, u8 RegNum, char **pError)
* 功能		:	主机写从机多个指定寄存器数据解包
* 参数		:	pPackBuff:打包缓冲区,pPackLen:打包的数据长度;SlaveAddr:从机地址;RegAddr:写寄存器地址;RegNum:寄存器数量;pError:错误说明
* 返回		:	MRTU_ERROR
* 依赖		:	底层通信驱动
* 作者		:	cp1300@139.com
* 时间		:	2014-03-24
* 最后修改时间:	2016-11-04
* 说明		: 	写多个寄存器数据解包
*************************************************************************************************************************/
MRTU_ERROR MODBUS_RTU::WriteMultRegUnpack(BYTE *pPackBuff, DWORD PackLen, u8 SlaveAddr, u16 RegAddr, u8 RegNum, char **pError)
{
MRTU_WRIT_EMULT_RFRAME *pReFrame;				//返回数据帧格式
MRTU_UNU_FRAME	*pUnuFrame;						//返回的异常数据帧格式
u16 crc16;
u8 i;

if (pPackBuff == nullptr)		//缓冲区无效
{
if (pError != nullptr) *pError = "缓冲区无效!";
return MRTU_HANDLE_ERROR;	//句柄无效
}
if (PackLen < 3)
{
if (pError != nullptr) *pError = "返回数据长度过短!";
return MRTU_LEN_ERROR;	//返回数据长度错误
}

pReFrame = (MRTU_WRIT_EMULT_RFRAME *)pPackBuff;
//检查地址
if (pReFrame->addr != SlaveAddr)
{
if (pError != nullptr) *pError = "返回的从机地址错误!";
return MRTU_ADDR_ERROR;
}
//对接受的数据进行CRC校验
crc16 = usMBCRC16(pPackBuff, PackLen - 2);//计算CRC16
if ((pPackBuff[PackLen - 1] != (crc16 >> 8)) || (pPackBuff[PackLen - 2] != (crc16 & 0xff)))
{
if (pError != nullptr) *pError = "CRC校验错误";
return MRTU_CRC_ERROR;				//返回CRC校验错误
}
//返回的功能码不一致
if (pReFrame->fun != (u8)MRTU_FUN_MWRITE)
{
pUnuFrame = (MRTU_UNU_FRAME *)pPackBuff;		//异常数据帧
if (pUnuFrame->ErrorFun == ((u8)MRTU_FUN_MWRITE | 0x80))//返回有异常
{
switch (pUnuFrame->unu)
{
case 1: //异常码1
{
if (pError != nullptr) *pError = "返回异常码1!";
return MRTU_UNUS1_ERROR;
}
case 2: //异常码2
{
if (pError != nullptr) *pError = "返回异常码2!";
return MRTU_UNUS2_ERROR;
}
case 3: //异常码3
{
if (pError != nullptr) *pError = "返回异常码3!";
return MRTU_UNUS3_ERROR;
}
case 4: //异常码4
{
if (pError != nullptr) *pError = "返回异常码4!";
return MRTU_UNUS4_ERROR;
}
case 5://异常码5
{
if (pError != nullptr) *pError = "返回异常码5!";
return MRTU_UNUS5_ERROR;
}
case 6://异常码6
{
if (pError != nullptr) *pError = "返回异常码6!";
return MRTU_UNUS6_ERROR;
}
default:
{
if (pError != nullptr) *pError = "返回未知异常码!";
return MRTU_OTHER_ERROR;
}
}
}
else
{
if (pError != nullptr) *pError = "返回功能码错误!";
return MRTU_FUNR_ERROR;
}
}
//判断数据是否写入
if (SWAP16(pReFrame->StartReg) != RegAddr)	//返回的寄存器地址不一致
{
if (pError != nullptr) *pError = "返回寄存器地址错误!";
return MRTU_REG_ERROR;					//返回寄存器错误
}
if (SWAP16(pReFrame->RegNum) != RegNum)
{
if (pError != nullptr) *pError = "数据写入错误,返回的寄存器数量不一致!";
return MRTU_WRITE_ERROR;				//写入数据错误
}

if (pError != nullptr) *pError = "写入成功!";
return MRTU_OK;								//返回成功
}

/*************************************************************************************************************************
* 函数		:	MRTU_ERROR MODEBUS_HOST_ReadMultReg(MODEBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[])
* 功能		:	主机读取从机指定多个连续寄存器(需要初始化回调通信接口)
* 参数		:	RegType:读取的寄存器类型;SlaveAddr:从机地址;RegAddr:需读取的寄存器地址;RegNum:寄存器数量;pRegData:返回寄存器的值,至少为RegNum的2倍;pError:错误信息
* 返回		:	MRTU_ERROR:通信状态
* 依赖		:	底层通信驱动
* 作者		:	cp1300@139.com
* 时间		:	2014-03-24
* 最后修改时间	:	2016-11-04
* 说明		: 	输入输出的数据都为小端模式
返回的寄存器的值按照循序存放在pRegData中
需要先初始化通信接口,并且会申请 MODBUS_RTU_PACK_MAX_SIZE+1 字节堆内存用于支持可重入
*************************************************************************************************************************/
MRTU_ERROR  MODBUS_RTU::ReadMultReg(READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[], char **pError)
{
BYTE PackBuff[MODBUS_RTU_PACK_MAX_SIZE+1];
DWORD len;

if (ReadMultRegPack(PackBuff, &len, RegType, SlaveAddr, RegAddr, RegNum, pError) == false)
{
return MRTU_HANDLE_ERROR;
}
//调用回调进行
if (this->pSendDataCollBack == nullptr)
{
if (pError == nullptr) *pError = "发送回调函数无效!";
return MRTU_HANDLE_ERROR;
}
try
{
if (this->pSendDataCollBack(PackBuff, len) == false)
{
if (pError == nullptr) *pError = "发送数据失败!";
return MRTU_SEND_ERROR;
}
}
catch (Exception^ e)
{
if (pError == nullptr) *pError = "发送数据发生了异常!";
return MRTU_SEND_ERROR;
}
//发送完成了,调用接收回调进行数据接收
if (this->pReadDataCollBack == nullptr)
{
if (pError == nullptr) *pError = "接收回调函数无效!";
return MRTU_READ_ERROR;
}
try
{
if (this->pReadDataCollBack(PackBuff, &len) == false)
{
if (pError == nullptr) *pError = "接收数据失败!";
return MRTU_READ_ERROR;
}
}
catch (Exception^ e)
{
if (pError == nullptr) *pError = "接收数据发生了异常!";
return MRTU_READ_ERROR;
}
if (len == 0)
{
if (pError == nullptr) *pError = "接收数据超时!";
return MRTU_TIME_OUT;
}
if (len > (256 + 7))
{
if (pError == nullptr) *pError = "接收数据溢出!";
return MRTU_OVER_ERROR;
}
//数据接收完成了,开始解析
return ReadMultRegUnpack(PackBuff, len, RegType, SlaveAddr,RegAddr, RegNum, pRegData, pError);
}

/*************************************************************************************************************************
* 函数		:	MRTU_ERROR MODBUS_RTU::WriteOnetReg(u8 SlaveAddr, u16 RegAddr, u16 RegData, char **pError)
* 功能		:	主机写入从机一个寄存器(需要初始化回调通信接口)
* 参数		:	SlaveAddr:从机地址;RegAddr:写寄存器地址;RegData:寄存器的值;pError:错误提示
* 返回		:	MRTU_ERROR:通信状态
* 依赖		:	底层通信驱动
* 作者		:	cp1300@139.com
* 时间		:	2014-03-24
* 最后修改时间	:	2016-11-04
* 说明		: 	输入输出的数据都为小端模式
需要先初始化通信接口,并且会申请 MODBUS_RTU_PACK_MAX_SIZE+1 字节堆内存用于支持可重入
*************************************************************************************************************************/
MRTU_ERROR MODBUS_RTU::WriteOnetReg(u8 SlaveAddr, u16 RegAddr, u16 RegData, char **pError)
{
BYTE PackBuff[MODBUS_RTU_PACK_MAX_SIZE + 1];
DWORD len;

if (WriteOnetRegPack(PackBuff, &len, SlaveAddr, RegAddr, RegData, pError) == false)
{
return MRTU_HANDLE_ERROR;
}
//调用回调进行
if (this->pSendDataCollBack == nullptr)
{
if (pError == nullptr) *pError = "发送回调函数无效!";
return MRTU_HANDLE_ERROR;
}
try
{
if (this->pSendDataCollBack(PackBuff, len) == false)
{
if (pError == nullptr) *pError = "发送数据失败!";
return MRTU_SEND_ERROR;
}
}
catch (Exception^ e)
{
if (pError == nullptr) *pError = "发送数据发生了异常!";
return MRTU_SEND_ERROR;
}
//发送完成了,调用接收回调进行数据接收
if (this->pReadDataCollBack == nullptr)
{
if (pError == nullptr) *pError = "接收回调函数无效!";
return MRTU_READ_ERROR;
}
try
{
if (this->pReadDataCollBack(PackBuff, &len) == false)
{
if (pError == nullptr) *pError = "接收数据失败!";
return MRTU_READ_ERROR;
}
}
catch (Exception^ e)
{
if (pError == nullptr) *pError = "接收数据发生了异常!";
return MRTU_READ_ERROR;
}
if (len == 0)
{
if (pError == nullptr) *pError = "接收数据超时!";
return MRTU_TIME_OUT;
}
if (len > (256 + 7))
{
if (pError == nullptr) *pError = "接收数据溢出!";
return MRTU_OVER_ERROR;
}
//数据接收完成了,开始解析
return WriteOneRegUnpack(PackBuff, len, SlaveAddr, RegAddr,  RegData, pError);
}

/*************************************************************************************************************************
* 函数		:	MRTU_ERROR MODBUS_RTU::WriteMultReg(u8 SlaveAddr, u16 RegAddr, u16 pRegData[],u8 RegNum,  char **pError)
* 功能		:	主机写从机多个指定寄存器(需要初始化回调通信接口)
* 参数		:	SlaveAddr:从机地址;RegAddr:写寄存器地址;RegNum:寄存器数量;pError:错误说明
* 返回		:	MRTU_ERROR
* 依赖		:	底层通信驱动
* 作者		:	cp1300@139.com
* 时间		:	2014-03-24
* 最后修改时间:	2016-11-04
* 说明		: 	写多个寄存器数据解包
*************************************************************************************************************************/
MRTU_ERROR MODBUS_RTU::WriteMultReg(u8 SlaveAddr, u16 RegAddr, u16 pRegData[],u8 RegNum,  char **pError)
{
BYTE PackBuff[MODBUS_RTU_PACK_MAX_SIZE + 1];
DWORD len;

if (WriteMultRegPack(PackBuff, &len, SlaveAddr, RegAddr, pRegData, RegNum, pError) == false)
{
return MRTU_HANDLE_ERROR;
}
//调用回调进行
if (this->pSendDataCollBack == nullptr)
{
if (pError == nullptr) *pError = "发送回调函数无效!";
return MRTU_HANDLE_ERROR;
}
try
{
if (this->pSendDataCollBack(PackBuff, len) == false)
{
if (pError == nullptr) *pError = "发送数据失败!";
return MRTU_SEND_ERROR;
}
}
catch (Exception^ e)
{
if (pError == nullptr) *pError = "发送数据发生了异常!";
return MRTU_SEND_ERROR;
}
//发送完成了,调用接收回调进行数据接收
if (this->pReadDataCollBack == nullptr)
{
if (pError == nullptr) *pError = "接收回调函数无效!";
return MRTU_READ_ERROR;
}
try
{
if (this->pReadDataCollBack(PackBuff, &len) == false)
{
if (pError == nullptr) *pError = "接收数据失败!";
return MRTU_READ_ERROR;
}
}
catch (Exception^ e)
{
if (pError == nullptr) *pError = "接收数据发生了异常!";
return MRTU_READ_ERROR;
}
if (len == 0)
{
if (pError == nullptr) *pError = "接收数据超时!";
return MRTU_TIME_OUT;
}
if (len > (256 + 7))
{
if (pError == nullptr) *pError = "接收数据溢出!";
return MRTU_OVER_ERROR;
}
//数据接收完成了,开始解析
return WriteMultRegUnpack(PackBuff, len, SlaveAddr, RegAddr, RegNum, pError);
}

//MODBUS CRC16计算
//结果为大端模式
BIG_U16  MODBUS_RTU::usMBCRC16( u8 * pucFrame, u16 usLen )
{
static const u8 aucCRCHi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40
};

static const u8 aucCRCLo[] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,
0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,
0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,
0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,
0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89,
0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83,
0x41, 0x81, 0x80, 0x40
};

u8           ucCRCHi = 0xFF;
u8           ucCRCLo = 0xFF;
int             iIndex;

while( usLen-- )
{
iIndex = ucCRCLo ^ *( pucFrame++ );
ucCRCLo = ( u8 )( ucCRCHi ^ aucCRCHi[iIndex] );
ucCRCHi = aucCRCLo[iIndex];
}
return ( u16 )( ucCRCHi << 8 | ucCRCLo );
}


//modbus-rtu.h

/*************************************************************************************************************
* 文件名:			MODBUS_RTU.h
* 功能:			MODBUS_RTU通信协议层
* 作者:			cp1300@139.com
* 创建时间:		2014-03-24
* 最后修改时间:	2016-11-04
* 详细:			MODBUS RTU通信协议层
2016-03-21:增加防止接收数据过短导致异常
2016-11-04:增加回调接口,将数据收发接口使用回调函数
*************************************************************************************************************/
#ifndef _MODBUS_RTU_H_
#define _MODBUS_RTU_H_

#include "windows.h"

//基本数据类型定义
#ifndef u8
#define u8 BYTE
#endif //u8
#ifndef u16
#define u16 WORD
#endif //u16
#ifndef u32
#define u32 DWORD
#endif //u32
#ifndef s16
#define s16 INT16
#endif //s16
#ifndef s32
#define s32 int
#endif //s32

//16位整形数高低对调
#define SWAP16(x)   (((x & 0xff00) >> 8) | ((x & 0xff) << 8))

//最大数据包大小
#define MODBUS_RTU_PACK_MAX_SIZE		300

//支持的功能码
#define MRTU_FUN_READ_HOLD		0x03			//读保持寄存器,可读写寄存器为保持寄存器
#define MRTU_FUN_READ_INPUT		0x04			//读输入寄存器,为只读寄存器
#define MRTU_FUN_WRITE			0x06			//写单个保持寄存器
#define MRTU_FUN_MWRITE			0x10			//写多个保持寄存器

//大端数据标记
#define BIG_U16		u16							//16位整形数,需要转换为大端模式,兼容modubus

//读取寄存器类型选择
typedef enum
{
HOLD_REG 	= 	MRTU_FUN_READ_HOLD,			//保持寄存器
INPUT_REG	=	MRTU_FUN_READ_INPUT,		//输入寄存器
} READ_REG_TYPE;

//数据读取 主机数据帧,主机读取从机的数据帧
typedef  struct
{
u8	addr;				//地址 address
u8	fun;				//功能码 function
BIG_U16	StartReg;		//数据起始地址
BIG_U16	RegNum;			//需要读取的寄存器个数
BIG_U16	CRC16;			//CRC16
} MRTU_READ_FRAME;			//MODBUS RTU master Read Reg Frame

//预置单个保持寄存器,主机写从机单个寄存器的数据帧
//从机返回数据帧与主机预置单个寄存器数据帧一样
typedef  struct
{
u8	addr;				//地址 address
u8	fun;				//功能码 function
BIG_U16	StartReg;		//数据起始地址
BIG_U16	RegData;		//数据值
BIG_U16 crc16;			//CRC校验值
} MRTU_WRITE_FRAME;			//MODBUS RTU master Write Reg Frame

//预置多个保持寄存器,主机写从机多个寄存器的数据帧
typedef  struct
{
u8	addr;				//地址 address
u8	fun;				//功能码 function
BIG_U16	StartReg;		//数据起始地址
BIG_U16	RegNum;			//寄存器数量
u8	DataLen;			//数据长度
u8	DataBuff[2];		//寄存器的值
} MRTU_WRITE_MULT_FRAME;

//预置多个保持寄存器后返回数据帧,从机返回主机的数据帧
typedef  struct
{
u8	addr;				//地址 address
u8	fun;				//功能码 function
BIG_U16	StartReg;		//数据起始地址
BIG_U16	RegNum;			//寄存器数量
BIG_U16 crc16;			//CRC校验值
} MRTU_WRIT_EMULT_RFRAME;

//读取从机返回数据帧格式,从机返回给主机的数据帧
typedef  struct
{
u8	addr;				//地址 address
u8	fun;				//功能码 function
u8	DataLen;			//数据长度
u8	DataBuff[2];		//数据区,CRC16放在最后结尾处
//MRTU_REG16	CRC16;	//CRC16
} MRTU_RETURN_FRAME;	//MODBUS RTU master Read Reg Frame

//从机返回的异常数据帧,从机返回的异常数据帧
typedef  struct
{
u8	addr;				//地址 address
u8	ErrorFun;			//错误功能码 function+0x80
u8	unu;				//异常码
u8	crc16H;				//CRC16放在最后结尾处
u8	crc16L;				//CRC16放在最后结尾处
} MRTU_UNU_FRAME;

//从机数据包解析后的相关信息
typedef struct
{
u8	SlaveAddr;	//主机发送的从机地址
u8 	RegNum;		//主机需要读取从机的寄存器数量
u8	fun;		//主机发送给从机的功能码
u16 StartReg;	//主机需要读写的从机寄存器地址
} MRTU_SLAVE_INFO;

//异常码定义
typedef enum
{
MRTU_UNUS1		=	0x01,	//异常码1,无效的操作码
MRTU_UNUS2		=	0x02,	//异常码2,无效的数据地址
MRTU_UNUS3		=	0x03,	//异常码3,无效的数据值
MRTU_UNUS4		=	0x04,	//异常码4,无效操作
MRTU_UNUS5		=	0x05,	//异常码5
MRTU_UNUS6		=	0x06,	//异常码6
} MRTU_UNUS;

//错误状态
typedef enum
{
MRTU_OK 				= 	0,		//OK
MRTU_TIME_OUT 			= 	1,		//超时
MRTU_OVER_ERROR 		= 	2,		//溢出
MRTU_CRC_ERROR			=	3,		//CRC错误
MRTU_ADDR_ERROR			=	4,		//地址错误,返回地址不一致
MRTU_REG_ERROR			=	5,		//寄存器地址错误,返回寄存器地址不一致
MRTU_FUNR_ERROR			=	6,		//功能码错误,返回功能码不一致或者不支持的功能码
MRTU_HANDLE_ERROR		=	7,		//通信回调接口错误,或缓冲区错误
MRTU_REGN_ERROR			=	8,		//寄存器数量错误
MRTU_LEN_ERROR			=	9,		//返回数据长度错误
MRTU_WRITE_ERROR		=	10,		//写寄存器错误,写入与读取不一致
MRTU_SEND_ERROR			=	11,		//发送数据失败
MRTU_READ_ERROR			=	12,		//读取数据失败
MRTU_UNUS1_ERROR		=	0x81,	//异常码1,无效的操作码
MRTU_UNUS2_ERROR		=	0x82,	//异常码2,无效的数据地址
MRTU_UNUS3_ERROR		=	0x83,	//异常码3,无效的数据值
MRTU_UNUS4_ERROR		=	0x84,	//异常码4,无效操作
MRTU_UNUS5_ERROR		=	0x85,	//异常码5
MRTU_UNUS6_ERROR		=	0x86,	//异常码6
MRTU_OTHER_ERROR = 0xff
} MRTU_ERROR;

//发送回调函数定义
typedef bool(*MODBUS_SendDataCollBack)(BYTE *, DWORD);			//发送缓冲区与发送数据大小;返回:发送成功返回true,发送失败返回false
//接收回调函数定义
typedef bool(*MODBUS_ReadDataCollBack)(BYTE *, DWORD *);			//接收缓冲区与接收数据长度;返回:读取成功返回true,读取失败,接口错误返回false,返回true后数据长度为0算作超时,返回false一般都是接口错误

class MODBUS_RTU
{
private:
MODBUS_SendDataCollBack pSendDataCollBack;	//发送回调函数指针
MODBUS_ReadDataCollBack pReadDataCollBack;	//接收回调函数指针
u16 usMBCRC16(u8 * pucFrame, u16 usLen);	//crc计算

public:
//数据包打包与解包接口-不涉及到数据发送与接收
bool ReadMultRegPack(BYTE *pPackBuff, DWORD *pPackLen, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum, char **pError);
MRTU_ERROR ReadMultRegUnpack(BYTE *pPackBuff, DWORD PackLen, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[], char **pError);
bool WriteOnetRegPack(BYTE *pPackBuff, DWORD *pPackLen, u8 SlaveAddr, u16 RegAddr, u16 RegData, char **pError);
MRTU_ERROR WriteOneRegUnpack(BYTE *pPackBuff, DWORD PackLen, u8 SlaveAddr, u16 RegAddr, u16 RegData, char **pError);
bool WriteMultRegPack(BYTE *pPackBuff, DWORD *pPackLen, u8 SlaveAddr, u16 RegAddr, u16 pRegData[], u8 RegNum, char **pError);
MRTU_ERROR WriteMultRegUnpack(BYTE *pPackBuff, DWORD PackLen, u8 SlaveAddr, u16 RegAddr, u8 RegNum, char **pError);

//带回调模式通信接口初始化
//pSendDataCollBack:发送回调函数指针;pReadDataCollBack:接收回调函数指针;TimeOut:接收超时时间
void MODBUS_RTU::InterfaceInit(MODBUS_SendDataCollBack pSendDataCollBack, MODBUS_ReadDataCollBack pReadDataCollBack)
{
this->pSendDataCollBack = pSendDataCollBack;	//发送回调函数指针
this->pReadDataCollBack = pReadDataCollBack;	//接收回调函数指针
}
MRTU_ERROR ReadMultReg(READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[], char **pError);
MRTU_ERROR WriteOnetReg(u8 SlaveAddr, u16 RegAddr, u16 RegData, char **pError);
MRTU_ERROR WriteMultReg(u8 SlaveAddr, u16 RegAddr, u16 pRegData[], u8 RegNum, char **pError);

//构造函数
MODBUS_RTU()
{
this->pSendDataCollBack = nullptr;
this->pReadDataCollBack = nullptr;
}

//析构函数
~MODBUS_RTU()
{

}
} ;

#endif /*_MODBUS_RTU_H_*/


//托管回调定义

delegate bool ProcessDelegateSend(BYTE *, DWORD);	//定义发送数据回调函数托管
delegate bool ProcessDelegateRead(BYTE *, DWORD *);	//定义发接收据回调函数托管
ProcessDelegateSend ^SendCallBack;					//声明发送回调函数托管
ProcessDelegateRead ^ReadCallBack;					//声明接收回调函数托管
this->SendCallBack = gcnew ProcessDelegateSend(this, &温湿度采集::Form1::Uart_Send);
this->ReadCallBack = gcnew ProcessDelegateRead(this, &温湿度采集::Form1::Uart_WaitAndRead);


//接口定义

//串口发送函数-用于回调
bool Uart_Send(BYTE *pData, DWORD DataLen)
{
this->mUart->UART_ClearRxBuff(this->mHandle);	//清空接收缓冲区
this->mUart->UART_ClearTxBuff(this->mHandle);	//清空发送缓冲区
return this->mUart->UART_SendData(this->mHandle, pData, DataLen);	//调用串口发送数据
}

//串口接收函数-用于回调
bool Uart_WaitAndRead(BYTE *pData, DWORD *pDataLen)
{
DWORD len;

len = UartWait(20, 500);
if (len)
{
if (len > MODBUS_RTU_PACK_MAX_SIZE) len = MODBUS_RTU_PACK_MAX_SIZE;							//必须限制数据包大小
if (this->mUart->UART_ReadData(this->mHandle, pData, len) <= 0)							//读取串口接收到的数据
{
*pDataLen = 0;
return false;	//通信接口错误
}
else
{
*pDataLen = len;
return true;	//读取成功了
}
}
else
{
*pDataLen = 0;		//长度为0,没有读取到数据
return true;		//通信接口没有发生异常
}
}

//等待串口接收完成
DWORD UartWait(WORD ByteTimeOut, WORD RxTimeOut)
{
DWORD cnt = 0;
DWORD i, j = RxTimeOut / ByteTimeOut + 1;

for (i = 0; i < j;i ++)
{
cnt = this->mUart->UART_GetRxCnt(this->mHandle);
Sleep(ByteTimeOut);
if ((cnt > 0) && cnt == (this->mUart->UART_GetRxCnt(this->mHandle)))
{
return cnt;
}
}

return 0;
}


//初始化回调接口道modbus-RTU

IntPtr pvFun1, pvFun2;

pvFun1 = Marshal::GetFunctionPointerForDelegate(this->SendCallBack);//获取发送托管的回调指针
pvFun2 = Marshal::GetFunctionPointerForDelegate(this->ReadCallBack);//获取接收托管的回调指针
//初始化Modbus-rtu的回调函数指针
this->mModbus->InterfaceInit((MODBUS_SendDataCollBack)pvFun1.ToInt32(), (MODBUS_ReadDataCollBack)pvFun2.ToInt32());


//串口数据打包方式读取数据,只负责数据打包,发送接口自己随意定义

//串口数据打包方式读取寄存器测试
this->mModbus->ReadMultRegPack(PackBuff, &len, HOLD_REG, 1, 0, 3, &pError);		//读取多个寄存器打包
this->mUart->UART_ClearRxBuff(this->mHandle);	//清空接收缓冲区
this->mUart->UART_ClearTxBuff(this->mHandle);	//清空发送缓冲区
if (this->mUart->UART_SendData(this->mHandle, PackBuff, len) == false)
{
sprintf_s(this->pShowBuff, 1024 - 1, "串口发送失败,关闭串口重试!");
Sleep(1000);
isUartInit = false;
}
else
{
cnt = UartWait(20, 500);
if (cnt > 3)
{
if (cnt > 120) cnt = 120;
this->mUart->UART_ReadData(this->mHandle, UartRxBuff, cnt);						//读取串口接收到的数据
mError = this->mModbus->ReadMultRegUnpack(UartRxBuff, cnt, HOLD_REG, 1, 0, 3, RegData, &pError);	//读取单个寄存器数据解包
if (mError == MRTU_OK)
{
Temp = (short int)RegData[1];
Hump = RegData[2];
sprintf_s(this->pShowBuff, 1024 - 1, "温度1:%d.%d℃\t     湿度1:%d.%d%%RH", Temp/10,abs(Temp)%10, Hump/10, Hump % 10);
}
else
{
sprintf_s(this->pShowBuff, 1024 - 1, "数据解析有误,错误:%s!", pError);
}
}
else
{
sprintf_s(this->pShowBuff, 1024 - 1, "接收数据超时!");
}
}
Sleep(1000);



//回调方式读取,会简单很多

//回调方式读取寄存器测试
if (this->mModbus->ReadMultReg(HOLD_REG, 1, 0, 3, RegData, &pError) == MRTU_OK)
{
Temp = (short int)RegData[1];
Hump = RegData[2];
sprintf_s(this->pShowBuff, 1024 - 1, "温度:%d.%d℃\t     湿度:%d.%d%%RH", Temp / 10, abs(Temp) % 10, Hump / 10, Hump % 10);
}
else
{
sprintf_s(this->pShowBuff, 1024 - 1, "读取失败,错误:%s!", pError);
}
Sleep(1000);




测试用的form.h程序

#pragma once
#include "user/uart.h"
#include "user/MODBUS_RTU.h"
#include "user/userlib.h"
#include "windows.h"
#include "stdio.h"

namespace 温湿度采集 {

using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
using namespace System::Threading;
using namespace System::Runtime::InteropServices;

/// <summary>
/// Form1 摘要
/// </summary>
public ref class Form1 : public System::Windows::Forms::Form
{

private: System::Windows::Forms::Timer^ timer1;
private: System::Threading::Thread ^UartThread; //数据处理线程
public:

delegate bool ProcessDelegateSend(BYTE *, DWORD); //定义发送数据回调函数托管 delegate bool ProcessDelegateRead(BYTE *, DWORD *); //定义发接收据回调函数托管 ProcessDelegateSend ^SendCallBack; //声明发送回调函数托管 ProcessDelegateRead ^ReadCallBack; //声明接收回调函数托管
UART *mUart;
HANDLE mHandle;
MODBUS_RTU *mModbus;
char *pShowBuff;

Form1(void) //构造函数
{
InitializeComponent();
//
//TODO: 在此处添加构造函数代码
//
this->pShowBuff = new char[1024];
this->pShowBuff[0] = 0;
this->mUart = new UART;
this->mModbus = new MODBUS_RTU;
//数据处理线程
this->UartThread = gcnew Thread(gcnew ThreadStart(this, &温湿度采集::Form1::UartDataThread));
this->UartThread->IsBackground = TRUE; //将读取线程设置为后台线程,这样在UI线程关闭后可以自动退出
this->UartThread->Start();

this->SendCallBack = gcnew ProcessDelegateSend(this, &温湿度采集::Form1::Uart_Send); this->ReadCallBack = gcnew ProcessDelegateRead(this, &温湿度采集::Form1::Uart_WaitAndRead);
}

//串口发送函数-用于回调 bool Uart_Send(BYTE *pData, DWORD DataLen) { this->mUart->UART_ClearRxBuff(this->mHandle); //清空接收缓冲区 this->mUart->UART_ClearTxBuff(this->mHandle); //清空发送缓冲区 return this->mUart->UART_SendData(this->mHandle, pData, DataLen); //调用串口发送数据 } //串口接收函数-用于回调 bool Uart_WaitAndRead(BYTE *pData, DWORD *pDataLen) { DWORD len; len = UartWait(20, 500); if (len) { if (len > MODBUS_RTU_PACK_MAX_SIZE) len = MODBUS_RTU_PACK_MAX_SIZE; //必须限制数据包大小 if (this->mUart->UART_ReadData(this->mHandle, pData, len) <= 0) //读取串口接收到的数据 { *pDataLen = 0; return false; //通信接口错误 } else { *pDataLen = len; return true; //读取成功了 } } else { *pDataLen = 0; //长度为0,没有读取到数据 return true; //通信接口没有发生异常 } } //等待串口接收完成 DWORD UartWait(WORD ByteTimeOut, WORD RxTimeOut) { DWORD cnt = 0; DWORD i, j = RxTimeOut / ByteTimeOut + 1; for (i = 0; i < j;i ++) { cnt = this->mUart->UART_GetRxCnt(this->mHandle); Sleep(ByteTimeOut); if ((cnt > 0) && cnt == (this->mUart->UART_GetRxCnt(this->mHandle))) { return cnt; } } return 0; }
//串口数据处理线程
void UartDataThread(void)
{
bool isUartInit = false; //串口初始化状态
DWORD Error;
WCHAR ComName[16];
DWORD BaudRate;
BYTE UartRxBuff[128];
int cnt = 0;
DWORD len;
BYTE PackBuff[128];
this->mHandle = 0;
MRTU_ERROR mError;
WORD RegData[8];
short int Temp; //温度
WORD Hump;
char *pError;
IntPtr pvFun1, pvFun2; pvFun1 = Marshal::GetFunctionPointerForDelegate(this->SendCallBack);//获取发送托管的回调指针 pvFun2 = Marshal::GetFunctionPointerForDelegate(this->ReadCallBack);//获取接收托管的回调指针 //初始化Modbus-rtu的回调函数指针 this->mModbus->InterfaceInit((MODBUS_SendDataCollBack)pvFun1.ToInt32(), (MODBUS_ReadDataCollBack)pvFun2.ToInt32());
while (1)
{
while (isUartInit == false)
{
if (this->mHandle != 0)
{
this->mUart->UART_Close(this->mHandle);
this->mHandle = 0;//串口已经关闭了,句柄无效
}
USER_LIB.GetStringToIniFile(L"\\config.ini", L"配置", L"串口", L"COM1", ComName, 16 - 1);//读取字符串配置
BaudRate = USER_LIB.GetIntToIniFile(L"\\config.ini", L"配置", L"波特率", 9600);
this->mHandle = this->mUart->UART_Init(ComName, BaudRate, 1024, &Error);//初始化串口
if (this->mHandle == 0)
{
isUartInit = false;
sprintf_s(this->pShowBuff, 1024 - 1, "初始化串口失败,错误:%d!", Error);
Sleep(2000);
}
else //串口初始化成功了
{
isUartInit = true;
}
}

//串口已经初始化完成了

//串口数据打包方式读取寄存器测试 this->mModbus->ReadMultRegPack(PackBuff, &len, HOLD_REG, 1, 0, 3, &pError); //读取多个寄存器打包 this->mUart->UART_ClearRxBuff(this->mHandle); //清空接收缓冲区 this->mUart->UART_ClearTxBuff(this->mHandle); //清空发送缓冲区 if (this->mUart->UART_SendData(this->mHandle, PackBuff, len) == false) { sprintf_s(this->pShowBuff, 1024 - 1, "串口发送失败,关闭串口重试!"); Sleep(1000); isUartInit = false; } else { cnt = UartWait(20, 500); if (cnt > 3) { if (cnt > 120) cnt = 120; this->mUart->UART_ReadData(this->mHandle, UartRxBuff, cnt); //读取串口接收到的数据 mError = this->mModbus->ReadMultRegUnpack(UartRxBuff, cnt, HOLD_REG, 1, 0, 3, RegData, &pError); //读取单个寄存器数据解包 if (mError == MRTU_OK) { Temp = (short int)RegData[1]; Hump = RegData[2]; sprintf_s(this->pShowBuff, 1024 - 1, "温度1:%d.%d℃\t 湿度1:%d.%d%%RH", Temp/10,abs(Temp)%10, Hump/10, Hump % 10); } else { sprintf_s(this->pShowBuff, 1024 - 1, "数据解析有误,错误:%s!", pError); } } else { sprintf_s(this->pShowBuff, 1024 - 1, "接收数据超时!"); } } Sleep(1000);

//回调方式读取寄存器测试
if (this->mModbus->ReadMultReg(HOLD_REG, 1, 0, 3, RegData, &pError) == MRTU_OK)
{
Temp = (short int)RegData[1];
Hump = RegData[2];
sprintf_s(this->pShowBuff, 1024 - 1, "温度:%d.%d℃\t 湿度:%d.%d%%RH", Temp / 10, abs(Temp) % 10, Hump / 10, Hump % 10);
}
else
{
sprintf_s(this->pShowBuff, 1024 - 1, "读取失败,错误:%s!", pError);
}
Sleep(1000);

//回调方式写入单个寄存器测试
if (this->mModbus->WriteOnetReg(1, 0, 123, &pError) == MRTU_OK)
{
sprintf_s(this->pShowBuff, 1024 - 1, "写入寄存器0成功");
}
else
{
sprintf_s(this->pShowBuff, 1024 - 1, "写入寄存器0错误:%s!", pError);
}

Sleep(1000);
//回调方式写入多个寄存器测试
RegData[0] = 10;
RegData[1] = 11;
RegData[2] = 12;
if (this->mModbus->WriteMultReg(1, 10, RegData, 3,&pError) == MRTU_OK)
{
sprintf_s(this->pShowBuff, 1024 - 1, "写入寄存器10 11 12成功");
}
else
{
sprintf_s(this->pShowBuff, 1024 - 1, "写入寄存器10 11 12错误:%s!", pError);
}

Sleep(1000);

//串口数据打包方式写入一个寄存器测试
this->mModbus->WriteOnetRegPack(PackBuff, &len, 1, 8, 1234, &pError);
this->mUart->UART_ClearRxBuff(this->mHandle); //清空接收缓冲区
this->mUart->UART_ClearTxBuff(this->mHandle); //清空发送缓冲区
this->mUart->UART_SendData(this->mHandle, PackBuff, len);
cnt = UartWait(20, 500);
if (cnt > 0)
{
this->mUart->UART_ReadData(this->mHandle, UartRxBuff, cnt); //读取串口接收到的数据
mError = this->mModbus->WriteOneRegUnpack(UartRxBuff, cnt, 1, 8, 1234, &pError); //读取单个寄存器数据解包
if (mError == MRTU_OK)
{

sprintf_s(this->pShowBuff, 1024 - 1, "写入成功!");
}
else
{
sprintf_s(this->pShowBuff, 1024 - 1, "数据写入错误:%s!", pError);
}
}
else
{
sprintf_s(this->pShowBuff, 1024 - 1, "接收数据超时!");
}

Sleep(1000);

//串口数据打包方式写入多个寄存器测试
RegData[0] = 1;
RegData[1] = 2;
RegData[2] = 3;
RegData[3] = 4;
this->mModbus->WriteMultRegPack(PackBuff, &len, 1, 4, RegData, 4, &pError);
this->mUart->UART_ClearRxBuff(this->mHandle); //清空接收缓冲区
this->mUart->UART_ClearTxBuff(this->mHandle); //清空发送缓冲区
this->mUart->UART_SendData(this->mHandle, PackBuff, len);
cnt = UartWait(20, 500);
if (cnt > 0)
{
this->mUart->UART_ReadData(this->mHandle, UartRxBuff, cnt); //读取串口接收到的数据
mError = this->mModbus->WriteMultRegUnpack(UartRxBuff, cnt, 1, 4, 4, &pError); //读取单个寄存器数据解包
if (mError == MRTU_OK)
{

sprintf_s(this->pShowBuff, 1024 - 1, "写入成功!");
}
else
{
sprintf_s(this->pShowBuff, 1024 - 1, "数据写入错误:%s!", pError);
}
}
else
{
sprintf_s(this->pShowBuff, 1024 - 1, "接收数据超时!");
}

Sleep(1000);

}
}

protected:
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
~Form1() //析构函数
{
if (components)
{
delete components;
delete this->mUart;
delete this->pShowBuff;
delete this->mModbus;
}
}
private: System::Windows::Forms::Label^ label1;
private: System::ComponentModel::IContainer^ components;
protected:

private:
/// <summary>
/// 必需的设计器变量。
/// </summary>

#pragma region Windows Form Designer generated code
/// <summary>
/// 设计器支持所需的方法 - 不要
/// 使用代码编辑器修改此方法的内容。
/// </summary>
void InitializeComponent(void)
{
this->components = (gcnew System::ComponentModel::Container());
this->label1 = (gcnew System::Windows::Forms::Label());
this->timer1 = (gcnew System::Windows::Forms::Timer(this->components));
this->SuspendLayout();
//
// label1
//
this->label1->AutoSize = true;
this->label1->Font = (gcnew System::Drawing::Font(L"微软雅黑", 15.75F, System::Drawing::FontStyle::Regular, System::Drawing::GraphicsUnit::Point,
static_cast<System::Byte>(134)));
this->label1->Location = System::Drawing::Point(12, 9);
this->label1->Name = L"label1";
this->label1->Size = System::Drawing::Size(59, 28);
this->label1->TabIndex = 0;
this->label1->Text = L"温度:";
//
// timer1
//
this->timer1->Enabled = true;
this->timer1->Interval = 300;
this->timer1->Tick += gcnew System::EventHandler(this, &Form1::timer1_Tick);
//
// Form1
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 12);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->ClientSize = System::Drawing::Size(444, 49);
this->Controls->Add(this->label1);
this->MaximizeBox = false;
this->Name = L"Form1";
this->Opacity = 0.9;
this->StartPosition = System::Windows::Forms::FormStartPosition::CenterScreen;
this->Text = L"温度传感器";
this->FormClosed += gcnew System::Windows::Forms::FormClosedEventHandler(this, &Form1::Form1_FormClosed);
this->ResumeLayout(false);
this->PerformLayout();

}
#pragma endregion
//程序退出
private: System::Void Form1_FormClosed(System::Object^ sender, System::Windows::Forms::FormClosedEventArgs^ e) {
exit(0); //强制退出
}
//定时器事件
private: System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e) {
this->label1->Text = CharToString(this->pShowBuff);
}
};
}




完整的测试工程:http://download.csdn.net/detail/cp1300/9690505
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  VC++ MODBUS-RTU