/*--------------------------------------------------------------------------------------
*  @file     RTCSetup.c
*  @author   ZhangJing
*  @version  base on stm32f0x   
*  @date     2015.09.11
*  @brief    RTC驱动
---------------------------------------------------------------------------------------*/
#include "stm32f10x_gpio.h"
#include "stm32f10x_rtc.h"
#include "stm32f10x_pwr.h"
#include "stm32f10x_bkp.h"
#include "stm32f10x_exti.h"
#include "TypeDefine.h"
#include "DrawLCDGUI.h"
#include "RTCSetup.h"
#include "FM31256.h"

//extern SpeakerWorkType speakerWorkStep;//蜂鸣器工作步骤

const uint8_t table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正数据表
const uint8_t mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};//平年月份日期表
static ErrorStatus HSEStartUpStatus;//HSE时钟启动状态

/*********************************************************************************
*  Function:	Is_Leap_Year						                                                           
*  Object:		判断是否是闰年
*  输入:			年份
*  输出:			该年是不是闰年。1是 0不是	                                     
*  备注:			//月份   1  2  3  4  5  6  7  8  9  10 11 12
							//闰年   31 29 31 30 31 30 31 31 30 31 30 31
							//非闰年 31 28 31 30 31 30 31 31 30 31 30 31              
**********************************************************************************/
uint8_t Is_Leap_Year(uint16_t year)
{                   
	if( year % 4 == 0 ) //必须能被4整除
	{
		if( year % 100 == 0 )
		{
			if( year % 400 == 0 )
			{
				return 1;//如果以00结尾,还能被400整除 
			}	
			else 
			{
				return 0; 
			}
		}
		else 
		{
			return 1;   
		}
	}
	else 
	{
		return 0; 
	}
} 

/*********************************************************************************
*  Function:	RTC_Get_Week						                                                           
*  Object:		获得现在是星期几
*  输入:			公历年月日
*  输出:			星期号                                  
*  备注:			1、uint8_t yearH,yearL;年的高低字节计算		
*							2、uint16_t temp2;作为计算的临时变量
**********************************************************************************/
uint8_t RTC_Get_Week(uint16_t year,uint8_t month,uint8_t day)
{
	uint16_t temp2;
	uint8_t yearH,yearL;
	
	yearH = year / 100;     
	yearL = year % 100; 

	// 如果为21世纪,年份数加100 
	if( yearH > 19 )
	{
		yearL += 100;
	}

	//所过闰年数只算1900年之后的
	temp2 = yearL + yearL / 4;
	temp2 = temp2 % 7; 
	temp2 = temp2 + day + table_week[month - 1];
	if( yearL % 4 == 0 && month < 3 )
	{
		temp2--;
	}
	return( temp2 % 7 );
}

/*********************************************************************************
*  Function:	RTC_Set						                                                           
*  Object:		设置时钟
*  输入:			月份数据表
*  输出:			0：成功 其它：错误代码                                     
*  备注:			把输入的时钟转换为秒钟
*							以1970年1月1日为基准
*							1970~2099年为合法年份							
**********************************************************************************/
uint8_t RTC_Set(uint16_t syear,uint8_t smon,uint8_t sday,uint8_t hour,uint8_t min,uint8_t sec)
{
	uint16_t t;
	uint32_t seccount=0;
	
// 	 RTC_ITConfig(RTC_IT_SEC, DISABLE);        //关闭秒中断
//    RTC_WaitForLastTask();

	if( syear < 2015 || syear > 2099 )
	{
		return 1;//syear范围1970-2099,此处范围设置为2015-2099       
	}		

	for( t = 1970;t < syear;t++) //把所有年份的秒钟相加
	{
		if( Is_Leap_Year( t ) )
		{
			seccount += 31622400;//闰年的秒钟数
		}
		else 
		{
			seccount += 31536000;//平年的秒钟数
		}
	}
	smon -= 1;
	for( t = 0;t < smon;t++ )         //把前面月份的秒钟数相加
	{
		seccount += (uint32_t)mon_table[t] * 86400;//月份秒钟数相加
		if( Is_Leap_Year( syear ) && ( t == 1 ) )
		{
			seccount += 86400;//闰年2月份增加一天的秒钟数   
		}			
	}
	seccount += (uint32_t)( sday - 1 ) * 86400;//把前面日期的秒钟数相加
	seccount += (uint32_t)hour * 3600;//小时秒钟数
	seccount += (uint32_t)min * 60;//分钟秒钟数
	seccount += sec;//最后的秒钟数加上去
	
	//设置时钟
// 	RCC->APB1ENR|=1<<28;//使能电源时钟

// 	RCC->APB1ENR|=1<<27;//使能备份时钟

// 	PWR->CR|=1<<8;    //取消备份区写保护

	// 	//以上三步是必须的！
	
	RTC_WaitForLastTask();
	RTC_SetCounter(seccount);
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
	PWR_BackupAccessCmd(ENABLE);
	RTC_WaitForLastTask();
	RTC_ITConfig(RTC_IT_SEC, ENABLE); //打开秒中断
	return 0;
}

