include "sys.h"
#include "delay.h"
#include "usart.h"
#include "rtc.h" 		    
 
/*	
		//我们一般用 BKP 来存储 RTC 的校验值或者记录一些重要的数据，相当于一个 EEPROM，
		//不过这个 EEPROM 并不是真正的 EEPROM，而是需要电池来维持它的数据。
		//读写后备寄存器的流程
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); 
		PWR_BackupAccessCmd(ENABLE); 
		//先执行上面两个函数后才能读写bkp。
	
		//读bkp的时候就是 
		data=BKP_ReadBackupRegister(BKP_DRx);
		
		//写bkp的时候就是 
		BKP_WriteBackupRegister(BKP_DRx, data);
		
		//BKP_DRx就是bkp寄存器，比如BKP_DR11。
		//注意data最多是16位的数据。
*/

_calendar_obj calendar;//时钟结构体 


//实时时钟配置
//初始化RTC时钟,同时检测时钟是否工作正常
//BKP->DR1用于保存是否第一次配置的设置
//返回0:正常
//其他:错误代码
u8 RTC_Init(void)	//PC 13 14 15
{	
	u8 temp=0;
	GPIO_InitTypeDef GPIO_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟   	
	PWR_BackupAccessCmd(ENABLE);	//使能后备寄存器访问 
	
	//RCC_LSEConfig( RCC_LSE_OFF ); /* 关闭外部低速时钟,PC14+PC15可以用作普通IO*/
	//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14|GPIO_Pin_15 ;
	//GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉输入（低功耗用）         
	//GPIO_Init(GPIOC, &GPIO_InitStructure);
	
//	BKP_TamperPinCmd(DISABLE);  /* 关闭入侵检测功能,PC13可以用作普通IO*/	
//	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 ;
//	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉输入（低功耗用）         
//	GPIO_Init(GPIOC, &GPIO_InitStructure);
	
		//PA13/14/15 & PB3/4默认配置为JTAG功能
		//GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);
		// 改变指定管脚的映射 GPIO_Remap_SWJ_Disable SWJ 完全禁用（JTAG+SW-DP）	
		//GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE);
		// 改变指定管脚的映射 GPIO_Remap_SWJ_JTAGDisable ，JTAG-DP 禁用 + SW-DP 使能
	
	//要向备份区域写入数据，就要先取消备份区域写保护（写保护在每次硬复位之后被使能），
	//否则是无法向备份区域写入数据的。我们需要用到向备份区域写入一个字节，来标记时钟已经
	//配置过了，这样避免每次复位之后重新配置时钟。 取消备份区域写保护的库函数实现方法是：
	//BKP中包括了42个16位的寄存器，共可保存84字节的内容，它们由VBAT的供电来维挂。
	if (BKP_ReadBackupRegister(BKP_DR1) != 0x5050)	//检查是不是第一次配置时钟，从指定的后备寄存器中读出数据:后备电源断电后需要重新设置时间
	{	 			
		BKP_DeInit();	//复位备份区域
		//在取消备份区域写保护之后，我们可以先对这个区域复位，以清除前面的设置，当然这个操作不要每次都执行，因为备份区域的复位将导致之前存在的数据丢失。
	
		RCC_LSEConfig(RCC_LSE_ON);	//设置外部低速晶振(LSE),使用外设低速晶振，改变系统时钟不会影响RTC准确性
		while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250)	//检查指定的RCC标志位设置与否,等待低速晶振就绪
		{
			temp++;
			delay_ms(10);
		}
		if(temp>=250)return 1;//初始化时钟失败,晶振有问题	    
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);		//设置RTC时钟(RTCCLK),选择LSE作为RTC时钟    
		RCC_RTCCLKCmd(ENABLE);	//使能RTC时钟  
		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
		
		RTC_WaitForSynchro();		//等待RTC寄存器同步  
		RTC_ITConfig(RTC_IT_SEC, ENABLE);		//使能RTC秒中断
		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
		
		RTC_EnterConfigMode();	//进入RTC配置模式，允许配置
		RTC_SetPrescaler(32767); //设置RTC预分频的值
		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
		RTC_Set(2018,1,1,12,0,0);  //设置时间	//年 月 日 时 分 秒
		RTC_ExitConfigMode(); //退出RTC配置模式  
		
		BKP_WriteBackupRegister(BKP_DR1, 0X5050);	//向指定的后备寄存器中写入用户程序数据
	}
	else//正常初始化，不需要改变时间
	{
		RTC_WaitForSynchro();	//等待RTC寄存器同步  
		RTC_ITConfig(RTC_IT_SEC, ENABLE);	//使能RTC秒中断
		RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
	}
	
	NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;	//RTC全局中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;	//先占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;				//从优先级位
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;						//使能该通道中断
	NVIC_Init(&NVIC_InitStructure);		//根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器	器
	
	//RTC_Get();//更新时间，填充结构体	
	
	return 0; //ok
}	



