/*************************** (C) COPYRIGHT 2017 CXHY ****************************
 * 文件名  ：RS485.c
 * 描  述  ：RS485通信处理函数，包含3部分：接收帧程序、发送帧程序及执行命令程序。     
 * 版  本  ：V1.0
 * 作  者  ：XuZhongPing
 * 日  期  ：2017/02/10
*********************************************************************************/
#include "delay.h"
#include "sys.h"
#include "RS485.h"
#include "usart.h"
#include "timer.h"
#include "DataProcessing.h"	 
#include "message.h"

/*通讯变量定义*/
#define BaudRate 19200 								//波特率1200bps

uint8_t Address = 0x00;								//设备地址
uint8_t RS485_RX_BUF[128];  					//接收缓冲,最大64个字节
uint8_t RS485_TX_BUF[64]; 						//发送缓冲,最大64个字节

//主程序变量定义
uint8_t RS485_RX_STA = 0x00; 					//RS485接收状态标记
uint8_t RS485_communicationStatus;		//RS485通讯成功与否标志
uint16_t RS485_serialNumber;					//RS485通信流水号
uint16_t RS485_RX_serialNumber;				//RS485通信流水号
uint8_t rxCmd;												//从机返回的指令码

/*
***********************************************************************
*	函 数 名: RS485_Init
*	功能说明: 初始化RS485接口
*	形    参:	无
*	返 回 值: 无
***********************************************************************
*/
void RS485_Init(void)
{
	//GPIO端口设置
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		//使能GPIOA时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);	//使能USART2时钟
	
	//RS485_TX_EN  PA.0  RS485发送接收控制脚
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;				 				//PA0端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 		//推挽输出
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_ResetBits(GPIOA, GPIO_Pin_0);    									//输出0，默认接收状态
	
	//USART2_TX   PA.2
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; 							//PA.2
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;					//复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure); 									//初始化PA2
 
	//USART2_RX	  PA.3
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;		//浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);  								//初始化PA3

	//USART 初始化设置

	USART_InitStructure.USART_BaudRate = BaudRate;										//一般设置为9600;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;				//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;						//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;								//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;		//收发模式
	USART_Init(USART2, &USART_InitStructure); //初始化串口
	
	//Usart2 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0 ;				//抢占优先级0
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;								//子优先级0
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;										//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);																		//根据指定的参数初始化VIC寄存器
   
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);										//开启中断
	USART_Cmd(USART2, ENABLE);                    										//使能串口 
}

/*
***********************************************************************
*	函 数 名: USART2_IRQHandler
*	功能说明: 串口2中断服务子函数，用于RS485接收
*	形    参:	无
*	返 回 值: 无
***********************************************************************
*/
void USART2_IRQHandler(void)
{
	uint8_t res;
	static uint8_t RX_State = 0;						//接收状态
	static uint8_t CS = 0;		  						//接收核验和
	static uint8_t RX_Size = 0;							//接收通讯数据长度
	static uint8_t Byte_Count = 0;	  			//接收数据字节计数

	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
	{
		res = USART_ReceiveData(USART2);	//读取接收到的数据
// 		printf("RX_DATA = %x\r\n", res);
		switch(RX_State)
		{
			case 0:	  //第一前导字节
				if(res == 0xfe){
					RX_State = 1;
				}else{
					RX_State = 0;
				}
				break;
			case 1:	  //第二前导字节
				if(res == 0xfe){
					RX_State = 1;						//转到接收帧头
				}else{
					if(res == 0x68)
					{
						RX_State = 3;						//转到接收设备通讯地址
						CS = 0x68;
					}else{
						RX_State = 0;
					}
				}
				break;
// 			case 2:	  //接收帧头
// 				if(res == 0x68){
// 					RX_State = 3;						//转到接收设备通讯地址
// 					CS = 0x68;
// 				}else{
// 					RX_State = 0;
// 				}
// 				break;
			case 3:		//接收设备通讯地址
				RS485_RX_BUF[0] = res;
				CS += res;
				RX_State = 4;
				break;
			case 4:	  //接收帧头
				if(res == 0x68){
					RX_State = 5;						//转到接收通讯命令
					CS += 0x68;
				}else{
					RX_State = 0;
				}
				break;
			case 5:		//接收通讯命令
				RS485_RX_BUF[1] = res;
				CS += res;
				RX_State = 6;							//转到接收数据长度
				break;
			case 6:		//接收通讯数据长度
				RS485_RX_BUF[2] = res;
				CS += res;
				RX_Size = res;						//接收的数据长度
				if(RX_Size == 0x00){			//如果数据长度为0，则直接转到接收校验码
					RX_State = 8;
				}else{
					RX_State = 7;						//转到接收数据
				}
				break;				
			case 7:	  //接收N个字节数据
				if(Byte_Count < RX_Size-1){
					RS485_RX_BUF[Byte_Count + 3] = res;
					CS += res;
					Byte_Count++;
				}else{
					RS485_RX_BUF[Byte_Count + 3] = res;
					CS += res;
					Byte_Count = 0;					//已完成N个字节的数据接收计数归0
					RX_State = 8;						//转到接收核验码
				}
				break;
			case 8:	  //接收校验
				if(CS != res){
					RX_State = 0;
				}else{
					RX_State = 9;						//转到接收帧尾码0x16
				}
				break;
			case 9:	  //接收帧尾
				if(res == 0x16){ 					//是帧尾吗？
					RS485_RX_STA = 0xfd; 		//一帧接收成功，标记USART_RX_STA=0xfd通知主程序进行通讯处理
// 					printf("\r\n<--\r\n");
				}
				RX_State = 0;
				break;
			default:  //其它
				break;
		}
	}
}

