⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 modbus.c

📁 基于Hitech和C166的Modbus协议
💻 C
📖 第 1 页 / 共 2 页
字号:
//****************************************************************************
// 文件名: MODBUS.C
// 功      能: 实现从机MODBUS功能,负责接收主机命令,校验,应答
// 规      范: 使用RTU 模式CRC校验
// 串      口: 1位起始位,8位数据位,无校验位,1位停止位19200 波特率
// 作      者: 李龙胜
// 版      本: V1.0
// 日      期: 2008.09.05
// 修      订: 2008.12.17
// 说     明 : 移植到双芯片主机中由串口0改为串口1
//****************************************************************************

//****************************************************************************
// @Project Includes
//****************************************************************************
#include "MAIN.H"

//****************************************************************************
// @全局变量
//****************************************************************************

bit			Asc1_receive_ok;     	    	
bit			Asc1_transfer_ok;    	    	
bit			Asc1_lastFrame_indeal;		


ubyte			Asc1_Rx_BUF[Asc1_Rx_lengh_max]	=
{
	0, 0, 
};
ubyte			Asc1_Tx_BUF[Asc1_Tx_lengh_max]	=
{
	0, 0, 
};

ubyte			*Asc1_Rx_BUF_point			= Asc1_Rx_BUF;
ubyte			*Asc1_Tx_BUF_point			= Asc1_Tx_BUF;

ubyte			Asc1_Rx_lengh				= 0;
ubyte			Asc1_Tx_lengh				= 0;

const unsigned int	crc_ta[256]				=
{
	0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 0xD801, 0x18C0,
	0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, 0xF001, 0x30C0, 0x3180, 0xF141,
	0x3300, 0xF3C1, 0xF281, 0x3240, 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0,
	0x2A80, 0xEA41, 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
	0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 0xBE01, 0x7EC0,
	0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741,
	0x5500, 0x95C1, 0x9481, 0x5440, 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0,
	0x4C80, 0x8C41, 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
};



//****************************************************************************
// @Function  	   void  Modbus_deal()
// @Description        在main()中循环执行
//****************************************************************************
void Modbus_deal()
{
	//检查是否有收到帧信息
	//ASC1_vSendData(0x55);
	if (Asc1_receive_ok)
	{
		Asc1_receive_ok = 0; 
		//标记处于帧处理中
		//Asc1_lastFrame_indeal = 1;
		//是否开辟限时处理功能,防止超时未应答,
		//免得一直不退出上一帧处理
		//比如定时器T3之???ms延时
		//判定是否为本地址帧
		if (Asc1_Rx_BUF[0] == Modbus_Node)
		{
			//判定地址符合情况下才进入帧处理
			Frame_deal();
		}
		//不是本机地址直接退出
		else
		{
			#if(MODBUS_DebugMode)
				#if(Asc1_Send_while == 1)
					ASC1_vSendData_While( 0xAA );
				#endif
			#endif
			// do nothing
		}			
		//完成帧处理
		Asc1_lastFrame_indeal = 0;
		//长度清零,指针指向数组第一个空间
		Asc1_Rx_lengh = 0;
		Asc1_Rx_BUF_point = Asc1_Rx_BUF;
	}
}
//****************************************************************************
// @Function  	   void crc_cal( ubyte* ptr, ubyte len )
// @Description        计算待发送的数据之CRC 值 并插入发送缓冲
// @Parameters	* ptr : 待发送的数组的首地址 
//     			    len    : 待发送的数组之字节数,不包括CRC码
//****************************************************************************
__inline void crc_cal(ubyte *ptr, ubyte len)
{
	uword	crc1	= 0xffff;    // 初始化 
	ubyte	temp	= 0;
	while (len > 0)
	{
		crc1 = ( crc1 >> 8 ) ^ crc_ta[( crc1 ^ *ptr ) & 0xff];
		len--;
		ptr++;
	} 

	//往发送缓冲区写CRC之低字节	
	*ptr = ( ubyte ) ( crc1 % 256 );
	//往发送缓冲区写CRC之高字节	
	ptr++;
	*ptr = ( ubyte ) ( crc1 / 256 );
}

