单片机modebus RTU通信实现,采用C语言,可适用于单片机,VC,安卓等(转)

2019-04-15 16:10发布

源:单片机modebus RTU通信实现,采用C语言,可适用于单片机,VC,安卓等 //modebus_rtu.c /************************************************************************************************************* * 文件名: MODEBUS_RTU.c * 功能: MODEBUS_RTU通信协议层 * 作者: cp1300@139.com * 创建时间: 2014-03-24 * 最后修改时间:2014-11-17 * 详细: MODEBUS RTU通信协议层 *************************************************************************************************************/ #include "system.h" #include "usart.h" #include "delay.h" #include "MODEBUS_RTU.h" //调试开关 #define MODEBUS_RTU_DBUG 1 #if MODEBUS_RTU_DBUG #include "system.h" #define modebus_debug(format,...) uart_printf(format,##__VA_ARGS__) #else #define modebus_debug(format,...) / / #endif //MODEBUS_RTU_DBUG /************************************************************************************************************************* * 函数 : bool MODEBUS_Init(MODEBUS_HANDLE *pHandle, u8 UartCh, u32 BaudRate, u8 *pRxBuff,u8 *pTxBuff, u32 RxBuffSize, u32 TimeOut) * 功能 : MODEBUS 初始化 * 参数 : pHandle:当前初始化的modebus句柄,UartCh:使用的串口通道;BaudRate:使用的波特率;pRxBuff:接收缓冲区指针; RxBuffSize:接收缓冲区大小;pTxBuff:发送缓冲区指针;TimeOut:接收超时,单位ms * 返回 : FALSE:初始化失败;TRUE:初始化成功 * 依赖 : 串口 * 作者 : cp1300@139.com * 时间 : 2014-09-25 * 最后修改时间 : 2014-11-10 * 说明 : 收发缓冲区可以与发送缓冲区使用同一缓冲区 发送缓冲区必须大于最大数据包大小,否则会出现内存溢出 *************************************************************************************************************************/ bool MODEBUS_Init(MODEBUS_HANDLE *pHandle, u8 UartCh, u32 BaudRate, u8 *pRxBuff,u8 *pTxBuff, u32 RxBuffSize, u32 TimeOut) { if(pHandle == NULL) return FALSE; pHandle->TxPacketNum = 0; //发送数据包计数 pHandle->RxPacketNum = 0; //接收数据包计数 pHandle->ErrorNum = 0; //通信错误计数 pHandle->ReturnTime = 0; //数据返回时间 //设置串口 if(MODEBUS_UartInit(UartCh, BaudRate) == FALSE) //初始化串口 { pHandle->UartCh = 0xff; //通道无效 pHandle->pRxBuff = pHandle->pTxBuff = NULL; //缓冲区无效 pHandle->RxBuffSize = 0; //缓冲区大小为0 } MODEBUS_SetRxBuff(UartCh, pRxBuff, RxBuffSize); MODEBUS_DisableRx(UartCh); //关闭串口接收 pHandle->UartCh = UartCh; //通道 pHandle->pRxBuff = pRxBuff; pHandle->pTxBuff = pTxBuff; //缓冲区 pHandle->RxBuffSize = RxBuffSize; //缓冲区大小 if(TimeOut == 0) TimeOut = 1; pHandle->TimeOut = TimeOut; pHandle->BaudRate = BaudRate; return TRUE; } #if(MODEBUS_RTU_HOST) //开启主机模式 /************************************************************************************************************************* * 函数 : MRTU_ERROR MODEBUS_HOST_ReadReg(MODEBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u16 *pRegData) * 功能 : 主机读取从机一个指定寄存器 * 参数 : pHandle:modebus句柄;RegType:读取的寄存器类型;SlaveAddr:从机地址;RegAddr:需读取的寄存器地址;pRegData:寄存器的值 * 返回 : MRTU_ERROR:通信状态 * 依赖 : 底层通信驱动 * 作者 : cp1300@139.com * 时间 : 2014-03-24 * 最后修改时间 : 2014-11-16 * 说明 : MOUEBUS RTU读取数据,读取一个寄存器 输入输出的数据都为小端模式 *************************************************************************************************************************/ MRTU_ERROR MODEBUS_HOST_ReadReg(MODEBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u16 *pRegData) { MRTU_READ_FRAME *pFrame; //发送数据帧格式 MRTU_RETURN_FRAME *pReFrame; //返回数据帧格式 MRTU_UNU_FRAME *pUnuFrame; //返回的异常数据帧格式 u16 crc16; u16 cnt1, cnt2=0; //接收数据计数器 u16 TimeOut; u16 TimeDelay = 0; //用于计算数据接收延时 if(pHandle == NULL) return MRTU_HANDLE_ERROR; //句柄无效 TimeOut = pHandle->TimeOut/10+1; //超时初值 pFrame = (MRTU_READ_FRAME *)pHandle->pTxBuff; //数据结构填充 pFrame->addr = SlaveAddr; //从机地址 pFrame->fun = (u8)RegType; //功能码,读取 pFrame->StartReg = SWAP16(RegAddr); //寄存器起始地址 pFrame->RegNum = SWAP16(1); //需要读取的寄存器数量,1 crc16 = usMBCRC16(pHandle->pTxBuff, 6); //计算CRC16 pFrame->CRC16 = crc16; //crc16 #if MODEBUS_RTU_DBUG { u16 i; modebus_debug(" <- MODEBUS RTU TXD(%dB)(CRC:0x%04X): ",8,crc16); for(i = 0;i < 8;i ++) { modebus_debug("0x%02X ",pHandle->pTxBuff[i]); } modebus_debug(" "); } #endif //MODEBUS_RTU_DBUG MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, 6+2); //发送数据 MODEBUS_ClearRxCnt(pHandle->UartCh); //清除接收缓冲区 MODEBUS_GetDataOver(pHandle->UartCh); //清除溢出标志 MODEBUS_EnableRx(pHandle->UartCh); //使能接收 //等待数据返回 do { cnt1 = cnt2; MODEBUS_Delay10MS(); //延时10ms if(MODEBUS_GetDataOver(pHandle->UartCh) == SET) //查看是否发生溢出 { MODEBUS_DisableRx(pHandle->UartCh); //关闭接收 MODEBUS_ClearRxCnt(pHandle->UartCh); //清除接收缓冲区 modebus_debug("接收溢出! "); return MRTU_OVER_ERROR; //返回溢出错误 } cnt2 = MODEBUS_GetDataCnt(pHandle->UartCh); //获取接收数据计数器 if(cnt1 == cnt2) //完成接收数据了,退出等待 { TimeOut --; if((cnt1 > 0)&&(TimeOut!=0)) TimeOut=1; //数据接收完毕,退出 TimeDelay ++; } else { TimeOut = pHandle->TimeOut/10+1; //有数据,计数器复位 } }while(TimeOut); TimeDelay -= 1; //等待完毕 MODEBUS_DisableRx(pHandle->UartCh); //关闭接收 MODEBUS_ClearRxCnt(pHandle->UartCh); //清除接收缓冲区 if(cnt1 == 0) //没有接收到数据 { modebus_debug("接收超时(%dmS)! ",TimeDelay*10); pHandle->ReturnTime = 0xffff; //接收数据超时 return MRTU_TIME_OUT; //返回超时 } pHandle->ReturnTime = TimeDelay*10; //数据返回时间 #if MODEBUS_RTU_DBUG { u16 i; modebus_debug(" -> MODEBUS RTU RXD(%dB)(ping:%dmS): ",cnt1,TimeDelay*10); for(i = 0;i < cnt1;i ++) { modebus_debug("0x%02X ", pHandle->pRxBuff[i]); } modebus_debug(" "); } #endif //MODEBUS_RTU_DBUG pReFrame = (MRTU_RETURN_FRAME *)pHandle->pRxBuff; //检查地址 if(pReFrame->addr != SlaveAddr) { modebus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02X ",SlaveAddr, pReFrame->addr); return MRTU_ADDR_ERROR; } //对接受的数据进行CRC校验 crc16 = usMBCRC16(pHandle->pRxBuff, cnt1-2);//计算CRC16 if((pHandle->pRxBuff[cnt1-1] != (crc16 >> 8)) || (pHandle->pRxBuff[cnt1-2] != (crc16 & 0xff))) { modebus_debug("CRC校验错误,计算CRC为:0x%04X,返回CRC为:0x%04X ",crc16,(u16)(pHandle->pRxBuff[cnt1-2]<<8)|pHandle->pRxBuff[cnt1-1]); return MRTU_CRC_ERROR; //返回CRC校验错误 } //返回的功能码不一致 if(pReFrame->fun != (u8)RegType) { pUnuFrame = (MRTU_UNU_FRAME *)pHandle->pRxBuff; //异常数据帧 if(pUnuFrame->ErrorFun == ((u8)RegType|0x80)) //返回有异常 { modebus_debug("返回异常,异常码%d ", pUnuFrame->unu); switch(pUnuFrame->unu) { case 1: return MRTU_UNUS1_ERROR; //异常码1 case 2: return MRTU_UNUS2_ERROR; //异常码2 case 3: return MRTU_UNUS3_ERROR; //异常码3 case 4: return MRTU_UNUS4_ERROR; //异常码4 case 5: return MRTU_UNUS5_ERROR; //异常码5 case 6: return MRTU_UNUS6_ERROR; //异常码6 default: return MRTU_OTHER_ERROR; } } else { modebus_debug("返回错误,返回功能码为0x%02X ", pReFrame->fun); return MRTU_FUNR_ERROR; } } //判断数据长度 if(pReFrame->DataLen != 2) { modebus_debug("返回数据长度错误,读取%d个寄存器,共%dB,只返回了%dB ",1, 1*2, pReFrame->DataLen); return MRTU_LEN_ERROR; //返回数据长度错误 } //获取返回的寄存器的值 *pRegData = pReFrame->DataBuff[0]; *pRegData <<= 8; *pRegData |= pReFrame->DataBuff[1]; return MRTU_OK; //返回成功 } /************************************************************************************************************************* * 函数 : MRTU_ERROR MODEBUS_HOST_ReadMultReg(MODEBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[]) * 功能 : 主机读取从机指定多个连续寄存器 * 参数 : pHandle:modebus句柄;RegType:读取的寄存器类型;SlaveAddr:从机地址;RegAddr:需读取的寄存器地址;RegNum:寄存器数量;pRegData:返回寄存器的值,至少为RegNum的2倍 返回的寄存器的值按照循序存放在pRegData中 * 返回 : MRTU_ERROR:通信状态 * 依赖 : 底层通信驱动 * 作者 : cp1300@139.com * 时间 : 2014-03-24 * 最后修改时间 : 2014-11-16 * 说明 : MOUEBUS RTU读取数据,读取一个寄存器 输入输出的数据都为小端模式 *************************************************************************************************************************/ MRTU_ERROR MODEBUS_HOST_ReadMultReg(MODEBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[]) { MRTU_READ_FRAME *pFrame; //发送数据帧格式 MRTU_RETURN_FRAME *pReFrame; //返回数据帧格式 MRTU_UNU_FRAME *pUnuFrame; //返回的异常数据帧格式 u16 crc16; u16 cnt1, cnt2=0; //接收数据计数器 u16 TimeOut; u16 TimeDelay = 0; //用于计算数据接收延时 u8 i; if(pHandle == NULL) return MRTU_HANDLE_ERROR; //句柄无效 TimeOut = pHandle->TimeOut/10+1; //超时初值 pFrame = (MRTU_READ_FRAME *)pHandle->pTxBuff; //数据结构填充 pFrame->addr = SlaveAddr; //从机地址 pFrame->fun = (u8)RegType; //功能码,读取 pFrame->StartReg = SWAP16(RegAddr); //寄存器起始地址 if((RegNum > 127) || (RegNum == 0)) return MRTU_REGN_ERROR; //寄存器数量错误 pFrame->RegNum = SWAP16(RegNum); //需要读取的寄存器数量 crc16 = usMBCRC16(pHandle->pTxBuff, 6); //计算CRC16 pFrame->CRC16 = crc16; //crc16 #if MODEBUS_RTU_DBUG { u16 i; modebus_debug(" <- MODEBUS RTU TXD(%dB)(CRC:0x%04X): ",8,crc16); for(i = 0;i < 8;i ++) { modebus_debug("0x%02X ",pHandle->pTxBuff[i]); } modebus_debug(" "); } #endif //MODEBUS_RTU_DBUG MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, 6+2); //发送数据 MODEBUS_ClearRxCnt(pHandle->UartCh); //清除接收缓冲区 MODEBUS_GetDataOver(pHandle->UartCh); //清除溢出标志 MODEBUS_EnableRx(pHandle->UartCh); //使能接收 //等待数据返回 do { cnt1 = cnt2; MODEBUS_Delay10MS(); //延时10ms if(MODEBUS_GetDataOver(pHandle->UartCh) == SET) //查看是否发生溢出 { MODEBUS_DisableRx(pHandle->UartCh); //关闭接收 MODEBUS_ClearRxCnt(pHandle->UartCh); //清除接收缓冲区 modebus_debug("接收溢出! "); return MRTU_OVER_ERROR; //返回溢出错误 } cnt2 = MODEBUS_GetDataCnt(pHandle->UartCh); //获取接收数据计数器 if(cnt1 == cnt2) //完成接收数据了,退出等待 { TimeOut --; if((cnt1 > 0)&&(TimeOut!=0)) TimeOut=1; //数据接收完毕,退出 TimeDelay ++; } else { TimeOut = pHandle->TimeOut/10+1; //有数据,计数器复位 } }while(TimeOut); TimeDelay -= 1; //等待完毕 MODEBUS_DisableRx(pHandle->UartCh); //关闭接收 MODEBUS_ClearRxCnt(pHandle->UartCh); //清除接收缓冲区 if(cnt1 == 0) //没有接收到数据 { modebus_debug("接收超时(%dmS)! ",TimeDelay*10); pHandle->ReturnTime = 0xffff; //接收数据超时 return MRTU_TIME_OUT; //返回超时 } pHandle->ReturnTime = TimeDelay*10; //数据返回时间 #if MODEBUS_RTU_DBUG { u16 i; modebus_debug(" -> MODEBUS RTU RXD(%dB)(ping:%dmS): ",cnt1,TimeDelay*10); for(i = 0;i < cnt1;i ++) { modebus_debug("0x%02X ", pHandle->pRxBuff[i]); } modebus_debug(" "); } #endif //MODEBUS_RTU_DBUG pReFrame = (MRTU_RETURN_FRAME *)pHandle->pRxBuff; //检查地址 if(pReFrame->addr != SlaveAddr) { modebus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02X ",SlaveAddr, pReFrame->addr); return MRTU_ADDR_ERROR; } //对接受的数据进行CRC校验 crc16 = usMBCRC16(pHandle->pRxBuff, cnt1-2);//计算CRC16 if((pHandle->pRxBuff[cnt1-1] != (crc16 >> 8)) || (pHandle->pRxBuff[cnt1-2] != (crc16 & 0xff))) { modebus_debug("CRC校验错误,计算CRC为:0x%04X,返回CRC为:0x%04X ",crc16,(u16)(pHandle->pRxBuff[cnt1-2]<<8)|pHandle->pRxBuff[cnt1-1]); return MRTU_CRC_ERROR; //返回CRC校验错误 } //返回的功能码不一致 if(pReFrame->fun != (u8)RegType) { pUnuFrame = (MRTU_UNU_FRAME *)pHandle->pRxBuff; //异常数据帧 if(pUnuFrame->ErrorFun == ((u8)RegType|0x80)) //返回有异常 { modebus_debug("返回异常,异常码%d ", pUnuFrame->unu); switch(pUnuFrame->unu) { case 1: return MRTU_UNUS1_ERROR; //异常码1 case 2: return MRTU_UNUS2_ERROR; //异常码2 case 3: return MRTU_UNUS3_ERROR; //异常码3 case 4: return MRTU_UNUS4_ERROR; //异常码4 case 5: return MRTU_UNUS5_ERROR; //异常码5 case 6: return MRTU_UNUS6_ERROR; //异常码6 default: return MRTU_OTHER_ERROR; } } else { modebus_debug("返回错误,返回功能码为0x%02X ", pReFrame->fun); return MRTU_FUNR_ERROR; } } //判断数据长度 if(pReFrame->DataLen != (RegNum*2)) { modebus_debug("返回数据长度错误,读取%d个寄存器,共%dB,只返回了%dB ",RegNum, RegNum*2, pReFrame->DataLen); 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]; } return MRTU_OK; //返回成功 } /************************************************************************************************************************* * 函数 : MRTU_ERROR MODEBUS_HOST_WriteReg(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u16 RegData) * 功能 : 主机写从机一个指定寄存器 * 参数 : pHandle:modebus句柄;SlaveAddr:从机地址;RegAddr:写寄存器地址;RegData:寄存器的值 * 返回 : MRTU_ERROR:通信状态 * 依赖 : 底层通信驱动 * 作者 : cp1300@139.com * 时间 : 2014-03-24 * 最后修改时间 : 2014-11-16 * 说明 : MOUEBUS RTU写从机一个保持寄存器 输入输出的数据都为小端模式 预置单个寄存器的发送与接收数据包格式完全一致,理论上发送与接收的数据都应该一致 *************************************************************************************************************************/ MRTU_ERROR MODEBUS_HOST_WriteReg(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u16 RegData) { MRTU_WRITE_FRAME *pFrame, *pReFrame;//发送数据帧格式 MRTU_UNU_FRAME *pUnuFrame; //返回的异常数据帧格式 u16 crc16; u16 cnt1, cnt2=0; //接收数据计数器 u16 TimeOut; u16 TimeDelay = 0; //用于计算数据接收延时 if(pHandle == NULL) return MRTU_HANDLE_ERROR; //句柄无效 TimeOut = pHandle->TimeOut/10+1; //超时初值 pFrame = (MRTU_WRITE_FRAME *)pHandle->pTxBuff; //数据结构填充 pFrame->addr = SlaveAddr; //从机地址 pFrame->fun = (u8)MRTU_FUN_WRITE; //功能码,预置单个寄存器 pFrame->StartReg = SWAP16(RegAddr); //寄存器起始地址 pFrame->RegData = SWAP16(RegData); //写入寄存器内容 pFrame->crc16 = usMBCRC16(pHandle->pTxBuff, 6); //计算CRC16 #if MODEBUS_RTU_DBUG { u16 i; modebus_debug(" <- MODEBUS RTU TXD(%dB)(CRC:0x%04X): ",8,crc16); for(i = 0;i < 8;i ++) { modebus_debug("0x%02X ",pHandle->pTxBuff[i]); } modebus_debug(" "); } #endif //MODEBUS_RTU_DBUG MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, 6+2); //发送数据 MODEBUS_ClearRxCnt(pHandle->UartCh); //清除接收缓冲区 MODEBUS_GetDataOver(pHandle->UartCh); //清除溢出标志 MODEBUS_EnableRx(pHandle->UartCh); //使能接收 //等待数据返回 do { cnt1 = cnt2; MODEBUS_Delay10MS(); //延时10ms if(MODEBUS_GetDataOver(pHandle->UartCh) == SET) //查看是否发生溢出 { MODEBUS_DisableRx(pHandle->UartCh); //关闭接收 MODEBUS_ClearRxCnt(pHandle->UartCh); //清除接收缓冲区 modebus_debug("接收溢出! "); return MRTU_OVER_ERROR; //返回溢出错误 } cnt2 = MODEBUS_GetDataCnt(pHandle->UartCh); //获取接收数据计数器 if(cnt1 == cnt2) //完成接收数据了,退出等待 { TimeOut --; if((cnt1 > 0)&&(TimeOut!=0)) TimeOut=1; //数据接收完毕,退出 TimeDelay ++; } else { TimeOut = pHandle->TimeOut/10+1; //有数据,计数器复位 } }while(TimeOut); TimeDelay -= 1; //等待完毕 MODEBUS_DisableRx(pHandle->UartCh); //关闭接收 MODEBUS_ClearRxCnt(pHandle->UartCh); //清除接收缓冲区 if(cnt1 == 0) //没有接收到数据 { modebus_debug("接收超时(%dmS)! ",TimeDelay*10); pHandle->ReturnTime = 0xffff; //接收数据超时 return MRTU_TIME_OUT; //返回超时 } pHandle->ReturnTime = TimeDelay*10; //数据返回时间 #if MODEBUS_RTU_DBUG { u16 i; modebus_debug(" -> MODEBUS RTU RXD(%dB)(ping:%dmS): ",cnt1,TimeDelay*10); for(i = 0;i < cnt1;i ++) { modebus_debug("0x%02X ", pHandle->pRxBuff[i]); } modebus_debug(" "); } #endif //MODEBUS_RTU_DBUG pReFrame = (MRTU_WRITE_FRAME *)pHandle->pRxBuff; //检查地址 if(pReFrame->addr != SlaveAddr) { modebus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02X ",SlaveAddr, pReFrame->addr); return MRTU_ADDR_ERROR; } //对接受的数据进行CRC校验 crc16 = usMBCRC16(pHandle->pRxBuff, cnt1-2);//计算CRC16 if((pHandle->pRxBuff[cnt1-1] != (crc16 >> 8)) || (pHandle->pRxBuff[cnt1-2] != (crc16 & 0xff))) { modebus_debug("CRC校验错误,计算CRC为:0x%04X,返回CRC为:0x%04X ",crc16,(u16)(pHandle->pRxBuff[cnt1-2]<<8)|pHandle->pRxBuff[cnt1-1]); return MRTU_CRC_ERROR; //返回CRC校验错误 } //返回的功能码不一致 if(pReFrame->fun != (u8)MRTU_FUN_WRITE) { pUnuFrame = (MRTU_UNU_FRAME *)pHandle->pRxBuff; //异常数据帧 if(pUnuFrame->ErrorFun == ((u8)MRTU_FUN_WRITE|0x80))//返回有异常 { modebus_debug("返回异常,异常码%d ", pUnuFrame->unu); switch(pUnuFrame->unu) { case 1: return MRTU_UNUS1_ERROR; //异常码1 case 2: return MRTU_UNUS2_ERROR; //异常码2 case 3: return MRTU_UNUS3_ERROR; //异常码3 case 4: return MRTU_UNUS4_ERROR; //异常码4 case 5: return MRTU_UNUS5_ERROR; //异常码5 case 6: return MRTU_UNUS6_ERROR; //异常码6 default: return MRTU_OTHER_ERROR; } } else { modebus_debug("返回错误,返回功能码为0x%02X ", pReFrame->fun); return MRTU_FUNR_ERROR; } } //判断数据是否写入 if(SWAP16(pReFrame->StartReg) != RegAddr) //返回的寄存器地址不一致 { modebus_debug("返回寄存器地址错误,写入寄存器%d,返回寄存器%d ",RegAddr, pReFrame->StartReg); return MRTU_REG_ERROR; //返回寄存器错误 } if(SWAP16(pReFrame->RegData) != RegData) { modebus_debug("数据写入错误,写入值:0x%04X,返回了:0x%04X ",RegData, pReFrame->RegData); return MRTU_WRITE_ERROR; //写入数据错误 } return MRTU_OK; //返回成功 } /************************************************************************************************************************* * 函数 : MRTU_ERROR MODEBUS_HOST_WriteMultReg(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[]) * 功能 : 主机写从机多个指定寄存器 * 参数 : pHandle:modebus句柄;SlaveAddr:从机地址;RegAddr:写寄存器地址;RegNum:寄存器数量, pRegData:需要写入的寄存器的值 写入寄存器的值按照循序排列,使用小端格式,大小必须为RegNum*2 * 返回 : MRTU_ERROR:通信状态 * 依赖 : 底层通信驱动 * 作者 : cp1300@139.com * 时间 : 2014-03-24 * 最后修改时间 : 2014-11-16 * 说明 : MOUEBUS RTU写从机一个保持寄存器 输入输出的数据都为小端模式 返回数据寄存器位置与寄存器数量 *************************************************************************************************************************/ MRTU_ERROR MODEBUS_HOST_WriteMultReg(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[]) { MRTU_WRITE_MULT_FRAME *pFrame; //发送数据帧格式 MRTU_WRIT_EMULT_RFRAME *pReFrame; //返回数据帧格式 MRTU_UNU_FRAME *pUnuFrame; //返回的异常数据帧格式 u16 crc16; u16 cnt1, cnt2=0; //接收数据计数器 u16 TimeOut; u16 TimeDelay = 0; //用于计算数据接收延时 u8 i; if(pHandle == NULL) return MRTU_HANDLE_ERROR; //句柄无效 TimeOut = pHandle->TimeOut/10+1; //超时初值 pFrame = (MRTU_WRITE_MULT_FRAME *)pHandle->pTxBuff; //数据结构填充 pFrame->addr = SlaveAddr; //从机地址 pFrame->fun = (u8)MRTU_FUN_MWRITE; //功能码,预置多个寄存器 pFrame->StartReg = SWAP16(RegAddr); //寄存器起始地址 if((RegNum > 127) || (RegNum == 0)) return MRTU_REGN_ERROR; //寄存器数量错误 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(pHandle->pTxBuff, 7+pFrame->DataLen); //计算CRC16,高低位对调过 pFrame->DataBuff[pFrame->DataLen] = crc16&0xff; //高位 pFrame->DataBuff[pFrame->DataLen+1]=crc16>>8; //低位