0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

淺談RTC實時時鐘特征與原理

GReq_mcu168 ? 來源:CSDN技術(shù)社區(qū) ? 作者:聚優(yōu)致成 ? 2021-06-30 15:54 ? 次閱讀

一、RTC實時時鐘特征與原理

查看STM32中文手冊 16 實時時鐘(RTC)(308頁)

RTC (Real Time Clock):實時時鐘

實時時鐘是一個獨立的定時器。RTC模塊擁有一組連續(xù)計數(shù)的計數(shù)器,在相應(yīng)軟件配置下,可提供時鐘日歷的功能。修改計數(shù)器的值可以重新設(shè)置系統(tǒng)當(dāng)前的時間和日期。

RTC模塊和時鐘配置系統(tǒng)(RCC_BDCR寄存器)處于后備區(qū)域,即在系統(tǒng)復(fù)位或從待機模式喚醒后, RTC的設(shè)置和時間維持不變。

系統(tǒng)復(fù)位后,對后備寄存器和RTC的訪問被禁止,這是為了防止對后備區(qū)域(BKP)的意外寫操作。執(zhí)行以下操作將使能對后備寄存器和RTC的訪問:

● 設(shè)置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能電源和后備接口時鐘

● 設(shè)置寄存器PWR_CR的DBP位,使能對后備寄存器和RTC的訪問。

RTC特征

編程的預(yù)分頻系數(shù):分頻系數(shù)最高為220。

● 32位的可編程計數(shù)器,可用于較長時間段的測量。

● 2個分離的時鐘:用于APB1接口的PCLK1和RTC時鐘(RTC時鐘的頻率必須小于PCLK1時鐘

頻率的四分之一以上)。

● 可以選擇以下三種RTC的時鐘源:

— HSE時鐘除以128;

— LSE振蕩器時鐘;

— LSI振蕩器時鐘(詳見6.2.8節(jié)RTC時鐘)。

● 2個獨立的復(fù)位類型:

— APB1接口由系統(tǒng)復(fù)位;

— RTC核心(預(yù)分頻器、鬧鐘、計數(shù)器和分頻器)只能由后備域復(fù)位(詳見6.1.3節(jié))。

● 3個專門的可屏蔽中斷:

— 鬧鐘中斷,用來產(chǎn)生一個軟件可編程的鬧鐘中斷。

— 秒中斷,用來產(chǎn)生一個可編程的周期性中斷信號(最長可達1秒)。

— 溢出中斷,指示內(nèi)部可編程計數(shù)器溢出并回轉(zhuǎn)為0的狀態(tài)。

二、RTC由兩部分組成

**APB1接口:**用來和APB1總線相連。通過APB1接口可以訪問RTC的相關(guān)寄存器(預(yù)分頻值,計數(shù)器值,鬧鐘值)。

**RTC核心:**由一組可編程計數(shù)器組成。分兩個主要模塊。

第一個是RTC預(yù)分頻模塊,它可以編程產(chǎn)生最長1秒的RTC時間基TR_CLK。如果設(shè)置了秒中斷允許位,可以產(chǎn)生秒中斷。

第二個是32位的可編程計數(shù)器,可被初始化為當(dāng)前時間。系統(tǒng)時間按TR_CLK周期累加并與存儲在RTC_ALR寄存器中的可編程時間相比,當(dāng)匹配時候如果設(shè)置了鬧鐘中斷允許位,可以產(chǎn)生鬧鐘中斷。

RTC內(nèi)核完全獨立于APB1接口,軟件通過APB1接口對RTC相關(guān)寄存器訪問。但是相關(guān)寄存器只在RTC APB1時鐘進行重新同步的RTC時鐘的上升沿被更新。所以軟件必須先等待寄存器同步標(biāo)志位(RTC_CRL的RSF位)被硬件置1才讀。

三、RTC時鐘源

首先講一下時鐘源:

三種不同的時鐘源可被用來驅(qū)動系統(tǒng)時鐘(SYSCLK):

● HSI振蕩器時鐘

● HSE振蕩器時鐘

● PLL時鐘

這些設(shè)備有以下2種二級時鐘源:

● 40kHz低速內(nèi)部RC,可以用于驅(qū)動獨立看門狗和通過程序選擇驅(qū)動RTC。RTC用于從停機/待機模式下自動喚醒系統(tǒng)。

● 32.768kHz低速外部晶體也可用來通過程序選擇驅(qū)動RTC(RTCCLK)。

參看:STM32時鐘系統(tǒng)

當(dāng)不被使用時,任一個時鐘源都可被獨立地啟動或關(guān)閉,由此優(yōu)化系統(tǒng)功耗。

用戶可通過多個預(yù)分頻器配置AHB、高速APB(APB2)和低速APB(APB1)域的頻率。AHB和APB2域的最大頻率是72MHz。APB1域的最大允許頻率是36MHz。SDIO接口的時鐘頻率固定為HCLK/2。

RCC通過AHB時鐘(HCLK)8分頻后作為Cortex系統(tǒng)定時器(SysTick)的外部時鐘。通過對SysTick控制與狀態(tài)寄存器的設(shè)置,可選擇上述時鐘或Cortex(HCLK)時鐘作為SysTick時鐘。ADC時鐘由高速APB2時鐘經(jīng)2、 4、 6或8分頻后獲得。

