當(dāng)前位置:首頁 > 芯聞號 > 充電吧
[導(dǎo)讀]當(dāng)前使用的是STM32+ucos_ii編寫的,可以移植到安卓以及VC .NET等方便移植使用,采用modebus poll測試過.只需要修改響應(yīng)的通信接口即可,方便多串口使用//modebus_rtu

當(dāng)前使用的是STM32+ucos_ii編寫的,可以移植到安卓以及VC .NET等方便移植使用,采用modebus poll測試過.

只需要修改響應(yīng)的通信接口即可,方便多串口使用



//modebus_rtu.c


/*************************************************************************************************************
?*?文件名:		MODEBUS_RTU.c
?*?功能:		MODEBUS_RTU通信協(xié)議層
?*?作者:		cp1300@139.com
?*?創(chuàng)建時間:	2014-03-24
?*?最后修改時間:2014-11-17
?*?詳細(xì):		MODEBUS?RTU通信協(xié)議層
*************************************************************************************************************/
#include?"system.h"
#include?"usart.h"
#include?"delay.h"
#include?"MODEBUS_RTU.h"






//調(diào)試開關(guān)
#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





/*************************************************************************************************************************
*?函數(shù)	:	bool?MODEBUS_Init(MODEBUS_HANDLE?*pHandle,?u8?UartCh,?u32?BaudRate,?u8?*pRxBuff,u8?*pTxBuff,?u32?RxBuffSize,?u32?TimeOut)
*?功能	:	MODEBUS?初始化
*?參數(shù)	:	pHandle:當(dāng)前初始化的modebus句柄,UartCh:使用的串口通道;BaudRate:使用的波特率;pRxBuff:接收緩沖區(qū)指針;
			RxBuffSize:接收緩沖區(qū)大小;pTxBuff:發(fā)送緩沖區(qū)指針;TimeOut:接收超時,單位ms
*?返回	:	FALSE:初始化失敗;TRUE:初始化成功
*?依賴	:	串口
*?作者	:	cp1300@139.com
*?時間	:	2014-09-25
*?最后修改時間?:?2014-11-10
*?說明	:?	收發(fā)緩沖區(qū)可以與發(fā)送緩沖區(qū)使用同一緩沖區(qū)
			發(fā)送緩沖區(qū)必須大于最大數(shù)據(jù)包大小,否則會出現(xiàn)內(nèi)存溢出
*************************************************************************************************************************/
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;													//發(fā)送數(shù)據(jù)包計數(shù)
	pHandle->RxPacketNum?=?0;													//接收數(shù)據(jù)包計數(shù)
	pHandle->ErrorNum?=?0;														//通信錯誤計數(shù)
	pHandle->ReturnTime?=?0;													//數(shù)據(jù)返回時間
	//設(shè)置串口
	if(MODEBUS_UartInit(UartCh,?BaudRate)?==?FALSE)								//初始化串口
	{
		pHandle->UartCh?=?0xff;													//通道無效
		pHandle->pRxBuff?=?pHandle->pTxBuff?=?NULL;								//緩沖區(qū)無效
		pHandle->RxBuffSize?=?0;												//緩沖區(qū)大小為0
	}
	MODEBUS_SetRxBuff(UartCh,?pRxBuff,?RxBuffSize);					
	MODEBUS_DisableRx(UartCh);													//關(guān)閉串口接收
	pHandle->UartCh?=?UartCh;													//通道
	pHandle->pRxBuff?=?pRxBuff;
	pHandle->pTxBuff?=?pTxBuff;													//緩沖區(qū)
	pHandle->RxBuffSize?=?RxBuffSize;											//緩沖區(qū)大小
	if(TimeOut?==?0)?TimeOut?=?1;
	pHandle->TimeOut?=?TimeOut;
	pHandle->BaudRate?=?BaudRate;
	
	return?TRUE;
}




#if(MODEBUS_RTU_HOST)?//開啟主機(jī)模式
/*************************************************************************************************************************
*?函數(shù)	:	MRTU_ERROR?MODEBUS_HOST_ReadReg(MODEBUS_HANDLE?*pHandle,?READ_REG_TYPE?RegType,?u8?SlaveAddr,?u16?RegAddr,?u16?*pRegData)
*?功能	:	主機(jī)讀取從機(jī)一個指定寄存器
*?參數(shù)	:	pHandle:modebus句柄;RegType:讀取的寄存器類型;SlaveAddr:從機(jī)地址;RegAddr:需讀取的寄存器地址;pRegData:寄存器的值
*?返回	:	MRTU_ERROR:通信狀態(tài)
*?依賴	:	底層通信驅(qū)動
*?作者	:	cp1300@139.com
*?時間	:	2014-03-24
*?最后修改時間?:?2014-11-16
*?說明	:?	MOUEBUS?RTU讀取數(shù)據(jù),讀取一個寄存器
			輸入輸出的數(shù)據(jù)都為小端模式
*************************************************************************************************************************/
MRTU_ERROR?MODEBUS_HOST_ReadReg(MODEBUS_HANDLE?*pHandle,?READ_REG_TYPE?RegType,?u8?SlaveAddr,?u16?RegAddr,?u16?*pRegData)
{
	MRTU_READ_FRAME?*pFrame;		//發(fā)送數(shù)據(jù)幀格式
	MRTU_RETURN_FRAME?*pReFrame;	//返回數(shù)據(jù)幀格式
	MRTU_UNU_FRAME	*pUnuFrame;		//返回的異常數(shù)據(jù)幀格式
	u16?crc16;
	u16?cnt1,?cnt2=0;				//接收數(shù)據(jù)計數(shù)器
	u16?TimeOut;
	u16?TimeDelay?=?0;				//用于計算數(shù)據(jù)接收延時

	
	if(pHandle?==?NULL)?return?MRTU_HANDLE_ERROR;	//句柄無效
	TimeOut?=?pHandle->TimeOut/10+1;				//超時初值
	pFrame?=?(MRTU_READ_FRAME?*)pHandle->pTxBuff;
	//數(shù)據(jù)結(jié)構(gòu)填充
	pFrame->addr?=?SlaveAddr;						//從機(jī)地址
	pFrame->fun?=?(u8)RegType;						//功能碼,讀取
	pFrame->StartReg?=?SWAP16(RegAddr);				//寄存器起始地址
	pFrame->RegNum?=?SWAP16(1);						//需要讀取的寄存器數(shù)量,1	
	crc16?=?usMBCRC16(pHandle->pTxBuff,?6);			//計算CRC16
	pFrame->CRC16?=?crc16;							//crc16

#if?MODEBUS_RTU_DBUG
	{
		u16?i;
		
		modebus_debug("rn?MODEBUS?RTU?RXD(%dB)(ping:%dmS):rn",cnt1,TimeDelay*10);
		for(i?=?0;i?<?cnt1;i?++)
		{
			modebus_debug("0x%02X?",?pHandle->pRxBuff[i]);
		}
		modebus_debug("rn");
	}
#endif	//MODEBUS_RTU_DBUG
	
	pReFrame?=?(MRTU_RETURN_FRAME?*)pHandle->pRxBuff;
	//檢查地址
	if(pReFrame->addr?!=?SlaveAddr)
	{
		modebus_debug("地址錯誤,目標(biāo)地址為:0x%02X,返回地址為:0x%02Xrn",SlaveAddr,?pReFrame->addr);
		return?MRTU_ADDR_ERROR;
	}
	//對接受的數(shù)據(jù)進(jìn)行CRC校驗
	crc16?=?usMBCRC16(pHandle->pRxBuff,?cnt1-2);//計算CRC16
	if((pHandle->pRxBuff[cnt1-1]?!=?(crc16?>>?8))?||?(pHandle->pRxBuff[cnt1-2]?!=?(crc16?&?0xff)))
	{
		modebus_debug("CRC校驗錯誤,計算CRC為:0x%04X,返回CRC為:0x%04Xrn",crc16,(u16)(pHandle->pRxBuff[cnt1-2]<pRxBuff[cnt1-1]);
		return?MRTU_CRC_ERROR;				//返回CRC校驗錯誤
	}
	//返回的功能碼不一致
	if(pReFrame->fun?!=?(u8)RegType)
	{
		pUnuFrame?=?(MRTU_UNU_FRAME?*)pHandle->pRxBuff;		//異常數(shù)據(jù)幀
		if(pUnuFrame->ErrorFun?==?((u8)RegType|0x80))		//返回有異常
		{
			modebus_debug("返回異常,異常碼%drn",?pUnuFrame->unu);
			switch(pUnuFrame->unu)
			{
				case?1:?return?MRTU_UNUS1_ERROR;			//異常碼1
				case?2:?return?MRTU_UNUS2_ERROR;			//異常碼2
				case?3:?return?MRTU_UNUS3_ERROR;			//異常碼3
				case?4:?return?MRTU_UNUS4_ERROR;			//異常碼4
				case?5:?return?MRTU_UNUS5_ERROR;			//異常碼5
				case?6:?return?MRTU_UNUS6_ERROR;			//異常碼6
				default:?return?MRTU_OTHER_ERROR;
			}
		}
		else
		{
			modebus_debug("返回錯誤,返回功能碼為0x%02Xrn",?pReFrame->fun);
			return?MRTU_FUNR_ERROR;
		}
	}
	//判斷數(shù)據(jù)長度
	if(pReFrame->DataLen?!=?2)
	{
		modebus_debug("返回數(shù)據(jù)長度錯誤,讀取%d個寄存器,共%dB,只返回了%dBrn",1,?1*2,?pReFrame->DataLen);
		return?MRTU_LEN_ERROR;				//返回數(shù)據(jù)長度錯誤
	}
	//獲取返回的寄存器的值
	*pRegData?=?pReFrame->DataBuff[0];
	*pRegData?<DataBuff[1];
	
	return?MRTU_OK;						//返回成功?
}




/*************************************************************************************************************************
*?函數(shù)	:	MRTU_ERROR?MODEBUS_HOST_ReadMultReg(MODEBUS_HANDLE?*pHandle,?READ_REG_TYPE?RegType,?u8?SlaveAddr,?u16?RegAddr,?u8?RegNum,?u16?pRegData[])
*?功能	:	主機(jī)讀取從機(jī)指定多個連續(xù)寄存器
*?參數(shù)	:	pHandle:modebus句柄;RegType:讀取的寄存器類型;SlaveAddr:從機(jī)地址;RegAddr:需讀取的寄存器地址;RegNum:寄存器數(shù)量;pRegData:返回寄存器的值,至少為RegNum的2倍
			返回的寄存器的值按照循序存放在pRegData中
*?返回	:	MRTU_ERROR:通信狀態(tài)
*?依賴	:	底層通信驅(qū)動
*?作者	:	cp1300@139.com
*?時間	:	2014-03-24
*?最后修改時間?:?2014-11-16
*?說明	:?	MOUEBUS?RTU讀取數(shù)據(jù),讀取一個寄存器
			輸入輸出的數(shù)據(jù)都為小端模式
*************************************************************************************************************************/
MRTU_ERROR?MODEBUS_HOST_ReadMultReg(MODEBUS_HANDLE?*pHandle,?READ_REG_TYPE?RegType,?u8?SlaveAddr,?u16?RegAddr,?u8?RegNum,?u16?pRegData[])
{
	MRTU_READ_FRAME?*pFrame;		//發(fā)送數(shù)據(jù)幀格式
	MRTU_RETURN_FRAME?*pReFrame;	//返回數(shù)據(jù)幀格式
	MRTU_UNU_FRAME	*pUnuFrame;		//返回的異常數(shù)據(jù)幀格式
	u16?crc16;
	u16?cnt1,?cnt2=0;				//接收數(shù)據(jù)計數(shù)器
	u16?TimeOut;
	u16?TimeDelay?=?0;				//用于計算數(shù)據(jù)接收延時
	u8?i;

	
	if(pHandle?==?NULL)?return?MRTU_HANDLE_ERROR;	//句柄無效
	TimeOut?=?pHandle->TimeOut/10+1;				//超時初值
	pFrame?=?(MRTU_READ_FRAME?*)pHandle->pTxBuff;
	//數(shù)據(jù)結(jié)構(gòu)填充
	pFrame->addr?=?SlaveAddr;						//從機(jī)地址
	pFrame->fun?=?(u8)RegType;						//功能碼,讀取
	pFrame->StartReg?=?SWAP16(RegAddr);				//寄存器起始地址
	if((RegNum?>?127)?||?(RegNum?==?0))	return?MRTU_REGN_ERROR;	//寄存器數(shù)量錯誤
	pFrame->RegNum?=?SWAP16(RegNum);				//需要讀取的寄存器數(shù)量
	crc16?=?usMBCRC16(pHandle->pTxBuff,?6);			//計算CRC16
	pFrame->CRC16?=?crc16;							//crc16

#if?MODEBUS_RTU_DBUG
	{
		u16?i;
		
		modebus_debug("rn?MODEBUS?RTU?RXD(%dB)(ping:%dmS):rn",cnt1,TimeDelay*10);
		for(i?=?0;i?<?cnt1;i?++)
		{
			modebus_debug("0x%02X?",?pHandle->pRxBuff[i]);
		}
		modebus_debug("rn");
	}
#endif	//MODEBUS_RTU_DBUG
	
	pReFrame?=?(MRTU_RETURN_FRAME?*)pHandle->pRxBuff;
	//檢查地址
	if(pReFrame->addr?!=?SlaveAddr)
	{
		modebus_debug("地址錯誤,目標(biāo)地址為:0x%02X,返回地址為:0x%02Xrn",SlaveAddr,?pReFrame->addr);
		return?MRTU_ADDR_ERROR;
	}
	//對接受的數(shù)據(jù)進(jìn)行CRC校驗
	crc16?=?usMBCRC16(pHandle->pRxBuff,?cnt1-2);//計算CRC16
	if((pHandle->pRxBuff[cnt1-1]?!=?(crc16?>>?8))?||?(pHandle->pRxBuff[cnt1-2]?!=?(crc16?&?0xff)))
	{
		modebus_debug("CRC校驗錯誤,計算CRC為:0x%04X,返回CRC為:0x%04Xrn",crc16,(u16)(pHandle->pRxBuff[cnt1-2]<pRxBuff[cnt1-1]);
		return?MRTU_CRC_ERROR;				//返回CRC校驗錯誤
	}
	//返回的功能碼不一致
	if(pReFrame->fun?!=?(u8)RegType)
	{
		pUnuFrame?=?(MRTU_UNU_FRAME?*)pHandle->pRxBuff;		//異常數(shù)據(jù)幀
		if(pUnuFrame->ErrorFun?==?((u8)RegType|0x80))		//返回有異常
		{
			modebus_debug("返回異常,異常碼%drn",?pUnuFrame->unu);
			switch(pUnuFrame->unu)
			{
				case?1:?return?MRTU_UNUS1_ERROR;			//異常碼1
				case?2:?return?MRTU_UNUS2_ERROR;			//異常碼2
				case?3:?return?MRTU_UNUS3_ERROR;			//異常碼3
				case?4:?return?MRTU_UNUS4_ERROR;			//異常碼4
				case?5:?return?MRTU_UNUS5_ERROR;			//異常碼5
				case?6:?return?MRTU_UNUS6_ERROR;			//異常碼6
				default:?return?MRTU_OTHER_ERROR;
			}
		}
		else
		{
			modebus_debug("返回錯誤,返回功能碼為0x%02Xrn",?pReFrame->fun);
			return?MRTU_FUNR_ERROR;
		}
	}
	//判斷數(shù)據(jù)長度
	if(pReFrame->DataLen?!=?(RegNum*2))
	{
		modebus_debug("返回數(shù)據(jù)長度錯誤,讀取%d個寄存器,共%dB,只返回了%dBrn",RegNum,?RegNum*2,?pReFrame->DataLen);
		return?MRTU_LEN_ERROR;				//返回數(shù)據(jù)長度錯誤
	}
	//獲取返回的寄存器的值
	for(i?=?0;i?<?RegNum;i?++)
	{
		pRegData[i]?=?pReFrame->DataBuff[i*2];
		pRegData[i]?<DataBuff[i*2+1];
	}
	
	return?MRTU_OK;						//返回成功?
}






/*************************************************************************************************************************
*?函數(shù)	:	MRTU_ERROR?MODEBUS_HOST_WriteReg(MODEBUS_HANDLE?*pHandle,u8?SlaveAddr,?u16?RegAddr,?u16?RegData)
*?功能	:	主機(jī)寫從機(jī)一個指定寄存器
*?參數(shù)	:	pHandle:modebus句柄;SlaveAddr:從機(jī)地址;RegAddr:寫寄存器地址;RegData:寄存器的值
*?返回	:	MRTU_ERROR:通信狀態(tài)
*?依賴	:	底層通信驅(qū)動
*?作者	:	cp1300@139.com
*?時間	:	2014-03-24
*?最后修改時間?:?2014-11-16
*?說明	:?	MOUEBUS?RTU寫從機(jī)一個保持寄存器
			輸入輸出的數(shù)據(jù)都為小端模式
			預(yù)置單個寄存器的發(fā)送與接收數(shù)據(jù)包格式完全一致,理論上發(fā)送與接收的數(shù)據(jù)都應(yīng)該一致
*************************************************************************************************************************/
MRTU_ERROR?MODEBUS_HOST_WriteReg(MODEBUS_HANDLE?*pHandle,u8?SlaveAddr,?u16?RegAddr,?u16?RegData)
{
	MRTU_WRITE_FRAME?*pFrame,?*pReFrame;//發(fā)送數(shù)據(jù)幀格式
	MRTU_UNU_FRAME	*pUnuFrame;			//返回的異常數(shù)據(jù)幀格式
	u16?crc16;
	u16?cnt1,?cnt2=0;					//接收數(shù)據(jù)計數(shù)器
	u16?TimeOut;
	u16?TimeDelay?=?0;					//用于計算數(shù)據(jù)接收延時

	
	if(pHandle?==?NULL)?return?MRTU_HANDLE_ERROR;	//句柄無效
	TimeOut?=?pHandle->TimeOut/10+1;				//超時初值
	pFrame?=?(MRTU_WRITE_FRAME?*)pHandle->pTxBuff;
	//數(shù)據(jù)結(jié)構(gòu)填充
	pFrame->addr?=?SlaveAddr;						//從機(jī)地址
	pFrame->fun?=?(u8)MRTU_FUN_WRITE;				//功能碼,預(yù)置單個寄存器
	pFrame->StartReg?=?SWAP16(RegAddr);				//寄存器起始地址
	pFrame->RegData?=?SWAP16(RegData);				//寫入寄存器內(nèi)容
	pFrame->crc16?=?usMBCRC16(pHandle->pTxBuff,?6);	//計算CRC16

#if?MODEBUS_RTU_DBUG
	{
		u16?i;
		
		modebus_debug("rn?MODEBUS?RTU?RXD(%dB)(ping:%dmS):rn",cnt1,TimeDelay*10);
		for(i?=?0;i?<?cnt1;i?++)
		{
			modebus_debug("0x%02X?",?pHandle->pRxBuff[i]);
		}
		modebus_debug("rn");
	}
#endif	//MODEBUS_RTU_DBUG
	
	pReFrame?=?(MRTU_WRITE_FRAME?*)pHandle->pRxBuff;
	//檢查地址
	if(pReFrame->addr?!=?SlaveAddr)
	{
		modebus_debug("地址錯誤,目標(biāo)地址為:0x%02X,返回地址為:0x%02Xrn",SlaveAddr,?pReFrame->addr);
		return?MRTU_ADDR_ERROR;
	}
	//對接受的數(shù)據(jù)進(jìn)行CRC校驗
	crc16?=?usMBCRC16(pHandle->pRxBuff,?cnt1-2);//計算CRC16
	if((pHandle->pRxBuff[cnt1-1]?!=?(crc16?>>?8))?||?(pHandle->pRxBuff[cnt1-2]?!=?(crc16?&?0xff)))
	{
		modebus_debug("CRC校驗錯誤,計算CRC為:0x%04X,返回CRC為:0x%04Xrn",crc16,(u16)(pHandle->pRxBuff[cnt1-2]<pRxBuff[cnt1-1]);
		return?MRTU_CRC_ERROR;				//返回CRC校驗錯誤
	}
	//返回的功能碼不一致
	if(pReFrame->fun?!=?(u8)MRTU_FUN_WRITE)
	{
		pUnuFrame?=?(MRTU_UNU_FRAME?*)pHandle->pRxBuff;		//異常數(shù)據(jù)幀
		if(pUnuFrame->ErrorFun?==?((u8)MRTU_FUN_WRITE|0x80))//返回有異常
		{
			modebus_debug("返回異常,異常碼%drn",?pUnuFrame->unu);
			switch(pUnuFrame->unu)
			{
				case?1:?return?MRTU_UNUS1_ERROR;			//異常碼1
				case?2:?return?MRTU_UNUS2_ERROR;			//異常碼2
				case?3:?return?MRTU_UNUS3_ERROR;			//異常碼3
				case?4:?return?MRTU_UNUS4_ERROR;			//異常碼4
				case?5:?return?MRTU_UNUS5_ERROR;			//異常碼5
				case?6:?return?MRTU_UNUS6_ERROR;			//異常碼6
				default:?return?MRTU_OTHER_ERROR;
			}
		}
		else
		{
			modebus_debug("返回錯誤,返回功能碼為0x%02Xrn",?pReFrame->fun);
			return?MRTU_FUNR_ERROR;
		}
	}
	//判斷數(shù)據(jù)是否寫入
	if(SWAP16(pReFrame->StartReg)?!=?RegAddr)	//返回的寄存器地址不一致
	{
		modebus_debug("返回寄存器地址錯誤,寫入寄存器%d,返回寄存器%drn",RegAddr,?pReFrame->StartReg);
		return?MRTU_REG_ERROR;					//返回寄存器錯誤
	}
	if(SWAP16(pReFrame->RegData)?!=?RegData)
	{
		modebus_debug("數(shù)據(jù)寫入錯誤,寫入值:0x%04X,返回了:0x%04Xrn",RegData,?pReFrame->RegData);
		return?MRTU_WRITE_ERROR;				//寫入數(shù)據(jù)錯誤
	}

	return?MRTU_OK;								//返回成功?
}




/*************************************************************************************************************************
*?函數(shù)	:	MRTU_ERROR?MODEBUS_HOST_WriteMultReg(MODEBUS_HANDLE?*pHandle,u8?SlaveAddr,?u16?RegAddr,?u8?RegNum,?u16?pRegData[])
*?功能	:	主機(jī)寫從機(jī)多個指定寄存器
*?參數(shù)	:	pHandle:modebus句柄;SlaveAddr:從機(jī)地址;RegAddr:寫寄存器地址;RegNum:寄存器數(shù)量,?pRegData:需要寫入的寄存器的值
			寫入寄存器的值按照循序排列,使用小端格式,大小必須為RegNum*2
*?返回	:	MRTU_ERROR:通信狀態(tài)
*?依賴	:	底層通信驅(qū)動
*?作者	:	cp1300@139.com
*?時間	:	2014-03-24
*?最后修改時間?:?2014-11-16
*?說明	:?	MOUEBUS?RTU寫從機(jī)一個保持寄存器
			輸入輸出的數(shù)據(jù)都為小端模式
			返回數(shù)據(jù)寄存器位置與寄存器數(shù)量
*************************************************************************************************************************/
MRTU_ERROR?MODEBUS_HOST_WriteMultReg(MODEBUS_HANDLE?*pHandle,u8?SlaveAddr,?u16?RegAddr,?u8?RegNum,?u16?pRegData[])
{
	MRTU_WRITE_MULT_FRAME?*pFrame;					//發(fā)送數(shù)據(jù)幀格式
	MRTU_WRIT_EMULT_RFRAME?*pReFrame;				//返回數(shù)據(jù)幀格式
	MRTU_UNU_FRAME	*pUnuFrame;						//返回的異常數(shù)據(jù)幀格式
	u16?crc16;
	u16?cnt1,?cnt2=0;								//接收數(shù)據(jù)計數(shù)器
	u16?TimeOut;
	u16?TimeDelay?=?0;								//用于計算數(shù)據(jù)接收延時
	u8?i;
	
	if(pHandle?==?NULL)?return?MRTU_HANDLE_ERROR;	//句柄無效
	TimeOut?=?pHandle->TimeOut/10+1;				//超時初值
	pFrame?=?(MRTU_WRITE_MULT_FRAME?*)pHandle->pTxBuff;
	//數(shù)據(jù)結(jié)構(gòu)填充
	pFrame->addr?=?SlaveAddr;						//從機(jī)地址
	pFrame->fun?=?(u8)MRTU_FUN_MWRITE;				//功能碼,預(yù)置多個寄存器
	pFrame->StartReg?=?SWAP16(RegAddr);				//寄存器起始地址
	if((RegNum?>?127)?||?(RegNum?==?0))	return?MRTU_REGN_ERROR;	//寄存器數(shù)量錯誤
	pFrame->RegNum?=?SWAP16(RegNum);				//寫入寄存器數(shù)量
	pFrame->DataLen?=?2*RegNum;						//數(shù)據(jù)長度
	//循環(huán)寫入數(shù)據(jù)
	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,高低位對調(diào)過
	pFrame->DataBuff[pFrame->DataLen]?=?crc16&0xff;	//高位
	pFrame->DataBuff[pFrame->DataLen+1]=crc16>>8;	//低位
	
#if?MODEBUS_RTU_DBUG
	{
		u16?i;
		
		modebus_debug("rn?MODEBUS?RTU?RXD(%dB)(ping:%dmS):rn",cnt1,TimeDelay*10);
		for(i?=?0;i?<?cnt1;i?++)
		{
			modebus_debug("0x%02X?",?pHandle->pRxBuff[i]);
		}
		modebus_debug("rn");
	}
#endif	//MODEBUS_RTU_DBUG
	
	pReFrame?=?(MRTU_WRIT_EMULT_RFRAME?*)pHandle->pRxBuff;
	//檢查地址
	if(pReFrame->addr?!=?SlaveAddr)
	{
		modebus_debug("地址錯誤,目標(biāo)地址為:0x%02X,返回地址為:0x%02Xrn",SlaveAddr,?pReFrame->addr);
		return?MRTU_ADDR_ERROR;
	}
	//對接受的數(shù)據(jù)進(jìn)行CRC校驗
	crc16?=?usMBCRC16(pHandle->pRxBuff,?cnt1-2);//計算CRC16
	if((pHandle->pRxBuff[cnt1-1]?!=?(crc16?>>?8))?||?(pHandle->pRxBuff[cnt1-2]?!=?(crc16?&?0xff)))
	{
		modebus_debug("CRC校驗錯誤,計算CRC為:0x%04X,返回CRC為:0x%04Xrn",crc16,(u16)(pHandle->pRxBuff[cnt1-2]<pRxBuff[cnt1-1]);
		return?MRTU_CRC_ERROR;				//返回CRC校驗錯誤
	}
	//返回的功能碼不一致
	if(pReFrame->fun?!=?(u8)MRTU_FUN_MWRITE)
	{
		pUnuFrame?=?(MRTU_UNU_FRAME?*)pHandle->pRxBuff;		//異常數(shù)據(jù)幀
		if(pUnuFrame->ErrorFun?==?((u8)MRTU_FUN_MWRITE|0x80))//返回有異常
		{
			modebus_debug("返回異常,異常碼%drn",?pUnuFrame->unu);
			switch(pUnuFrame->unu)
			{
				case?1:?return?MRTU_UNUS1_ERROR;			//異常碼1
				case?2:?return?MRTU_UNUS2_ERROR;			//異常碼2
				case?3:?return?MRTU_UNUS3_ERROR;			//異常碼3
				case?4:?return?MRTU_UNUS4_ERROR;			//異常碼4
				case?5:?return?MRTU_UNUS5_ERROR;			//異常碼5
				case?6:?return?MRTU_UNUS6_ERROR;			//異常碼6
				default:?return?MRTU_OTHER_ERROR;
			}
		}
		else
		{
			modebus_debug("返回錯誤,返回功能碼為0x%02Xrn",?pReFrame->fun);
			return?MRTU_FUNR_ERROR;
		}
	}
	//判斷數(shù)據(jù)是否寫入
	if(SWAP16(pReFrame->StartReg)?!=?RegAddr)	//返回的寄存器地址不一致
	{
		modebus_debug("返回寄存器地址錯誤,寫入寄存器%d,返回寄存器%drn",RegAddr,?pReFrame->StartReg);
		return?MRTU_REG_ERROR;					//返回寄存器錯誤
	}
	if(SWAP16(pReFrame->RegNum)?!=?RegNum)
	{
		modebus_debug("寫入寄存器數(shù)量錯誤,寫入%d個寄存器,返回%d個寄存器rn",RegNum,?pReFrame->RegNum);
		return?MRTU_WRITE_ERROR;				//寫入數(shù)據(jù)錯誤
	}

	return?MRTU_OK;								//返回成功?
}
#endif?//MODEBUS_RTU_HOST



#if(MODEBUS_RTU_SLAVE)?//開啟從機(jī)模式
/*************************************************************************************************************************
*?函數(shù)	:	bool?MODEBUS_SLAVE_RetrunUnu(MODEBUS_HANDLE?*pHandle,u8?SlaveAddr,?u8?Fun,?MRTU_UNUS?Unus)
*?功能	:	從機(jī)返回異常編碼
*?參數(shù)	:	pHandle:modebus句柄;SlaveAddr:從機(jī)地址;Fun:來自主機(jī)的功能碼;Unus:異常碼,見MRTU_UNUS
*?返回	:	TRUE:發(fā)送成功;FALSE:發(fā)送失敗
*?依賴	:	底層通信驅(qū)動
*?作者	:	cp1300@139.com
*?時間	:	2014-03-24
*?最后修改時間?:?2014-11-17
*?說明	:?	從機(jī)返回異常碼給主機(jī),異常碼見:MRTU_UNUS
			MRTU_UNUS1	異常碼1,無效的操作碼
			MRTU_UNUS2	異常碼2,無效的數(shù)據(jù)地址
			MRTU_UNUS3	異常碼3,無效的數(shù)據(jù)值
			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;				//返回異常數(shù)據(jù)包
	u16?crc16;
	
	if(pHandle?==?NULL)?return?FALSE;	//句柄無效
	//數(shù)據(jù)結(jié)構(gòu)填充
	pFrame?=?(MRTU_UNU_FRAME?*)pHandle->pTxBuff;
	pFrame->addr?=?SlaveAddr;						//從機(jī)地址
	pFrame->ErrorFun?=?(u8)Fun|0x80;				//功能碼+0x80,出現(xiàn)異常
	pFrame->unu?=?(u8)Unus;							//異常編碼
	crc16?=?usMBCRC16(pHandle->pTxBuff,?3);			//計算CRC16,高低位對調(diào)過
	pFrame->crc16H?=?crc16?&?0xff;
	pFrame->crc16L?=?crc16>>8;
#if?MODEBUS_RTU_DBUG
	{
		u16?i;
		
		modebus_debug("rn?MODEBUS?RTU?RXD(%dB)(CRC:0x%04X):rn",DataLen,crc16);
		for(i?=?0;i?<?DataLen;i?++)
		{
			modebus_debug("0x%02X?",pHandle->pRxBuff[i]);
		}
		modebus_debug("rn");
	}
#endif	//MODEBUS_RTU_DBUG
	
	
	if((pHandle->pRxBuff[DataLen-1]?==?(crc16?>>?8))?&&?(pHandle->pRxBuff[DataLen-2]?==?(crc16?&?0xff)))
	{
		//判斷功能碼
		switch(pHandle->pRxBuff[1])
		{
			case?MRTU_FUN_READ_HOLD		:	//0x03讀保持寄存器,可讀寫寄存器為保持寄存器
			case?MRTU_FUN_READ_INPUT	:	//0x04讀輸入寄存器,為只讀寄存器	
			{
				pReadFrame?=?(MRTU_READ_FRAME?*)pHandle->pRxBuff;
				if((SWAP16(pReadFrame->RegNum)?>?127)?||?(SWAP16(pReadFrame->RegNum)?==?0))	
				{
					modebus_debug("讀取寄存器數(shù)量錯誤,讀取寄存器數(shù)量為:%drn",?SWAP16(pReadFrame->RegNum));
					MODEBUS_SLAVE_RetrunUnu(pHandle,?pHandle->pRxBuff[0],?pHandle->pRxBuff[1],?MRTU_UNUS2);	//返回異常2
					return?MRTU_REGN_ERROR;	//寄存器數(shù)量錯誤
				}
			}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("寫寄存器數(shù)量錯誤,讀取寄存器數(shù)量為:%drn",?SWAP16(pWriteMultFrame->RegNum));
					MODEBUS_SLAVE_RetrunUnu(pHandle,?pHandle->pRxBuff[0],?pHandle->pRxBuff[1],?MRTU_UNUS2);	//返回異常2
					return?MRTU_REGN_ERROR;	//寄存器數(shù)量錯誤
				}
				else?if(pWriteMultFrame->DataLen?!=?(2*SWAP16(pWriteMultFrame->RegNum)))
				{
					modebus_debug("寫寄存器數(shù)據(jù)長度錯誤,需要寫入%d個寄存器,長度為:%dB,收到數(shù)據(jù)長度為:%dBrn",?pWriteMultFrame->RegNum,?2*pWriteMultFrame->RegNum,?pWriteMultFrame->DataLen);
					MODEBUS_SLAVE_RetrunUnu(pHandle,?pHandle->pRxBuff[0],?pHandle->pRxBuff[1],?MRTU_UNUS3);	//返回異常3
					return?MRTU_REGN_ERROR;	//寄存器數(shù)量錯誤
				}
			}break;
			default:	//不支持的功能碼,返回異常1
			{
				modebus_debug("不支持的操作碼:0x%02Xrn",?pHandle->pRxBuff[1]);
				MODEBUS_SLAVE_RetrunUnu(pHandle,?pHandle->pRxBuff[0],?pHandle->pRxBuff[1],?MRTU_UNUS1);	//返回異常1
				return?MRTU_FUNR_ERROR;
			}
		}
		
		*pFun?=?pHandle->pRxBuff[1];	//返回功能碼
		return?MRTU_OK;					//返回成功
	}
	else
	{
		modebus_debug("CRC校驗錯誤,計算CRC為:0x%04X,返回CRC為:0x%04Xrn",crc16,(u16)(pHandle->pRxBuff[DataLen-2]<pRxBuff[DataLen-1]);
		return?MRTU_CRC_ERROR;							//返回CRC校驗錯誤
	}
}






/*************************************************************************************************************************
*?函數(shù)	:	MRTU_ERROR?MODEBUS_SLAVE_ReturnReadReg(MODEBUS_HANDLE?*pHandle,?u8?Fun,?u8?SlaveAddr,?u16?RegAddr,?u8?RegNum,?u16?pRegData[])
*?功能	:	從機(jī)返回主機(jī)讀取的寄存器
*?參數(shù)	:	pHandle:modebus句柄;Fun:讀取的功能碼;SlaveAddr:從機(jī)地址;RegAddr:需讀取的寄存器地址;RegNum:寄存器數(shù)量;pRegData:返回寄存器的值,至少為RegNum的2倍
			返回的寄存器的值按照循序存放在pRegData中
*?返回	:	MRTU_ERROR:通信狀態(tài)
*?依賴	:	底層通信驅(qū)動
*?作者	:	cp1300@139.com
*?時間	:	2014-03-24
*?最后修改時間?:?2014-11-16
*?說明	:?	MOUEBUS?RTU主機(jī)讀取從機(jī)的指定寄存器,可以為保持寄存器,也可以為輸入寄存器,可以一次讀取多個
			輸入輸出的數(shù)據(jù)都為小端模式
			注意:如果直接使用數(shù)據(jù)幀的寄存器數(shù)量以及地址,必須高地位交換
*************************************************************************************************************************/
MRTU_ERROR?MODEBUS_SLAVE_ReturnReadReg(MODEBUS_HANDLE?*pHandle,?u8?Fun,?u8?SlaveAddr,?u16?RegAddr,?u8?RegNum,?u16?pRegData[])
{
	MRTU_RETURN_FRAME?*pFrame;		//返回數(shù)據(jù)幀格式
	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;	//寄存器數(shù)量錯誤
	pFrame?=?(MRTU_RETURN_FRAME?*)pHandle->pTxBuff;
	//數(shù)據(jù)結(jié)構(gòu)填充
	pFrame->addr?=?SlaveAddr;						//從機(jī)地址
	pFrame->fun?=?Fun;								//功能碼,讀取
	pFrame->DataLen?=?2*RegNum;						//數(shù)據(jù)長度
	//循環(huán)寫入返回的數(shù)據(jù)
	for(i?=?0;i?<?RegNum;i?++)
	{
		pFrame->DataBuff[2*i]?=?pRegData[i]>>8;		//數(shù)據(jù)高位
		pFrame->DataBuff[2*i+1]?=?pRegData[i]&0xff;	//數(shù)據(jù)低位
	}
	crc16?=?usMBCRC16(pHandle->pTxBuff,?3+pFrame->DataLen);//計算CRC16
	pFrame->DataBuff[pFrame->DataLen]?=?crc16&0xff;	//數(shù)據(jù)發(fā)送交換過
	pFrame->DataBuff[pFrame->DataLen+1]?=?crc16>>8;	//數(shù)據(jù)發(fā)送交換過

#if?MODEBUS_RTU_DBUG
	{
		u16?i;
		
		modebus_debug("rn<-?MODEBUS?RTU?TXD(%dB)(CRC:0x%04X):rn",3+pFrame->DataLen+2,crc16);
		for(i?=?0;i?<?3+pFrame->DataLen+2;i?++)
		{
			modebus_debug("0x%02X?",pHandle->pTxBuff[i]);
		}
		modebus_debug("rn");
	}
#endif	//MODEBUS_RTU_DBUG
	
	MODEBUS_SendData(pHandle->UartCh,?pHandle->pTxBuff,?3+pFrame->DataLen+2);	//發(fā)送數(shù)據(jù)

	return?MRTU_OK;						//返回成功?
}










/*************************************************************************************************************************
*?函數(shù)	:	MRTU_ERROR?MODEBUS_SLAVE_ReturnWriteHoldReg(MODEBUS_HANDLE?*pHandle,u8?SlaveAddr,?u16?RegAddr,?u16?RegData)
*?功能	:	從機(jī)返回主機(jī)預(yù)置單個保持寄存器
*?參數(shù)	:	pHandle:modebus句柄;Fun:讀取的功能碼;SlaveAddr:從機(jī)地址;RegAddr:需讀取的寄存器地址;RegData:返回寄存器的值
*?返回	:	MRTU_ERROR:通信狀態(tài)
*?依賴	:	底層通信驅(qū)動
*?作者	:	cp1300@139.com
*?時間	:	2014-03-24
*?最后修改時間?:?2014-11-16
*?說明	:?	MOUEBUS?RTU主機(jī)寫單個寄存器成功后返回
			輸入輸出的數(shù)據(jù)都為小端模式
			注意:如果直接使用數(shù)據(jù)幀的寄存器數(shù)量以及地址,必須高地位交換
*************************************************************************************************************************/
MRTU_ERROR?MODEBUS_SLAVE_ReturnWriteHoldReg(MODEBUS_HANDLE?*pHandle,u8?SlaveAddr,?u16?RegAddr,?u16?RegData)
{
	MRTU_WRITE_FRAME?*pFrame;		//返回數(shù)據(jù)幀格式
	u16?crc16;
	
	if(pHandle?==?NULL)?return?MRTU_HANDLE_ERROR;	//句柄無效
	pFrame?=?(MRTU_WRITE_FRAME?*)pHandle->pTxBuff;
	//數(shù)據(jù)結(jié)構(gòu)填充
	pFrame->addr?=?SlaveAddr;						//從機(jī)地址
	pFrame->fun?=?MRTU_FUN_WRITE;					//功能碼,預(yù)置單個寄存器
	pFrame->StartReg?=?SWAP16(RegAddr);				//寄存器地址
	pFrame->RegData?=?SWAP16(RegData);				//寄存器的值
	pFrame->crc16?=?usMBCRC16(pHandle->pTxBuff,?6);//計算CRC16

#if?MODEBUS_RTU_DBUG
	{
		u16?i;
		
		modebus_debug("rn<-?MODEBUS?RTU?TXD(%dB)(CRC:0x%04X):rn",8,crc16);
		for(i?=?0;i?<?8;i?++)
		{
			modebus_debug("0x%02X?",pHandle->pTxBuff[i]);
		}
		modebus_debug("rn");
	}
#endif	//MODEBUS_RTU_DBUG
	
	MODEBUS_SendData(pHandle->UartCh,?pHandle->pTxBuff,?8);	//發(fā)送數(shù)據(jù)

	return?MRTU_OK;						//返回成功?
}







/*************************************************************************************************************************
*?函數(shù)	:	MRTU_ERROR?MODEBUS_SLAVE_ReturnWriteMultHoldReg(MODEBUS_HANDLE?*pHandle,?u8?SlaveAddr,?u16?RegAddr,?u8?RegNum)
*?功能	:	從機(jī)返回主機(jī)預(yù)置多個保持寄存器
*?參數(shù)	:	pHandle:modebus句柄;SlaveAddr:從機(jī)地址;RegAddr:需讀取的寄存器地址;RegNum:需要讀取的寄存器數(shù)量
*?返回	:	MRTU_ERROR:通信狀態(tài)
*?依賴	:	底層通信驅(qū)動
*?作者	:	cp1300@139.com
*?時間	:	2014-03-24
*?最后修改時間?:?2014-11-16
*?說明	:?	MOUEBUS?RTU主機(jī)寫單個寄存器成功后返回
			輸入輸出的數(shù)據(jù)都為小端模式
			注意:如果直接使用數(shù)據(jù)幀的寄存器數(shù)量以及地址,必須高地位交換
*************************************************************************************************************************/
MRTU_ERROR?MODEBUS_SLAVE_ReturnWriteMultHoldReg(MODEBUS_HANDLE?*pHandle,?u8?SlaveAddr,?u16?RegAddr,?u8?RegNum)
{
	MRTU_WRIT_EMULT_RFRAME?*pFrame;		//返回數(shù)據(jù)幀格式

	if(pHandle?==?NULL)?return?MRTU_HANDLE_ERROR;	//句柄無效
	if((RegNum?>?127)?||?(RegNum?==?0))	return?MRTU_REGN_ERROR;	//寄存器數(shù)量錯誤
	pFrame?=?(MRTU_WRIT_EMULT_RFRAME?*)pHandle->pTxBuff;
	//數(shù)據(jù)結(jié)構(gòu)填充
	pFrame->addr?=?SlaveAddr;						//從機(jī)地址
	pFrame->fun?=?MRTU_FUN_MWRITE;					//功能碼,預(yù)置多個寄存器
	pFrame->StartReg?=?SWAP16(RegAddr);				//寄存器起始地址
	pFrame->RegNum?=?SWAP16(RegNum);				//寄存器數(shù)量
	pFrame->crc16?=?usMBCRC16(pHandle->pTxBuff,?6);	//計算CRC16
本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