/*********************************************************************************
*  Function:	RTC_Get						                                                           
*  Object:		得到当前时间
*  输入:			无
*  输出:			0：成功 其它：错误代码                                     
*  备注:			1、static uint16_t daycnt=0;天数计算
*							2、uint32_t timecount=0;时间计算 
*							3、uint32_t temp=0;临时变量
*							4、uint16_t temp1=0;临时变量						
**********************************************************************************/
uint8_t RTC_Get(void)
{
	static uint16_t daycnt=0;
	uint32_t timecount=0; 
	uint32_t temp=0;
	uint16_t temp1=0;
	
	timecount = RTC_GetCounter();
	
// 	timecount=RTC->CNTH;//得到计数器中的值(秒钟数)
// 	timecount<<=16;
// 	timecount+=RTC->CNTL;    
	
	temp = timecount / 86400;   //得到天数(秒钟数对应的)
	if( daycnt != temp )//超过一天了
	{
		daycnt = temp;
		temp1 = 1970;  //从1970开始
		
		while( temp >= 365 )
		{                        
			if( Is_Leap_Year( temp1 ) )//是闰年
			{
// 				if( temp >= 366 )
// 				{
// 					temp -= 366;//闰年的秒钟数
// 				}
// 				else 
// 				{
// 					temp1++;
// 					break;
// 				}
				temp -= 366;//闰年的秒钟数
			}
			else 
			{
				temp -= 365;       //平年
// 				temp1++;
			}		
			temp1++;
		}
		displayTimeBuf.year = temp1;//得到年份
		temp1 = 0;	
		while( temp >= 28 )//超过了一个月
		{
			if( Is_Leap_Year( displayTimeBuf.year ) && ( temp1 == 1 ) )//当年是不是闰年/2月份
			{
				if( temp >= 29 )
				{
					temp -= 29;//闰年的秒钟数
				}
				else 
				{
					break; 
				}
			}
			else 
			{
				if( temp >= mon_table[temp1] )
				{
					temp -= mon_table[temp1];//平年
				}
				else
				{
					break;
				}
			}
			temp1++; 
		}
		displayTimeBuf.month = temp1 + 1;//得到月份
		displayTimeBuf.date = temp + 1;  //得到日期
	}
	
	temp = timecount % 86400;     //得到秒钟数   
	displayTimeBuf.hour = temp / 3600;     //小时
	displayTimeBuf.minute = ( temp % 3600 ) / 60; //分钟      
	displayTimeBuf.seconds = ( temp % 3600 ) %60; //秒钟
	//displayTimeBuf.week = RTC_Get_Week( displayTimeBuf.year,displayTimeBuf.month,displayTimeBuf.date );//获取星期 
	
	return 0;		
}

