当前位置:首页 > 芯闻号 > 充电吧
[导读]当前使用的是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
本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