12.1 概述
實時時鐘Real TimeClock(簡稱RTC),實時時鐘芯片是日常生活中應用最為廣泛的消費類電子產品之一。它為人們提供精確的實時時間,或者為電子系統提供精確的時間基準,目前實時時鐘芯片大多采用精度較高的晶體振蕩器作為時鐘源。有些時鐘芯片為了在主電源掉電時,還可以工作,需要外加電池供電。
現在的ARM體系處理器基本都會內置RTC模塊,STM32也不例外。STM32內部RTC結構如下圖所示。
![poYBAGP_An-AXUzUAAGljwT34VQ575.png](https://file.elecfans.com/web2/M00/94/B0/poYBAGP_An-AXUzUAAGljwT34VQ575.png)
RTC主要有兩個部分組成,第一部分的APB1接口用來和APB1總線相連,此單元還包含一組16位寄存器,可通過APB1總線對其進行讀寫操作。另一部分由一組可編程計數器組成,分成兩個主要模塊。第一個模塊是RTC的預分頻模塊,它可編程產生最長為1秒的RTC時間基準TR_CLK。RTC的預分頻模塊包含了一個20位的可編程分頻器。如果在RTC_CR寄存器中設置了相應的允許位,則在每個TR_CLK周期中RTC產生一個中斷。第二個模塊是一個32位的可編程計數器,可被初始化為當前的系統時間。系統時間按TR_CLK周期累加并與存儲在RTC_ALR寄存器中的可編程時間相比較,如果RTC_CR控制寄存器中設置了相應允許位,比較匹配時將產生一個鬧鐘中斷。
RTC模塊和時鐘配置系統是在后備區域,即在系統復位或從待機模式喚醒后RTC的設置和時間維持不變。但是在系統復位后,會自動禁止訪問后備寄存器和RTC,以防止對后備區域的意外寫操作。所以在要設置時間之前,先要取消備份區域寫保護。
12.2 相關寄存器
12.2.1 控制寄存器1:RTC_CRH
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
- |
OWIE |
ALRIE |
SECIE |
Bit 2:允許溢出中斷位
0:屏蔽溢出中斷
1:允許溢出中斷
Bit 1:允許鬧鐘中斷
0:屏蔽鬧鐘中斷
1:允許鬧鐘中斷
Bit 0:允許秒中斷
0:屏蔽秒中斷
1:允許秒中斷
12.2.2 控制寄存器2:RTC_CRL
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
- |
RTOFF |
CNF |
RSF |
OWF |
ALRF |
SECF |
Bit 5:RTC操作關閉
0:寫操作未完成
1:操作已完成
Bit 4:配置標志
0:退出配置模式
1:進入配置模式
Bit 3:RTC同步標志
RTC_CNT寄存器和RTC_DIV寄存器由軟件更新或清0時,此位由硬件置1。在APB1復位后,或APB1時鐘停止后,此位必須由軟件清0。要進行任何的讀操作之前,用戶程序必須等待這位被硬件置1
0:寄存器尚未被同步
1:寄存器已經被同步
Bit 2:溢出標志
當32位可編程計數器溢出時,此位由硬件置1。此位只能由軟件清0
0:無溢出
1:32位可編程計數器溢出
Bit 1:鬧鐘標志
當32位可編程計數器達到RTC_ALR寄存器所設置的預定值,此位由硬件置1。此位只能由軟件清0。
0:無鬧鐘
1:有鬧鐘
Bit 0:秒標志
當32位可編程預分頻器溢出時,此位由硬件置1,同時RTC計數器加1。此位只能由軟件清除。
0:秒標志條件不成立
1:秒標志條件成立
12.2.3 預分頻裝載寄存器1:RTC_PRLH
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
- |
PRL[19:16] |
Bit 3~Bit 0:RTC預分頻裝載值高位
12.2.4 預分頻裝載寄存器2:RTC_PRLL
15 |
14 |
13 |
12 |
11 |
10 |
9 |
8 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
PRL[15:0] |
Bit 15~Bit 0:RTC預分頻裝載值低位
注:RTC時鐘頻率根據預分頻寄存器的值有如下計算公式。
![pYYBAGP_AoyABJbhAAAR-b703dM921.png](https://file.elecfans.com/web2/M00/95/32/pYYBAGP_AoyABJbhAAAR-b703dM921.png)
其中RTCCLK代表的RTC的輸入時鐘,一般默認32.768kHz。
12.3 實驗例程
功能:讀取RTC的日期顯示在LCD上面。
(1)創建rtc.h文件輸入以下代碼。
/*********************************************************************************************************
*********************************************************************************************************/
#ifndef _RTC_H_
#define _RTC_H_
#include "sys.h"
/*********************************************************************************************************
數 據 結 構
*********************************************************************************************************/
typedef struct
{
u8 year; //年
u8 month; //月
u8 date; //日
u8 hour; //時
u8 minute; //分
u8 second; //秒
}RTC_Data;
extern RTC_Data RTC_Time;
/*********************************************************************************************************
函 數 列 表
*********************************************************************************************************/
void RTC_Init( void ) ; //RTC初始化
void RTC_Set_Time( u8 year, u8 month, u8 date, u8 hour, u8 minute, u8 second ) ; //設置時間
void RTC_Get_Time( void ) ; //獲取時間
#endif
(2)創建rtc.c文件并輸入以下代碼。
#include "rtc.h"
/***************************************************
Name :RTC_Init
Fuction :RTC初始化
Parameter :None
Return :None
***************************************************/
void RTC_Init()
{
if( BKP->DR1!=0x5050 )
{
RCC->APB1ENR |= 1<<28 ; //使能PWR時鐘
RCC->APB1ENR |= 1<<27 ; //使能BKP時鐘,RTC校準在BKP相關寄存器中
PWR->CR |= 1<<8 ; //取消BKP相關寄存器寫保護
RCC->BDCR |= 1<<16 ; //備份區域軟復位
RCC->BDCR &= ~( 1<<16 ) ; //備份區域軟復位結束
RCC->BDCR |= 1<<0 ; //開啟外部低速振蕩器
while( ( RCC->BDCR&0x02 )!=0x02 ) ; //等待外部時鐘就緒
RCC->BDCR |= 1<<8 ; //LSI作為RTC時鐘
RCC->BDCR |= 1<<15 ; //RTC時鐘使能
while( !( RTC->CRL&( 1<<5 ) ) ) ; //等待RTC寄存器最后一次操作完成
while( !( RTC->CRL&( 1<<3 ) ) ) ; //等待RTC寄存器同步完成
RTC->CRH &= ~( 7<<0 ) ; //不允許中斷,CRH寄存器低三位有效
while( !( RTC->CRL&( 1<<5 ) ) ) ; //等待RTC寄存器最后一次操作完成
RTC->CRL |= 1<<4 ; //進入配置模式
RTC->PRLH = 0 ;
RTC->PRLL = 32767 ; //設定分頻值
RTC->CRL &= ~( 1<<4 ) ; //退出配置模式
while( !( RTC->CRL&( 1<<5 ) ) ) ; //等待RTC寄存器最后一次操作完成
BKP->DR1 = 0x5050 ;
}
else
{
while( !( RTC->CRL&( 1<<3 ) ) ) ; //等待RTC寄存器同步
while( !( RTC->CRL&( 1<<5 ) ) ) ; //等待RTC寄存器操作完成
}
}
/***************************************************
Name :Is_Leap_Year
Function :閏年判定
Parameter :
year:年份
Return :閏年
***************************************************/
u8 Is_Leap_Year( u16 year )
{
//必須能被4整除
if( year%4==0 )
{
if( year%100==0 )
{
if( year%400==0 )
return 1 ;
else
return 0 ;
}
else
return 1 ;
}
else
return 0;
}
/***************************************************
Name :RTC_Set_Time
Fuction :設置時間
Parameter :
year:年
month:月
date:日
hour:時
minute:分
second:秒
Return :None
***************************************************/
u8 mon_table[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } ;
void RTC_Set_Time( u8 year, u8 month, u8 date, u8 hour, u8 minute, u8 second )
{
u16 t ;
u32 seccount = 0 ;
//把所有年份的秒鐘相加
for( t=1970; tAPB1ENR |= 1<<28 ; //使能電源時鐘
RCC->APB1ENR |= 1<<27 ; //使能備份時鐘
PWR->CR |= 1<<8 ; //取消備份區寫保護
RTC->CRL |= 1<<4 ; //允許配置
RTC->CNTL = seccount&0xFFFF ;
RTC->CNTH = seccount>>16 ;
RTC->CRL &= ~( 1<<4 ) ; //配置更新
while( ( RTC->CRL&0x20 )!=0x20 ) ; //等待RTC寄存器操作完成
RTC_Get_Time() ; //設置完之后更新一下數據
}
/***************************************************
Name :RTC_Get_Time
Fuction :獲取時間
Parameter :None
Return :None
***************************************************/
RTC_Data RTC_Time;
void RTC_Get_Time()
{
u16 daycnt=0;
u32 timecount=0;
u32 temp=0;
u16 temp1=0;
//得到計數器中的值
timecount = RTC->CNTH ;
timecount <<= 16 ;
timecount += RTC->CNTL ;
//得到天數
temp = timecount/86400 ;
//超過一天了
if( daycnt!=temp )
{
daycnt = temp ;
temp1 = 1970 ; //從1970年開始
while( temp>=365 )
{
//閏年
if( Is_Leap_Year( temp1 ) )
{
if( temp>=366 )
temp -= 366 ; //閏年的秒鐘數
else
break ;
}
else
temp -= 365; //平年
temp1 ++ ;
}
RTC_Time.year = temp1-2000 ; //得到年份
temp1 = 0 ;
//超過了一個月
while( temp>=28 )
{
if( Is_Leap_Year( RTC_Time.year+2000 )&&( temp1==1 ) )//當年是不是閏年/2月份
{
if( temp>=29 )
temp -= 29 ;//閏年的秒鐘數
else
break;
}
else
{
if( temp>=mon_table[ temp1 ] )
temp -= mon_table[ temp1 ] ; //平年
else
break ;
}
temp1 ++ ;
}
RTC_Time.month = temp1+1 ; //得到月份
RTC_Time.date = temp+1 ; //得到日期
}
temp = timecount%86400 ; //得到秒鐘數
RTC_Time.hour = temp/3600 ; //小時
RTC_Time.minute = ( temp%3600 )/60 ; //分鐘
RTC_Time.second = ( temp%3600 )%60 ; //秒鐘
}+2000;>
(3)創建1.c文件并輸入以下代碼。
#include "sys.h"
#include "delay.h"
#include "usart1.h"
#include "lcd.h"
#include "rtc.h"
int main()
{
u8 Str[ 50 ] ;
STM32_Clock_Init( 9 ) ; //STM32時鐘初始化
SysTick_Init( 72 ) ; //SysTick初始化
USART1_Init( 72, 115200 ) ; //初始化串口1波特率115200
LCD_Init() ; //LCD初始化
RTC_Init() ;
RTC_Set_Time( 20, 12, 10, 10, 8, 0 ) ;
while( 1 )
{
RTC_Get_Time() ;
sprintf( ( char * )Str, "20%02d-%02d-%02d %02d:%02d:%02d", RTC_Time.year, RTC_Time.month, RTC_Time.date, RTC_Time.hour, RTC_Time.minute, RTC_Time.second ) ;
LCD_ShowString( 10, 10, Str ) ;
delay_ms( 500 ) ;
}
}
-
電子產品
+關注
關注
6文章
1179瀏覽量
58554 -
實時時鐘
+關注
關注
4文章
248瀏覽量
66002 -
RTC
+關注
關注
2文章
544瀏覽量
67117
發布評論請先 登錄
相關推薦
《DNESP32S3使用指南-IDF版_V1.6》第二十三章 RTC實驗
![](https://file.elecfans.com/web2/M00/3B/50/poYBAGJJWb2ATG4kAAQqS_0h94I067.jpg)
![](https://file1.elecfans.com/web2/M00/90/CB/wKgZomTcvzqACKICAAAmyErSY44112.png)
![](https://file1.elecfans.com/web2/M00/90/D9/wKgZomTdeTuAaZXuAAC7iIlNneE536.png)
![](https://file1.elecfans.com/web2/M00/90/DD/wKgaomTdeaGAGiTXAADp2zXgBHo076.png)
STM32 RTC實驗問題
【連載】【星光閃電STM32F407開發板】第十二章 RTC實驗
為什么我進行RTC實驗時LCD顯示的秒鐘快了10倍?
RTC實驗lCD顯示重影
如何通過串口超級終端修改RTC時間
STM32入門學習筆記之RTC實驗(上)
![STM32入門學習筆記之<b class='flag-5'>RTC</b><b class='flag-5'>實驗</b>(上)](https://file.elecfans.com/web2/M00/91/EF/pYYBAGPtoP-AZGo9AAGfo77Fwa4705.jpg)
評論