/*********************************************************************************
*  Function:	RTCInit						                                                           
*  Object:		RTC计时功能初始化
*  输入:			无
*  输出:			无	                                     
*  备注:			uint16_t waitForOscSource;临时计算变量              
**********************************************************************************/
void RTCInit(void)
{
	uint16_t waitForOscSource;	
	//在BKP后备寄存器1中，存储了一个特殊字符0xA5A5
	//第一次上电或后备电源掉电后，该寄存器数据丢失
	//表明RTC数据丢失，需重新配置
	if( BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5 )
	{
		//重新配置RTC
		/* Enable PWR and BKP clocks */
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
		
		/* Allow access to BKP Domain */
		PWR_BackupAccessCmd(ENABLE);
		
		/* Reset Backup Domain */
		BKP_DeInit();
		
		/* Enable LSE */
		RCC_LSEConfig(RCC_LSE_ON);
		for( waitForOscSource = 0;waitForOscSource < 5000;waitForOscSource++ )
		{

		}

// 		/* Wait till LSE is ready */
 		while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);
		
		/* Select LSE as RTC Clock Source */
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
		
		/* Enable RTC Clock */
		RCC_RTCCLKCmd(ENABLE);

		/* Wait for RTC registers synchronization */
		RTC_WaitForSynchro();

// 		/* Wait until last write operation on RTC registers has finished */
// 		RTC_WaitForLastTask();
// 		
// 		/* Enable the RTC Second */
// 		RTC_ITConfig(RTC_IT_SEC, ENABLE);	
		
		/* Wait until last write operation on RTC registers has finished */
		RTC_WaitForLastTask();		
		
		/* Set RTC prescaler: set RTC period to 1sec */
		RTC_SetPrescaler(32767); /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */

		/* Wait until last write operation on RTC registers has finished */
		RTC_WaitForLastTask();

		//配置完成后,向后备电源写特殊字符0xA5A5

		BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);

		RTC_Set(2015,01,01,0,0,0);//默认时间
	}
	else
	{

		//若后备电源没有掉电,则无需重新配置RTC
		//这里我们可以利用RCC_GetFlagStatus()函数查看本次复位类型
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

		for( waitForOscSource = 0;waitForOscSource < 5000;waitForOscSource++ );
		
		if(RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET)
		{		
			//这是上电复位
		}
		else if(RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET)
		{
			//这是外部RESET管脚复位
		}
		
		//清除RCC中复位标志
		RCC_ClearFlag();
		
		//虽然RTC模块不需要配置,且掉电后依靠后备电源依然运行

		//但是每次上电后,还是要使能RTCCLK???????

		//RCC_RTCCLKCmd(ENABLE);
		
		//等待RTC时钟与APB1时钟同步
		//RTC_WaitForSynchro();
		
		//使能秒中断
		RTC_ITConfig(RTC_IT_SEC, ENABLE);

		//等待操作完成
		RTC_WaitForLastTask();
	}
	return;
}

/*********************************************************************************
*  Function:	SystemTimeUpdate						                                                           
*  Object:		系统时间更新
*  输入:			无
*  输出:			无	                                     
*  备注:			1、通过 TaskSchedulerFlag.timeReadFlag标志位来判断是否更新时间  
*							2、每读取一次时间进行一次时间显示界面的刷新
**********************************************************************************/
void SystemTimeUpdate( void )
{
	if( TaskSchedulerFlag.timeReadFlag == TASK_FLAG_SET )//时间读取
	{
		if( realTimeData.stateRun != Poweroff )
		{
			//获取当前时间
			if( speakerWorkStep != emSpeakerNoneWork)
			    return;
			GetCurDateTimeFromRTC();
			
			if( emCurrentPicture == TimeDisp )//更新显示时间页面
			{
				emDisplayPicture = TimeDisp;	
			}			
			
			TaskSchedulerFlag.timeReadFlag = TASK_FLAG_CLEAR;
		}
	}
}

/*********************************************************************************
*  Function:	RTC_NVIC_Config						                                                           
*  Object:		RTC中断配置
*  输入:			无
*  输出:			无	                                     
*  备注:			NVIC_InitTypeDef NVIC_InitStructure; 中断配置结构体              
**********************************************************************************/
void RTC_NVIC_Config(void)
{    
    NVIC_InitTypeDef NVIC_InitStructure;

    NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;        			//RTC全局中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;   //先占优先级1位，从优先级3位
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;   
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;							//使能该通道中断
    NVIC_Init(&NVIC_InitStructure);        											//根据NVIC_InitStruct中指定参数初始化外设NVIC寄存器
}