u8 RTC_IRQH_FLAG;//1秒中断
u8 year[]="0000",month[]="00",day[]="00",hour[]="00",min[]="00",second[]="00";//储存时间
u8 RCTtime[15];//存储时间字符串

//RTC时钟中断
//每秒触发一次  
//extern u16 tcnt; 
void RTC_IRQHandler(void)
{		 
	if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒钟中断
	{		
			RTC_IRQH_FLAG=1;	
			RTC_Get();//更新时间
			//GET_RTC_TIME(year,month,day,hour,min,second,0);//填充时间数组		占用时间0.6ms
 	}
//	if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断
//	{
//		RTC_ClearITPendingBit(RTC_IT_ALR);		//清闹钟中断	  	
//	  RTC_Get();				//更新时间   
//  	printf("Alarm Time:%d-%d-%d %d:%d:%d\n",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);//输出闹铃时间		
//  } 				  								 
	RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);	//清秒中断和溢出中断
	RTC_WaitForLastTask();	  	    						 	   	 
}



//设置时钟//设置时间实际上就是设置 RTC 的计数值，时间与计数值之间是需要换算的。
//把输入的时钟转换为秒钟
//以1970年1月1日为基准
//1970~2099年为合法年份
//返回值:0,成功;其他:错误代码.					 
u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; ////月份数据表			月修正数据表	  
const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};//平年的月份日期表

u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
{
	u16 t;
	u32 seccount=0;
	if(syear<1970||syear>2099)return 1;	//超时   
	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+=(u32)mon_table[t]*86400;//月份秒钟数相加
		if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数	   
	}
	seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加 
	seccount+=(u32)hour*3600;//小时秒钟数
  seccount+=(u32)min*60;	 //分钟秒钟数
	seccount+=sec;//最后的秒钟加上去

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟  
	PWR_BackupAccessCmd(ENABLE);	//使能RTC和后备寄存器访问 
	RTC_SetCounter(seccount);	//设置RTC计数器的值
	RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成  	
	return 0;	    
}




//得到实时时间
u8 *GET_RTC_TIME(	u8 year[4],u8 month[2],u8 day[2],u8 hour[2],u8 min[2],u8 second[2] ,u8 CMD)
{
		u32 timecount=0; 
		u32 temp=0;
		u16 temp1=0;
	
		u8 static time[sizeof("20190617161523")];	
		//u8 year[]="0000",month[]="00",day[]="00",hour[]="00",min[]="00",second[]="00";
		timecount=RTC_GetCounter();	//得到实时时钟秒钟数 
		temp=timecount/86400;   //得到距 1970年1月1日 总的天数(秒钟数对应的)			
	
		temp1=1970;	//从1970年开始
		while(temp>=365)//超过1年的填上
		{				 
				if(Is_Leap_Year(temp1))//是闰年
				{
						if(temp>=366)temp-=366;//闰年的秒钟数
						else {temp1++;break;}  
				}
				else 
						temp-=365;	  //平年 
				temp1++;  
		} 
		Num_To_Ascii(temp1,4,year);			
				
		temp1=0;
		while(temp>=28)//超过了一个月
		{
				if(Is_Leap_Year(calendar.w_year)&&temp1==1)//当年是不是闰年/2月份
				{
					if(temp>=29)temp-=29;//闰年的秒钟数
					else break; 
				}
				else 
				{
					if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
					else break;
				}
				temp1++;  
		}
		Num_To_Ascii(temp1+1,2,month);							
		Num_To_Ascii(temp+1,2,day);			
				
		temp=timecount%86400; //得到剩余总秒钟数   
		
		Num_To_Ascii(temp/3600,2,hour);			
		Num_To_Ascii((temp%3600)/60,2,min);					
		Num_To_Ascii((temp%3600)%60,2,second );	
		
/////////////////////////////////////////////////////////////////
		if(CMD)
		{
				strcpy((char*)time,(char*)year);		//年
				strcat((char*)time,(char*)month);		//月
				strcat((char*)time,(char*)day);			//日
				strcat((char*)time,(char*)hour);		//时
				strcat((char*)time,(char*)min);			//分
				strcat((char*)time,(char*)second);	//秒
		}
		return time;	
}