定時器時鐘頻率分配由硬件按以下2種情況自動設(shè)置:

如果相應(yīng)的APB預(yù)分頻系數(shù)是1,定時器的時鐘頻率與所在APB總線頻率一致。

否則,定時器的時鐘頻率被設(shè)為與其相連的APB總線頻率的2倍。

如上圖,有五個時鐘源,為HSI、HSE、LSI、LSE、PLL。

接下來我們一一看一下:

HSE時鐘

**高速外部時鐘信號(HSE)**由以下兩種時鐘源產(chǎn)生:

● HSE外部晶體/陶瓷諧振器

● HSE用戶外部時鐘

為了減少時鐘輸出的失真和縮短啟動穩(wěn)定時間,晶體/陶瓷諧振器和負載電容器必須盡可能地靠

近振蕩器引腳。負載電容值必須根據(jù)所選擇的振蕩器來調(diào)整。

外部時鐘源(HSE旁路)

在這個模式里,必須提供外部時鐘。它的頻率最高可達25MHz。用戶可通過設(shè)置在時鐘控制寄存器中的HSEBYP和HSEON位來選擇這一模式。外部時鐘信號(50%占空比的方波、正弦波或三角波)必須連到SOC_IN引腳,同時保證OSC_OUT引腳懸空。見圖9。

外部晶體/陶瓷諧振器(HSE晶體)

**4~16Mz外部振蕩器可為系統(tǒng)提供更為精確的主時鐘。**相關(guān)的硬件配置可參考圖9,進一步信息可參考數(shù)據(jù)手冊的電氣特性部分。

在時鐘控制寄存器RCC_CR中的HSERDY位用來指示高速外部振蕩器是否穩(wěn)定。在啟動時,直到這一位被硬件置’1’,時鐘才被釋放出來。如果在時鐘中斷寄存器RCC_CIR中允許產(chǎn)生中斷,將會產(chǎn)生相應(yīng)中斷。

HSE晶體可以通過設(shè)置時鐘控制寄存器里RCC_CR中的HSEON位被啟動和關(guān)閉。

HSI時鐘

HSI時鐘信號由內(nèi)部8MHz的RC振蕩器產(chǎn)生,可直接作為系統(tǒng)時鐘或在2分頻后作為PLL輸入。

HSI RC振蕩器能夠在不需要任何外部器件的條件下提供系統(tǒng)時鐘。它的啟動時間比HSE晶體振蕩器短。然而,即使在校準(zhǔn)之后它的時鐘頻率精度仍較差。

校準(zhǔn)

制造工藝決定了不同芯片的RC振蕩器頻率會不同,這就是為什么每個芯片的HSI時鐘頻率在出廠前已經(jīng)被ST校準(zhǔn)到1%(25°C)的原因。系統(tǒng)復(fù)位時,工廠校準(zhǔn)值被裝載到時鐘控制寄存器的HSICAL[7:0]位。

如果用戶的應(yīng)用基于不同的電壓或環(huán)境溫度,這將會影響RC振蕩器的精度??梢酝ㄟ^時鐘控制寄存器里的HSITRIM[4:0]位來調(diào)整HSI頻率。

時鐘控制寄存器中的HSIRDY位用來指示HSI RC振蕩器是否穩(wěn)定。在時鐘啟動過程中,直到這一位被硬件置’1’, HSI RC輸出時鐘才被釋放。HSI RC可由時鐘控制寄存器中的HSION位來啟動和關(guān)閉。

如果HSE晶體振蕩器失效, HSI時鐘會被作為備用時鐘源。

PLL

內(nèi)部PLL可以用來倍頻HSI RC的輸出時鐘或HSE晶體輸出時鐘。

PLL的設(shè)置(選擇HIS振蕩器除2或HSE振蕩器為PLL的輸入時鐘,和選擇倍頻因子)必須在其被激活前完成。一旦PLL被激活,這些參數(shù)就不能被改動。

如果PLL中斷在時鐘中斷寄存器里被允許,當(dāng)PLL準(zhǔn)備就緒時,可產(chǎn)生中斷申請。

如果需要在應(yīng)用中使用USB接口, PLL必須被設(shè)置為輸出48或72MHZ時鐘,用于提供48MHz的USBCLK時鐘。

LSE時鐘

**LSE晶體是一個32.768kHz的低速外部晶體或陶瓷諧振器。**它為實時時鐘或者其他定時功能提供一個低功耗且精確的時鐘源。

LSE晶體通過在備份域控制寄存器(RCC_BDCR)里的LSEON位啟動和關(guān)閉。

在備份域控制寄存器(RCC_BDCR)里的LSERDY指示LSE晶體振蕩是否穩(wěn)定。在啟動階段,直到這個位被硬件置’1’后, LSE時鐘信號才被釋放出來。如果在時鐘中斷寄存器里被允許,可產(chǎn)生中斷申請。

外部時鐘源(LSE旁路)

在這個模式里必須提供一個32.768kHz頻率的外部時鐘源。你可以通過設(shè)置在備份域控制寄存器(RCC_BDCR)里的LSEBYP和LSEON位來選擇這個模式。具有50%占空比的外部時鐘信號(方波、正弦波或三角波)必須連到OSC32_IN引腳,同時保證OSC32_OUT引腳懸空,見圖9。