//****************************************************************************
// @Function  	   uword crc_check( ubyte* ptr, ubyte len )
// @Description        计算待接收的数据之CRC 值 ,正确则返回0
// @Rerurn    	    正确的编码返回 0x00
// @Parameters	* ptr : 待接收的数组的首地址 
//     			   len    : 待接收的数组之字节数,包括CRC码
//****************************************************************************
__inline uword crc_check(ubyte *ptr, ubyte len)
{
	uword	crc1	= 0xffff;    // 初始化 
	#if(CRC_NeedSend)
	uword	temp	= 0;
	#endif
	while (len > 0)
	{
		crc1 = ( crc1 >> 8 ) ^ crc_ta[( crc1 ^ *ptr ) & 0xff];
		len--;
		ptr++;

		//调试帧数据时需要发送
		#if(CRC_NeedSend)	
			temp = crc1 / 256;
			#if(Asc1_Send_while == 1)
				ASC1_vSendData_While( temp );
			#endif
			temp = crc1 % 256;
			#if(Asc1_Send_while == 1)
				ASC1_vSendData_While( temp );
			#endif
		#endif
	}    	
	return crc1;
}

//****************************************************************************
// @Function  	  void  Frame_deal()
// @Description       处理接收到的数据帧,在main中查询执行
//     			   在地址也符合的情况下才进入帧处理
// @Rerurn    	    
// @Parameters	
//****************************************************************************
__inline void Frame_deal()
{
	uword	temp1	= 0;	
	//进行CRC 校验,正确的校验返回为0
	temp1 = crc_check( Asc1_Rx_BUF, Asc1_Rx_lengh );  		
	if (temp1 == 0)
	{
		#if(MODBUS_DebugMode == 1)
		//{   调试时
		#if(Asc1_Send_while == 1)
			ASC1_vSendData_While( 0x88 );
		#endif
		Frame_Data_Analyse();
		//}
		#else
		//{   正常处理
		Frame_Data_Analyse();
		//}
		#endif
	}			
	//校验未通过处理
	else
	{
		#if(MODBUS_DebugMode)
		//{   调试时
		#if(Asc1_Send_while == 1)
			ASC1_vSendData_While( 0x99 );
		#endif
		//}
		#else
		//{   正常处理
		#if(MODBUS_CRCerr_needDeal)
		//{  CRC 校验出错 需处理
		#if(Asc1_Send_while == 1)
			ASC1_vSendData_While( 0x09 );
		#endif
		//}
		#endif
		//}	
		#endif
	}
}
//****************************************************************************
// @Function  	  void  Frame_Data_Analyse()
// @Description       在正确的CRC 校验之后进行对数据分析处理
// @Rerurn    	    
// @Parameters	
//****************************************************************************
__inline void Frame_Data_Analyse()
{
	//-----------功能码----------
	//#define READ_Coils_State     		01 
	//#define READ_Inputs_State  		02 
	//#define READ_Hold_Registers 	 	03 
	//#define READ_Input_Registers 		04 
	//#define WRITE_One_Coil      		05 
	//#define WRITE_One_Register  		06
	//#define WRITE_Some_Coil      		15 
	//#define WRITE_Some_Register 	16

	//对应答作初步准备
	Asc1_Tx_BUF[0] = Modbus_Node;
	Asc1_Tx_lengh = 1;
	//----根据功能码分类-----
	switch (Asc1_Rx_BUF[1])
	{
	case READ_Coils_State:
		//01
		Deal_READ_Coils_State();
		break;
	case READ_Inputs_State:
		//02
		break;
	case READ_Hold_Registers:
		//03
		Deal_READ_Hold_Registers();
		break;
	case READ_Input_Registers:
		//04
		break;
	case WRITE_One_Coil:
		//05
		Deal_WRITE_One_Coilr();
		break;
	case WRITE_One_Register:
		//06
		Deal_WRITE_One_Register();
		break;
	case WRITE_Some_Coil:
		//15
		Deal_WRITE_Some_Coil();
		break;
	case WRITE_Some_Register:
		//16
		Deal_WRITE_Some_Register();
		break;				
	default:
		break;
	}
}

