源:
单片机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;
//低位