//说明见工程文件夹下的Doc文件夹内Readme.txt文件
//======================================================================

//【不动】GLOBLE_VAR宏定义及包含总头文件
#define GLOBLE_VAR  //只需在main.c中定义一次，用来处理全局变量声明与使用问题
#include "includes.h"   //包含总头文件

//【根据实际需要增删】main.c使用的内部函数声明处
void wdogResetJudge();
void pinResetJudge();
void shakeDataInit();
uint_8 updateJudge();
void updateNotify(uint_8 type);

int main(void)
 {
	//（1）【根据实际需要增删】声明主函数使用的变量（声明时不准赋值）
	uint_8 muFlag;  //更新状态
	uint_32 mCount;         //用于延时计数
	uint_32 msp;            //获取用户程序主站指针（用于判断是否存在User程序）
	uint_8* mstr;           //获取更新标记updateFlag（判断是否需要更新）

	//（2）【不动】 关总中断
    DISABLE_INTERRUPTS;

    //190229-冷复位清零-------------------------
    //（3）冷复位判断
    //若是冷复位，给有关全局变量赋初值
    if(BGET(RCM_SRS0_POR_SHIFT,RCM_SRS0))
	{
    	memset(gcRecvBuf,0,1024);
    	gcRecvLen=0;
    	memset(gcUpdateFlag,0,10);
    	WDResetCount=0;
    	WDTimeCount=0;
    	gFuncFlag=0;
    	PINResetCount=0;
    	PINTimeCount=0;
    	NMICount=0;
	}

	//（3）【根据实际需要增删】 初始化外设模块
	light_init(LIGHT_RED,LIGHT_OFF);//初始化BIOS运行指示灯（PTD7）
	emuart_init(UART_1,115200);   //初始化BIOS程序更新串口（UART1）
	systick_init(CORE_CLOCK_DIV_16,500); //systick 中断时间间隔为500ms
	flash_init();                 //初始化Flash构件
	shakeDataInit();

    //（6）【不变】开总中断
    ENABLE_INTERRUPTS;            //开总中断

	muFlag = 0;
	mCount = 0;

	//（7）判断是否需要进行更新操作
	muFlag = updateJudge();
	if(muFlag != 0) gFunFlag = 1;  //需进行更新提示操作
	else gcRecvLen = 0;

	wdogResetJudge();  //如果短时间发生了多次看门狗复位，则不进入用户程序
	pinResetJudge();   //如果短时间发生了多次按键复位，则不进入用户程序

	//主循环开始======================================================
	for(;;)
	{
		switch(gFunFlag)
		{
		case 0:  //无需更新数据，跳转User程序执行
			light_control(LIGHT_RED,LIGHT_OFF);
			cpy_nvic();                       //中断向量表复制至RAM区
			//跳转User程序
			projectJump(USERBASE);
			break;
		case 1:  //需进行更新提示操作
			updateNotify(muFlag);
			gFunFlag = 10;
			break;
		default:  //其他情况，绿灯闪烁，提示处于BIOS程序
			mCount++;
			while(mCount > 0x5ffff)
			{
				mCount = 0;
				light_change(LIGHT_RED);
			}
		}
	}
	//主循环结束======================================================
    return 0;
}

uint_64 sysTimeGet()
{
	return gTime;
}

void sysTimeSet(uint_64 sec)
{
	gTime = sec;
}



//=====================================================================
//函数名称：wdogResetJudge
//函数返回：无
//参数说明：无
//功能概要：判断是否复位源为看门狗复位，如是且看门狗中断次数超过3次（2分钟内），则不进入用户程序
//=====================================================================
void wdogResetJudge()
{
	//防止初始化出错
	if((WDResetCount)>3)	WDResetCount=0;
	if((WDTimeCount)>240)   WDTimeCount=0;
	//如果发生了看门狗复位
	if(BGET(RCM_SRS0_WDOG_SHIFT,RCM_SRS0))
	{
		(WDResetCount)++;   //看门狗复位次数+1
		//如果两分钟内发生了3次看门狗复位的话，不进入用户程序
 		if((WDResetCount)>=3 &&(WDTimeCount) <240)
		{
			WDResetCount=0;
			WDTimeCount=0;
			//改变标志位的值，使之后的判断不会进入用户程序，同时保存到对应的SRAM地址中
			gFuncFlag=10;
			gFunFlag = gFuncFlag;
		}
 		//防止判断出错
 		if((WDTimeCount)>=240||(WDResetCount)>3)
		{
			WDTimeCount=0;
			WDResetCount=0;
		}
	}
}

//=====================================================================
//函数名称：PINResetJudge
//函数返回：无
//参数说明：无
//功能概要：判断是否复位源为按键复位，如是且按键中断次数超过3次（1分钟内），则不进入用户程序
//=====================================================================
void pinResetJudge()
{
	uint_8 i;
	//如果发生了按键复位
	if(BGET(RCM_SRS0_PIN_SHIFT,RCM_SRS0))
	{
		//更改标志位，使程序最终能运行到用户程序，同时修改对应SRAM中的值
		gFuncFlag=0;
		gFunFlag = gFuncFlag;
		//防止初始化出错
		if((PINResetCount)>3)	 PINResetCount=0;
		if((PINTimeCount)>120)  PINTimeCount=0;
		if((gFuncFlag)>10)      gFuncFlag=0;
		//按键复位次数+1
		(PINResetCount)++;
		//如果120次时间计数(60s之内)发生了3次以上的按键复位
		if((PINResetCount)>=3 && (PINTimeCount)<120)
		{
			//修改标志位以及对应SRAM的值，使程序不会运行到用户程序
			gFuncFlag=10;
			 gFunFlag = gFuncFlag;

		}
		//小灯闪烁代码，闪烁次数为4
		for(i=0;i<4;i++)
		{
			light_change(LIGHT_RED);//小灯状态反转
		    Delay_ms(500);
		}

	}
}

//=========================================================================
//函数名称：updateNotify
//参数说明：type:更新方式。
//      1：提示可串口更新；2：提示可远程更新；3：串口、远程都提示可更新
//函数返回：无
//功能概要：依据更新方式进行更新提示
//=========================================================================
void updateNotify(uint_8 type)
{
}

//=========================================================================
//函数名称：userSectSet
//参数说明：startNo：User程序起始扇区号；endNo：User程序结束扇区号
//函数返回：无
//功能概要：获取User程序起始和结束扇区号
//=========================================================================
void userSectSet(uint_32 startNo,uint_32 endNo,uint_32 codeSize)
{
	//（1）设置User程序起始和结束扇区号
	gUSNo = startNo;
	gUENo = endNo;
	//（2）设置握手帧中保留更新最大字节
	gShakeData.reserveNum = (SECTORNUM-endNo-1)*SECTORSIZE;
	gShakeData.uCodeSize = codeSize;
}

//=====================================================================
//函数名称：BiosSecProtect
//函数返回：无
//参数说明：无
//功能概要：保护BIOS程序的扇区空间（0~BIOSSECT_END扇区）
//=====================================================================
void BiosSecProtect()
{
	uint_8 i;
	for(i=0;i<(USERSECTOR-1);i+=2)
	{
		flash_protect(i);
	}
}



//=====================================================================
//函数名称：FlashData_get
//函数返回：无
//参数说明：无
//功能概要：获取保存在第26扇区的终端配置信息
//=====================================================================
uint_8 FlashData_get(FlashData* UserFlashDataAddr)
{
	flash_read_logic((uint_8*)(UserFlashDataAddr),28,0,sizeof(FlashData));
	return 1;
}

//=====================================================================
//函数名称：FlashData_set
//函数返回：无
//参数说明：无
//功能概要：配置保存在第26扇区的终端配置信息
//=====================================================================
uint_8 FlashData_set(FlashData UserFlashDataAddr)
{
	flash_erase(28);
	flash_write(28,0,sizeof(FlashData),(uint_8*)(&UserFlashDataAddr));
	return 1;
}

//=========================================================================
//函数名称：shakeDataInit
//参数说明：无
//函数返回：无
//功能概要：握手帧数据初始化
//=========================================================================
void shakeDataInit()
{
	//（1）变量声明

	//（2）握手帧数据赋值
	uecom_typeGet(gShakeData.uecomType);
	memcpy(gShakeData.mcuType,MCUTYPE,sizeof(MCUTYPE));
	gShakeData.uStartAds = USERBASE;
	gShakeData.replaceNum = (SECTORNUM-USERSECTOR)*SECTORSIZE;
	gShakeData.reserveNum = 0;
}
//=========================================================================
//函数名称：updateJudge
//参数说明：无
//函数返回：0：无需更新；1：串口更新；2：远程更新；3：无User程序
//功能概要：进行更新状态判断，若需更新则进一步明确是串口更新还是远程更新
//=========================================================================
uint_8 updateJudge()
{
	//（1）变量声明
	uint_8 uflag;  //更新接收标志
	uint_32 msp;   //保存在flash中的主栈指针
	uint_8 rv;     //返回值
	uint_8 buf[100]; //100字节数据用于存放返回数据
	uint_16 bufLen;  //返回数据长度

	rv = 0;  //默认无需更新
	//（2）是否需要进行更新操作
	if(strcmp(gcUpdateFlag,"uart\0") == 0)  //需进行串口更新操作
	{
		gcUpdateFlag[0] = 0; //清uart标志
		//整体更新跳回BIOS后，ueupdate需重新初始化
		ueupdate_init(USERSECTOR,gUENo,SECTORSIZE);
		uflag = ueupdate_reinit(gcRecvBuf,gcRecvLen);
		if(uflag == 1) //收到更新开始后，返回信息提示上位机可发送其他数据
		{
			ueupdate_backGet(buf,&bufLen);
			emuart_sendN(bufLen,buf);  //返回信息
		}
		gcRecvLen = 0;
		rv = 1;  //需进行串口更新
	}
	else if(strcmp(gcUpdateFlag,"remote\0") == 0)  //需进行远程更新操作
	{
		rv = 2;  //需进行远程更新
	}
	//190227-NMI（2/2）---
	else if(strcmp(gcUpdateFlag,"NMI-had5") == 0)  //NMI中断5次以上
	{
		rv = 3;  //当作无User程序
		memcpy(gcUpdateFlag,"NMI-NO-5\0",9);  //清1分钟内有5次以上NMI中断标志
	}
	//-------------------
	else  //无需更新User程序
	{
		//读取User中断向量表首地址
		flash_read_logic((uint_8*)&msp,USERSECTOR,0,4);
		//判断User程序是否存在（若msp不等于栈顶，则没有用户程序）
		if(msp == STACKTOP)  //存在User程序
		{
			rv = 0;  //无需更新且存在User程序
		}
		else  //不存在User程序
		{
			gcRecvLen = 0;
			rv = 3;  //无User程序
		}
	}

	return rv;
}