LSI時鐘

LSI RC擔(dān)當(dāng)一個低功耗時鐘源的角色,它可以在停機和待機模式下保持運行,為獨立看門狗和自動喚醒單元提供時鐘。**LSI時鐘頻率大約40kHz(在30kHz和60kHz之間)。**進一步信息請參考數(shù)據(jù)手冊中有關(guān)電氣特性部分。

LSI RC可以通過控制/狀態(tài)寄存器(RCC_CSR)里的LSION位來啟動或關(guān)閉。

在控制/狀態(tài)寄存器(RCC_CSR)里的LSIRDY位指示低速內(nèi)部振蕩器是否穩(wěn)定。在啟動階段,直到這個位被硬件設(shè)置為’1’后,此時鐘才被釋放。如果在時鐘中斷寄存器(RCC_CIR)里被允許,將產(chǎn)生LSI中斷申請。

注意:只有大容量和互聯(lián)型產(chǎn)品可以進行LSI校準(zhǔn)

LSI校準(zhǔn)

可以通過校準(zhǔn)內(nèi)部低速振蕩器LSI來補償其頻率偏移,從而獲得精度可接受的RTC時間基數(shù),以及獨立看門狗(IWDG)的超時時間(當(dāng)這些外設(shè)以LSI為時鐘源)。

**校準(zhǔn)可以通過使用TIM5的輸入時鐘(TIM5_CLK)測量LSI時鐘頻率實現(xiàn)。**測量以HSE的精度為保證,軟件可以通過調(diào)整RTC的20位預(yù)分頻器來獲得精確的RTC時鐘基數(shù),以及通過計算得到精確的獨立看門狗(IWDG)的超時時間。

LSI校準(zhǔn)步驟如下:

打開TIM5,設(shè)置通道4為輸入捕獲模式;

設(shè)置AFIO_MAPR的TIM5_CH4_IREMAP位為’1’,在內(nèi)部把LSI連接到TIM5的通道4;

通過TIM5的捕獲/比較4事件或者中斷來測量LSI時鐘頻率;

根據(jù)測量結(jié)果和期望的RTC時間基數(shù)和獨立看門狗的超時時間,設(shè)置20位預(yù)分頻器。

四、RTC時鐘

**通 過 設(shè) 置 備 份 域 控 制 寄 存 器 (RCC_BDCR) 里 的 RTCSEL[1:0] 位 , RTCCLK 時鐘源可以由HSE/128、LSE或LSI時鐘提供。**除非備份域復(fù)位,此選擇不能被改變。

LSE時鐘在備份域里,但HSE和LSI時鐘不是。因此:

● 如果LSE被選為RTC時鐘:

— 只要VBAT維持供電,盡管VDD供電被切斷, RTC仍繼續(xù)工作。

● 如果LSI被選為自動喚醒單元(AWU)時鐘:

— 如果VDD供電被切斷, AWU狀態(tài)不能被保證。有關(guān)LSI校準(zhǔn),詳見6.2.5節(jié)LSI時鐘。

● 如果HSE時鐘128分頻后作為RTC時鐘:

— 如果VDD供電被切斷或內(nèi)部電壓調(diào)壓器被關(guān)閉(1.8V域的供電被切斷),則RTC狀態(tài)不確定。

— 必須設(shè)置電源控制寄存器(見4.4.1節(jié))的DPB位(取消后備區(qū)域的寫保護)為’1’。

五、RTC寄存器

上面都是從STM32中文手冊里摘取的。大概了解一下RTC和時鐘。

不過講的有點扯,里面有多好寄存器,不知道是干啥的。接下來重點看一下這些寄存器。

RTC控制寄存器高位(RTC_CRH)

RTC控制寄存器低位(RTC_CRL)

①修改CRH/CRL寄存器,必須先判斷RSF位,確定已經(jīng)同步。

②修改CNT,ALR,PRL的時候,必須先配置CNF位進入配置模式,修改完之后,設(shè)置CNF位為0退出配置模式

③同時在對RTC相關(guān)寄存器寫操作之前,必須判斷上一次寫操作已經(jīng)結(jié)束,也就是判斷RTOFF位是否置位。

RTC預(yù)分頻裝載寄存器(RTC_PRLH/RTC_PRLL)

預(yù)分頻裝載寄存器用來保存RTC預(yù)分頻器的周期計數(shù)值。它們受RTC_CR寄存器的RTOFF位保護,僅當(dāng)RTOFF值為’1’時允許進行寫操作。

配置RTC寄存器

必須設(shè)置RTC_CRL 寄 存 器 中 的CNF位 , 使RTC進入配置模式后 , 才能寫 入RTC_PRL、RTC_CNT、 RTC_ALR寄存器。

另外,對RTC任何寄存器的寫操作,都必須在前一次寫操作結(jié)束后進行??梢酝ㄟ^查詢RTC_CR寄存器中的RTOFF狀態(tài)位,判斷RTC寄存器是否處于更新中。僅當(dāng)RTOFF狀態(tài)位是’1’時,才可以寫入RTC寄存器。

配置過程:

查詢RTOFF位,直到RTOFF的值變?yōu)椤?’

置CNF值為1,進入配置模式

對一個或多個RTC寄存器進行寫操作

清除CNF標(biāo)志位,退出配置模式

查詢RTOFF,直至RTOFF位變?yōu)椤?’以確認(rèn)寫操作已經(jīng)完成。

僅當(dāng)CNF標(biāo)志位被清除時,寫操作才能進行,這個過程至少需要3個RTCCLK周期。

讀RTC寄存器

RTC核完全獨立于RTC APB1接口。

軟件通過APB1接口訪問RTC的預(yù)分頻值、 計數(shù)器值和鬧鐘值。但是,相關(guān)的可讀寄存器只在與RTC APB1時鐘進行重新同步的RTC時鐘的上升沿被更新。RTC標(biāo)志也是如此的。

這意味著,如果APB1接口曾經(jīng)被關(guān)閉,而讀操作又是在剛剛重新開啟APB1之后,則在第一次的內(nèi)部寄存器更新之前,從APB1上讀出的RTC寄存器數(shù)值可能被破壞了(通常讀到0)。下述幾種

情況下能夠發(fā)生這種情形:

● 發(fā)生系統(tǒng)復(fù)位或電源復(fù)位

● 系統(tǒng)剛從待機模式喚醒(參見第4.3節(jié):低功耗模式)。

● 系統(tǒng)剛從停機模式喚醒(參見第4.3節(jié):低功耗模式)。

所有以上情況中, APB1接口被禁止時(復(fù)位、無時鐘或斷電)RTC核仍保持運行狀態(tài)。

因此,若在讀取RTC寄存器時, RTC的APB1接口曾經(jīng)處于禁止?fàn)顟B(tài),則軟件首先必須等待RTC_CRL寄存器中的RSF位(寄存器同步標(biāo)志)被硬件置’1’。

注:RTC的 APB1接口不受WFI和WFE等低功耗模式的影響

六、RTC相關(guān)庫函數(shù)講解

庫函數(shù)所在文件:stm32f10x_rtc.c / stm32f10x_rtc.h

RTC時鐘源和時鐘操作函數(shù):

void RCC_RTCCLKConfig(uint32_t CLKSource);//時鐘源選擇 void RCC_RTCCLKCmd(FunctionalState NewState)//時鐘使能RTC配置函數(shù)(預(yù)分頻,計數(shù)值:

void RTC_SetPrescaler(uint32_t PrescalerValue);//預(yù)分頻配置:PRLH/PRLLvoid RTC_SetCounter(uint32_t CounterValue);//設(shè)置計數(shù)器值:CNTH/CNTLvoid RTC_SetAlarm(uint32_t AlarmValue);//鬧鐘設(shè)置:ALRH/ALRLRTC中斷設(shè)置函數(shù):

void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState);//CRH

RTC允許配置和退出配置函數(shù):

void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState);//CRH

同步函數(shù):

void RTC_WaitForLastTask(void);//等待上次操作完成:CRL位RTOFFvoid RTC_WaitForSynchro(void);//等待時鐘同步:CRL位RSF相關(guān)狀態(tài)位獲取清除函數(shù):

FlagStatus RTC_GetFlagStatus(uint16_t RTC_FLAG);void RTC_ClearFlag(uint16_t RTC_FLAG);ITStatus RTC_GetITStatus(uint16_t RTC_IT);void RTC_ClearITPendingBit(uint16_t RTC_IT);其他相關(guān)函數(shù)(BKP等)

PWR_BackupAccessCmd();//BKP后備區(qū)域訪問使能RCC_APB1PeriphClockCmd();//使能PWR和BKP時鐘RCC_LSEConfig();//開啟LSE,RTC選擇LSE作為時鐘源PWR_BackupAccessCmd();//BKP后備區(qū)域訪問使能uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR);//讀BKP寄存器void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data);//寫B(tài)KP 七、RTC配置一般步驟 1) 使能電源時鐘和備份區(qū)域時鐘。 前面已經(jīng)介紹了,我們要訪問 RTC 和備份區(qū)域就必須先使能電源時鐘和備份區(qū)域時鐘。

RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

2) 取消備份區(qū)寫保護。

要向備份區(qū)域?qū)懭霐?shù)據(jù),就要先取消備份區(qū)域?qū)懕Wo(寫保護在每次硬復(fù)位之后被使能),否則是無法向備份區(qū)域?qū)懭霐?shù)據(jù)的。我們需要用到向備份區(qū)域?qū)懭胍粋€字節(jié),來標(biāo)記時鐘已經(jīng)配置過了,這樣避免每次復(fù)位之后重新配置時鐘。取消備份區(qū)域?qū)懕Wo的庫函數(shù)實現(xiàn)方法是:

PWR_BackupAccessCmd(ENABLE); //使能 RTC 和后備寄存器訪問

3) 復(fù)位備份區(qū)域,開啟外部低速振蕩器。

在取消備份區(qū)域?qū)懕Wo之后,我們可以先對這個區(qū)域復(fù)位,以清除前面的設(shè)置,當(dāng)然這個操作不要每次都執(zhí)行,因為備份區(qū)域的復(fù)位將導(dǎo)致之前存在的數(shù)據(jù)丟失,所以要不要復(fù)位,要看情況而定。然后我們使能外部低速振蕩器,注意這里一般要先判斷 RCC_BDCR 的 LSERDY位來確定低速振蕩器已經(jīng)就緒了才開始下面的操作。

