一、STM32F1 RTC介紹
1.1 RTC簡介
STM32 的實時時鐘( RTC)是一個獨立的定時器。 STM32 的 RTC 模 塊擁有一組連續計數的計數器,在相應軟體配置下,可提供時鐘月曆的 功能。修改計數器的值可以重新設定系統目前的時間和日期。 RTC子產品和時鐘配置是在後備區域,無論器件狀态如何(運作模式、 低功耗模式或處于複位狀态),隻要保證後備區域供電正常,RTC便不會 停止工作,是以通常會在後備區域供電端加一個紐扣電池,即使主電源 停止供電,後備電源也會啟動供電,進而保證RTC時鐘不停的運作,隻有 當主電源和後備紐扣電池都沒有電的時,RTC才停止工作。 從 RTC 的定時器特性來說,它是一個 32 位的計數器,隻能向上計 數。它的時鐘來源有三種,分别為高速外部時鐘的 128 分頻( HSE/128 )、 低速内部時鐘 LSI 以及低速外部時鐘 LSE。
1.2電源

電池備份區域
使用電池或其他電源連接配接到VBAT腳上,當VDD斷電時,可以儲存備份寄存器的内容和維持RTC的
功能。
VBAT腳也為RTC、LSE振蕩器和PC13至PC15供電,這保證當主要電源被切斷時RTC能繼續工作。切換到VBAT供電由複位子產品中的掉電複位功能控制。
如果應用中沒有使用外部電池,VBAT必須連接配接到VDD引腳上。
1.3備份寄存器(BKP)簡介
備份寄存器是42個16位的寄存器,可用來存儲84個位元組的使用者應用程式資料。他們處在備份域裡,當VDD電源被切斷,他們仍然由VBAT維持供電。當系統在待機模式下被喚醒,或系統複位或電源複位時,他們也不會被複位。
此外,BKP控制寄存器用來管理侵入檢測和RTC校準功能。
複位後,對備份寄存器和RTC的通路被禁止,并且備份域被保護以防止可能存在的意外的寫操作。執行以下操作可以使能對備份寄存器和RTC的通路。
● 通過設定寄存器RCC_APB1ENR的PWREN和BKPEN位來打開電源和後備接口的時鐘
● 電源控制寄存器(PWR_CR)的DBP位來使能對後備寄存器和RTC的通路。
1.4實時時鐘(RTC)
RTC簡介
實時時鐘是一個獨立的定時器。RTC子產品擁有一組連續計數的計數器,在相應軟體配置下,可提供時鐘月曆的功能。修改計數器的值可以重新設定系統目前的時間和日期。
RTC子產品和時鐘配置系統(RCC_BDCR寄存器)處于後備區域,即在系統複位或從待機模式喚醒後,RTC的設定和時間維持不變。
系統複位後,對後備寄存器和RTC的通路被禁止,這是為了防止對後備區域(BKP)的意外寫操作。執行以下操作将使能對後備寄存器和RTC的通路:(BKP中也提到過)
● 設定寄存器RCC_APB1ENR的PWREN和BKPEN位,使能電源和後備接口時鐘
● 設定寄存器PWR_CR的DBP位,使能對後備寄存器和RTC的通路。
二、功能描述
1、概述
RTC由兩個主要部分組成(參見下圖)。第一部分(APB1接口)用來和APB1總線相連。此單元還包含一組16位寄存器,可通過APB1總線對其進行讀寫操作(參見16.4節)。APB1接口由APB1總線時鐘驅動,用來與APB1總線接口。
另一部分(RTC核心)由一組可程式設計計數器組成,分成兩個主要子產品。
第一個子產品是RTC的預分頻子產品,它可程式設計産生最長為1秒的RTC時間基準TR_CLK。RTC的預分頻子產品包含了一個20位的可程式設計分頻器(RTC預分頻器)。如果在RTC_CR寄存器中設定了相應的允許位,則在每個實時時鐘(RTC)TR_CLK周期中RTC産生一個中斷(秒中斷)。
第二個子產品是一個32位的可程式設計計數器,可被初始化為目前的系統時間。系統時間按TR_CLK周期累加并與存儲在RTC_ALR寄存器中的可程式設計時間相比較,如果RTC_CR控制寄存器中設定了相應允許位,比較比對時将産生一個鬧鐘中斷。
2、複位過程
除了RTC_PRL、RTC_ALR、RTC_CNT和RTC_DIV寄存器外,所有的系統寄存器都由系統複位或電源複位進行異步複位。
RTC_PRL、RTC_ALR、RTC_CNT和RTC_DIV寄存器僅能通過備份域複位信号複位。
3、讀RTC寄存器
RTC核完全獨立于RTC APB1接口。
軟體通過APB1接口通路RTC的預分頻值、計數器值和鬧鐘值。但是,相關的可讀寄存器隻在與RTC與 APB1時鐘進行重新同步的RTC時鐘的上升沿被更新。RTC标志也是如此的。這意味着,如果APB1接口曾經被關閉,而讀操作又是在剛剛重新開啟APB1之後,則在第一次的内部寄存器更新之前,從APB1上讀出的RTC寄存器數值可能被破壞了(通常讀到0)。下述幾種情況下能夠發生這種情形:
● 發生系統複位或電源複位
● 系統剛從待機模式喚醒(參見第4.3節:低功耗模式)。
● 系統剛從停機模式喚醒(參見第4.3節:低功耗模式)。
所有以上情況中,APB1接口被禁止時(複位、無時鐘或斷電)RTC核仍保持運作狀态。
是以,若在讀取RTC寄存器時,RTC的APB1接口曾經處于禁止狀态,則軟體首先必須等待RTC_CRL寄存器中的RSF位(寄存器同步标志)被硬體置’1’。
注:RTC的 APB1接口不受WFI和WFE等低功耗模式的影響。
4、寫RTC寄存器
必須設定RTC_CRL寄存器中的CNF位,使RTC進入配置模式後,才能寫入RTC_PRL、RTC_CNT、RTC_ALR寄存器。
另外,對RTC任何寄存器的寫操作,都必須在前一次寫操作結束後進行。可以通過查詢RTC_CR寄存器中的RTOFF狀态位,判斷RTC寄存器是否處于更新中。僅當RTOFF狀态位是1時,才可以寫入RTC寄存器。
三、STM32F1 RTC配置步驟
- 使能PWR和BKP時鐘。調用函數:RCC_APB1PeriphClockCmd();
- 使能後備寄存器通路。調用函數:PWR_BackupAccessCmd();
- 配置RTC時鐘源,使能RTC時鐘。調用函數:RCC_RTCCLKConfig();RCC_RTCCLKCmd();
- 如果使用LSE,要打開LSE:RCC_LSEConfig(RCC_LSE_ON);
- 設定RTC預分頻系數。調用函數:RTC_SetPrescaler();
- 設定時間。調用函數:RTC_SetCounter();
- 開啟相關中斷(如果需要)。調用函數:RTC_ITConfig();
- 編寫中斷服務函數。調用函數:RTC_IRQHandler();
- 部分操作要等待寫操作完成和同步。調用函數:RTC_WaitForLastTask();RTC_WaitForSynchro();
四、程式舉例
編寫RTC控制程式 本章所要實作的功能是:設定RTC時間日期初值,在RTC秒中斷内使用 序列槽列印出RTC日期和時間,D1訓示燈閃爍提示系統運作。
程式架構如下 :
(1)初始化RTC,設定RTC時間日期初值 (2)開啟RTC的秒中斷,編寫RTC中斷函數, (3)在RTC中斷内更新時間并列印輸出 (4)編寫主函數
1 #ifndef _rtc_H
2 #define _rtc_H
3
4 #include "system.h"
5
6
7 u8 RTCx_Init(void);
8 void RTC_GET(void);
9
10 typedef struct
11 {
12 u8 hour;
13 u8 min;
14 u8 sec;
15 }_calender;
16
17 extern _calender calender;
18
19
20
21 #endif
分析RTC_Init()函數:RTC初始化函數。
初始化時按照之前的RTC一般步驟進行配置,這裡需要注意的是,為了區分是否是第一次執行RTC_Init()函數,必須判斷後配寄存器中是否寫如果某個值(向BKP_DR1寄存器寫入0xA0A0,寫入其他的數字也可以)如果寫入不用再初始化。
為什麼要區分是否執行過RTC_Init?
如果由于 斷電/ 複位/喚醒等待 等因素,程式中斷但RTC時鐘以及後備寄存器區域還在執行;等恢複供電重新啟動程式時,這不能再對RTC時鐘進行初始化,否則一直初始化,那麼RTC作為時鐘就沒什麼實際作用。
1 if (BKP_ReadBackupRegister(BKP_DR1) != 0xA0A0//(從指定的後備寄存器中讀出資料)判斷是否初始化過
2 {
3 //第一次進行初始化(RTC_Init)
4 BKP_WriteBackupRegister(BKP_DR1, 0XA0A0); //向指定的後備寄存器中寫入使用者程式資料
5 }
6 else//(已經初始化過)系統繼續計時
7 {
8 //不是第一次進行初始化(RTC_Init)
9 }
代碼44:使用外部低速晶振(LSE)時需要檢查指定的RCC相應的标志位是否設定,等待低速晶振就緒。
1 #include "rtc.h"
2 #include "systick.h"
3 #include "ustrt.h"
4
5
6 _calender calender;
7
8
9 void RTC_NVIC_Config() //設定RTC中斷優先級
10 {
11 NVIC_InitTypeDef NVIC_InitStruct;
12
13 NVIC_InitStruct.NVIC_IRQChannel=RTC_IRQn;
14 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;
15 NVIC_InitStruct.NVIC_IRQChannelSubPriority=3;
16 NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE ;
17 NVIC_Init(&NVIC_InitStruct);
18
19 }
20
21 void RTC_GET() //擷取 RTC 計數器的值并進行處理
22 {
23 u32 timedata;
24 timedata=RTC_GetCounter();
25 calender.hour=timedata/3600;
26 calender.min=timedata%3600/60;
27 calender.sec=timedata%3600%60;
28 }
29
30 //傳回0:初始化失敗
31 //傳回1:初始化成功
32 u8 RTCx_Init()
33 {
34 u8 time;
35 RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
36 RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
37 PWR_BackupAccessCmd(ENABLE);
38
39
40 if(BKP_ReadBackupRegister(BKP_DR1)!=0xA0a0) //從指定的後備寄存器讀資料)檢查是不是第一次配置時鐘
41 {
42 BKP_DeInit(); //将後備寄存器初始化
43 RCC_LSEConfig(RCC_LSE_ON); //将RCC_LSE時鐘開啟
44 while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)==RESET && time<250) //檢測LSE時鐘是否開啟
45 {
46 time++;
47 delay_ms(10);
48 }
49 if(time>=250)
50 {
51 return 1;
52 }
53 RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //配置RTC的時鐘為LSE
54 RCC_RTCCLKCmd(ENABLE); //RTC時鐘輸入開啟
55 RTC_WaitForLastTask(); //等待直到RTC寄存器上的上一次寫操作完成。
56 RTC_WaitForSynchro(); //等待,直到RTC寄存器(RTC_CNT、RTC_ALR和RTC_PRL)與RTC APB時鐘同步。
57 RTC_ITConfig(RTC_IT_SEC, ENABLE);
58 RTC_WaitForLastTask(); //等待直到RTC寄存器上的上一次寫操作完成。
59 RTC_EnterConfigMode(); // 允許配置
60 RTC_SetPrescaler(32767); //設定 RTC 預分頻的值
61 RTC_WaitForLastTask(); //等待直到RTC寄存器上的上一次寫操作完成。
62 RTC_SetCounter(0x1111); //設定 RTC 計數器的值 初始化時間17:34:55
63 RTC_ExitConfigMode(); //退出 RTC 配置模式
64 BKP_WriteBackupRegister(BKP_DR1,0xA0a0); //向指定的後備寄存器中寫入使用者程式資料
65
66 }
67 else //(系統之前已經進行過相應初始化)系統繼續計時
68 {
69 RTC_WaitForLastTask(); //等待直到RTC寄存器上的上一次寫操作完成。
70 RTC_WaitForSynchro(); //等待,直到RTC寄存器(RTC_CNT、RTC_ALR和RTC_PRL)與RTC APB時鐘同步。
71 RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能或者失能指定的 RTC 中斷
72 }
73
74 RTC_NVIC_Config(); //RCT中斷優先級别設定
75 RTC_GET(); //擷取 RTC 計數器的值
76 return 0;
77 }
78
79 void RTC_IRQHandler(void) //RTC中斷函數
80 {
81 if(RTC_GetITStatus(RTC_IT_SEC)!=0) //檢查指定的 RTC 中斷發生與否(秒中斷)
82 {
83 RTC_GET();
84 printf("RTC_Time:%d:%d:%d\r\n",calender.hour,calender.min,calender.sec);
85 }
86 RTC_ClearITPendingBit(RTC_IT_SEC); //清除 RTC 的中斷待處理位
87 }
1 #include "system.h"
2 #include "led.h"
3 #include "systick.h"
4 #include "ustrt.h"
5 #include "rtc.h"
6
7 int main()
8 {
9 u8 i=0;
10
11
12 SysTick_Init(72);
13 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
14 LED_Init();
15 ustrt_Init(9600);
16 RTCx_Init();
17
18 while(1)
19 {
20
21 i++;
22 if(i%20==0)
23 {
24 led1=!led1;
25 }
26 delay_ms(10);
27 }
28 }