前言:
1.博客基于ARM Cortex-M3內(nèi)核的STM32F103ZET6和標準3.5.0庫;
2.如有不足之處,還請多多指教
一 RTC是什么?
1. 從結(jié)構(gòu)上講就是一個獨立的定時器;
2. 從功能上來說就是為系統(tǒng)提供系統(tǒng)掉電不復位的日歷時間;
RTC分為兩個完全能獨立的部分:1. APB1接口;2. RTC核心;
功能:
(1)APB1總線連接APB1接口并負責驅(qū)動APB1接口,接口內(nèi)部包含一組16位寄存器,可以通過APB1總線對其進行讀寫操作。
(2)RTC核心由RTC20位預分頻模塊和32位可編程計數(shù)器模塊組成;
Ⅰ RTC預分頻模塊包含一個20位的可編程分頻器RTC_DIV。預分頻模塊為32位計數(shù)器模塊提供時基單元,這個很重要;預分頻器內(nèi)的是一遞變(遞減)的值,當遞減結(jié)束時如果開啟相應的中斷,將會觸發(fā)中斷,并且RTC_PRL重裝載寄存器將會將之前寫入的分頻值再次裝載到預分頻器;
Ⅱ 計數(shù)器(RTC_CNT)概念很好理解,當時基單元是1S時,計數(shù)器可以記錄136年左右的時間,夠用了;若配置并使能了RTC_ALR(鬧鐘寄存器),當RTC_CNT的值與其相等時,將會觸發(fā)鬧鐘中斷;
二 RTC的時鐘源的選擇
(1)從圖片中可以看出有三個時鐘源:
1. HSE的128分頻
2. LSE (用的最多)
3. LSI
三 相關(guān)寄存器
(1)備份域控制寄存器RCC_BDCR
LSEON,LSEBYP,RTCSEL和RTCEN位都處于備份域,這些位在復位后處于寫保護的狀態(tài),只有在電源控制寄存器PWR_CR中的DBP位置1后才能對這些位進行改動;
RTCEN:RTC時鐘使能位;0:RTC關(guān)閉;1:RTC開啟
RTCSEL[1:0]:RTC時鐘源選擇 00:無時鐘,01:LSE振蕩器(用的最多),10:LSI振蕩器,11:HSE振蕩器/128
LSEBYP:外部低速時鐘振蕩器旁路(旁路可以理解為開關(guān),被旁路既是被關(guān)閉);0:LSE時鐘未被旁路(關(guān)閉);1:LSE時鐘被旁路(關(guān)閉);
LSEON:外部低速振蕩器使能;0:外部32KHz振蕩器關(guān)閉;1:外部32KHz振蕩器開啟;
(2)控制寄存器(高位)RTC_CRH
RTC_CRH是用來控制RTC的相關(guān)中斷的
OWIE:允許溢出中斷位; 0:屏蔽RTC_CNT溢出中斷;1:使能RTC_CNT溢出中斷;
ALRIE:允許鬧鐘中斷; 0:屏蔽鬧鐘中斷;1:使能鬧鐘中斷;
SECIE:允許秒中斷; 0:屏蔽秒中斷;1:使能秒中斷;
(3)控制寄存器(低位)RTC_CRL(重要)
RTOFF(只讀):RTC操作; 0:上次對RTC寄存器的寫操作仍在進行;1:上次對RTC寄存器的寫操作已經(jīng)完成;對RTC寄存器操作時,必須要完成一次寫操作之后(RTOFF=1)才能進行下次寫操作
CNF:配置標志(重要);在對ALR,DIV,CNT寄存器配置之前,必須先配置該位軟件寫1:進入如配置模式,對CNT,ALR,PRL進行賦值;0:退出配置模式(開始更新RTC寄存器);
RSF:寄存器同步標志;0:寄存器尚未同步;1:寄存器已經(jīng)同步;
每當RTC_CNT和RTC_DIV由軟件更新或清0時,此位由硬件置1;應用程序必須等待這位被硬件置1,以確保CNT,ALR,PRL已經(jīng)被同步;
在APB1復位或APB1時鐘停止后,此位必須由軟件清0;
重要:由于APB1接口和RTC核心完全獨立,處理器在復位,睡眠,停機環(huán)境下是繼續(xù)工作的,但APB1接口停止了工作所以這里就出現(xiàn)了一個問題是:當處理器從復位睡眠停機情況下恢復過來的前一段時間,APB1接口和RTC核心還沒有完成同步工作;此時不能讀取RTC的值,不能對CRH/CRL進行配置,要等RSF=1后,才可以對RTC的值進行讀??;
OWF:溢出標志; 0:CNT無溢出; 1:CNT溢出;
ALRF:鬧鐘標志: 0:CNT≠ALR,沒有產(chǎn)生鬧鐘; 1:CNT≥ALR,鬧鐘產(chǎn)生;
SECF:秒標志; 0:RTC_DIV沒有溢出;1:RTC_DIV溢出;
(4)RTC預分頻裝載寄存器RTC_PRLH(高)和RTC_PRLL(低)
這是兩個寄存器(唉!搞不懂為什么要整兩個,可能為后期分頻系數(shù)增大做準備把)
分頻公式為: Ftr_clk = Frtcclk/[19:0]+1
比如我們最常用的Frtc_clk = 32.768KHz ,([19:0]+1)=32768 ,代入得Ftr_clk = 1Hz = 1S;
(5)RTC預分頻器余數(shù)寄存器RTC_DIVH(高)和RTC_DIVL(低)
(6)備份寄存器BKP
和其他寄存器不同,備份寄存器包含42個16位的寄存器(共84字節(jié),中小容量STM芯片存儲空間為20字節(jié) );
功能:
1. 侵入檢測;2. RTC校準;3. (常用)用BKP來存儲RTC的校驗值或者記錄些重要的數(shù)據(jù),但是這里不要誤理解為是EEPROM,功能類似,但是這個所在備份區(qū)域一旦完全掉電,就GG了;
特性:(重要)
1. BKP處于備份域,當Vdd掉電時,仍然有Vbat來供電。
2. 在系統(tǒng)待機喚醒,系統(tǒng)復位,掉電復位時,備份域內(nèi)的數(shù)據(jù)將不會被復位,但一旦這些情況出現(xiàn),備份域和RTC都不能被訪問了,這是為了保護這些情況發(fā)生時備份域和RTC的值不會被改變;這時需要下面操作才能繼續(xù)訪問:
Ⅰ 配置寄存器RCC_APB1ENR的PWREN位和BKPEN位來打開電源和后備接口電源;
Ⅱ 電源控制寄存器PWR_CR的DBP位來使能對后備寄存器和RTC的訪問;
四 編程步驟
(1)使能電源時鐘和備份區(qū)域時鐘;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR " RCC_APB1Periph_BKP,ENABLE);
在上面解釋備份寄存器中講到的特性“Ⅰ”可以找到解釋;
(2)取消備份區(qū)寫保護;
PWR_BackupAccessCmd(ENABLE);
對備份區(qū)域?qū)懭氡Wo是在每次復位后被使能,即復位后不能向備份區(qū)域?qū)懭霐?shù)據(jù)或修改寄存器,RTC核心的屬于備份區(qū)域,在進行時鐘初始化的時候,不能對其寫入數(shù)據(jù)是不合理的,所以必須要取消備份區(qū)寫保護;在上面解釋備份寄存器中講到的特性“Ⅱ”可以找到解釋;
(3)復位備份區(qū);
BKP_DeInit();
在取消對備份區(qū)域的寫保護完成后,備份區(qū)域可能還存在以前設置過的一些數(shù)據(jù)和配置;因為要重新配置,所以這里要初始化一下;
(4)開啟外部低速振蕩器
RCC_LSEConfig(RCC_LSE_ON); //配置RCC_BDCR的LSEON位,開啟RTC常用的時鐘源:LSE ;
特別注意:這個函數(shù)執(zhí)行完畢后不能急著執(zhí)行其他操作,因為是要啟動外部晶振,所以需要一些時間;如果啟動完成,備份域控制寄存器RCC_BDCR的LSERDY位將會被硬件置1;
(5)配置并使能RTC時鐘源
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //配置RCC_BDCR的RTCSEL[1:0]位,三個時鐘源,LSE最常用;
RCC_RTCCLKCmd(ENABLE); //配置RCC_BDCR的RTCEN位,啟動RTC時鐘
**特別注意:**RTC時鐘源開啟之后,需要等到時鐘源RTC_CNT,ALR,PRL三個寄存器同步了,才能對RTC進行操作;
所以這里要等待一下:
RTC_WaitForSynchro(); //檢測RTC_CRL的RSF位,知道同步完成,結(jié)束函數(shù);
(6)配置RTC核心:分頻和時鐘;
Ⅰ RTC_EnterConfigMode(); //配置RTC_CRL的CNF位,允許配置
Ⅱ RTC_SetPrescaler( u32 ); //設置分頻數(shù) 通常為32767
RTC_WaitForLastTask(); //等待分頻數(shù)配置完成
Ⅲ RTC_Set(2009,12,2,10,0,55); //這個函數(shù)是自己寫的;
Ⅳ RTC_ExitConfigMode(); //配置RTC_CRL的CNF位,禁止配置
(7)向備份區(qū)域?qū)懭胪瓿蓵r鐘配置的信息;
BKP_WriteBackupRegister(BKP_DR1,0x5050);
//有寫入函數(shù)必然會有讀函數(shù),關(guān)于這個應用的解釋可以在上面的備份寄存器中找到解釋;
補充:以上程序步驟中如果還要添加鬧鐘中斷,秒中斷,溢出中斷,均可以添加;
下面為原子例程:
u8RTC_Init(void){u8temp=0;if(BKP_ReadBackupRegister(BKP_DR1)!=0x5050){RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP,ENABLE);PWR_BackupAccessCmd(ENABLE);BKP_DeInit();RCC_LSEConfig(RCC_LSE_ON);while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)==RESET&&temp<250){temp++;delay_ms(10);}if(temp>=250)return1;RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);RCC_RTCCLKCmd(ENABLE);RTC_WaitForLastTask();RTC_WaitForSynchro();RTC_ITConfig(RTC_IT_SEC,ENABLE);RTC_WaitForLastTask();RTC_EnterConfigMode();RTC_SetPrescaler(32767);RTC_WaitForLastTask();RTC_Set(2009,12,2,10,0,55);RTC_ExitConfigMode();BKP_WriteBackupRegister(BKP_DR1,0x5050);}else{RTC_WaitForLastTask();RTC_ITConfig(RTC_IT_SEC,ENABLE);RTC_WaitForLastTask();}RTC_NVIC_Config();RTC_Get();return0;}