/*********************************************************************************
*  Function:	RTC_Alarm_Configuration						                                                           
*  Object:		RTC闹钟初始化
*  输入:			无
*  输出:			无	                                     
*  备注:			启动时钟、配置LSI做RTC时钟、设置预分频40000得到1Hz     
*							设置运行时间WORK_TIMES
**********************************************************************************/
void RTC_Alarm_Configuration(void)
{
	/* Enable PWR and BKP clocks */
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
	/* Allow access to BKP Domain */
	PWR_BackupAccessCmd(ENABLE);	
	/* Reset Backup Domain */
	BKP_DeInit();

	/* RTC clock source configuration ----------------------------------------*/
	/* Enable the LSI OSC */
	RCC_LSICmd(ENABLE);
	/* Wait till LSI is ready */
	while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET)
	{
	}
	/* Select the RTC Clock Source */
	RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
	
	/* Enable the RTC Clock */
	RCC_RTCCLKCmd(ENABLE);
	/* Wait for RTC registers synchronization */
	RTC_WaitForSynchro();
	/* Wait until last write operation on RTC registers has finished */
	RTC_WaitForLastTask(); 
	/* 使能RTC闹钟中断*/
	RTC_ITConfig(RTC_IT_ALR, ENABLE);	
	/* Wait until last write operation on RTC registers has finished */
	RTC_WaitForLastTask();	
	/* Set RTC prescaler: set RTC period to 1sec */
	RTC_SetPrescaler(40000);	
	/* Wait until last write operation on RTC registers has finished */
	RTC_WaitForLastTask();

	//中断配置
	RTC_NVIC_Config();

	//设置运行WORK_TIMES
	RTC_SetAlarm(RTC_GetCounter() + WORK_TIMES);
	RTC_WaitForLastTask();
}

/*********************************************************************************
*  Function:	StopMode_Configuration						                                                           
*  Object:		停止模式RTC闹钟初始化
*  输入:			无
*  输出:			无	                                     
*  备注:			启动时钟、配置LSI做RTC时钟、设置预分频40000得到1Hz     
**********************************************************************************/
void StopMode_Configuration(void)
{
	EXTI_InitTypeDef EXTI_InitStructure;

	/* Enable PWR and BKP clocks */
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
	/* Allow access to BKP Domain */
	PWR_BackupAccessCmd(ENABLE);	
	/* Reset Backup Domain */
	BKP_DeInit();

	/* RTC clock source configuration ----------------------------------------*/
	/* Enable the LSI OSC */
	RCC_LSICmd(ENABLE);
	/* Wait till LSI is ready */
	while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET)
	{
	}
	/* Select the RTC Clock Source */
	RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
	/* Enable the RTC Clock */
	RCC_RTCCLKCmd(ENABLE);
	/* Wait for RTC registers synchronization */
	RTC_WaitForSynchro();
	/* Wait until last write operation on RTC registers has finished */
	RTC_WaitForLastTask(); 
	/* Set RTC prescaler: set RTC period to 1sec */
	RTC_SetPrescaler(40000);	
	/* Wait until last write operation on RTC registers has finished */
	RTC_WaitForLastTask();


	/* Configure EXTI Line17(RTC Alarm) to generate an interrupt on rising edge */
	EXTI_ClearITPendingBit(EXTI_Line17);
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event;
	EXTI_InitStructure.EXTI_Line = EXTI_Line17;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure);
}

/*******************************************************************************
* Function Name  : SYSCLKConfig_STOP
* Description    : Configures system clock after wake-up from STOP: enable HSE, PLL
*                  and select PLL as system clock source.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void StopMode_SYSCLKConfig_STOP(void)
{
  /* Enable HSE */
  RCC_HSEConfig(RCC_HSE_ON);

  /* Wait till HSE is ready */
  HSEStartUpStatus = RCC_WaitForHSEStartUp();

  if(HSEStartUpStatus == SUCCESS)
  {
    /* Enable PLL */ 
    RCC_PLLCmd(ENABLE);

    /* Wait till PLL is ready */
    while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
    {
    }

    /* Select PLL as system clock source */
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    /* Wait till PLL is used as system clock source */
    while(RCC_GetSYSCLKSource() != 0x08)
    {
    }
  }
}

/*********************************************************************************
*  Function:	StopMode_EnterStopMode						                                                           
*  Object:		进入停止模式
*  输入:			uint32_t s，闹钟触发时间
*  输出:			无	                                     
*  备注:			设置待机时间并进入停止，闹钟事件发生自动退出stop模式
*							进入stop模式，闹钟唤醒后跳出函数运行
*							RAM/GPIO状态保持
*							s为单位秒
**********************************************************************************/
void StopMode_EnterStopMode(uint32_t s)
{
	//设置待机时间
	RTC_SetAlarm(RTC_GetCounter() + s);
	RTC_WaitForLastTask();

	/* LDO不关掉,闹钟事件唤醒*/
	PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFE);	

	//唤醒后重新配置系统时钟
	StopMode_SYSCLKConfig_STOP();	

}