備份區(qū)域復(fù)位的函數(shù)是:

BKP_DeInit();//復(fù)位備份區(qū)域

開啟外部低速振蕩器的函數(shù)是:

RCC_LSEConfig(RCC_LSE_ON);// 開啟外部低速振蕩器

4) 選擇 RTC 時鐘,并使能。

這里我們將通過 RCC_BDCR 的 RTCSEL 來選擇選擇外部 LSI 作為 RTC 的時鐘。然后通過RTCEN 位使能 RTC 時鐘。庫函數(shù)中,選擇 RTC 時鐘的函數(shù)是:

RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //選擇 LSE 作為 RTC 時鐘

對于 RTC 時鐘的選擇,還有 RCC_RTCCLKSource_LSI 和RCC_RTCCLKSource_HSE_Div128 這兩個,顧名思義,前者為 LSI,后者為 HSE 的 128 分頻,這在時鐘系統(tǒng)章節(jié)有講解過。

使能 RTC 時鐘的函數(shù)是:

RCC_RTCCLKCmd(ENABLE); //使能 RTC 時鐘

5) 設(shè)置 RTC 的分頻,以及配置 RTC 時鐘。

在開啟了 RTC 時鐘之后,我們要做的就是設(shè)置 RTC 時鐘的分頻數(shù),通過 RTC_PRLH 和RTC_PRLL 來設(shè)置,然后等待 RTC 寄存器操作完成,并同步之后,設(shè)置秒鐘中斷。然后設(shè)置RTC 的允許配置位(RTC_CRH 的 CNF 位),設(shè)置時間(其實就是設(shè)置 RTC_CNTH 和 RTC_CNTL兩個寄存器)。下面我們一一這些步驟用到的庫函數(shù):在進行 RTC 配置之前首先要打開允許配置位(CNF),庫函數(shù)是:

RTC_EnterConfigMode();/// 允許配置

在配置完成之后,千萬別忘記更新配置同時退出配置模式,函數(shù)是:

RTC_ExitConfigMode();//退出配置模式, 更新配置

設(shè)置 RTC 時鐘分頻數(shù), 庫函數(shù)是:

void RTC_SetPrescaler(uint32_t PrescalerValue);

這個函數(shù)只有一個入口參數(shù),就是 RTC 時鐘的分頻數(shù),很好理解。

然后是設(shè)置秒中斷允許, RTC 使能中斷的函數(shù)是:

void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState);

這個函數(shù)的第一個參數(shù)是設(shè)置秒中斷類型,這些通過宏定義定義的。對于使能秒中斷方法是:

RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能 RTC 秒中斷

八、RTC程序

這篇文章復(fù)制粘貼了這么多,感覺不到一絲有用的東西。算了,還是看一下,程序是怎么寫的吧。

RTC_Init

//實時時鐘配置//初始化 RTC 時鐘,同時檢測時鐘是否工作正常//BKP-》DR1 用于保存是否第一次配置的設(shè)置//返回 0:正常//其他:錯誤代碼u8 RTC_Init(void){u8 temp=0;//檢查是不是第一次配置時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR |RCC_APB1Periph_BKP, ENABLE); //①使能 PWR 和 BKP 外設(shè)時鐘PWR_BackupAccessCmd(ENABLE); //②使能后備寄存器訪問if (BKP_ReadBackupRegister(BKP_DR1) != 0x5050) //從指定的后備寄存器中//讀出數(shù)據(jù):讀出了與寫入的指定數(shù)據(jù)不相乎{BKP_DeInit(); //③復(fù)位備份區(qū)域RCC_LSEConfig(RCC_LSE_ON); //設(shè)置外部低速晶振(LSE)while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp《250)//檢查指定的 RCC 標(biāo)志位設(shè)置與否,等待低速晶振就緒{temp++;delay_ms(10);}if(temp》=250)return 1;//初始化時鐘失敗,晶振有問題RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //設(shè)置 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_SetPrescaler(32767); //設(shè)置 RTC 預(yù)分頻的值RTC_WaitForLastTask(); //等待最近一次對 RTC 寄存器的寫操作完成RTC_Set(2015,1,14,17,42,55); //設(shè)置時間RTC_ExitConfigMode(); //退出配置模式BKP_WriteBackupRegister(BKP_DR1, 0X5050); //向指定的后備寄存器中//寫入用戶程序數(shù)據(jù) 0x5050}else//系統(tǒng)繼續(xù)計時{RTC_WaitForSynchro(); //等待最近一次對 RTC 寄存器的寫操作完成RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能 RTC 秒中斷RTC_WaitForLastTask(); //等待最近一次對 RTC 寄存器的寫操作完成}RTC_NVIC_Config(); //RCT 中斷分組設(shè)置RTC_Get(); //更新時間return 0; //ok}RTC_Set

