C语言编程:单片机modebus RTU通信实现,可适用于单片机,VC,安卓等
时间:2018-11-22 10:14:01
[导读]当前使用的是STM32+ucos_ii编写的,可以移植到安卓以及VC .NET等方便移植使用,采用modebus poll测试过.只需要修改响应的通信接口即可,方便多串口使用//modebus_rtu
当前使用的是STM32+ucos_ii编写的,可以移植到安卓以及VC .NET等方便移植使用,采用modebus poll测试过.
只需要修改响应的通信接口即可,方便多串口使用
//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("rn MODEBUS RTU RXD(%dB)(ping:%dmS):rn",cnt1,TimeDelay*10);
for(i = 0;i < cnt1;i ++)
{
modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
}
modebus_debug("rn");
}
#endif //MODEBUS_RTU_DBUG
pReFrame = (MRTU_RETURN_FRAME *)pHandle->pRxBuff;
//检查地址
if(pReFrame->addr != SlaveAddr)
{
modebus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02Xrn",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%04Xrn",crc16,(u16)(pHandle->pRxBuff[cnt1-2]<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("返回异常,异常码%drn", 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%02Xrn", pReFrame->fun);
return MRTU_FUNR_ERROR;
}
}
//判断数据长度
if(pReFrame->DataLen != 2)
{
modebus_debug("返回数据长度错误,读取%d个寄存器,共%dB,只返回了%dBrn",1, 1*2, pReFrame->DataLen);
return MRTU_LEN_ERROR; //返回数据长度错误
}
//获取返回的寄存器的值
*pRegData = pReFrame->DataBuff[0];
*pRegData <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("rn MODEBUS RTU RXD(%dB)(ping:%dmS):rn",cnt1,TimeDelay*10);
for(i = 0;i < cnt1;i ++)
{
modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
}
modebus_debug("rn");
}
#endif //MODEBUS_RTU_DBUG
pReFrame = (MRTU_RETURN_FRAME *)pHandle->pRxBuff;
//检查地址
if(pReFrame->addr != SlaveAddr)
{
modebus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02Xrn",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%04Xrn",crc16,(u16)(pHandle->pRxBuff[cnt1-2]<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("返回异常,异常码%drn", 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%02Xrn", pReFrame->fun);
return MRTU_FUNR_ERROR;
}
}
//判断数据长度
if(pReFrame->DataLen != (RegNum*2))
{
modebus_debug("返回数据长度错误,读取%d个寄存器,共%dB,只返回了%dBrn",RegNum, RegNum*2, pReFrame->DataLen);
return MRTU_LEN_ERROR; //返回数据长度错误
}
//获取返回的寄存器的值
for(i = 0;i < RegNum;i ++)
{
pRegData[i] = pReFrame->DataBuff[i*2];
pRegData[i] <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("rn MODEBUS RTU RXD(%dB)(ping:%dmS):rn",cnt1,TimeDelay*10);
for(i = 0;i < cnt1;i ++)
{
modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
}
modebus_debug("rn");
}
#endif //MODEBUS_RTU_DBUG
pReFrame = (MRTU_WRITE_FRAME *)pHandle->pRxBuff;
//检查地址
if(pReFrame->addr != SlaveAddr)
{
modebus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02Xrn",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%04Xrn",crc16,(u16)(pHandle->pRxBuff[cnt1-2]<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("返回异常,异常码%drn", 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%02Xrn", pReFrame->fun);
return MRTU_FUNR_ERROR;
}
}
//判断数据是否写入
if(SWAP16(pReFrame->StartReg) != RegAddr) //返回的寄存器地址不一致
{
modebus_debug("返回寄存器地址错误,写入寄存器%d,返回寄存器%drn",RegAddr, pReFrame->StartReg);
return MRTU_REG_ERROR; //返回寄存器错误
}
if(SWAP16(pReFrame->RegData) != RegData)
{
modebus_debug("数据写入错误,写入值:0x%04X,返回了:0x%04Xrn",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; //低位
#if MODEBUS_RTU_DBUG
{
u16 i;
modebus_debug("rn MODEBUS RTU RXD(%dB)(ping:%dmS):rn",cnt1,TimeDelay*10);
for(i = 0;i < cnt1;i ++)
{
modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
}
modebus_debug("rn");
}
#endif //MODEBUS_RTU_DBUG
pReFrame = (MRTU_WRIT_EMULT_RFRAME *)pHandle->pRxBuff;
//检查地址
if(pReFrame->addr != SlaveAddr)
{
modebus_debug("地址错误,目标地址为:0x%02X,返回地址为:0x%02Xrn",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%04Xrn",crc16,(u16)(pHandle->pRxBuff[cnt1-2]<pRxBuff[cnt1-1]);
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("返回异常,异常码%drn", 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%02Xrn", pReFrame->fun);
return MRTU_FUNR_ERROR;
}
}
//判断数据是否写入
if(SWAP16(pReFrame->StartReg) != RegAddr) //返回的寄存器地址不一致
{
modebus_debug("返回寄存器地址错误,写入寄存器%d,返回寄存器%drn",RegAddr, pReFrame->StartReg);
return MRTU_REG_ERROR; //返回寄存器错误
}
if(SWAP16(pReFrame->RegNum) != RegNum)
{
modebus_debug("写入寄存器数量错误,写入%d个寄存器,返回%d个寄存器rn",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, 3); //计算CRC16,高低位对调过
pFrame->crc16H = crc16 & 0xff;
pFrame->crc16L = crc16>>8;
#if MODEBUS_RTU_DBUG
{
u16 i;
modebus_debug("rn MODEBUS RTU RXD(%dB)(CRC:0x%04X):rn",DataLen,crc16);
for(i = 0;i < DataLen;i ++)
{
modebus_debug("0x%02X ",pHandle->pRxBuff[i]);
}
modebus_debug("rn");
}
#endif //MODEBUS_RTU_DBUG
if((pHandle->pRxBuff[DataLen-1] == (crc16 >> 8)) && (pHandle->pRxBuff[DataLen-2] == (crc16 & 0xff)))
{
//判断功能码
switch(pHandle->pRxBuff[1])
{
case MRTU_FUN_READ_HOLD : //0x03读保持寄存器,可读写寄存器为保持寄存器
case MRTU_FUN_READ_INPUT : //0x04读输入寄存器,为只读寄存器
{
pReadFrame = (MRTU_READ_FRAME *)pHandle->pRxBuff;
if((SWAP16(pReadFrame->RegNum) > 127) || (SWAP16(pReadFrame->RegNum) == 0))
{
modebus_debug("读取寄存器数量错误,读取寄存器数量为:%drn", SWAP16(pReadFrame->RegNum));
MODEBUS_SLAVE_RetrunUnu(pHandle, pHandle->pRxBuff[0], pHandle->pRxBuff[1], 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) > 127) || (SWAP16(pWriteMultFrame->RegNum) == 0))
{
modebus_debug("写寄存器数量错误,读取寄存器数量为:%drn", SWAP16(pWriteMultFrame->RegNum));
MODEBUS_SLAVE_RetrunUnu(pHandle, pHandle->pRxBuff[0], pHandle->pRxBuff[1], MRTU_UNUS2); //返回异常2
return MRTU_REGN_ERROR; //寄存器数量错误
}
else if(pWriteMultFrame->DataLen != (2*SWAP16(pWriteMultFrame->RegNum)))
{
modebus_debug("写寄存器数据长度错误,需要写入%d个寄存器,长度为:%dB,收到数据长度为:%dBrn", pWriteMultFrame->RegNum, 2*pWriteMultFrame->RegNum, pWriteMultFrame->DataLen);
MODEBUS_SLAVE_RetrunUnu(pHandle, pHandle->pRxBuff[0], pHandle->pRxBuff[1], MRTU_UNUS3); //返回异常3
return MRTU_REGN_ERROR; //寄存器数量错误
}
}break;
default: //不支持的功能码,返回异常1
{
modebus_debug("不支持的操作码:0x%02Xrn", pHandle->pRxBuff[1]);
MODEBUS_SLAVE_RetrunUnu(pHandle, pHandle->pRxBuff[0], pHandle->pRxBuff[1], MRTU_UNUS1); //返回异常1
return MRTU_FUNR_ERROR;
}
}
*pFun = pHandle->pRxBuff[1]; //返回功能码
return MRTU_OK; //返回成功
}
else
{
modebus_debug("CRC校验错误,计算CRC为:0x%04X,返回CRC为:0x%04Xrn",crc16,(u16)(pHandle->pRxBuff[DataLen-2]<pRxBuff[DataLen-1]);
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 > 127) || (RegNum == 0)) return MRTU_REGN_ERROR; //寄存器数量错误
pFrame = (MRTU_RETURN_FRAME *)pHandle->pTxBuff;
//数据结构填充
pFrame->addr = SlaveAddr; //从机地址
pFrame->fun = Fun; //功能码,读取
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, 3+pFrame->DataLen);//计算CRC16
pFrame->DataBuff[pFrame->DataLen] = crc16&0xff; //数据发送交换过
pFrame->DataBuff[pFrame->DataLen+1] = crc16>>8; //数据发送交换过
#if MODEBUS_RTU_DBUG
{
u16 i;
modebus_debug("rn<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):rn",3+pFrame->DataLen+2,crc16);
for(i = 0;i < 3+pFrame->DataLen+2;i ++)
{
modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
}
modebus_debug("rn");
}
#endif //MODEBUS_RTU_DBUG
MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, 3+pFrame->DataLen+2); //发送数据
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, 6);//计算CRC16
#if MODEBUS_RTU_DBUG
{
u16 i;
modebus_debug("rn<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):rn",8,crc16);
for(i = 0;i < 8;i ++)
{
modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
}
modebus_debug("rn");
}
#endif //MODEBUS_RTU_DBUG
MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, 8); //发送数据
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 > 127) || (RegNum == 0)) 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, 6); //计算CRC16 