//数字 转 ascii
void Num_To_Ascii(u16 num,u8 len,u8 *buf)
{
		u8 i=0;
		
		//高位为0则补0
		for(i=0;i<len-1;i++)
		{
				if(num < pow(10,len-i-1)) //高位为0
				{
						buf[len-i-1] = '0'; //转成ascii	
						len--;
				}
				else
					break;
		}		
		for(i=len;i>0;i--)
		{				
				buf[i-1] = num % 10 + 48; //转成ascii
				num=num/10;
				//printf("%c ",buf[i]);
		}
}

////创建时间文件名指针
//u8 *Creat_File(void)
//{	
//		u8 ECG_Directory[]="0:/ECG/\0";	//文件的生成位置
//		u8 static file_buf[sizeof(GET_RTC_TIME(year,month,day,hour,min,second,0))+sizeof(ECG_Directory)+4];
//		
//		strcpy((char*)file_buf,(char*)ECG_Directory);	
//		strcat((char*)file_buf,(char*)GET_RTC_TIME(year,month,day,hour,min,second,1));	
//		strcat((char*)file_buf,".bin\0");		
//		return file_buf;
//}





//判断是否是闰年函数
//月份   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
//输入:年份
//输出:该年份是不是闰年.1,是.0,不是
u8 Is_Leap_Year(u16 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;	
}	





////初始化闹钟		  
////以1970年1月1日为基准
////1970~2099年为合法年份
////syear,smon,sday,hour,min,sec：闹钟的年月日时分秒   
////返回值:0,成功;其他:错误代码.
//u8 RTC_Alarm_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
//{
//	u16 t;
//	u32 seccount=0;
//	if(syear<1970||syear>2099)return 1;	   
//	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+=(u32)mon_table[t]*86400;//月份秒钟数相加
//		if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数	   
//	}
//	seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加 
//	seccount+=(u32)hour*3600;//小时秒钟数
//    seccount+=(u32)min*60;	 //分钟秒钟数
//	seccount+=sec;//最后的秒钟加上去 			    
//	//设置时钟
//	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟   
//	PWR_BackupAccessCmd(ENABLE);	//使能后备寄存器访问  
//	//上面三步是必须的!
//	
//	RTC_SetAlarm(seccount);
// 
//	RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成  	
//	
//	return 0;	    
//}



//得到当前的时间 更新结构体
//返回值:0,成功;其他:错误代码.
u8 RTC_Get(void)
{
	static u16 daycnt=0;
	u32 timecount=0; 
	u32 temp=0;
	u16 temp1=0;	
  
  timecount=RTC_GetCounter();	//得到实时时钟秒钟数 
 	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;}  
					}
					else temp-=365;	  //平年 
					temp1++;  
			}   
			calendar.w_year=temp1;//得到年份
			temp1=0;
			while(temp>=28)//超过了一个月
			{
					if(Is_Leap_Year(calendar.w_year)&&temp1==1)//当年是不是闰年/2月份
					{
						if(temp>=29)temp-=29;//闰年的秒钟数
						else break; 
					}
					else 
					{
						if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
						else break;
					}
					temp1++;  
			}
			calendar.w_month=temp1+1;	//得到月份
			calendar.w_date=temp+1;  	//得到日期 
	}
	temp=timecount%86400;     		//得到总秒钟数   	   
	calendar.hour=temp/3600;     	//小时
	calendar.min=(temp%3600)/60; 	//分钟	
	calendar.sec=(temp%3600)%60; 	//秒钟
	calendar.week=RTC_Get_Week(calendar.w_year,calendar.w_month,calendar.w_date);//获取星期   
	return 0;
}	 

//获得现在是星期几
//功能描述:输入公历日期得到星期(只允许1901-2099年)
//输入参数：公历年月日 
//返回值：星期号																						 
u8 RTC_Get_Week(u16 year,u8 month,u8 day)
{	
	u16 temp2;
	u8 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);
}			  
