/*
***********************************************************************
*	函 数 名: Send_Data
*	功能说明: 发送一个字节数据
*	形    参:	发送数据
*	返 回 值: 无
***********************************************************************
*/
void Send_Data(uint8_t Serial_data)
{
// 	CTRL485 = 1;													//设置为发送模式
	USART_SendData(USART2, Serial_data);	//向串口发送数据
	while(USART_GetFlagStatus(USART2, USART_FLAG_TC) != SET);//等待发送结束
// 	CTRL485 = 0;													//恢复为接收模式	
}

/*
***********************************************************************
*	函 数 名: Send_One
*	功能说明: 发送一帧数据
*	形    参:	发送数据帧地址指针
*	返 回 值: 无
***********************************************************************
*/
void Send_One(uint8_t *s)
{
	uint8_t i;
	uint8_t CS = 0x68;					//接收核验和
	uint8_t TX_Size = 0;				//接收通讯数据长度
	printf("\r\n----->RS485--> %x\r\n", *(s+1));
	CTRL485 = 1;		 						//RS-485 通讯，切换为发送状态
	
	delay_ms(1);
	Send_Data(0xfe); 						//发送2个前导字节
	Send_Data(0xfe);
	Send_Data(0xfe);
	Send_Data(0x68); 						//发送帧头
	Send_Data(*s); 							//发送地址
	CS += *s;
	Send_Data(0x68); 						//发送帧头
	CS += 0x68;
	Send_Data(*(s+1)); 					//发送帧命令
	CS += *(s+1);
	TX_Size = *(s+2);						//取出通讯数据长度
	Send_Data(TX_Size); 				//发送数据长度
	CS += *(s+2);
	for(i=0; i<TX_Size; i++) 		//发送数据
	{
		Send_Data(*(s+i+3));
		CS += (*(s+i+3));
	}
	Send_Data(CS); 							//发送校验字节
	Send_Data(0x16); 						//发送帧尾

	delay_ms(1);
	CTRL485 = 0; 								//一帧发送完毕，恢复信道切换功能
}

/*
***********************************************************************
*	函 数 名: Serial_Address
*	功能说明: 通讯地址判断
*	形    参:	s 指向从通讯地址
*	返 回 值: 0x00 错误，0x01 正确，0x02 广播地址
***********************************************************************
*/
static uint8_t Serial_Address(uint8_t *s)
{
	if(*s == 0x99)
	{
		return(0x02); 				//是广播地址
	}
	if(*s == Address)
	{
		return(0x01); 				//地址匹配正确
	}
	return(0x00); 					//地址匹配不正确
}

