单片机modebus RTU通信实现,采用C语言,可适用于单片机,VC,安卓等(转)
源:单片机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 = ; //发送数据包计数
pHandle->RxPacketNum = ; //接收数据包计数
pHandle->ErrorNum = ; //通信错误计数
pHandle->ReturnTime = ; //数据返回时间
//设置串口
if(MODEBUS_UartInit(UartCh, BaudRate) == FALSE) //初始化串口
{
pHandle->UartCh = 0xff; //通道无效
pHandle->pRxBuff = pHandle->pTxBuff = NULL; //缓冲区无效
pHandle->RxBuffSize = ; //缓冲区大小为0
}
MODEBUS_SetRxBuff(UartCh, pRxBuff, RxBuffSize);
MODEBUS_DisableRx(UartCh); //关闭串口接收
pHandle->UartCh = UartCh; //通道
pHandle->pRxBuff = pRxBuff;
pHandle->pTxBuff = pTxBuff; //缓冲区
pHandle->RxBuffSize = RxBuffSize; //缓冲区大小
if(TimeOut == ) TimeOut = ;
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=; //接收数据计数器
u16 TimeOut;
u16 TimeDelay = ; //用于计算数据接收延时 if(pHandle == NULL) return MRTU_HANDLE_ERROR; //句柄无效
TimeOut = pHandle->TimeOut/+; //超时初值
pFrame = (MRTU_READ_FRAME *)pHandle->pTxBuff;
//数据结构填充
pFrame->addr = SlaveAddr; //从机地址
pFrame->fun = (u8)RegType; //功能码,读取
pFrame->StartReg = SWAP16(RegAddr); //寄存器起始地址
pFrame->RegNum = SWAP16(); //需要读取的寄存器数量,1
crc16 = usMBCRC16(pHandle->pTxBuff, ); //计算CRC16
pFrame->CRC16 = crc16; //crc16 #if MODEBUS_RTU_DBUG
{
u16 i; modebus_debug("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",,crc16);
for(i = ;i < ;i ++)
{
modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
}
modebus_debug("\r\n");
}
#endif //MODEBUS_RTU_DBUG MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, +); //发送数据
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("接收溢出!\r\n");
return MRTU_OVER_ERROR; //返回溢出错误
}
cnt2 = MODEBUS_GetDataCnt(pHandle->UartCh); //获取接收数据计数器
if(cnt1 == cnt2) //完成接收数据了,退出等待
{
TimeOut --;
if((cnt1 > )&&(TimeOut!=)) TimeOut=; //数据接收完毕,退出
TimeDelay ++;
}
else
{
TimeOut = pHandle->TimeOut/+; //有数据,计数器复位
}
}while(TimeOut);
TimeDelay -= ;
//等待完毕
MODEBUS_DisableRx(pHandle->UartCh); //关闭接收
MODEBUS_ClearRxCnt(pHandle->UartCh); //清除接收缓冲区
if(cnt1 == ) //没有接收到数据
{
modebus_debug("接收超时(%dmS)!\r\n",TimeDelay*);
pHandle->ReturnTime = 0xffff; //接收数据超时
return MRTU_TIME_OUT; //返回超时
}
pHandle->ReturnTime = TimeDelay*; //数据返回时间 #if MODEBUS_RTU_DBUG
{
u16 i; modebus_debug("\r\n-> MODEBUS RTU RXD(%dB)(ping:%dmS):\r\n",cnt1,TimeDelay*);
for(i = ;i < cnt1;i ++)
{
modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
}
modebus_debug("\r\n");
}
#endif //MODEBUS_RTU_DBUG pReFrame = (MRTU_RETURN_FRAME *)pHandle->pRxBuff;
//检查地址
if(pReFrame->addr != SlaveAddr)
{
modebus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02X\r\n",SlaveAddr, pReFrame->addr);
return MRTU_ADDR_ERROR;
}
//对接受的数据进行CRC校验
crc16 = usMBCRC16(pHandle->pRxBuff, cnt1-);//计算CRC16
if((pHandle->pRxBuff[cnt1-] != (crc16 >> )) || (pHandle->pRxBuff[cnt1-] != (crc16 & 0xff)))
{
modebus_debug("CRC校验错误,计算CRC为:0x%04X,返回CRC为:0x%04X\r\n",crc16,(u16)(pHandle->pRxBuff[cnt1-]<<)|pHandle->pRxBuff[cnt1-]);
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\r\n", pUnuFrame->unu);
switch(pUnuFrame->unu)
{
case : return MRTU_UNUS1_ERROR; //异常码1
case : return MRTU_UNUS2_ERROR; //异常码2
case : return MRTU_UNUS3_ERROR; //异常码3
case : return MRTU_UNUS4_ERROR; //异常码4
case : return MRTU_UNUS5_ERROR; //异常码5
case : return MRTU_UNUS6_ERROR; //异常码6
default: return MRTU_OTHER_ERROR;
}
}
else
{
modebus_debug("返回错误,返回功能码为0x%02X\r\n", pReFrame->fun);
return MRTU_FUNR_ERROR;
}
}
//判断数据长度
if(pReFrame->DataLen != )
{
modebus_debug("返回数据长度错误,读取%d个寄存器,共%dB,只返回了%dB\r\n",, *, pReFrame->DataLen);
return MRTU_LEN_ERROR; //返回数据长度错误
}
//获取返回的寄存器的值
*pRegData = pReFrame->DataBuff[];
*pRegData <<= ;
*pRegData |= pReFrame->DataBuff[]; 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=; //接收数据计数器
u16 TimeOut;
u16 TimeDelay = ; //用于计算数据接收延时
u8 i; if(pHandle == NULL) return MRTU_HANDLE_ERROR; //句柄无效
TimeOut = pHandle->TimeOut/+; //超时初值
pFrame = (MRTU_READ_FRAME *)pHandle->pTxBuff;
//数据结构填充
pFrame->addr = SlaveAddr; //从机地址
pFrame->fun = (u8)RegType; //功能码,读取
pFrame->StartReg = SWAP16(RegAddr); //寄存器起始地址
if((RegNum > ) || (RegNum == )) return MRTU_REGN_ERROR; //寄存器数量错误
pFrame->RegNum = SWAP16(RegNum); //需要读取的寄存器数量
crc16 = usMBCRC16(pHandle->pTxBuff, ); //计算CRC16
pFrame->CRC16 = crc16; //crc16 #if MODEBUS_RTU_DBUG
{
u16 i; modebus_debug("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",,crc16);
for(i = ;i < ;i ++)
{
modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
}
modebus_debug("\r\n");
}
#endif //MODEBUS_RTU_DBUG MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, +); //发送数据
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("接收溢出!\r\n");
return MRTU_OVER_ERROR; //返回溢出错误
}
cnt2 = MODEBUS_GetDataCnt(pHandle->UartCh); //获取接收数据计数器
if(cnt1 == cnt2) //完成接收数据了,退出等待
{
TimeOut --;
if((cnt1 > )&&(TimeOut!=)) TimeOut=; //数据接收完毕,退出
TimeDelay ++;
}
else
{
TimeOut = pHandle->TimeOut/+; //有数据,计数器复位
}
}while(TimeOut);
TimeDelay -= ;
//等待完毕
MODEBUS_DisableRx(pHandle->UartCh); //关闭接收
MODEBUS_ClearRxCnt(pHandle->UartCh); //清除接收缓冲区
if(cnt1 == ) //没有接收到数据
{
modebus_debug("接收超时(%dmS)!\r\n",TimeDelay*);
pHandle->ReturnTime = 0xffff; //接收数据超时
return MRTU_TIME_OUT; //返回超时
}
pHandle->ReturnTime = TimeDelay*; //数据返回时间 #if MODEBUS_RTU_DBUG
{
u16 i; modebus_debug("\r\n-> MODEBUS RTU RXD(%dB)(ping:%dmS):\r\n",cnt1,TimeDelay*);
for(i = ;i < cnt1;i ++)
{
modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
}
modebus_debug("\r\n");
}
#endif //MODEBUS_RTU_DBUG pReFrame = (MRTU_RETURN_FRAME *)pHandle->pRxBuff;
//检查地址
if(pReFrame->addr != SlaveAddr)
{
modebus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02X\r\n",SlaveAddr, pReFrame->addr);
return MRTU_ADDR_ERROR;
}
//对接受的数据进行CRC校验
crc16 = usMBCRC16(pHandle->pRxBuff, cnt1-);//计算CRC16
if((pHandle->pRxBuff[cnt1-] != (crc16 >> )) || (pHandle->pRxBuff[cnt1-] != (crc16 & 0xff)))
{
modebus_debug("CRC校验错误,计算CRC为:0x%04X,返回CRC为:0x%04X\r\n",crc16,(u16)(pHandle->pRxBuff[cnt1-]<<)|pHandle->pRxBuff[cnt1-]);
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\r\n", pUnuFrame->unu);
switch(pUnuFrame->unu)
{
case : return MRTU_UNUS1_ERROR; //异常码1
case : return MRTU_UNUS2_ERROR; //异常码2
case : return MRTU_UNUS3_ERROR; //异常码3
case : return MRTU_UNUS4_ERROR; //异常码4
case : return MRTU_UNUS5_ERROR; //异常码5
case : return MRTU_UNUS6_ERROR; //异常码6
default: return MRTU_OTHER_ERROR;
}
}
else
{
modebus_debug("返回错误,返回功能码为0x%02X\r\n", pReFrame->fun);
return MRTU_FUNR_ERROR;
}
}
//判断数据长度
if(pReFrame->DataLen != (RegNum*))
{
modebus_debug("返回数据长度错误,读取%d个寄存器,共%dB,只返回了%dB\r\n",RegNum, RegNum*, pReFrame->DataLen);
return MRTU_LEN_ERROR; //返回数据长度错误
}
//获取返回的寄存器的值
for(i = ;i < RegNum;i ++)
{
pRegData[i] = pReFrame->DataBuff[i*];
pRegData[i] <<= ;
pRegData[i] |= pReFrame->DataBuff[i*+];
} 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=; //接收数据计数器
u16 TimeOut;
u16 TimeDelay = ; //用于计算数据接收延时 if(pHandle == NULL) return MRTU_HANDLE_ERROR; //句柄无效
TimeOut = pHandle->TimeOut/+; //超时初值
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, ); //计算CRC16 #if MODEBUS_RTU_DBUG
{
u16 i; modebus_debug("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",,crc16);
for(i = ;i < ;i ++)
{
modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
}
modebus_debug("\r\n");
}
#endif //MODEBUS_RTU_DBUG MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, +); //发送数据
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("接收溢出!\r\n");
return MRTU_OVER_ERROR; //返回溢出错误
}
cnt2 = MODEBUS_GetDataCnt(pHandle->UartCh); //获取接收数据计数器
if(cnt1 == cnt2) //完成接收数据了,退出等待
{
TimeOut --;
if((cnt1 > )&&(TimeOut!=)) TimeOut=; //数据接收完毕,退出
TimeDelay ++;
}
else
{
TimeOut = pHandle->TimeOut/+; //有数据,计数器复位
}
}while(TimeOut);
TimeDelay -= ;
//等待完毕
MODEBUS_DisableRx(pHandle->UartCh); //关闭接收
MODEBUS_ClearRxCnt(pHandle->UartCh); //清除接收缓冲区
if(cnt1 == ) //没有接收到数据
{
modebus_debug("接收超时(%dmS)!\r\n",TimeDelay*);
pHandle->ReturnTime = 0xffff; //接收数据超时
return MRTU_TIME_OUT; //返回超时
}
pHandle->ReturnTime = TimeDelay*; //数据返回时间 #if MODEBUS_RTU_DBUG
{
u16 i; modebus_debug("\r\n-> MODEBUS RTU RXD(%dB)(ping:%dmS):\r\n",cnt1,TimeDelay*);
for(i = ;i < cnt1;i ++)
{
modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
}
modebus_debug("\r\n");
}
#endif //MODEBUS_RTU_DBUG pReFrame = (MRTU_WRITE_FRAME *)pHandle->pRxBuff;
//检查地址
if(pReFrame->addr != SlaveAddr)
{
modebus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02X\r\n",SlaveAddr, pReFrame->addr);
return MRTU_ADDR_ERROR;
}
//对接受的数据进行CRC校验
crc16 = usMBCRC16(pHandle->pRxBuff, cnt1-);//计算CRC16
if((pHandle->pRxBuff[cnt1-] != (crc16 >> )) || (pHandle->pRxBuff[cnt1-] != (crc16 & 0xff)))
{
modebus_debug("CRC校验错误,计算CRC为:0x%04X,返回CRC为:0x%04X\r\n",crc16,(u16)(pHandle->pRxBuff[cnt1-]<<)|pHandle->pRxBuff[cnt1-]);
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\r\n", pUnuFrame->unu);
switch(pUnuFrame->unu)
{
case : return MRTU_UNUS1_ERROR; //异常码1
case : return MRTU_UNUS2_ERROR; //异常码2
case : return MRTU_UNUS3_ERROR; //异常码3
case : return MRTU_UNUS4_ERROR; //异常码4
case : return MRTU_UNUS5_ERROR; //异常码5
case : return MRTU_UNUS6_ERROR; //异常码6
default: return MRTU_OTHER_ERROR;
}
}
else
{
modebus_debug("返回错误,返回功能码为0x%02X\r\n", pReFrame->fun);
return MRTU_FUNR_ERROR;
}
}
//判断数据是否写入
if(SWAP16(pReFrame->StartReg) != RegAddr) //返回的寄存器地址不一致
{
modebus_debug("返回寄存器地址错误,写入寄存器%d,返回寄存器%d\r\n",RegAddr, pReFrame->StartReg);
return MRTU_REG_ERROR; //返回寄存器错误
}
if(SWAP16(pReFrame->RegData) != RegData)
{
modebus_debug("数据写入错误,写入值:0x%04X,返回了:0x%04X\r\n",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=; //接收数据计数器
u16 TimeOut;
u16 TimeDelay = ; //用于计算数据接收延时
u8 i; if(pHandle == NULL) return MRTU_HANDLE_ERROR; //句柄无效
TimeOut = pHandle->TimeOut/+; //超时初值
pFrame = (MRTU_WRITE_MULT_FRAME *)pHandle->pTxBuff;
//数据结构填充
pFrame->addr = SlaveAddr; //从机地址
pFrame->fun = (u8)MRTU_FUN_MWRITE; //功能码,预置多个寄存器
pFrame->StartReg = SWAP16(RegAddr); //寄存器起始地址
if((RegNum > ) || (RegNum == )) return MRTU_REGN_ERROR; //寄存器数量错误
pFrame->RegNum = SWAP16(RegNum); //写入寄存器数量
pFrame->DataLen = *RegNum; //数据长度
//循环写入数据
for(i = ;i < RegNum;i ++)
{
pFrame->DataBuff[*i] = pRegData[i]>>; //高位
pFrame->DataBuff[*i+] = pRegData[i]&0xff; //低位
}
crc16 = usMBCRC16(pHandle->pTxBuff, +pFrame->DataLen); //计算CRC16,高低位对调过
pFrame->DataBuff[pFrame->DataLen] = crc16&0xff; //高位
pFrame->DataBuff[pFrame->DataLen+]=crc16>>; //低位 #if MODEBUS_RTU_DBUG
{
u16 i; modebus_debug("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",+pFrame->DataLen+,crc16);
for(i = ;i < +pFrame->DataLen+;i ++)
{
modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
}
modebus_debug("\r\n");
}
#endif //MODEBUS_RTU_DBUG MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, +pFrame->DataLen+); //发送数据
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("接收溢出!\r\n");
return MRTU_OVER_ERROR; //返回溢出错误
}
cnt2 = MODEBUS_GetDataCnt(pHandle->UartCh); //获取接收数据计数器
if(cnt1 == cnt2) //完成接收数据了,退出等待
{
TimeOut --;
if((cnt1 > )&&(TimeOut!=)) TimeOut=; //数据接收完毕,退出
TimeDelay ++;
}
else
{
TimeOut = pHandle->TimeOut/+; //有数据,计数器复位
}
}while(TimeOut);
TimeDelay -= ;
//等待完毕
MODEBUS_DisableRx(pHandle->UartCh); //关闭接收
MODEBUS_ClearRxCnt(pHandle->UartCh); //清除接收缓冲区
if(cnt1 == ) //没有接收到数据
{
modebus_debug("接收超时(%dmS)!\r\n",TimeDelay*);
pHandle->ReturnTime = 0xffff; //接收数据超时
return MRTU_TIME_OUT; //返回超时
}
pHandle->ReturnTime = TimeDelay*; //数据返回时间 #if MODEBUS_RTU_DBUG
{
u16 i; modebus_debug("\r\n-> MODEBUS RTU RXD(%dB)(ping:%dmS):\r\n",cnt1,TimeDelay*);
for(i = ;i < cnt1;i ++)
{
modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
}
modebus_debug("\r\n");
}
#endif //MODEBUS_RTU_DBUG pReFrame = (MRTU_WRIT_EMULT_RFRAME *)pHandle->pRxBuff;
//检查地址
if(pReFrame->addr != SlaveAddr)
{
modebus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02X\r\n",SlaveAddr, pReFrame->addr);
return MRTU_ADDR_ERROR;
}
//对接受的数据进行CRC校验
crc16 = usMBCRC16(pHandle->pRxBuff, cnt1-);//计算CRC16
if((pHandle->pRxBuff[cnt1-] != (crc16 >> )) || (pHandle->pRxBuff[cnt1-] != (crc16 & 0xff)))
{
modebus_debug("CRC校验错误,计算CRC为:0x%04X,返回CRC为:0x%04X\r\n",crc16,(u16)(pHandle->pRxBuff[cnt1-]<<)|pHandle->pRxBuff[cnt1-]);
return MRTU_CRC_ERROR; //返回CRC校验错误
}
//返回的功能码不一致
if(pReFrame->fun != (u8)MRTU_FUN_MWRITE)
{
pUnuFrame = (MRTU_UNU_FRAME *)pHandle->pRxBuff; //异常数据帧
if(pUnuFrame->ErrorFun == ((u8)MRTU_FUN_MWRITE|0x80))//返回有异常
{
modebus_debug("返回异常,异常码%d\r\n", pUnuFrame->unu);
switch(pUnuFrame->unu)
{
case : return MRTU_UNUS1_ERROR; //异常码1
case : return MRTU_UNUS2_ERROR; //异常码2
case : return MRTU_UNUS3_ERROR; //异常码3
case : return MRTU_UNUS4_ERROR; //异常码4
case : return MRTU_UNUS5_ERROR; //异常码5
case : return MRTU_UNUS6_ERROR; //异常码6
default: return MRTU_OTHER_ERROR;
}
}
else
{
modebus_debug("返回错误,返回功能码为0x%02X\r\n", pReFrame->fun);
return MRTU_FUNR_ERROR;
}
}
//判断数据是否写入
if(SWAP16(pReFrame->StartReg) != RegAddr) //返回的寄存器地址不一致
{
modebus_debug("返回寄存器地址错误,写入寄存器%d,返回寄存器%d\r\n",RegAddr, pReFrame->StartReg);
return MRTU_REG_ERROR; //返回寄存器错误
}
if(SWAP16(pReFrame->RegNum) != RegNum)
{
modebus_debug("写入寄存器数量错误,写入%d个寄存器,返回%d个寄存器\r\n",RegNum, pReFrame->RegNum);
return MRTU_WRITE_ERROR; //写入数据错误
} return MRTU_OK; //返回成功
}
#endif //MODEBUS_RTU_HOST #if(MODEBUS_RTU_SLAVE) //开启从机模式
/*************************************************************************************************************************
* 函数 : bool MODEBUS_SLAVE_RetrunUnu(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u8 Fun, MRTU_UNUS Unus)
* 功能 : 从机返回异常编码
* 参数 : pHandle:modebus句柄;SlaveAddr:从机地址;Fun:来自主机的功能码;Unus:异常码,见MRTU_UNUS
* 返回 : TRUE:发送成功;FALSE:发送失败
* 依赖 : 底层通信驱动
* 作者 : cp1300@139.com
* 时间 : 2014-03-24
* 最后修改时间 : 2014-11-17
* 说明 : 从机返回异常码给主机,异常码见:MRTU_UNUS
MRTU_UNUS1 异常码1,无效的操作码
MRTU_UNUS2 异常码2,无效的数据地址
MRTU_UNUS3 异常码3,无效的数据值
MRTU_UNUS4 异常码4,无效操作
MRTU_UNUS5 异常码5
MRTU_UNUS6 异常码6
*************************************************************************************************************************/
bool MODEBUS_SLAVE_RetrunUnu(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u8 Fun, MRTU_UNUS Unus)
{
MRTU_UNU_FRAME *pFrame; //返回异常数据包
u16 crc16; if(pHandle == NULL) return FALSE; //句柄无效
//数据结构填充
pFrame = (MRTU_UNU_FRAME *)pHandle->pTxBuff;
pFrame->addr = SlaveAddr; //从机地址
pFrame->ErrorFun = (u8)Fun|0x80; //功能码+0x80,出现异常
pFrame->unu = (u8)Unus; //异常编码
crc16 = usMBCRC16(pHandle->pTxBuff, ); //计算CRC16,高低位对调过
pFrame->crc16H = crc16 & 0xff;
pFrame->crc16L = crc16>>;
#if MODEBUS_RTU_DBUG
{
u16 i; modebus_debug("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",, crc16);
for(i = ;i < ;i ++)
{
modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
}
modebus_debug("\r\n");
}
#endif //MODEBUS_RTU_DBUG MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, ); //发送数据 return TRUE;
} /*************************************************************************************************************************
* 函数 : MRTU_ERROR MODEBUS_SLAVE_FramesUnpack(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u32 DataLen, u8 *pFun)
* 功能 : 从机模式接收数据拆包
* 参数 : pHandle:modebus句柄;SlaveAddr:从机地址;DataLen:接收数据长度;pFun:来自主机的功能码
* 返回 : MRTU_ERROR:状态,只有MRTU_OK:才是有效数据包
* 依赖 : 底层通信驱动
* 作者 : cp1300@139.com
* 时间 : 2014-03-24
* 最后修改时间 : 2014-11-17
* 说明 : 需要等数据接收完毕后拆包
*************************************************************************************************************************/
MRTU_ERROR MODEBUS_SLAVE_FramesUnpack(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u32 DataLen, u8 *pFun)
{
u16 crc16;
MRTU_READ_FRAME *pReadFrame; //来自主机的读取数据帧格式
MRTU_WRITE_MULT_FRAME *pWriteMultFrame; //来自主机的写多个保持寄存器 *pFun = 0xff; //功能码无效
if(pHandle->pRxBuff[] != SlaveAddr)
{
modebus_debug("地址不符,丢弃;目标地址:0x%02X;本机地址:0x%02X;\r\n", pHandle->pRxBuff[], SlaveAddr);
return MRTU_ADDR_ERROR;
}
//对接受的数据进行CRC校验
crc16 = usMBCRC16(pHandle->pRxBuff, DataLen-); //计算CRC16 #if MODEBUS_RTU_DBUG
{
u16 i; modebus_debug("\r\n-> MODEBUS RTU RXD(%dB)(CRC:0x%04X):\r\n",DataLen,crc16);
for(i = ;i < DataLen;i ++)
{
modebus_debug("0x%02X ",pHandle->pRxBuff[i]);
}
modebus_debug("\r\n");
}
#endif //MODEBUS_RTU_DBUG if((pHandle->pRxBuff[DataLen-] == (crc16 >> )) && (pHandle->pRxBuff[DataLen-] == (crc16 & 0xff)))
{
//判断功能码
switch(pHandle->pRxBuff[])
{
case MRTU_FUN_READ_HOLD : //0x03读保持寄存器,可读写寄存器为保持寄存器
case MRTU_FUN_READ_INPUT : //0x04读输入寄存器,为只读寄存器
{
pReadFrame = (MRTU_READ_FRAME *)pHandle->pRxBuff;
if((SWAP16(pReadFrame->RegNum) > ) || (SWAP16(pReadFrame->RegNum) == ))
{
modebus_debug("读取寄存器数量错误,读取寄存器数量为:%d\r\n", SWAP16(pReadFrame->RegNum));
MODEBUS_SLAVE_RetrunUnu(pHandle, pHandle->pRxBuff[], pHandle->pRxBuff[], MRTU_UNUS2); //返回异常2
return MRTU_REGN_ERROR; //寄存器数量错误
}
}break;
case MRTU_FUN_WRITE :break; //0x06写单个保持寄存器
case MRTU_FUN_MWRITE : //0x10写多个保持寄存器
{
pWriteMultFrame = (MRTU_WRITE_MULT_FRAME *)pHandle->pRxBuff;
if((SWAP16(pWriteMultFrame->RegNum) > ) || (SWAP16(pWriteMultFrame->RegNum) == ))
{
modebus_debug("写寄存器数量错误,读取寄存器数量为:%d\r\n", SWAP16(pWriteMultFrame->RegNum));
MODEBUS_SLAVE_RetrunUnu(pHandle, pHandle->pRxBuff[], pHandle->pRxBuff[], MRTU_UNUS2); //返回异常2
return MRTU_REGN_ERROR; //寄存器数量错误
}
else if(pWriteMultFrame->DataLen != (*SWAP16(pWriteMultFrame->RegNum)))
{
modebus_debug("写寄存器数据长度错误,需要写入%d个寄存器,长度为:%dB,收到数据长度为:%dB\r\n", pWriteMultFrame->RegNum, *pWriteMultFrame->RegNum, pWriteMultFrame->DataLen);
MODEBUS_SLAVE_RetrunUnu(pHandle, pHandle->pRxBuff[], pHandle->pRxBuff[], MRTU_UNUS3); //返回异常3
return MRTU_REGN_ERROR; //寄存器数量错误
}
}break;
default: //不支持的功能码,返回异常1
{
modebus_debug("不支持的操作码:0x%02X\r\n", pHandle->pRxBuff[]);
MODEBUS_SLAVE_RetrunUnu(pHandle, pHandle->pRxBuff[], pHandle->pRxBuff[], MRTU_UNUS1); //返回异常1
return MRTU_FUNR_ERROR;
}
} *pFun = pHandle->pRxBuff[]; //返回功能码
return MRTU_OK; //返回成功
}
else
{
modebus_debug("CRC校验错误,计算CRC为:0x%04X,返回CRC为:0x%04X\r\n",crc16,(u16)(pHandle->pRxBuff[DataLen-]<<)|pHandle->pRxBuff[DataLen-]);
return MRTU_CRC_ERROR; //返回CRC校验错误
}
} /*************************************************************************************************************************
* 函数 : MRTU_ERROR MODEBUS_SLAVE_ReturnReadReg(MODEBUS_HANDLE *pHandle, u8 Fun, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[])
* 功能 : 从机返回主机读取的寄存器
* 参数 : pHandle:modebus句柄;Fun:读取的功能码;SlaveAddr:从机地址;RegAddr:需读取的寄存器地址;RegNum:寄存器数量;pRegData:返回寄存器的值,至少为RegNum的2倍
返回的寄存器的值按照循序存放在pRegData中
* 返回 : MRTU_ERROR:通信状态
* 依赖 : 底层通信驱动
* 作者 : cp1300@139.com
* 时间 : 2014-03-24
* 最后修改时间 : 2014-11-16
* 说明 : MOUEBUS RTU主机读取从机的指定寄存器,可以为保持寄存器,也可以为输入寄存器,可以一次读取多个
输入输出的数据都为小端模式
注意:如果直接使用数据帧的寄存器数量以及地址,必须高地位交换
*************************************************************************************************************************/
MRTU_ERROR MODEBUS_SLAVE_ReturnReadReg(MODEBUS_HANDLE *pHandle, u8 Fun, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[])
{
MRTU_RETURN_FRAME *pFrame; //返回数据帧格式
u16 crc16;
u8 i; if(pHandle == NULL) return MRTU_HANDLE_ERROR; //句柄无效
if((Fun != MRTU_FUN_READ_INPUT) && (Fun != MRTU_FUN_READ_HOLD)) return MRTU_FUNR_ERROR; //功能码错误
if((RegNum > ) || (RegNum == )) return MRTU_REGN_ERROR; //寄存器数量错误
pFrame = (MRTU_RETURN_FRAME *)pHandle->pTxBuff;
//数据结构填充
pFrame->addr = SlaveAddr; //从机地址
pFrame->fun = Fun; //功能码,读取
pFrame->DataLen = *RegNum; //数据长度
//循环写入返回的数据
for(i = ;i < RegNum;i ++)
{
pFrame->DataBuff[*i] = pRegData[i]>>; //数据高位
pFrame->DataBuff[*i+] = pRegData[i]&0xff; //数据低位
}
crc16 = usMBCRC16(pHandle->pTxBuff, +pFrame->DataLen);//计算CRC16
pFrame->DataBuff[pFrame->DataLen] = crc16&0xff; //数据发送交换过
pFrame->DataBuff[pFrame->DataLen+] = crc16>>; //数据发送交换过 #if MODEBUS_RTU_DBUG
{
u16 i; modebus_debug("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",+pFrame->DataLen+,crc16);
for(i = ;i < +pFrame->DataLen+;i ++)
{
modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
}
modebus_debug("\r\n");
}
#endif //MODEBUS_RTU_DBUG MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, +pFrame->DataLen+); //发送数据 return MRTU_OK; //返回成功
} /*************************************************************************************************************************
* 函数 : MRTU_ERROR MODEBUS_SLAVE_ReturnWriteHoldReg(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u16 RegData)
* 功能 : 从机返回主机预置单个保持寄存器
* 参数 : pHandle:modebus句柄;Fun:读取的功能码;SlaveAddr:从机地址;RegAddr:需读取的寄存器地址;RegData:返回寄存器的值
* 返回 : MRTU_ERROR:通信状态
* 依赖 : 底层通信驱动
* 作者 : cp1300@139.com
* 时间 : 2014-03-24
* 最后修改时间 : 2014-11-16
* 说明 : MOUEBUS RTU主机写单个寄存器成功后返回
输入输出的数据都为小端模式
注意:如果直接使用数据帧的寄存器数量以及地址,必须高地位交换
*************************************************************************************************************************/
MRTU_ERROR MODEBUS_SLAVE_ReturnWriteHoldReg(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u16 RegData)
{
MRTU_WRITE_FRAME *pFrame; //返回数据帧格式
u16 crc16; if(pHandle == NULL) return MRTU_HANDLE_ERROR; //句柄无效
pFrame = (MRTU_WRITE_FRAME *)pHandle->pTxBuff;
//数据结构填充
pFrame->addr = SlaveAddr; //从机地址
pFrame->fun = MRTU_FUN_WRITE; //功能码,预置单个寄存器
pFrame->StartReg = SWAP16(RegAddr); //寄存器地址
pFrame->RegData = SWAP16(RegData); //寄存器的值
pFrame->crc16 = usMBCRC16(pHandle->pTxBuff, );//计算CRC16 #if MODEBUS_RTU_DBUG
{
u16 i; modebus_debug("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",,crc16);
for(i = ;i < ;i ++)
{
modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
}
modebus_debug("\r\n");
}
#endif //MODEBUS_RTU_DBUG MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, ); //发送数据 return MRTU_OK; //返回成功
} /*************************************************************************************************************************
* 函数 : MRTU_ERROR MODEBUS_SLAVE_ReturnWriteMultHoldReg(MODEBUS_HANDLE *pHandle, u8 SlaveAddr, u16 RegAddr, u8 RegNum)
* 功能 : 从机返回主机预置多个保持寄存器
* 参数 : pHandle:modebus句柄;SlaveAddr:从机地址;RegAddr:需读取的寄存器地址;RegNum:需要读取的寄存器数量
* 返回 : MRTU_ERROR:通信状态
* 依赖 : 底层通信驱动
* 作者 : cp1300@139.com
* 时间 : 2014-03-24
* 最后修改时间 : 2014-11-16
* 说明 : MOUEBUS RTU主机写单个寄存器成功后返回
输入输出的数据都为小端模式
注意:如果直接使用数据帧的寄存器数量以及地址,必须高地位交换
*************************************************************************************************************************/
MRTU_ERROR MODEBUS_SLAVE_ReturnWriteMultHoldReg(MODEBUS_HANDLE *pHandle, u8 SlaveAddr, u16 RegAddr, u8 RegNum)
{
MRTU_WRIT_EMULT_RFRAME *pFrame; //返回数据帧格式 if(pHandle == NULL) return MRTU_HANDLE_ERROR; //句柄无效
if((RegNum > ) || (RegNum == )) return MRTU_REGN_ERROR; //寄存器数量错误
pFrame = (MRTU_WRIT_EMULT_RFRAME *)pHandle->pTxBuff;
//数据结构填充
pFrame->addr = SlaveAddr; //从机地址
pFrame->fun = MRTU_FUN_MWRITE; //功能码,预置多个寄存器
pFrame->StartReg = SWAP16(RegAddr); //寄存器起始地址
pFrame->RegNum = SWAP16(RegNum); //寄存器数量
pFrame->crc16 = usMBCRC16(pHandle->pTxBuff, ); //计算CRC16
#if MODEBUS_RTU_DBUG
{
u16 i; modebus_debug("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",,pFrame->crc16);
for(i = ;i < ;i ++)
{
modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
}
modebus_debug("\r\n");
}
#endif //MODEBUS_RTU_DBUG MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, ); //发送数据 return MRTU_OK; //返回成功
} /*************************************************************************************************************************
* 函数 : MRTU_ERROR MODEBUS_SLAVE_ReadUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo)
* 功能 : 解析来自主机的读取寄存器命令
* 参数 : pHandle:modebus句柄;pFrameInfo:解析的信息结构
* 返回 : MRTU_ERROR:通信状态
* 依赖 : 底层通信驱动
* 作者 : cp1300@139.com
* 时间 : 2014-11-17
* 最后修改时间 : 2014-11-17
* 说明 : 用于将modebus的大端模式解析为小端模式
支持 MRTU_FUN_READ_HOLD,MRTU_FUN_READ_INPUT 命令解析
*************************************************************************************************************************/
void MODEBUS_SLAVE_ReadUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo)
{
MRTU_READ_FRAME *pReadRegFrame; //主机读取从机数据帧 pReadRegFrame = (MRTU_READ_FRAME *)pHandle->pRxBuff;
pFrameInfo->SlaveAddr = pReadRegFrame->addr; //从机地址
pFrameInfo->fun = pReadRegFrame->fun; //功能码
pFrameInfo->StartReg = SWAP16(pReadRegFrame->StartReg); //寄存器起始地址
pFrameInfo->RegNum = SWAP16(pReadRegFrame->RegNum); //寄存器数量
} /*************************************************************************************************************************
* 函数 : void MODEBUS_SLAVE_WriteUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo, u16 *pData)
* 功能 : 解析来自主机的预置单个寄存器命令
* 参数 : pHandle:modebus句柄;pFrameInfo:解析的信息结构;pData:需要写入从机的值
* 返回 : MRTU_ERROR:通信状态
* 依赖 : 底层通信驱动
* 作者 : cp1300@139.com
* 时间 : 2014-11-17
* 最后修改时间 : 2014-11-17
* 说明 : 用于将modebus的大端模式解析为小端模式
支持 MRTU_FUN_WRITE 命令解析
*************************************************************************************************************************/
void MODEBUS_SLAVE_WriteUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo, u16 *pData)
{
MRTU_WRITE_FRAME *pWriteRegFrame; //主机预置单个保持寄存器 pWriteRegFrame = (MRTU_WRITE_FRAME *)pHandle->pRxBuff;
pFrameInfo->SlaveAddr = pWriteRegFrame->addr; //从机地址
pFrameInfo->fun = pWriteRegFrame->fun; //功能码
pFrameInfo->StartReg = SWAP16(pWriteRegFrame->StartReg); //寄存器起始地址
pFrameInfo->RegNum = ; //寄存器数量
*pData = SWAP16(pWriteRegFrame->RegData); //需要写入的寄存器的值
} /*************************************************************************************************************************
* 函数 : void MODEBUS_SLAVE_WriteMultUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo, u16 *pDataBuff)
* 功能 : 解析来自主机的预置多个寄存器命令
* 参数 : pHandle:modebus句柄;pFrameInfo:解析的信息结构;pDataBuff:需要写入从机寄存器值的数组,必须足够大,防止溢出
* 返回 : MRTU_ERROR:通信状态
* 依赖 : 底层通信驱动
* 作者 : cp1300@139.com
* 时间 : 2014-11-17
* 最后修改时间 : 2014-11-17
* 说明 : 用于将modebus的大端模式解析为小端模式
支持 MRTU_FUN_MWRITE 命令解析
*************************************************************************************************************************/
void MODEBUS_SLAVE_WriteMultUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo, u16 *pDataBuff)
{
MRTU_WRITE_MULT_FRAME *pWriteMultRegFrame; //主机预置多个保持寄存器
u8 i; pWriteMultRegFrame = (MRTU_WRITE_MULT_FRAME *)pHandle->pRxBuff;
pFrameInfo->SlaveAddr = pWriteMultRegFrame->addr; //从机地址
pFrameInfo->fun = pWriteMultRegFrame->fun; //功能码
pFrameInfo->StartReg = SWAP16(pWriteMultRegFrame->StartReg); //寄存器起始地址
pFrameInfo->RegNum = SWAP16(pWriteMultRegFrame->RegNum); //寄存器数量
//需要写入的寄存器的值
for(i = ;i < pFrameInfo->RegNum;i ++)
{
pDataBuff[i] = pWriteMultRegFrame->DataBuff[*i];
pDataBuff[i] <<= ;
pDataBuff[i] |= pWriteMultRegFrame->DataBuff[*i+];
}
}
#endif //MODEBUS_RTU_SLAVE //MODEBUS CRC16计算
//结果为大端模式
BIG_U16 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 << | ucCRCLo );
} /*************************************************************************************************************************
* 函数 : void MODEBUS_32TO16(u16 *Out16H, u16 *Out16L, u32 In32)
* 功能 : 将32bit数据拆分为高低16位,并且使用大端模式,兼容modebus
* 参数 : Out16H:拆分的高16位,大端模式;Out16L:拆分的低16位,大端模式;In32:需要拆分的数据,小端模式,兼容STM32
* 返回 : 无
* 依赖 : 无
* 作者 : cp1300@139.com
* 时间 : 2014-05-27
* 最后修改时间 : 2014-05-27
* 说明 : 将STM32 32位数据拆分为兼容MODEBUS 大端模式
*************************************************************************************************************************/
void MODEBUS_32TO16(u16 *Out16H, u16 *Out16L, u32 In32)
{
*Out16H = SWAP16(In32 >> );
*Out16L = SWAP16(In32 & 0xffff);
} /*************************************************************************************************************************
* 函数 : u32 MODEBUS_16TO32(u16 In16H, u16 In16L)
* 功能 : 将modebus高低16位转换为小端模式的32位数
* 参数 : In16H:大端模式的高16位数;In16L:大端模式的低16位数
* 返回 : 32bit的整形数据
* 依赖 : 无
* 作者 : cp1300@139.com
* 时间 : 2014-05-27
* 最后修改时间 : 2014-05-27
* 说明 : 将modebus的2个16bit寄存器组成一个兼容STM32的32bit整形数
*************************************************************************************************************************/
u32 MODEBUS_16TO32(u16 In16H, u16 In16L)
{
u32 temp; temp = SWAP16(In16H);
temp <<= ;
temp |= SWAP16(In16L); return temp;
}
//modebus_rtu.h
/*************************************************************************************************************
* 文件名: MODEBUS_RTU.c
* 功能: MODEBUS_RTU通信协议层
* 作者: cp1300@139.com
* 创建时间: 2014-03-24
* 最后修改时间:2014-11-17
* 详细: MODEBUS RTU通信协议层
*************************************************************************************************************/
#ifndef _MODEBUS_RTU_H_
#define _MODEBUS_RTU_H_ #include "system.h"
#include "ucos_ii.h" /***********************配置相关************************/
#define MODEBUS_RTU_HOST 1 //1:开启主机模式;0:关闭主机模式
#define MODEBUS_RTU_SLAVE 1 //1:开启从机模式;0:关闭从机模式
/*********************************************************/ //16位整形数高低对调
#define SWAP16(x) (((x & 0xff00) >> 8) | ((x & 0xff) << 8)) /***********************关接口函数************************/
/**********************移植需要修改***********************/
#define MODEBUS_UartInit(ch,Speed) UARTx_Init((UART_CH_Type)ch, Speed, ENABLE) //串口初始化
#define MODEBUS_GetDataCnt(ch) UARTx_GetRxCnt((UART_CH_Type)ch) //获取接收数据计数器
#define MODEBUS_ClearRxCnt(ch) UARTx_ClearRxCnt((UART_CH_Type)ch) //清除接收数据计数器
#define MODEBUS_GetDataOver(ch) UARTx_GetRxBuffFullFlag((UART_CH_Type)ch) //获取数据溢出标志
#define MODEBUS_SendData(ch,pbuff,len) UARTx_SendData((UART_CH_Type)ch, pbuff, len) //数据发送
#define MODEBUS_SetRxBuff(ch, RxBuff, RxBuffSize) UARTx_SetRxBuff((UART_CH_Type)ch, RxBuff, RxBuffSize) //设置串口接收缓冲区
#define MODEBUS_DisableRx(ch) (UARTx_EnableRx((UART_CH_Type)ch, DISABLE)) //串口接收关闭
#define MODEBUS_EnableRx(ch) (UARTx_EnableRx((UART_CH_Type)ch, ENABLE)) //串口接收使能
#define MODEBUS_SetBaudRate(ch, x) (UARTx_SetBaudRate((UART_CH_Type)ch, x)) //设置串口波特率
//系统延时函数,根据实际修改,如果使用ucos建议使用ucos系统延时
#define MODEBUS_Delay10MS() OSTimeDlyHMSM(0,0,0,10) //10ms延时,字节超时固定为10ms
/*********************************************************/ //支持的功能码
#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 __packed struct
{
u8 addr; //地址 address
u8 fun; //功能码 function
BIG_U16 StartReg; //数据起始地址
BIG_U16 RegNum; //需要读取的寄存器个数
BIG_U16 CRC16; //CRC16
} MRTU_READ_FRAME; //MODEBUS RTU master Read Reg Frame //预置单个保持寄存器,主机写从机单个寄存器的数据帧
//从机返回数据帧与主机预置单个寄存器数据帧一样
typedef __packed struct
{
u8 addr; //地址 address
u8 fun; //功能码 function
BIG_U16 StartReg; //数据起始地址
BIG_U16 RegData; //数据值
BIG_U16 crc16; //CRC校验值
} MRTU_WRITE_FRAME; //MODEBUS RTU master Write Reg Frame //预置多个保持寄存器,主机写从机多个寄存器的数据帧
typedef __packed struct
{
u8 addr; //地址 address
u8 fun; //功能码 function
BIG_U16 StartReg; //数据起始地址
BIG_U16 RegNum; //寄存器数量
u8 DataLen; //数据长度
u8 DataBuff[]; //寄存器的值
} MRTU_WRITE_MULT_FRAME; //预置多个保持寄存器后返回数据帧,从机返回主机的数据帧
typedef __packed struct
{
u8 addr; //地址 address
u8 fun; //功能码 function
BIG_U16 StartReg; //数据起始地址
BIG_U16 RegNum; //寄存器数量
BIG_U16 crc16; //CRC校验值
} MRTU_WRIT_EMULT_RFRAME; //读取从机返回数据帧格式,从机返回给主机的数据帧
typedef __packed struct
{
u8 addr; //地址 address
u8 fun; //功能码 function
u8 DataLen; //数据长度
u8 DataBuff[]; //数据区,CRC16放在最后结尾处
//MRTU_REG16 CRC16; //CRC16
} MRTU_RETURN_FRAME; //MODEBUS RTU master Read Reg Frame //从机返回的异常数据帧,从机返回的异常数据帧
typedef __packed 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 = , //OK
MRTU_TIME_OUT = , //超时
MRTU_OVER_ERROR = , //溢出
MRTU_CRC_ERROR = , //CRC错误
MRTU_ADDR_ERROR = , //地址错误,返回地址不一致
MRTU_REG_ERROR = , //寄存器地址错误,返回寄存器地址不一致
MRTU_FUNR_ERROR = , //功能码错误,返回功能码不一致或者不支持的功能码
MRTU_HANDLE_ERROR = , //句柄错误,句柄为空
MRTU_REGN_ERROR = , //寄存器数量错误
MRTU_LEN_ERROR = , //返回数据长度错误
MRTU_WRITE_ERROR = , //写寄存器错误,写入与读取不一致
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; //MODEBUS句柄结构
typedef struct
{
u8 UartCh; //串口通道
u32 BaudRate; //通信波特率
u8 *pRxBuff; //接收缓冲区
u8 *pTxBuff; //发送缓冲区
u32 RxBuffSize; //接收缓冲区大小
u32 TxPacketNum; //发送数据包计数
u32 RxPacketNum; //接收数据包计数
u32 ErrorNum; //通信错误计数
u16 TimeOut; //通信超时时间,单位ms
u16 ReturnTime; //数据返回时间,单位ms,只对于主机有效
} MODEBUS_HANDLE; bool MODEBUS_Init(MODEBUS_HANDLE *pHandle, u8 UartCh, u32 BaudRate, u8 *pRxBuff,u8 *pTxBuff, u32 RxBuffSize, u32 TimeOut); //初始化modebus
#if(MODEBUS_RTU_HOST) //开启主机模式
MRTU_ERROR MODEBUS_HOST_ReadReg(MODEBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u16 *pRegData); //主机读取从机指定单个寄存器
MRTU_ERROR MODEBUS_HOST_ReadMultReg(MODEBUS_HANDLE *pHandle, READ_REG_TYPE RegType, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[]); //主机读取从机多个指定寄存器
MRTU_ERROR MODEBUS_HOST_WriteReg(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u16 RegData); //主机写从机单个保持寄存器
MRTU_ERROR MODEBUS_HOST_WriteMultReg(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[]); //主机写从机单个保持寄存器
#endif #if(MODEBUS_RTU_SLAVE) //开启从机模式
bool MODEBUS_SLAVE_RetrunUnu(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u8 Fun, MRTU_UNUS Unus); //从机返回异常码
MRTU_ERROR MODEBUS_SLAVE_FramesUnpack(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u32 DataLen, u8 *pFun); //从机解析数据包
MRTU_ERROR MODEBUS_SLAVE_ReturnReadReg(MODEBUS_HANDLE *pHandle, u8 Fun, u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[]); //从机返回主机读取的寄存器
MRTU_ERROR MODEBUS_SLAVE_ReturnWriteHoldReg(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u16 RegData); //从机返回主机写入单个寄存器命令
MRTU_ERROR MODEBUS_SLAVE_ReturnWriteMultHoldReg(MODEBUS_HANDLE *pHandle, u8 SlaveAddr, u16 RegAddr, u8 RegNum); //从机返回主机写多个寄存器命令
void MODEBUS_SLAVE_ReadUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo); //从机解析主机读取命令
void MODEBUS_SLAVE_WriteUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo, u16 *pData); //从机解析主机写单个寄存器命令
void MODEBUS_SLAVE_WriteMultUnpack(MODEBUS_HANDLE *pHandle, MRTU_SLAVE_INFO *pFrameInfo, u16 *pDataBuff); //从机解析主机多个寄存器命令
#endif u16 usMBCRC16( u8 * pucFrame, u16 usLen ); //crc计算
void MODEBUS_32TO16(u16 *Out16H, u16 *Out16L, u32 In32); //将32bit数据拆分为高低16位,并且使用大端模式,兼容modebus
u32 MODEBUS_16TO32(u16 In16H, u16 In16L); //将modebus高低16位转换为小端模式的32位数 #endif /*_MODEBUS_RTU_H_*/
//从机测试
//主机比较简单就不写了
//任务2:
//ModeBus
u8 ModebusBuff[];
void TaskModeBus(void *pdata)
{
MODEBUS_HANDLE ModebusHandle1;
MRTU_SLAVE_INFO FrameInfo; //modebus读写信息 u16 data;
u16 RegBuff[];
u8 i;
u32 cnt1 = ,cnt2 = ;
u8 Fun; for(i = ;i < ;i ++)
{
RegBuff[i] = i+;
}
MODEBUS_Init(&ModebusHandle1, UART_CH2, , ModebusBuff, ModebusBuff, -, );
MODEBUS_EnableRx(ModebusHandle1.UartCh); //使能接收
MODEBUS_ClearRxCnt(ModebusHandle1.UartCh); //清除接收
while()
{
cnt2 = cnt1;
cnt1 = MODEBUS_GetDataCnt(ModebusHandle1.UartCh);
if((cnt1 == cnt2) && (cnt1 != ))
{
MODEBUS_DisableRx(ModebusHandle1.UartCh); //关闭接收
MODEBUS_ClearRxCnt(ModebusHandle1.UartCh); //清除接收
if(MODEBUS_SLAVE_FramesUnpack(&ModebusHandle1, , cnt1, &Fun) == MRTU_OK)
{
uart_printf("收到数据,功能码:0x%02X\r\n", Fun);
switch(Fun)
{
case MRTU_FUN_READ_HOLD : //读保持寄存器,可读写寄存器为保持寄存器
case MRTU_FUN_READ_INPUT : //读输入寄存器,为只读寄存器
{
//解析
MODEBUS_SLAVE_ReadUnpack(&ModebusHandle1, &FrameInfo);
//数据处理
uart_printf("主机读取从机(%d)从%d开始的寄存器,共需要读取%d个\r\n", FrameInfo.SlaveAddr, FrameInfo.StartReg, FrameInfo.RegNum);
//返回
MODEBUS_SLAVE_ReturnReadReg(&ModebusHandle1, FrameInfo.fun, FrameInfo.SlaveAddr, FrameInfo.StartReg, FrameInfo.RegNum, RegBuff);
}break;
case MRTU_FUN_WRITE : //写单个保持寄存器
{
//解析
MODEBUS_SLAVE_WriteUnpack(&ModebusHandle1, &FrameInfo, &data);
//数据处理
uart_printf("主机写从机(%d)的寄存器%d值为:0x%02X\r\n", FrameInfo.SlaveAddr, FrameInfo.StartReg, data);
//返回
MODEBUS_SLAVE_ReturnWriteHoldReg(&ModebusHandle1, FrameInfo.SlaveAddr, FrameInfo.StartReg, data);
}break;
case MRTU_FUN_MWRITE : //写多个保持寄存器
{
//解析
MODEBUS_SLAVE_WriteMultUnpack(&ModebusHandle1, &FrameInfo, RegBuff);
//数据处理
uart_printf("主机写从机(%d),从%d开始的寄存器,共%d个,数据为:", FrameInfo.SlaveAddr, FrameInfo.StartReg, FrameInfo.RegNum);
for(i = ;i < FrameInfo.RegNum;i ++)
{
uart_printf("0x%04X ", RegBuff[i]); //打印数据
}
uart_printf("\r\n");
//返回
MODEBUS_SLAVE_ReturnWriteMultHoldReg(&ModebusHandle1, FrameInfo.SlaveAddr, FrameInfo.StartReg, FrameInfo.RegNum);
}break;
default:break;
} }
MODEBUS_EnableRx(ModebusHandle1.UartCh); //使能接收
}
OSTimeDlyHMSM(,,,);
}
}
模拟主机
从机接收处理
单片机modebus RTU通信实现,采用C语言,可适用于单片机,VC,安卓等(转)的更多相关文章
- 单片机modebus RTU通信实现,採用C语言,可适用于单片机,VC,安卓等
当前使用的是STM32+ucos_ii编写的,能够移植到安卓以及VC .NET等方便移植使用,採用modebus poll測试过. 仅仅须要改动响应的通信接口就可以,方便多串口使用 //modebus ...
- Modbus RTU 通信工具设计(转)
Modbus RTU 通信工具设计 Modbus 是一个工业上常用的通讯协议.一种通讯约定. ModBus 协议是应用层报文传输协议(OSI 模型第7层),它定义了一个与通信层无关的协议数据单元(PD ...
- C# NModbus RTU通信实现
Modbus协议时应用于电子控制器上的一种通用语言.通过此协议,控制器相互之间.控制器经由网络/串口和其它设备之间可以进行通信.它已经成为了一种工业标准.有了这个通信协议,不同的厂商生成的控制设备就可 ...
- c语言编写51单片机中断程序,执行过程是怎样的?
Q:c语言编写51单片机中断程序,执行过程是怎样的? 例如程序:#include<reg52.h> void main(void) { EA=1; //开放总中断 E ...
- Modbus RTU 通信应用案例
如何打开项目归档文件 例程中的TIA博途项目文件与STEP 7项目文件均为归档文件,需要按如下方式打开: TIA博途项目文件 1. 打开TIA博途软件,通过软件左下方“项目视图”按钮切换至项目视图: ...
- 【C语言】关于单片机中断详解
什么是中断?就是打断当前要做的事,转而去执行别的事情.比如小七我现在正在电脑前写帖子,突然老妈叫我帮她下楼拿点东西,于是我就收到了老妈给我的一个中断(可以叫做外部中断),当我去拿东西时,突然尿急(内部 ...
- WinCE下的串口通信开发(VS2005,VB.Net,VC++)
WinCE下的串口通信开发(VS2005,VB.Net,VC++) WinCE下的串口通信开发 一.利用Visual Basic 开发很简单,因为有现成的控件可以直接调用 以VS2005为例,首先 ...
- PC和单片机通过MODBUS RTU通信
最近研究了一下MODBUS通信,在STC12C5A60S2单片机上实现了MODBUS协议的部分功能,方便上位机从单片机系统上获取数据,比如由单片机获取的温度.湿度.或者控制信号的状态等.有了MODBU ...
- keil采用C语言模块化编程时全局变量、结构体的定义、声明以及头文件包含的处理方法
以前写单片机程序时总是把所用函数和变量都写在一个c文件里,后来遇到大点的项目,程序动则几千行,这种方式无疑会带来N多麻烦,相信大家都有所体验吧! 后来学会了在keil里进行模块化编程,即只把功能相同或 ...
随机推荐
- linux shell 远程执行命令
经常要部署多台服务器上面的应用,如果一个个机器的登录太麻烦. 所有就想到编写一个脚本来部署不同的服务器 前提条件: 配置ssh免登陆 如果不会的请参加我的另外一篇文章 http://blog.csdn ...
- js表单提交,判断文本框,用户名密码是否为空,JS表单检测!
当表单提交时先触发验证的js代码,当验证表单的方法返回true时才会提交表单返回false则不提交数据<script type="text/javascript">fu ...
- Node.js学习 - File Operation
同步异步 文件系统(fs 模块)模块中的方法均有异步和同步版本,例如读取文件内容的函数有异步的 fs.readFile() 和同步的 fs.readFileSync(). 异步的方法函数最后一个参数为 ...
- IDL 计算TVDI
介绍请看:http://blog.sina.com.cn/s/blog_764b1e9d0100wdrr.html 源码: IDL 源码PRO TVDI,NDVI,LST,NBINS,RES RES ...
- 双重检查锁定与延迟初始化(转自infoq)
很好的文章,转自http://www.infoq.com/cn/articles/double-checked-locking-with-delay-initialization 在java程序中,有 ...
- java+tomcat+Eclipse+mysql配置
Java下载及配置: 1. 下载:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html ...
- centos minimal Bind 主从服务器部署
实验环境 两台虚拟机BindM和BindS,装的系统都是centos6.3 minimal IP地址 主机名hostname 主DNS服务器 192.168.137.102 bindm.cas.c ...
- AXIS-web.xml里配置axis报错addChild: Child name 'AxisServlet' is not unique 解决办法
报错这个那么就表示,web.xml中有相同的AxisServlet的这个名字,可以把原来的删除配置自己的,也可以保留原来的,自己的不配置 Ctrl+f 搜索下就知道了
- zf-启动项目报错Server 127.0.0.1 has no instance named dlx 解决办法
由于百度出来的看不明白,于是我就在群里问,吴善如经理说:你这个问题我上次给李宽看过,用端口连,把instance去掉 然后我去掉之后 项目过程能够成功运行了,原来是这样
- FusionCharts 分类以及各个属性参数列表
<FusionCharts学习及使用笔记>之 第一篇 其实一直以来我都在有意无意的把我平常工作中遇到并解决的问题做个记录,放到我的网易博客中.但却一直没有想过如何把我使用的技术做一个系列化 ...