stm32f103 rtc 获取 日历 时钟
STM32的RTC只有一個32位的計數(shù)器用來計時,沒有寄存器來存年月日時分秒等。通過設(shè)置可以讓這個計數(shù)器1秒加1,從0-0XFFFFFFFF大概可以計時136年。程序要設(shè)置一個時間起點表示0,一般設(shè)置起始時間為1970-01-01 0:0:0。這是UNIX時間戳。如果要設(shè)置RTC時間,如2011-9-30 12:00FAMILY: 宋體">到1970-01-01 0:0:0有多少秒,把這個值寫到RTC計數(shù)器就可以了。如果要讀當前的年月日時分秒,先讀出32位RTC計數(shù)器值,然后以1970-01-01 0:0:0為時間基點,算出當前時間。
為什么要設(shè)置起始時間為1970-01-01 0:0:0,因為有一個TIME.H文件,里面有寫好的設(shè)置時間函數(shù),讀時間函數(shù),使用的參數(shù)就是32位計數(shù)器。不過我沒有找到這個文件。不使用這個文件的函數(shù)的話,起始時間可以設(shè)為任意值。
直接使用32位計數(shù)器來計算時間的優(yōu)點:
1、可以不使用RTC的秒中斷,因為在STM32掉電時,只有電池供電,這時RTC在計數(shù),但是STM32的內(nèi)核中斷應(yīng)該是不能使用的。
2、不用在備份區(qū)保存年月日等參數(shù)。
下面是網(wǎng)上找的程序,我實際測試過,是正確的。
//時間結(jié)構(gòu)體
typedef struct
{
?????? u8 hour;
?????? u8 min;
?????? u8 sec;?????????????????
?????? //公歷日月年周
?????? u16 w_year;
?????? u8? w_month;
?????? u8? w_date;
?????? u8? week;????????????
}tm;
tm timer;
/*
?*
?*
?*/
void RTC_Config(void)
{
???? u16 u16_WaitForOscSource;
???? //我們在BKP的后備寄存器1中,存了一個特殊字符0xA5A5
??? //第一次上電或后備電源掉電后,該寄存器數(shù)據(jù)丟失,
??? //表明RTC數(shù)據(jù)丟失,需要重新配置
??? if (BKP_ReadBackupRegister(BKP_DR1) != 0x5A5A)
??? {
??????? //重新配置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(u16_WaitForOscSource=0;u16_WaitForOscSource<5000;u16_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, 0x5A5A);
?????? ?RTC_Set(2011,01,01,0,0,0);//默認時間
??? }
??? else
??? {
??????? //若后備寄存器沒有掉電,則無需重新配置RTC
??????? //這里我們可以利用RCC_GetFlagStatus()函數(shù)查看本次復位類型
??????? RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
??? ?????? ?for(u16_WaitForOscSource=0;u16_WaitForOscSource<5000;u16_WaitForOscSource++);
??????? if (RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET)
??????? {
??????????? //這是上電復位
??????? }
??????? else if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET)
??????? {
??????????? //這是外部RST管腳復位
??????? }
??????? //清除RCC中復位標志
??????? RCC_ClearFlag();
?
??????? //雖然RTC模塊不需要重新配置,且掉電后依靠后備電池依然運行
??????? //但是每次上電后,還是要使能RTCCLK???????
??????? //RCC_RTCCLKCmd(ENABLE);
??????? //等待RTC時鐘與APB1時鐘同步
??????? //RTC_WaitForSynchro();
?
??????? //使能秒中斷
??????? RTC_ITConfig(RTC_IT_SEC, ENABLE);
??????? //等待操作完成
??????? RTC_WaitForLastTask();
??? }
?
?
?
??? return;
}
//判斷是否是閏年函數(shù)
//月份?? 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結(jié)尾,還要能被400整除 ????? ???
???????????????????? else return 0;??
????????????? }else return 1;??
?????? }else return 0;
}???? ?????????????????? ???
//設(shè)置時鐘
//把輸入的時鐘轉(zhuǎn)換為秒鐘
//以1970年1月1日為基準
//1970~2099年為合法年份
//返回值:0,成功;其他:錯誤代碼.
//月份數(shù)據(jù)表???????????????????????????????????????????????????????????????????????
u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; //月修正數(shù)據(jù)表 ?
//平年的月份日期表
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<2000||syear>2099)return 1;//syear范圍1970-2099,此處設(shè)置范圍為2000-2099???? ??
?????? for(t=1970;t<syear;t++) //把所有年份的秒鐘相加
?????? {
????????????? if(Is_Leap_Year(t))seccount+=31622400;//閏年的秒鐘數(shù)
????????????? else seccount+=31536000;????????????????? ? //平年的秒鐘數(shù)
?????? }
?????? smon-=1;
?????? for(t=0;t<smon;t++)????? ?? //把前面月份的秒鐘數(shù)相加
?????? {
????????????? seccount+=(u32)mon_table[t]*86400;//月份秒鐘數(shù)相加
????????????? if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//閏年2月份增加一天的秒鐘數(shù)????? ??
?????? }
?????? seccount+=(u32)(sday-1)*86400;//把前面日期的秒鐘數(shù)相加
?????? seccount+=(u32)hour*3600;//小時秒鐘數(shù)
??? seccount+=(u32)min*60;????? //分鐘秒鐘數(shù)
?????? seccount+=sec;//最后的秒鐘加上去
?????????????????????????????????????????????????????????????????????????????????????????? ???
?????? //設(shè)置時鐘
? /*? RCC->APB1ENR|=1<<28;//使能電源時鐘
??? RCC->APB1ENR|=1<<27;//使能備份時鐘
?????? PWR->CR|=1<<8;? ??//取消備份區(qū)寫保護
?????? //上面三步是必須的!*/
?????? PWR_BackupAccessCmd(ENABLE);
?????? RTC_WaitForLastTask();
?????? RTC_SetCounter(seccount);
?????? RTC_WaitForLastTask();
?????? return 0;? ???
}
//得到當前的時間
//返回值:0,成功;其他:錯誤代碼.
u8 RTC_Get(void)
{
?????? static u16 daycnt=0;
?????? u32 timecount=0;
?????? u32 temp=0;
?????? u16 temp1=0;
?
?????? timecount=RTC_GetCounter();
?????? ??
?????? /*timecount=RTC->CNTH;//得到計數(shù)器中的值(秒鐘數(shù))
?????? timecount<<=16;
?????? timecount+=RTC->CNTL;??? */??????????
?
?????? temp=timecount/86400;?? //得到天數(shù)(秒鐘數(shù)對應(yīng)的)
?????? if(daycnt!=temp)//超過一天了
?????? {???? ?
????????????? daycnt=temp;
????????????? temp1=1970;? //從1970年開始
????????????? while(temp>=365)
????????????? {?????????????????????????
???????????????????? if(Is_Leap_Year(temp1))//是閏年
???????????????????? {
??????????????????????????? if(temp>=366)temp-=366;//閏年的秒鐘數(shù)
??????????????????????????? else {temp1++;break;}?
???????????????????? }
???????????????????? else temp-=365;???? ? //平年
???????????????????? temp1++;?
????????????? }??
????????????? timer.w_year=temp1;//得到年份
????????????? temp1=0;
????????????? while(temp>=28)//超過了一個月
????????????? {
???????????????????? if(Is_Leap_Year(timer.w_year)&&temp1==1)//當年是不是閏年/2月份
???????????????????? {
??????????????????????????? if(temp>=29)temp-=29;//閏年的秒鐘數(shù)
??????????????????????????? else break;
???????????????????? }
???????????????????? else
???????????????????? {
??????????????????????????? if(temp>=mon_table[temp1])temp-=mon_table[temp1];//平年
??????????????????????????? else break;
???????????????????? }
???????????????????? temp1++;?
????????????? }
????????????? timer.w_month=temp1+1;//得到月份
????????????? timer.w_date=temp+1;? //得到日期
?????? }
?????? temp=timecount%86400;???? //得到秒鐘數(shù)?? ???
?????? timer.hour=temp/3600;???? //小時
?????? timer.min=(temp%3600)/60; //分鐘?????
?????? timer.sec=(temp%3600)%60; //秒鐘
?????? timer.week=RTC_Get_Week(timer.w_year,timer.w_month,timer.w_date);//獲取星期??
?????? return 0;
}????
//獲得現(xiàn)在是星期幾
//功能描述:輸入公歷日期得到星期(只允許1901-2099年)
//輸入?yún)?shù):公歷年月日
//返回值:星期號????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
u8 RTC_Get_Week(u16 year,u8 month,u8 day)
{????
?????? u16 temp2;
?????? u8 yearH,yearL;
??????
?????? yearH=year/100;???? yearL=year%100;
?????? // 如果為21世紀,年份數(shù)加100?
?????? if (yearH>19)yearL+=100;
?????? // 所過閏年數(shù)只算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);
}
總結(jié)
以上是生活随笔為你收集整理的stm32f103 rtc 获取 日历 时钟的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: STM32启动BOOT0 BOOT1设置
- 下一篇: Stm 32 IAP 在线 升级IA