//設(shè)置時鐘//把輸入的時鐘轉(zhuǎn)換為秒鐘//以 1970 年 1 月 1 日為基準(zhǔn)//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《1970||syear》2099)return 1;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; //最后的秒鐘加上去RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR |RCC_APB1Periph_BKP, ENABLE); //使能 PWR 和 BKP 外設(shè)時鐘PWR_BackupAccessCmd(ENABLE); //使能 RTC 和后備寄存器訪問RTC_SetCounter(seccount); //設(shè)置 RTC 計數(shù)器的值RTC_WaitForLastTask(); //等待最近一次對 RTC 寄存器的寫操作完成return 0;}RTC_Get

//得到當(dāng)前的時間,結(jié)果保存在 calendar 結(jié)構(gòu)體里面//返回值:0,成功;其他:錯誤代碼.u8 RTC_Get(void){ static u16 daycnt=0;u32 timecount=0;u32 temp=0;u16 temp1=0;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 break;}else temp-=365; //平年temp1++;}calendar.w_year=temp1; //得到年份temp1=0;while(temp》=28) //超過了一個月{if(Is_Leap_Year(calendar.w_year)&&temp1==1)//當(dāng)年是不是閏年/2 月份{if(temp》=29)temp-=29;//閏年的秒鐘數(shù)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; //得到秒鐘數(shù)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;}RTC_NVIC_Config

