#include <stm32f10x_tim.h>
#include "modbus.h"
#include "global.h"
#include "usart.h"

//modbus用通讯参数 

u8 Tim_Out;      //大于3.5个字符时间保守取3ms (波特率9600的时候大约2点几//毫秒)                                                                   
u8 Rcv_Complete; //一帧是否已经接受完成 
u8 Send_Complete;//一帧是否已经发送完成 
u8 Com_busy;     //通讯繁忙表示上一帧还未处理结束 
u8 Rcv_Buffer[20]; //用来存放接收到的完整的一帧数据 第一个字节用来存放接收到的有效字节数也就是数组中的有效字节数     
u8 Send_Buffer[20];//用来存放待发送的完整的一帧数据第一个字节用来存放待发送的有效字节数也就是数组中的有效字节数 
u8 Rcv_Data;        //用来存放接收的一个字节 
u8 Send_Data;				//用来存放要发送的一字节 
u8 Mod_Id;					//用来标志作为从站的站号 
u8 Rcv_Num;					//用来表示接收的一帧的有效字节数从功能码到CRC校验 
u8 Send_Num;				//用来表示待发送的一帧的字节数                 
u8 *PointToRcvBuf;	//用来指向接收的数据缓存 
u8 *PointToSendBuf;	//用来指向带发送的数据缓存 
u8 Comu_Busy;				//用来表示能否接收下一帧数据 
u8 HaveMes; 
u16 CRC16(u8 *puchMsgg,u8 usDataLen);//用于计算CRC校验码 
FlagStatus RX_status;


//CRC校验查表用参数 
/* CRC 高位字节值表*/ 
static u8 auchCRCHi[] = { 

    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 

    } ; 

    /* CRC低位字节值表*/ 
static u8 auchCRCLo[] = { 

    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 

    } ; 

void TIMER2_Init()//定时器初始化
{
	 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	 TIM_DeInit(TIM2);
	 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	 TIM_TimeBaseStructure.TIM_Period=300;//ARR的值
	 TIM_TimeBaseStructure.TIM_Prescaler=3600-1;
	 TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//向上计数模式
	 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
	 TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
	 TIM_Cmd(TIM2, ENABLE); //开启时钟
}

/* 
 函数功能用于Modbus初始化 
 函数输入Id为Modbus站号。           
 函数输出  无 。                                                                                      
*/ 
void ModInit(u8 Id) 
{ 
		//modbus参数初始化 
		PointToRcvBuf=Rcv_Buffer; 
		PointToSendBuf=Send_Buffer; 
		Send_Num=1;//发送的数据顺序输出数组的第几个数 
		Mod_Id=Id;//站号设置 
		Rcv_Buffer[1]=Mod_Id; 
		Send_Buffer[1]=Mod_Id;//站号 
		Comu_Busy=0; 
} 

 /************************************************************/
 
// 函数功能CRC校验用函数 
// 函数输入puchMsgg是要进行CRC校验的消息usDataLen是消息中字节数
// 函 数 输 出  计 算 出 来 的CRC校 验 码 。                                                                                      

/************************************************************/ 
u16 CRC16(u8 *puchMsgg,u8 usDataLen)       //puchMsgg是要进行CRC校验的消息usDataLen是消息中字节数
{   
		u8 uchCRCHi = 0xFF ; /* 高CRC字节初始化*/ 
    u8 uchCRCLo = 0xFF ; /* 低CRC 字节初始化*/ 
    u8 uIndex ; /* CRC循环中的索引*/ 
    while (usDataLen--) /* 传输消息缓冲区*/ 
    { 
				uIndex = uchCRCHi ^ *puchMsgg++ ; /* 计算CRC */ 
				uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex] ; 
				uchCRCLo = auchCRCLo[uIndex] ; 
    } 
		return ((uchCRCHi<< 8) | uchCRCLo) ; 
} 
 /* 
	函数功能对输入的信息帧进行处理按照功能码不同调用不同的函数处理 

  函数输入两个指针pointer_1指向用来存放输入信息帧的数组 

	pointer_2用来指向存放输出信息帧的数组两个数组的第一个元

	素都用来存放信息帧的有效字节个数 

  后面的元素按照Modbus协议组织。       

  函 数 输 出  无 。                                                                                      
*/ 
void MessageHandle(u8 *pointer_in,u8 *pointer_out)   
 { 
		 u16 CalKey;//计算出来的校验值 
		 u16 RcvKey;//接收到的校验值 
		 HaveMes=0;	//清除信息位 
								//获取接收到的校验值 
		 RcvKey=(u16)*(pointer_in+(*pointer_in-1));  RcvKey=RcvKey<<8; 
		 RcvKey=RcvKey|(u16)*(pointer_in+(*pointer_in)); 
		 CalKey=CRC16(pointer_in+1,*pointer_in-2); 
		 if(CalKey==RcvKey) 
		 { 
				switch(*(pointer_in+2))//第二个字节为功能码 
				{ 
					case 0x03:	
						ReadHoldingReg(pointer_in,pointer_out);//读保持寄存器 
						break;
					default:  
					{ 
						*(pointer_out+2)=*(pointer_in+2);//获取功能码 
						ErrorHandle(1,pointer_out);			 //功能码错误 
						break;
					} 
				} 
		 } 
		 else 
		 { 
					Comu_Busy=0; 
		 }    
 } 