//****************************************************************************
// @Function  	  void Send_Asc0_Tx_buf()
// @Description       处理发送缓冲区
//****************************************************************************
__inline void Send_Asc0_Tx_buf()
{
	ubyte	temp1;
	for (temp1 = 0; temp1 < Asc1_Tx_lengh; temp1++)
	{
		#if(Asc1_Send_while == 1)
			ASC1_vSendData_While( Asc1_Tx_BUF[temp1]  );
		#endif
	}
}

//****************************************************************************
// @Function  	  void Send_Asc0_Rx_buf_Back()
// @Description       正确处理完写,直接回复接收缓冲区
//****************************************************************************
__inline void Send_Asc0_Rx_buf_Back()
{
	ubyte	temp1;
	for (temp1 = 0; temp1 < Asc1_Rx_lengh; temp1++)
	{
		#if(Asc1_Send_while == 1)
			ASC1_vSendData_While( Asc1_Rx_BUF[temp1]  );
		#endif
	}
}

//****************************************************************************
// @Function  	  void Deal_READ_Coils_State()
// @Description       读一组逻辑线圈当前状态
//****************************************************************************
__inline void Deal_READ_Coils_State()
{
	bit	Deal_all_OK	= 0;
	ubyte	temp1, temp2, temp5, temp6, temp7, temp8, temp9;
	uword	temp3, temp4;
	//清零发送缓冲区
	for (temp1 = 0; temp1 < Asc1_Tx_lengh_max; temp1++)
	{
		Asc1_Tx_BUF[temp1] = 0;
	}
	//判定功能码是否合法(01,但是帧长超出)
	if (Asc1_Rx_lengh != 8)
	{
		Asc1_Tx_BUF[1] = 0x81;
		Asc1_Tx_BUF[2] = 0x01;
		Asc1_Tx_lengh = 3;
	}
	else
	{
		//判定输出数量是否合法(这里仅开辟80个位)
		temp3 = ( uword ) Asc1_Rx_BUF[4] * 256 + Asc1_Rx_BUF[5];
		if (temp3 > ModBus_PLC_Bit_max)		// 这里最多支持 80 bit
		{
			Asc1_Tx_BUF[1] = 0x81;
			Asc1_Tx_BUF[2] = 0x03;
			Asc1_Tx_lengh = 3;
		}
		else
		{
			//判定首地址、末地址是否合法
			temp4 = ( uword ) Asc1_Rx_BUF[2] * 256 + Asc1_Rx_BUF[3];	//首地址
			temp3 += temp4;									//末地址
			//判断首地址、末地址均合法否
			if (temp3 >= ModBus_PLC_Bit_max || temp4 >= ModBus_PLC_Bit_max)
			{
				Asc1_Tx_BUF[1] = 0x81;
				Asc1_Tx_BUF[2] = 0x02;
				Asc1_Tx_lengh = 3;
			}
			else
			{
				//处理读请求
				//开始地址: 第几个字节,第几位
				temp1 = Asc1_Rx_BUF[3] / 8;	//第几字节
				temp2 = ( ubyte ) ( Asc1_Rx_BUF[3] % 8 );	//该字节第几位
				//共发送几个字节+ 几位
				temp5 = ( ubyte ) ( Asc1_Rx_BUF[5] / 8 + 1 );	//几个字节
				//ASC1_vSendData(temp5);
				temp6 = ( ubyte ) ( Asc1_Rx_BUF[5] % 8 );	//加几位
				Asc1_Tx_BUF[0] = Modbus_Node;
				Asc1_Tx_BUF[1] = 0x01;
				Asc1_Tx_BUF[2] = temp5;
				for (temp7 = 0; temp7 < temp5; temp7++)
				{
					temp9 = PLC_Bit_Buffer_all.PLC_Bit_Buffer[temp1 + 1];
					temp3 = temp7 * 256 + PLC_Bit_Buffer_all.PLC_Bit_Buffer[temp1];
					temp3 >>= temp2;
					temp1++;
					temp8 = temp7 + 3;
					Asc1_Tx_BUF[temp8] = ( ubyte ) ( temp3 & 0x00FF );
				}
				temp2 = Asc1_Tx_BUF[temp8];
				//对最后一个字节的无效高位清0
				temp1 = 0;
				for (temp7 = 0; temp7 < temp6; temp7++)
				{
					temp1 <<= 1;	
					temp1 |= 0x01;
				}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -