• 单片机modebus RTU通信实现,採用C语言,可适用于单片机,VC,安卓等


    当前使用的是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("
    <- 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;	//低位
    	
    #if MODEBUS_RTU_DBUG
    	{
    		u16 i;
    		
    		modebus_debug("
    <- MODEBUS RTU TXD(%dB)(CRC:0x%04X):
    ",7+pFrame->DataLen+2,crc16);
    		for(i = 0;i < 7+pFrame->DataLen+2;i ++)
    		{
    			modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
    		}
    		modebus_debug("
    ");
    	}
    #endif	//MODEBUS_RTU_DBUG
    	
    	MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, 7+pFrame->DataLen+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_WRIT_EMULT_RFRAME *)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_MWRITE)
    	{
    		pUnuFrame = (MRTU_UNU_FRAME *)pHandle->pRxBuff;		//异常数据帧
    		if(pUnuFrame->ErrorFun == ((u8)MRTU_FUN_MWRITE|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->RegNum) != RegNum)
    	{
    		modebus_debug("写入寄存器数量错误,写入%d个寄存器,返回%d个寄存器
    ",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("
    <- MODEBUS RTU TXD(%dB)(CRC:0x%04X):
    ",5, crc16);
    		for(i = 0;i < 5;i ++)
    		{
    			modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
    		}
    		modebus_debug("
    ");
    	}
    #endif	//MODEBUS_RTU_DBUG
    	
    	MODEBUS_SendData(pHandle->UartCh, pHandle->pTxBuff, 5);	//发送数据
    	
    	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[0] != SlaveAddr)
    	{
    		modebus_debug("地址不符,丢弃;目标地址:0x%02X;本机地址:0x%02X;
    ", pHandle->pRxBuff[0], SlaveAddr);
    		return MRTU_ADDR_ERROR;
    	}
    	//对接受的数据进行CRC校验
    	crc16 = usMBCRC16(pHandle->pRxBuff, DataLen-2);			//计算CRC16	
    	
    #if MODEBUS_RTU_DBUG
    	{
    		u16 i;
    		
    		modebus_debug("
    -> MODEBUS RTU RXD(%dB)(CRC:0x%04X):
    ",DataLen,crc16);
    		for(i = 0;i < DataLen;i ++)
    		{
    			modebus_debug("0x%02X ",pHandle->pRxBuff[i]);
    		}
    		modebus_debug("
    ");
    	}
    #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("读取寄存器数量错误,读取寄存器数量为:%d
    ", 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("写寄存器数量错误,读取寄存器数量为:%d
    ", 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,收到数据长度为:%dB
    ", 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%02X
    ", 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%04X
    ",crc16,(u16)(pHandle->pRxBuff[DataLen-2]<<8)|pHandle->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("
    <- MODEBUS RTU TXD(%dB)(CRC:0x%04X):
    ",3+pFrame->DataLen+2,crc16);
    		for(i = 0;i < 3+pFrame->DataLen+2;i ++)
    		{
    			modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
    		}
    		modebus_debug("
    ");
    	}
    #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("
    <- 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, 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
    #if MODEBUS_RTU_DBUG
    	{
    		u16 i;
    		
    		modebus_debug("
    <- MODEBUS RTU TXD(%dB)(CRC:0x%04X):
    ",8,pFrame->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, 8);	//发送数据
    
    	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 = 1;										//寄存器数量
    	*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 = 0;i < pFrameInfo->RegNum;i ++)
    	{
    		pDataBuff[i] = pWriteMultRegFrame->DataBuff[2*i];
    		pDataBuff[i] <<= 8;
    		pDataBuff[i] |= pWriteMultRegFrame->DataBuff[2*i+1];
    	}			
    }
    #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 << 8 | 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 >> 16);
    	*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 <<= 16;
    	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[2];		//寄存器的值	
    } 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[2];		//数据区,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 				= 	0,		//OK
    	MRTU_TIME_OUT 			= 	1,		//超时
    	MRTU_OVER_ERROR 		= 	2,		//溢出
    	MRTU_CRC_ERROR			=	3,		//CRC错误
    	MRTU_ADDR_ERROR			=	4,		//地址错误,返回地址不一致
    	MRTU_REG_ERROR			=	5,		//寄存器地址错误,返回寄存器地址不一致
    	MRTU_FUNR_ERROR			=	6,		//功能码错误,返回功能码不一致或者不支持的功能码
    	MRTU_HANDLE_ERROR		=	7,		//句柄错误,句柄为空
    	MRTU_REGN_ERROR			=	8,		//寄存器数量错误
    	MRTU_LEN_ERROR			=	9,		//返回数据长度错误
    	MRTU_WRITE_ERROR		=	10,		//写寄存器错误,写入与读取不一致
    	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[128];
    void TaskModeBus(void *pdata)
    {
    	MODEBUS_HANDLE ModebusHandle1;
    	MRTU_SLAVE_INFO FrameInfo;	//modebus读写信息
    	
    	u16 data;
    	u16 RegBuff[20];
    	u8 i;
    	u32 cnt1 = 0,cnt2 = 0;
    	u8 Fun;
    	
    	
    	for(i = 0;i < 10;i ++)
    	{
    		RegBuff[i] = i+1;
    	}
    	MODEBUS_Init(&ModebusHandle1, UART_CH2, 115200, ModebusBuff, ModebusBuff, 128-1, 200);
    	MODEBUS_EnableRx(ModebusHandle1.UartCh);	//使能接收
    	MODEBUS_ClearRxCnt(ModebusHandle1.UartCh);	//清除接收
    	while(1)
    	{
    		cnt2 = cnt1;
    		cnt1 = MODEBUS_GetDataCnt(ModebusHandle1.UartCh);
    		if((cnt1 == cnt2) && (cnt1 != 0))
    		{
    			MODEBUS_DisableRx(ModebusHandle1.UartCh);	//关闭接收
    			MODEBUS_ClearRxCnt(ModebusHandle1.UartCh);	//清除接收
    			if(MODEBUS_SLAVE_FramesUnpack(&ModebusHandle1, 1, cnt1, &Fun) == MRTU_OK)
    			{
    				uart_printf("收到数据,功能码:0x%02X
    ", Fun);
    				switch(Fun)
    				{
    					case MRTU_FUN_READ_HOLD		:					//读保持寄存器,可读写寄存器为保持寄存器
    					case MRTU_FUN_READ_INPUT	:					//读输入寄存器,为仅仅读寄存器
    					{
    						//解析
    						MODEBUS_SLAVE_ReadUnpack(&ModebusHandle1, &FrameInfo);
    						//数据处理
    						uart_printf("主机读取从机(%d)从%d開始的寄存器,共须要读取%d个
    ", 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
    ", 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 = 0;i < FrameInfo.RegNum;i ++)
    						{
    							uart_printf("0x%04X ", RegBuff[i]);	//打印数据
    						}
    						uart_printf("
    ");
    						//返回
    						MODEBUS_SLAVE_ReturnWriteMultHoldReg(&ModebusHandle1, FrameInfo.SlaveAddr, FrameInfo.StartReg, FrameInfo.RegNum);
    					}break;
    					default:break;
    				}
    				
    			}
    			MODEBUS_EnableRx(ModebusHandle1.UartCh);	//使能接收
    		}
    		OSTimeDlyHMSM(0,0,0,200);
    	}	
    }

    模拟主机

    从机接收处理




  • 相关阅读:
    Idea配置注释
    mysql省市区数据表
    php实现汉字转拼音的类
    git push时如果不再弹出用户和密码的输入提示框该怎么办
    .gitignore 忽略特殊文件无效
    vmware中CentOS7网络设置教程详解
    Tp5中配置多模块开发
    安装tp6 并使用多应用模式
    ldf和mdf文件怎么还原到sqlserver数据库
    git本地仓库与远程仓库建立连接。
  • 原文地址:https://www.cnblogs.com/hrhguanli/p/5072153.html
Copyright © 2020-2023  润新知