//功能码03
 /*
	函数功能：返回数据保存 功能码03
  函数输入：输入存放数组、输出存放数组
  函数输出：无
*/
void ReadHoldingReg(u8 *pointer_1,u8 *pointer_2)//pointer_1用作输入pointer_2用作输出(未配置)
{  
	   if(*(pointer_1) == 7)
		 {
				if((*(pointer_1 + 3)) == 0x02)
				{
					X_Data[1] = *(pointer_1 + 4);
					X_Data[0] = *(pointer_1 + 5);
				}
		 }
		 Comu_Busy=0; 	
 } 


//错误校正
void  ErrorHandle(u8 Mode,u8 *Pointer) 

 { 
		 u16 SendKey;//要发送数据的校验值 
		 HaveMes=0;//清除信息位 

		 TIM_Cmd(TIM2,DISABLE); 

		 TIM_SetCounter(TIM3,0); 

		 Rcv_Complete=1;  Comu_Busy=1; 

		 Rcv_Buffer[0]=Rcv_Num; 

		 switch(Mode) 
		 { 

			case 1:*(Pointer+3)=0x01;//错误功能码 
			 break;    
			case 2:*(Pointer+3)=0x02;//错误地址 
			 break; 
			case 3:*(Pointer+3)=0x03;//错误数据 
			 break; 
			case 6:*(Pointer+3)=0x06;//从设备忙 

			 break; 
		 }
		 *Pointer=0x05;//输出寄存器有效数据个数  

		 *(Pointer+2)|=0x80;//功能码最高位置一

		 //写入校验码 

		 SendKey=CRC16(Pointer+1,*Pointer-2);      

		 //将计算出来的校验码装入输出数据缓存中 

		 *(Pointer+(*Pointer-1))=(u8)(SendKey>>8); 

		 *(Pointer+(*Pointer))=(u8)(SendKey&0x00FF);   

		 //启动数据发送
		 USART_ITConfig(USART3, USART_IT_TC, ENABLE);
 } 			//modbus参数

/* 函数功能用于Modbus信息发送 

 函数输入无。               

 函数输出无。              

 */ 

void ModSend(USART_TypeDef* USARTx)  
{  
	SendMode_485();		 //485发送
	Send_Data=*(PointToSendBuf+Send_Num); 
	USART_SendData(USARTx,Send_Data); 
	Send_Num++; 
	if(Send_Num>(*PointToSendBuf))//发送已经完成 
	 { 
			Comu_Busy=0; 
			*PointToSendBuf=0; 
			Rcv_Num=0; 
			Send_Num=1; 
			//启动数据发送 
			USART_ITConfig(USARTx, USART_IT_TC, DISABLE);//关闭数据发送中断  
	 }
	 ReceiveMode_485(USARTx);
}
 /* 
 
 函数功能用于Modbus信息接收 

 函数输入无。               

 函 数 输 出  无 。                                                                                      

*/ 

void ModRcv(u8 Rcv_Data) 
{ 
  HaveMes=1;					//表示接收到了信息 
  if(Comu_Busy!=1)    //如果不忙可以接收下一帧信息 
  {   
   TIM_Cmd(TIM2, DISABLE); //接收下一帧的时候关闭定时器中断
   TIM_SetCounter(TIM2,0); //给定时器清零
   if((Tim_Out!=0)&&(Rcv_Data==Mod_Id))//如果间隔时间超过了3.5个//字符同时接受的字节和自己的站号一致则认为接收开始 
   { 
			Rcv_Complete=0; //表示数据帧接收开始 
			Rcv_Num=0;			//接收数据个数初始化 
			Rcv_Num++;			//同时个数加一 
   } 
   if((0==Tim_Out)&&(0==Rcv_Complete))//如果处于接收一帧的正常过程中 
   { 
    if(Rcv_Num<100) 
    { 
     Rcv_Buffer[Rcv_Num+1]=Rcv_Data; //将数据放入接收数组中 
     Rcv_Num++;				//同时个数加一  
    } 
    else 
    { 
     Rcv_Complete=1; 
     Comu_Busy=1; 
     Rcv_Buffer[0]=Rcv_Num; 
     *(PointToSendBuf+2)=*(PointToRcvBuf+2);//获取功能码 
     ErrorHandle(6,PointToSendBuf);//表示超出了字节数(从机设备忙碌) 
     Rcv_Num=0; 
    } 
   } 
   Tim_Out=0;   TIM_Cmd(TIM2, ENABLE);//开启4.5ms计时三个半字符的保守估计 
  }   
} 

void USART3_IRQHandler(void)
{ 
	u8 data;
	
	if(USART_GetITStatus(USART3,USART_IT_RXNE)!=RESET)  //看指定的串口中断有没有发生
  {
		data = USART_ReceiveData(USART3);
		ModRcv(data); 
  }  
	if(USART_GetITStatus(USART3,USART_IT_TC)!=RESET) 	//传输完成中断
  { 
   ModSend(USART3); //用于modbus信息发送
  }   		
}
// 函数功能Modbus专用定时器TIM2 
void TIM2_IRQHandler(void)            
{ 
 if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
 { 
  TIM_ClearITPendingBit(TIM2, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源 
  Tim_Out=1; 
  TIM_Cmd(TIM2,DISABLE);   //禁止TIM2
  TIM_SetCounter(TIM2,0);  //设定有效值
  Rcv_Complete=1; 				 //接收完成标志
  Rcv_Buffer[0]=Rcv_Num; 	 //一帧的有效字节数
	  if(HaveMes!=0&&Rcv_Num>4) 
	  { 
	   Comu_Busy=1;
	   MessageHandle(PointToRcvBuf,PointToSendBuf);//信息中断 在这里面判断输入数据（输入数据，发送数据）
	  }    
  }  
}