/*
***********************************************************************
*	函 数 名: Serial_Command_Run
*	功能说明: 执行通讯命令
*	形    参:	s 发送数据帧地址指针
*	返 回 值: 0 错误，1 正确
***********************************************************************
*/
uint8_t Serial_Command_Run(uint8_t *s)
{
	uint8_t i;

// 	printf("\r\n<-- %x\r\n", *(s+1));
	rxCmd = *(s+1);
	switch(Serial_Address(s)) 		//判断地址是否正确
	{
		case 0x02: 	//广播地址0x99
	 		break;
			
		case 0x01: 	//正常地址
			printf("\r\n<-- %x\r\n", *(s+1));
			
			switch(*(s+1))		//通讯命令译码
			{
				case 0x00: 			//应答命令
					break;
					
				case 0x01: 			//读取超声波测量值
					distance = *(s+3);
					break;
					
				case 0x02: 			//读取重量值

					break;
					
				case 0x03: 			//读取温度值
					temperature = *(s+3);		//取出测距数据
					break;
					
				case 0x10:			//读取温度、测距、水位、重量、身份信息的组合		
// 					temperature = *(s+3);									//取出温度数据
// 					distance = *(s+4);										//取出测距数据
// 					water = *(s+5);												//取出水位数据
// 					weight = (uint16_t)(*(s+6)) << 8;			//取出重量高8位数据
// 					weight |= *(s+7);											//取出重量低8位数据
// 					
// 					receivePutInType = *(s+8);						//取出开门身份类型
// 					receiveDataLength = *(s+9);						//取出开门身份信息的数据长度
// 					
// 					memcpy(receiveDataBuf, (s+10), 33);		//取出开门身份信息内容
					break;
					
				case 0x21:			//主机请求获取用户输入
// 					temperature = *(s+3);									//取出温度数据
// 					getUserInfoMark = *(s+4);							//读取从机过程处理的状态（判断从机是否已经获取到用户投放请求信息）
// 					if(getUserInfoMark)										//取出用户相关信息
// 					{
// 						receivePutInType = *(s+5);					//取出开门身份类型（0x00->无；0x01->IC卡；0x02->二维码）
// 						receiveDataLength = *(s+6);					//取出开门身份信息的数据长度
// 						memcpy(receiveDataBuf, (s+7), 33);	//取出开门身份信息内容
// 					}
					break;
					
				case 0x22:			//主机要求从机清除上次的用户输入状态信息
// 					if(*(s+3))
// 					{
// 						clearPutInInfoMark = 1;
// 					}
					break;
					
				case 0x23:			//读取测距、重量的组合
// 					getPutInInfoMark = *(s+3);											//读取从机过程处理的状态
// 					if(getPutInInfoMark)														//从机已经完成过程处理
// 					{
// 						receiveDistance_a = *(s+4);										//取出测距数据
// 						receiveDistance_b = *(s+5);										//取出测距数据

// 						receiveWeight_a = (uint16_t)(*(s+6)) << 8;		//取出重量高8位数据
// 						receiveWeight_a |= *(s+7);										//取出重量低8位数据
// 						
// 						receiveWeight_b = (uint16_t)(*(s+8)) << 8;		//取出重量高8位数据
// 						receiveWeight_b |= *(s+9);										//取出重量低8位数据
// 						
// 						printf("\r\n<- 重量 = %2.2f Kg; %2.2f Kg\r\n", (float)receiveWeight_a/100, (float)receiveWeight_b/100);
// 						printf("\r\n<- 测距1 = %3.1f cm\r\n", (float)receiveDistance_a);
// 						printf("\r\n<- 测距2 = %3.1f cm\r\n", (float)receiveDistance_b);
// 						
// 					}
					break;
					
				case 0x24:			//收到MCU2发送来的需上传到后台的数据（用户投放信息）
					memset(receiveDataBuf, 0x00, 33);							//接收数据前先把之前的缓存内容清空
					
					//取出用户身份信息
					receivePutInType = *(s+3);										//取出开门身份类型（0x00->无；0x01->IC卡；0x02->二维码）
					receiveDataLength = *(s+4);										//取出开门身份信息的数据长度
					memcpy(receiveDataBuf, (s+5), 33);						//取出开门身份信息内容
					
					//取出第一组称重和测距数据
					weight_paper = (uint16_t)(*(s+38)) << 8;			//取出重量高8位数据
					weight_paper |= *(s+39);											//取出重量低8位数据
					distance_paper = *(s+40);											//取出测距数据
					
					//取出第二组称重和测距数据
					weight_plastic = (uint16_t)(*(s+41)) << 8;		//取出重量高8位数据
					weight_plastic |= *(s+42);										//取出重量低8位数据
					distance_plastic = *(s+43);										//取出测距数据
					
					//取出第三组称重和测距数据
					weight_metal = (uint16_t)(*(s+44)) << 8;			//取出重量高8位数据
					weight_metal |= *(s+45);											//取出重量低8位数据
					distance_metal = *(s+46);											//取出测距数据
					
					//取出第四组称重和测距数据
					weight_glass = (uint16_t)(*(s+47)) << 8;			//取出重量高8位数据
					weight_glass |= *(s+48);											//取出重量低8位数据
					distance_glass = *(s+49);											//取出测距数据
					
					receiveDataStatus = 1;													//收到MCU2发送的投放信息，将该标志位置1，由外部读取后清除

					break;
					
				case 0x25:			//接收到MCU2发送过来的设备工作信息（包含测距、温度）
					temperature = *(s+3);													//取出温度数据
							
					distance_paper = *(s+4);											//取出纸类桶测距数据
					distance_plastic = *(s+5);										//取出塑料桶测距数据
					distance_metal = *(s+6);											//取出金属桶测距数据
					distance_glass = *(s+7);											//取出玻璃桶测距数据
					
					recDeviceStatus = 1;													//收到MCU2发送的设备工作信息，将该标志位置1，由外部读取后清除

					break;
					
				default: 				//其他命令
					break;
			}
			
			break;

		case 0x00: //地址错误
			return 0;
			break;	
	}
	return 1; //帧执行正确，返回
}


/*
***********************************************************************
*	函 数 名: RS485_Task
*	功能说明: RS485通讯任务
*	形    参:	无
*	返 回 值: 无
***********************************************************************
*/
void RS485_Task(void)
{
}

/*
***********************************************************************
*	函 数 名: Receive_One
*	功能说明: RS485接收一次通讯任务
*	形    参:	无
*	返 回 值: 无
***********************************************************************
*/
void RS485_ReceiveTask(void)
{
	if(RS485_RX_STA == 0xfd)											//有接收到从机返回的数据，但要进行数据验证			
	{
		Serial_Command_Run(RS485_RX_BUF); 					//解释返回的数据
		RS485_RX_STA = 0;
	}
}

/******************** (C) COPYRIGHT 2017 CXHY *****END OF FILE*******************/
