1. 程式人生 > >stm32 RTC實時時鐘[操作暫存器+庫函式]

stm32 RTC實時時鐘[操作暫存器+庫函式]

#include <stm32f10x_lib.h>	
#include "rtc.h"
#include "stdio.h"

tm timer;					//定義時鐘結構體,主函式直接可以呼叫此結構體讀出時間

//平年的月份日期表,月份縮寫表
const u8 Days_Table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
const u8 Month_Table[12][3]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};

const u8* Week_Table[7]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};

//月修正資料表																		 
u8 const _Week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; 

void Rtc_Init(void)
{
	RCC->APB1ENR |= 1<<28;	//使能PWR時鐘
	RCC->APB1ENR |= 1<<27;  //使能BKP時鐘,RTC校準在BKP相關暫存器中
 	PWR->CR |= 1<<8;		//取消BKP相關暫存器防寫

	//RCC->BDCR |= 1<<16;	//備份區域軟復位
	//RCC->BDCR |= ~(1<<16);	//備份區域軟復位結束

	RCC->BDCR |= 1<<0;		//外部低速時鐘(LSE)使能

	while(!(RCC->BDCR & 0x02));	//等待外部時鐘就緒

	RCC->BDCR |= 1<<8;			//LSE作為RTC時鐘
	RCC->BDCR |= 1<<15;			//RTC時鐘使能

	while(!(RTC->CRL & (1<<5)));	//等待RTC暫存器最後一次操作完成
	while(!(RTC->CRL & (1<<3)));	//等待RTC暫存器同步完成

	RTC->CRH |= 0x07;				//允許溢位中斷[2],鬧鐘中斷[1],秒中斷[0],CRH暫存器低三位有效	

	while(!(RTC->CRL & (1<<5)));	//等待RTC暫存器最後一次操作完成

	RTC->CRL |=  1<<4;				//進入配置模式
	RTC->PRLH = 0x0000;				
	RTC->PRLL = 32767;				//設定分頻值

	//Rtc_TIME_AutoSet();				//將當前編譯時間寫入暫存器
	//Rtc_TIME_Set(2012,7,7,20,50,0);	//年,月,日,時,分,秒

	RTC->CRL &= ~(1<<4);			//退出配置模式,開始更新RTC暫存器
	while(!(RTC->CRL & (1<<5)));	//等待RTC暫存器最後一次操作完成

}


//設定RTC開始計時時間
void Rtc_TIME_Set(u16 year,u8 month,u8 date,u8 hour,u8 minute, u8 second)
{
		u32 sec;	

	    sec = Date_TO_Sec(year,month,date,hour,minute,second);
	
		//printf("\nRtc TIME Set  Sec = %x\n",sec);
	
		RCC->APB1ENR |= 1<<28;							//使能PWR時鐘,方便獨立呼叫此函式
		RCC->APB1ENR |= 1<<27;							//使能BKP時鐘
		PWR->CR |= 1<<8;								//取消防寫
	
		RTC-> CRL |= 1<<4;								//允許配置
		
		RTC-> CNTL = sec&0xffff;						//取低16位
		RTC-> CNTH = sec>>16;							//取高16位
	
		RTC-> CRL &= ~(1<<4);							//開始RTC暫存器更新
	
		while(!(RTC->CRL&(1<<5)));						//等待RTC暫存器操作完成 	
}