static void RTC_NVIC_Config(void){NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;//RTC全局中斷NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//先占優(yōu)先級1位,從優(yōu)先級3位NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//先占優(yōu)先級0位,從優(yōu)先級4位NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能該通道中斷NVIC_Init(&NVIC_InitStructure);//根據(jù)NVIC_InitStruct中指定的參數(shù)初始化外設(shè)NVIC寄存器}RTC_IRQHandler

//RTC 時鐘中斷//每秒觸發(fā)一次void RTC_IRQHandler(void){if (RTC_GetITStatus(RTC_IT_SEC) != RESET) //秒鐘中斷{RTC_Get(); //更新時間}if(RTC_GetITStatus(RTC_IT_ALR)!= RESET) //鬧鐘中斷{RTC_ClearITPendingBit(RTC_IT_ALR); //清鬧鐘中斷RTC_Get(); //更新時間printf(“Alarm Time:%d-%d-%d %d:%d:%d ”,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();}九、項目代碼

void BSP_RTC_Init(void){u32 i = 0;#if(INFO_OUT_RTC_INIT_EN 》 0)u8tmpBuf[60]=“”;#endif

/* Clear reset flags */RCC_ClearFlag();

// 這里標(biāo)志必須跟測試程序一致否則時間被復(fù)位成默認(rèn)if (BKP_ReadBackupRegister(BKP_DR1) != RTC_SAVE_FLAG){RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);/* Allow access to BKP Domain */PWR_BackupAccessCmd(ENABLE);

/* Backup data register value is not correct or not yet programmed (whenthe first time the program is executed) */

/* RTC Configuration */BSP_RTC_Config();

#if(DEF_RTCINFO_OUTPUTEN 》 0)if(dbgInfoSwt & DBG_INFO_RTC)myPrintf(“[RTC]: RTC finish configured.。。。 ”);#endif

/* Set default time */SYS_RTC.year=Default_year;SYS_RTC.month=Default_month;SYS_RTC.day=Default_day;SYS_RTC.hour=Default_hour;SYS_RTC.minute =Default_minute;SYS_RTC.second =Default_second;

/* Adjust time by values entred by the user on the hyperterminal */BSP_RTC_Set_Current(&SYS_RTC);

BKP_WriteBackupRegister(BKP_DR1, RTC_SAVE_FLAG);}else{/* Enable PWR and BKP clocks */RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

/* Allow access to BKP Domain */PWR_BackupAccessCmd(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);// 不能在系統(tǒng)運行前使能中斷//RTC_ITConfig(RTC_IT_ALR, ENABLE);// 系統(tǒng)鬧鐘中斷/* Wait until last write operation on RTC registers has finished *///RTC_WaitForLastTask();// 不能在系統(tǒng)運行前使能中斷

/* Initialize Date structure */SYS_RTC.year = BKP_ReadBackupRegister(BKP_DR4);SYS_RTC.month= BKP_ReadBackupRegister(BKP_DR3);SYS_RTC.day = BKP_ReadBackupRegister(BKP_DR2);

if(RTC_GetCounter() / 86399 != 0){for(i = 0; i 《 (RTC_GetCounter() / 86399); i++){BSP_Date_Update(&SYS_RTC);}

/* Wait until last write operation on RTC registers has finished */RTC_WaitForLastTask();RTC_SetCounter(RTC_GetCounter() % 86399);/* Wait until last write operation on RTC registers has finished */RTC_WaitForLastTask();

BKP_WriteBackupRegister(BKP_DR4, SYS_RTC.year);BKP_WriteBackupRegister(BKP_DR3, SYS_RTC.month);BKP_WriteBackupRegister(BKP_DR2, SYS_RTC.day);}}/* Clear the RTC Second Interrupt pending bit */RTC_ClearITPendingBit(RTC_IT_SEC);// 防止系統(tǒng)初始化未完成前進入中斷程序RTC_ClearFlag(RTC_IT_SEC);

/* Enable one second interrupe *///RTC_ITConfig(RTC_IT_SEC,ENABLE);// 不能在系統(tǒng)運行前使能中斷rtcInitFinish=1;// 設(shè)置初始化完成標(biāo)志}

void BSP_RTC_Config(void){//u32 counter = 0;uint32_t tmp = 0;RCC_ClocksTypeDef RCC_Clocks;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_ICInitTypeDef TIM_ICInitStructure;

/* Enable PWR and BKP clocks */RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);/* Allow access to BKP Domain */PWR_BackupAccessCmd(ENABLE);/* Reset Backup Domain */BKP_DeInit();RCC_LSICmd(ENABLE); //啟用LSIwhile (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET){}RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);RCC_RTCCLKCmd(ENABLE); // Enable RTC ClockRTC_WaitForSynchro();RTC_WaitForLastTask();RTC_SetPrescaler(40000); // RTC period = RTCCLK/RTC_PR = (4 KHz)/(4000+1) LSIRTC_WaitForLastTask();BKP_TamperPinCmd(DISABLE);BKP_RTCOutputConfig(BKP_RTCOutputSource_Second);RCC_GetClocksFreq(&RCC_Clocks);RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);GPIO_PinRemapConfig(GPIO_Remap_TIM5CH4_LSI, ENABLE);TIM_TimeBaseStructure.TIM_Prescaler = 0;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseStructure.TIM_Period = 0xFFFF;TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);TIM_ICInitStructure.TIM_Channel = TIM_Channel_4;TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;TIM_ICInitStructure.TIM_ICFilter = 0;TIM_ICInit(TIM5, &TIM_ICInitStructure);OperationComplete = 0;TIM_Cmd(TIM5, ENABLE);TIM5-》SR = 0;//TIM_ITConfig(TIM5, TIM_IT_CC4, ENABLE);

while (OperationComplete != 2){if (TIM_GetFlagStatus(TIM5, TIM_FLAG_CC4) == SET){tmpCC4[IncrementVar_OperationComplete()] = (uint16_t)(TIM5-》CCR4);TIM_ClearFlag(TIM5, TIM_FLAG_CC4);if (GetVar_OperationComplete() 》= 2){tmp = (uint16_t)(tmpCC4[1] - tmpCC4[0] + 1);SetVar_PeriodValue(tmp);}}}if (PeriodValue != 0){#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL)LsiFreq = (uint32_t)((uint32_t)(RCC_Clocks.PCLK1_Frequency) / (uint32_t)PeriodValue);#elseLsiFreq = (uint32_t)((uint32_t)(RCC_Clocks.PCLK1_Frequency * 2) / (uint32_t)PeriodValue);#endif}RTC_SetPrescaler(LsiFreq - 1);RTC_WaitForLastTask();

TIM_DeInit( TIM5 );}十、HSE作為RTC時鐘源

void RTC_Configuration(void){RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);PWR_BackupAccessCmd(ENABLE);/* Reset Backup Domain */BKP_DeInit();//使用外部高速晶振8M/128 = 62.5KRCC_RTCCLKConfig(RCC_RTCCLKSource_HSE_Div128);//允許RTCRCC_RTCCLKCmd(ENABLE);//等待RTC寄存器同步RTC_WaitForSynchro();

RTC_WaitForLastTask();//允許RTC的秒中斷(還有鬧鐘中斷和溢出中斷可設(shè)置)RTC_ITConfig(RTC_IT_SEC, ENABLE);

RTC_WaitForLastTask();//62500晶振預(yù)分頻值是62500,不過一般來說晶振都不那么準(zhǔn)RTC_SetPrescaler(62498); //如果需要校準(zhǔn)晶振,可修改此分頻值RTC_WaitForLastTask();//清除標(biāo)志RCC_ClearFlag();}編輯:jq

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 時鐘
    +關(guān)注

    關(guān)注

    10

    文章

    1714

    瀏覽量

    131274
  • 定時器
    +關(guān)注

    關(guān)注

    23

    文章

    3231

    瀏覽量

    114326
  • RTC
    RTC
    +關(guān)注

    關(guān)注

    2

    文章

    522

    瀏覽量

    66227
  • 可編程計數(shù)器
    +關(guān)注

    關(guān)注

    0

    文章

    3

    瀏覽量

    5601

原文標(biāo)題:STM32開發(fā) -- RTC詳解

文章出處:【微信號:mcu168,微信公眾號:硬件攻城獅】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    實時時鐘芯片RTC

    FRTC8563是NYFEA徠飛公司推出的一款實時時鐘芯片,采用SOP-8封裝形式。這種封裝形式具有體積小、引腳間距小、便于集成等特點,使得FRTC8563能夠方便地應(yīng)用于各種電子設(shè)備中
    的頭像 發(fā)表于 10-22 11:33 ?151次閱讀

    使用實時時鐘

    電子發(fā)燒友網(wǎng)站提供《使用實時時鐘庫.pdf》資料免費下載
    發(fā)表于 10-22 10:09 ?0次下載
    使用<b class='flag-5'>實時時鐘</b>庫

    適用于智能門鎖的低功耗RTC實時時鐘模塊RX8010SJ

    隨著智能家居的普及,智能門鎖已成為保障家庭安全的重要設(shè)備。作為智能門鎖的關(guān)鍵組件之一,實時時鐘RTC)模塊負責(zé)提供準(zhǔn)確的時間基準(zhǔn),確保門鎖的各種時間相關(guān)功能正常運行。一款適用于智能門鎖的低功耗
    的頭像 發(fā)表于 08-28 14:29 ?286次閱讀
    適用于智能門鎖的低功耗<b class='flag-5'>RTC</b><b class='flag-5'>實時時鐘</b>模塊RX8010SJ

    bq32000實時時鐘(RTC)數(shù)據(jù)表

    電子發(fā)燒友網(wǎng)站提供《bq32000實時時鐘(RTC)數(shù)據(jù)表.pdf》資料免費下載
    發(fā)表于 08-23 11:23 ?0次下載
    bq32000<b class='flag-5'>實時時鐘</b>(<b class='flag-5'>RTC</b>)數(shù)據(jù)表

    BQ32002實時時鐘(RTC)數(shù)據(jù)表

    電子發(fā)燒友網(wǎng)站提供《BQ32002實時時鐘(RTC)數(shù)據(jù)表.pdf》資料免費下載
    發(fā)表于 08-23 11:23 ?0次下載
    BQ32002<b class='flag-5'>實時時鐘</b>(<b class='flag-5'>RTC</b>)數(shù)據(jù)表

    bq3285E/L實時時鐘(RTC)數(shù)據(jù)表

    電子發(fā)燒友網(wǎng)站提供《bq3285E/L實時時鐘(RTC)數(shù)據(jù)表.pdf》資料免費下載
    發(fā)表于 08-23 11:22 ?0次下載
    bq3285E/L<b class='flag-5'>實時時鐘</b>(<b class='flag-5'>RTC</b>)數(shù)據(jù)表

    bq3285實時時鐘(RTC)數(shù)據(jù)表

    電子發(fā)燒友網(wǎng)站提供《bq3285實時時鐘(RTC)數(shù)據(jù)表.pdf》資料免費下載
    發(fā)表于 08-23 11:21 ?0次下載
    bq3285<b class='flag-5'>實時時鐘</b>(<b class='flag-5'>RTC</b>)數(shù)據(jù)表

    DP8573A實時時鐘(RTC)數(shù)據(jù)表

    電子發(fā)燒友網(wǎng)站提供《DP8573A實時時鐘(RTC)數(shù)據(jù)表.pdf》資料免費下載
    發(fā)表于 08-23 10:11 ?0次下載
    DP8573A<b class='flag-5'>實時時鐘</b>(<b class='flag-5'>RTC</b>)數(shù)據(jù)表

    TCXO RTC實時時鐘模塊的特性

    高精度實時時鐘模塊內(nèi)建TCXO數(shù)字式實時時鐘模塊的特征【序文】時間是我們?nèi)粘I钪械幕靖拍睢;疖囘\行管理、進出場管理等各種處理系統(tǒng)和應(yīng)用程序按照時間信息工作。在金融、股市領(lǐng)域中,時間操縱著巨大利益
    的頭像 發(fā)表于 08-14 11:44 ?270次閱讀
    TCXO <b class='flag-5'>RTC</b><b class='flag-5'>實時時鐘</b>模塊的特性

    RTC實時時鐘的基本概念和工作原理

    在現(xiàn)代電子設(shè)備中,精確的時間管理和控制是至關(guān)重要的。為了實現(xiàn)這一功能,實時時鐘RTC, Real-Time Clock)模塊被廣泛應(yīng)用于各種設(shè)備和系統(tǒng)中。RTC模塊作為一個獨立的定時器,能夠提供
    的頭像 發(fā)表于 05-27 15:43 ?3249次閱讀

    stm8l051 RTC LSI作為實時時鐘不準(zhǔn)怎么校準(zhǔn)?

    (CLK_RTCCLKSource_LSI, CLK_RTCCLKDiv_1); CLK_PeripheralClockConfig(CLK_Peripheral_RTC, ENABLE); 現(xiàn)在以LSI作為實時時鐘,十分鐘內(nèi)快了2分鐘,分頻2后右邊慢了 請問大神么 這個
    發(fā)表于 04-30 07:13

    愛普生實時時鐘模塊RTC

    愛普生是一家IC和晶體自主研發(fā)的公司,有自己的IC和晶振產(chǎn)線。所以我們能將RTC IC+晶體單元+外結(jié)電容封裝在一起,組成獨立RTC模塊。外圍不需要在接晶體和匹配電容。愛普生RTC能夠簡化客戶
    發(fā)表于 04-17 11:10 ?0次下載

    基于51單片機的實時時鐘設(shè)計

    電子發(fā)燒友網(wǎng)站提供《基于51單片機的實時時鐘設(shè)計.rar》資料免費下載
    發(fā)表于 01-12 10:43 ?12次下載

    基于51單片機的實時時鐘設(shè)計

    電子發(fā)燒友網(wǎng)站提供《基于51單片機的實時時鐘設(shè)計.rar》資料免費下載
    發(fā)表于 01-02 10:15 ?43次下載

    基于51單片機的實時時鐘+溫度應(yīng)用設(shè)計

    基于51單片機的實時時鐘+溫度應(yīng)用設(shè)計(仿真)
    發(fā)表于 01-02 09:55 ?6次下載