天天看點

單片機modebus RTU通信實作,采用C語言,可适用于單片機,VC,安卓等

目前使用的是STM32+ucos_ii編寫的,可以移植到安卓以及VC .NET等友善移植使用,采用modebus poll測試過.

隻需要修改響應的通信接口即可,友善多序列槽使用

//modebus_rtu.c

/*************************************************************************************************************
 * 檔案名:		MODEBUS_RTU.c
 * 功能:		MODEBUS_RTU通信協定層
 * 作者:		[email protected]
 * 建立時間:	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:初始化成功
* 依賴	:	序列槽
* 作者	:	[email protected]
* 時間	:	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:通信狀态
* 依賴	:	底層通信驅動
* 作者	:	[email protected]
* 時間	:	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("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",8,crc16);
		for(i = 0;i < 8;i ++)
		{
			modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
		}
		modebus_debug("\r\n");
	}
#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("接收溢出!\r\n");
			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)!\r\n",TimeDelay*10);
		pHandle->ReturnTime = 0xffff;							//接收資料逾時
		return MRTU_TIME_OUT;				//傳回逾時
	}
	pHandle->ReturnTime = TimeDelay*10;							//資料傳回時間
	
#if MODEBUS_RTU_DBUG
	{
		u16 i;
		
		modebus_debug("\r\n-> MODEBUS RTU RXD(%dB)(ping:%dmS):\r\n",cnt1,TimeDelay*10);
		for(i = 0;i < cnt1;i ++)
		{
			modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
		}
		modebus_debug("\r\n");
	}
#endif	//MODEBUS_RTU_DBUG
	
	pReFrame = (MRTU_RETURN_FRAME *)pHandle->pRxBuff;
	//檢查位址
	if(pReFrame->addr != SlaveAddr)
	{
		modebus_debug("位址錯誤,目标位址為:0x%02X,傳回位址為:0x%02X\r\n",SlaveAddr, pReFrame->addr);
		return MRTU_ADDR_ERROR;
	}
	//對接受的資料進行CRC校驗
	crc16 = usMBCRC16(pHandle->pRxBuff, cnt1-2);//計算CRC16
	if((pHandle->pRxBuff[cnt1-1] != (crc16 >> 8)) || (pHandle->pRxBuff[cnt1-2] != (crc16 & 0xff)))
	{
		modebus_debug("CRC校驗錯誤,計算CRC為:0x%04X,傳回CRC為:0x%04X\r\n",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\r\n", 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\r\n", pReFrame->fun);
			return MRTU_FUNR_ERROR;
		}
	}
	//判斷資料長度
	if(pReFrame->DataLen != 2)
	{
		modebus_debug("傳回資料長度錯誤,讀取%d個寄存器,共%dB,隻傳回了%dB\r\n",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:通信狀态
* 依賴	:	底層通信驅動
* 作者	:	[email protected]
* 時間	:	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("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",8,crc16);
		for(i = 0;i < 8;i ++)
		{
			modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
		}
		modebus_debug("\r\n");
	}
#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("接收溢出!\r\n");
			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)!\r\n",TimeDelay*10);
		pHandle->ReturnTime = 0xffff;							//接收資料逾時
		return MRTU_TIME_OUT;				//傳回逾時
	}
	pHandle->ReturnTime = TimeDelay*10;							//資料傳回時間
	
#if MODEBUS_RTU_DBUG
	{
		u16 i;
		
		modebus_debug("\r\n-> MODEBUS RTU RXD(%dB)(ping:%dmS):\r\n",cnt1,TimeDelay*10);
		for(i = 0;i < cnt1;i ++)
		{
			modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
		}
		modebus_debug("\r\n");
	}
#endif	//MODEBUS_RTU_DBUG
	
	pReFrame = (MRTU_RETURN_FRAME *)pHandle->pRxBuff;
	//檢查位址
	if(pReFrame->addr != SlaveAddr)
	{
		modebus_debug("位址錯誤,目标位址為:0x%02X,傳回位址為:0x%02X\r\n",SlaveAddr, pReFrame->addr);
		return MRTU_ADDR_ERROR;
	}
	//對接受的資料進行CRC校驗
	crc16 = usMBCRC16(pHandle->pRxBuff, cnt1-2);//計算CRC16
	if((pHandle->pRxBuff[cnt1-1] != (crc16 >> 8)) || (pHandle->pRxBuff[cnt1-2] != (crc16 & 0xff)))
	{
		modebus_debug("CRC校驗錯誤,計算CRC為:0x%04X,傳回CRC為:0x%04X\r\n",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\r\n", 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\r\n", pReFrame->fun);
			return MRTU_FUNR_ERROR;
		}
	}
	//判斷資料長度
	if(pReFrame->DataLen != (RegNum*2))
	{
		modebus_debug("傳回資料長度錯誤,讀取%d個寄存器,共%dB,隻傳回了%dB\r\n",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:通信狀态
* 依賴	:	底層通信驅動
* 作者	:	[email protected]
* 時間	:	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("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",8,crc16);
		for(i = 0;i < 8;i ++)
		{
			modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
		}
		modebus_debug("\r\n");
	}
#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("接收溢出!\r\n");
			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)!\r\n",TimeDelay*10);
		pHandle->ReturnTime = 0xffff;							//接收資料逾時
		return MRTU_TIME_OUT;				//傳回逾時
	}
	pHandle->ReturnTime = TimeDelay*10;							//資料傳回時間
	
#if MODEBUS_RTU_DBUG
	{
		u16 i;
		
		modebus_debug("\r\n-> MODEBUS RTU RXD(%dB)(ping:%dmS):\r\n",cnt1,TimeDelay*10);
		for(i = 0;i < cnt1;i ++)
		{
			modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
		}
		modebus_debug("\r\n");
	}
#endif	//MODEBUS_RTU_DBUG
	
	pReFrame = (MRTU_WRITE_FRAME *)pHandle->pRxBuff;
	//檢查位址
	if(pReFrame->addr != SlaveAddr)
	{
		modebus_debug("位址錯誤,目标位址為:0x%02X,傳回位址為:0x%02X\r\n",SlaveAddr, pReFrame->addr);
		return MRTU_ADDR_ERROR;
	}
	//對接受的資料進行CRC校驗
	crc16 = usMBCRC16(pHandle->pRxBuff, cnt1-2);//計算CRC16
	if((pHandle->pRxBuff[cnt1-1] != (crc16 >> 8)) || (pHandle->pRxBuff[cnt1-2] != (crc16 & 0xff)))
	{
		modebus_debug("CRC校驗錯誤,計算CRC為:0x%04X,傳回CRC為:0x%04X\r\n",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\r\n", 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\r\n", pReFrame->fun);
			return MRTU_FUNR_ERROR;
		}
	}
	//判斷資料是否寫入
	if(SWAP16(pReFrame->StartReg) != RegAddr)	//傳回的寄存器位址不一緻
	{
		modebus_debug("傳回寄存器位址錯誤,寫入寄存器%d,傳回寄存器%d\r\n",RegAddr, pReFrame->StartReg);
		return MRTU_REG_ERROR;					//傳回寄存器錯誤
	}
	if(SWAP16(pReFrame->RegData) != RegData)
	{
		modebus_debug("資料寫入錯誤,寫入值:0x%04X,傳回了:0x%04X\r\n",RegData, pReFrame->RegData);
		return MRTU_WRITE_ERROR;				//寫入資料錯誤
	}

	return MRTU_OK;								//傳回成功 
}




/*************************************************************************************************************************
* 函數	:	MRTU_ERROR MODEBUS_HOST_WriteMultReg(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u16 RegAddr, u8 RegNum, u16 pRegData[])
* 功能	:	主機寫從機多個指定寄存器
* 參數	:	pHandle:modebus句柄;SlaveAddr:從機位址;RegAddr:寫寄存器位址;RegNum:寄存器數量, pRegData:需要寫入的寄存器的值
			寫入寄存器的值按照循序排列,使用小端格式,大小必須為RegNum*2
* 傳回	:	MRTU_ERROR:通信狀态
* 依賴	:	底層通信驅動
* 作者	:	[email protected]
* 時間	:	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("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",7+pFrame->DataLen+2,crc16);
		for(i = 0;i < 7+pFrame->DataLen+2;i ++)
		{
			modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
		}
		modebus_debug("\r\n");
	}
#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("接收溢出!\r\n");
			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)!\r\n",TimeDelay*10);
		pHandle->ReturnTime = 0xffff;							//接收資料逾時
		return MRTU_TIME_OUT;				//傳回逾時
	}
	pHandle->ReturnTime = TimeDelay*10;							//資料傳回時間
	
#if MODEBUS_RTU_DBUG
	{
		u16 i;
		
		modebus_debug("\r\n-> MODEBUS RTU RXD(%dB)(ping:%dmS):\r\n",cnt1,TimeDelay*10);
		for(i = 0;i < cnt1;i ++)
		{
			modebus_debug("0x%02X ", pHandle->pRxBuff[i]);
		}
		modebus_debug("\r\n");
	}
#endif	//MODEBUS_RTU_DBUG
	
	pReFrame = (MRTU_WRIT_EMULT_RFRAME *)pHandle->pRxBuff;
	//檢查位址
	if(pReFrame->addr != SlaveAddr)
	{
		modebus_debug("位址錯誤,目标位址為:0x%02X,傳回位址為:0x%02X\r\n",SlaveAddr, pReFrame->addr);
		return MRTU_ADDR_ERROR;
	}
	//對接受的資料進行CRC校驗
	crc16 = usMBCRC16(pHandle->pRxBuff, cnt1-2);//計算CRC16
	if((pHandle->pRxBuff[cnt1-1] != (crc16 >> 8)) || (pHandle->pRxBuff[cnt1-2] != (crc16 & 0xff)))
	{
		modebus_debug("CRC校驗錯誤,計算CRC為:0x%04X,傳回CRC為:0x%04X\r\n",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\r\n", 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\r\n", pReFrame->fun);
			return MRTU_FUNR_ERROR;
		}
	}
	//判斷資料是否寫入
	if(SWAP16(pReFrame->StartReg) != RegAddr)	//傳回的寄存器位址不一緻
	{
		modebus_debug("傳回寄存器位址錯誤,寫入寄存器%d,傳回寄存器%d\r\n",RegAddr, pReFrame->StartReg);
		return MRTU_REG_ERROR;					//傳回寄存器錯誤
	}
	if(SWAP16(pReFrame->RegNum) != RegNum)
	{
		modebus_debug("寫入寄存器數量錯誤,寫入%d個寄存器,傳回%d個寄存器\r\n",RegNum, pReFrame->RegNum);
		return MRTU_WRITE_ERROR;				//寫入資料錯誤
	}

	return MRTU_OK;								//傳回成功 
}
#endif //MODEBUS_RTU_HOST



#if(MODEBUS_RTU_SLAVE) //開啟從機模式
/*************************************************************************************************************************
* 函數	:	bool MODEBUS_SLAVE_RetrunUnu(MODEBUS_HANDLE *pHandle,u8 SlaveAddr, u8 Fun, MRTU_UNUS Unus)
* 功能	:	從機傳回異常編碼
* 參數	:	pHandle:modebus句柄;SlaveAddr:從機位址;Fun:來自主機的功能碼;Unus:異常碼,見MRTU_UNUS
* 傳回	:	TRUE:發送成功;FALSE:發送失敗
* 依賴	:	底層通信驅動
* 作者	:	[email protected]
* 時間	:	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("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",5, crc16);
		for(i = 0;i < 5;i ++)
		{
			modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
		}
		modebus_debug("\r\n");
	}
#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:才是有效資料包
* 依賴	:	底層通信驅動
* 作者	:	[email protected]
* 時間	:	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;\r\n", pHandle->pRxBuff[0], SlaveAddr);
		return MRTU_ADDR_ERROR;
	}
	//對接受的資料進行CRC校驗
	crc16 = usMBCRC16(pHandle->pRxBuff, DataLen-2);			//計算CRC16	
	
#if MODEBUS_RTU_DBUG
	{
		u16 i;
		
		modebus_debug("\r\n-> MODEBUS RTU RXD(%dB)(CRC:0x%04X):\r\n",DataLen,crc16);
		for(i = 0;i < DataLen;i ++)
		{
			modebus_debug("0x%02X ",pHandle->pRxBuff[i]);
		}
		modebus_debug("\r\n");
	}
#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\r\n", 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\r\n", 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\r\n", 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\r\n", 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\r\n",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:通信狀态
* 依賴	:	底層通信驅動
* 作者	:	[email protected]
* 時間	:	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("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",3+pFrame->DataLen+2,crc16);
		for(i = 0;i < 3+pFrame->DataLen+2;i ++)
		{
			modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
		}
		modebus_debug("\r\n");
	}
#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:通信狀态
* 依賴	:	底層通信驅動
* 作者	:	[email protected]
* 時間	:	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("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",8,crc16);
		for(i = 0;i < 8;i ++)
		{
			modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
		}
		modebus_debug("\r\n");
	}
#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:通信狀态
* 依賴	:	底層通信驅動
* 作者	:	[email protected]
* 時間	:	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("\r\n<- MODEBUS RTU TXD(%dB)(CRC:0x%04X):\r\n",8,pFrame->crc16);
		for(i = 0;i < 8;i ++)
		{
			modebus_debug("0x%02X ",pHandle->pTxBuff[i]);
		}
		modebus_debug("\r\n");
	}
#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:通信狀态
* 依賴	:	底層通信驅動
* 作者	:	[email protected]
* 時間	:	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:通信狀态
* 依賴	:	底層通信驅動
* 作者	:	[email protected]
* 時間	:	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:通信狀态
* 依賴	:	底層通信驅動
* 作者	:	[email protected]
* 時間	:	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
* 傳回	:	無
* 依賴	:	無
* 作者	:	[email protected]
* 時間	:	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的整形資料
* 依賴	:	無
* 作者	:	[email protected]
* 時間	:	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通信協定層
 * 作者:		[email protected]
 * 建立時間:	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\r\n", Fun);
				switch(Fun)
				{
					case MRTU_FUN_READ_HOLD		:					//讀保持寄存器,可讀寫寄存器為保持寄存器
					case MRTU_FUN_READ_INPUT	:					//讀輸入寄存器,為隻讀寄存器
					{
						//解析
						MODEBUS_SLAVE_ReadUnpack(&ModebusHandle1, &FrameInfo);
						//資料處理
						uart_printf("主機讀取從機(%d)從%d開始的寄存器,共需要讀取%d個\r\n", FrameInfo.SlaveAddr,  FrameInfo.StartReg, FrameInfo.RegNum);
						//傳回
						MODEBUS_SLAVE_ReturnReadReg(&ModebusHandle1, FrameInfo.fun, FrameInfo.SlaveAddr, FrameInfo.StartReg, FrameInfo.RegNum, RegBuff);
					}break;
					case MRTU_FUN_WRITE			:					//寫單個保持寄存器
					{
						//解析
						MODEBUS_SLAVE_WriteUnpack(&ModebusHandle1, &FrameInfo, &data);
						//資料處理
						uart_printf("主機寫從機(%d)的寄存器%d值為:0x%02X\r\n", FrameInfo.SlaveAddr,  FrameInfo.StartReg, data);
						//傳回
						MODEBUS_SLAVE_ReturnWriteHoldReg(&ModebusHandle1, FrameInfo.SlaveAddr, FrameInfo.StartReg, data);
					}break;
					case MRTU_FUN_MWRITE		:					//寫多個保持寄存器
					{
						//解析
						MODEBUS_SLAVE_WriteMultUnpack(&ModebusHandle1, &FrameInfo, RegBuff);
						//資料處理
						uart_printf("主機寫從機(%d),從%d開始的寄存器,共%d個,資料為:", FrameInfo.SlaveAddr,  FrameInfo.StartReg, FrameInfo.RegNum);
						for(i = 0;i < FrameInfo.RegNum;i ++)
						{
							uart_printf("0x%04X ", RegBuff[i]);	//列印資料
						}
						uart_printf("\r\n");
						//傳回
						MODEBUS_SLAVE_ReturnWriteMultHoldReg(&ModebusHandle1, FrameInfo.SlaveAddr, FrameInfo.StartReg, FrameInfo.RegNum);
					}break;
					default:break;
				}
				
			}
			MODEBUS_EnableRx(ModebusHandle1.UartCh);	//使能接收
		}
		OSTimeDlyHMSM(0,0,0,200);
	}	
}
           

模拟主機

單片機modebus RTU通信實作,采用C語言,可适用于單片機,VC,安卓等

從機接收處理

單片機modebus RTU通信實作,采用C語言,可适用于單片機,VC,安卓等

繼續閱讀