stm32 RTC實時時鐘[操作暫存器+庫函式]
阿新 • • 發佈:2019-02-11
#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暫存器操作完成 }