VC++实现的ModBus-RTU主机接口函数(采用回调方式)
2016-11-04 16:50
417 查看
计算机上面使用Modbus读取传感器或相关设备还是比较常用的,之前写的Modbus-RTU协议将串口封装到了协议栈内,使用的时候遇到短板了,比如我最新需要使用TCP来读取Modbus设备,就不好用了,通过回调函数可以很简单的解决这个问题。
//modbus-rtu.c
//modbus-rtu.h
//托管回调定义
//接口定义
//初始化回调接口道modbus-RTU
//串口数据打包方式读取数据,只负责数据打包,发送接口自己随意定义
//回调方式读取,会简单很多
测试用的form.h程序
完整的测试工程:http://download.csdn.net/detail/cp1300/9690505
//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
相关文章推荐
- ModBus RTU协议CRC校验方式最简实现
- ModBus RTU协议 16 位CRC校验方式最简实现
- android入门_采用android-async-http开源项目的GET方式或POST方式实现登陆案例
- Spring---->采用静态配置文件方式实现AOP
- 使用INDY TCP组件实现基于协议采用XML方式的文件传输
- 在windows平台上使用Qt和libmodbus库实现modbus主机功能--转载
- VC++实现解析快捷方式
- 即时通讯实现方式(采用openfire和asmack方式)
- 关于Python中一种回调方式的实现
- 宿主机为linux、windows分别实现VMware三种方式上网
- 异步实现方式一:异步回调
- java回调机制的实现方式
- python - 采用TDD的方式,实现计算器功能
- 09_android入门_采用android-async-http开源项目的GET方式或POST方式实现登陆案例
- 采用JSP+JavaBean的方式进行简单的实现用户的网页登陆实例
- Spring---->采用静态配置文件方式实现AOP
- portaudio回调方式实现录制任意长度的音频
- C++中实现回调机制的几种方式
- 采用Informaker和pbl中dw相结合的方式实现强大的自定义报表功能
- 服务器的实现(采用poll方式,只能在linux下运行