//判斷是否是閏年函式
//
//判斷方法:
//		普通年能整除4且不能整除100的為閏年。(如2004年就是閏年,1900年不是閏年)
//		世紀年能整除400的是閏年。(如2000年是閏年,1900年不是閏年)
//
//返回: 1,是閏年 	0,不是閏年
u8 Is_LeapYear(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日的總秒數
//Bugs:此函式秒數會多20左右,所以函式返回值做了校正,校正後沒有問題
//待優化
u32 Date_TO_Sec(u16 year,u8 month,u8 date,u8 hour,u8 minute, u8 second)
{
	u16 t;
	u32 sec;

	if(year >= 1970 && year<= 2106)		  //判斷是否為合法年份,RTC的時間是從1970開始,只能由32位表示秒數,最大隻能到2106年左右
	{
		for(t= 1970 ;t<year;t++) 		  //所有年份秒數累加
		{
			if(Is_LeapYear(t))			  //判斷是否為閏年

				sec += 31622400; 
			else 	
				sec += 31536000;		
		}	


		for(t=0;t<month-1;t++)						   //月份秒數累加
		{
			sec += (u32) Days_Table[t]*86400;			
			if(Is_LeapYear(year) && t== 1) 				//閏年加一天的秒鐘數
				sec += 86400;							
	
		}
	
		sec += (u32)(date-1)*86400;						//本月日期的秒數累加
		sec += (u32)(hour)*3600;
		sec += (u32)(minute)*60;
		sec += second;
	}

	return sec-20;										//校正20秒,原因不詳

}



//自動獲取當前時間配置RTC
//可以根據MDK關鍵字獲取時間  
//__DATE__  獲取編譯日期, 格式為: Jul 7 2012  
//__TIME__  獲取編譯時間, 格式為: 14:54:44 

void Rtc_TIME_AutoSet()
{
   	u16 year,i=0,j=0;
	u8  mon,date,sec,min,hour;

	u8 *_date = __DATE__;
	u8 *_time = __TIME__;

   	for(i=0;i<12;i++)
	{
		for(j=0;j<3;j++)
		{
			if(Month_Table[i][j] == _date[j]) mon = i;	//得到月份
		}
	}


	if(_date[4]==' '){ 			//得到日期
		date=_date[5]-'0'; 		//-'0'操作將字元型轉換為整型,參考ASCII碼的轉換,eg '7'-'0' =7
	}else{ 
		date=10*(_date[4]-'0')+_date[5]-'0';
	}
		  

	year=1000*(_date[7]-'0')+100*(_date[8]-'0')+10*(_date[9]-'0')+_date[10]-'0';  //得到年份	   
	hour=10*(_time[0]-'0')+_time[1]-'0';  										  //得到小時
	min=10*(_time[3]-'0')+_time[4]-'0';  										  
	sec=10*(_time[6]-'0')+_time[7]-'0'; 
	

	//printf("\n%d-%d-%d  %d:%d:%d\n",year,mon,date,hour,min,sec);

	Rtc_TIME_Set(year,mon,date,hour,min,sec);
}


//獲取RTC時間
void Rtc_Get()
{
	u32 secs,days,temp,years = 1970,months = 0;		

	secs = RTC->CNTH; 	//讀取RTC的當前時間值(距1970年的總秒數)
	secs <<= 16;
	secs += RTC->CNTL;

	//printf("\nRtc_Get  Sec = %x\n",secs);

	days = secs/86400;
	if(days > 0)			//超過一天
	{
		temp = days;
		while(temp >= 365)	
		{
			if(Is_LeapYear(years))				//是閏年
			{
				if(temp >= 366) 
					temp -=	366;	//閏年的天數
				else
					break;
			}else{
			 	temp -= 365;
			}		 	
			years++;
		}
		
		timer.year = years;			  //得到年份

		while(days >= 28)
		{
			if(Is_LeapYear(years) && months ==1)	   //判斷是否為閏年的第二月
			{
				if(temp >= 29) 
					temp -= 29;	
				else
					break;
			}else{
				if(temp >= Days_Table[months]) 		
					temp -= Days_Table[months];
				else
					break;
			}

			months++;	
		}

		timer.month = months+1;				//得到月數
 		timer.date  = temp+1;				//得到日期
	}

	temp = secs % 86400;					//得到剩餘秒數
	timer.hour = temp/3600;					//得到小時
	timer.minute = (temp%3600)/60;			
	timer.second = (temp%3600)%60;
	timer.week = Rtc_DAY_Get(timer.year,timer.month,timer.date);

				
}

//判斷當前為星期幾					

u8 Rtc_DAY_Get(u16 year,u8 month,u8 day)
{	
	u16 temp;
	u8 yearH,yearL;
	
	yearH = year/100;	
	yearL = year%100; 

	// 如果為21世紀,年份數加100  
	if( yearH > 19 ) yearL += 100;

	// 所過閏年數只算1900年之後的  

	temp = yearL+yearL/4;
	temp = temp%7; 
	temp = temp + day + _Week[month-1];

	if( yearL%4 == 0 && month < 3 ) temp--;

	return(temp%7);
}

//設定鬧鐘時間

void Rtc_ALARM_Set(u16 year,u8 month,u8 date,u8 hour,u8 minute, u8 second)
{

		u32 sec;	

	    sec = Date_TO_Sec(year,month,date,hour,minute,second);


		RTC-> CRL |= 1<<4;								//允許配置

		//while(!(RTC->CRL&(1<<5)));						//RTOFF為1 才可以寫入ALRL和ALRH暫存器
		
		RTC-> ALRL = sec&0xffff;						//取低16位
		RTC-> ALRH = sec>>16;							//取高16位
	
		RTC-> CRL &= ~(1<<4);							//開始RTC暫存器更新
	
		while(!(RTC->CRL&(1<<5)));						//等待RTC暫存器操作完成

}