天天看點

(stm32f103學習總結)—RTC獨立定時器—實時時鐘實驗

一、STM32F1 RTC介紹

1.1 RTC簡介

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

1.2電源

(stm32f103學習總結)—RTC獨立定時器—實時時鐘實驗

電池備份區域

使用電池或其他電源連接配接到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控制寄存器中設定了相應允許位,比較比對時将産生一個鬧鐘中斷。

(stm32f103學習總結)—RTC獨立定時器—實時時鐘實驗

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 }