实时时钟是一个独立的定时器。RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
) ?2 w$ ]9 m1 {6 ?
9 j. U% S; S" K" ^5 h RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒后,RTC的设置和时间维持不变。
" a& l0 v, q* S# H+ F) o! P% }. q! C) T ~, m( V! G1 ^
系统复位后,对后备寄存器和RTC的访问被禁止,这是为了防止对后备区域(BKP)的意外写操作。执行以下操作将使能对后备寄存器和RTC的访问:2 [' y0 \" t# }5 I
( a, X6 Q2 Y& b5 _设置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能电源和后备接口时钟* C' _2 K. i7 L6 w2 F
设置寄存器PWR_CR的DBP位,使能对后备寄存器和RTC的访问。8 s; J h* _' K5 b& O7 q# e0 {# M, b d
下面直接通过代码来演示如何操作RTC。
6 J# t$ b4 ]. k+ Y
- m* e% f1 h7 K; Q1 c. r1 h- static void RTC_NVIC_Config( void )
0 l4 J. j7 [& ^! j - {* ?+ w3 g, Y8 F. I4 D
- NVIC_InitTypeDef NVIC_InitStructure;
4 \( i% B: u5 J8 i" |
. O: X, E- B! }8 z1 l- NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;. E% _3 h8 |; ]
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;8 v& T- @" {/ A7 e. H
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;
! ~/ z J) L& ~& Z8 k - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;2 O( L, T+ u, W' H
- NVIC_Init( &NVIC_InitStructure );
) _+ {0 O! x' L8 y- ]9 v- \' _ - }4 e7 C ^8 w6 q* N8 R0 D! ~1 K( I
2 b5 }5 q7 w8 `( t7 K- //实时时钟配置
l4 U; W1 U z% P. T, J - //初始化RTC时钟,同时检测时钟是否工作正常1 ?4 Y6 n8 S1 u
- //BKP->DR1用于保存是否第一次配置的设置
5 g/ A# O1 i1 o T8 y+ i - //返回0:正常
! B- q; f- E0 i9 G# S# n# a - //其他:错误代码
) L9 @8 l, i6 e1 S - u8 RTC_Init( void )
0 G. p! ?: o2 a. X g! Q& d. E - { x- g. o/ {* g" g
- u8 temp = 0;
0 c$ r8 o# i' W6 V# R, ~: r! O - RCC_APB1PeriphClockCmd( RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE );: K9 S0 Z! ~+ z2 Y$ W& W, a9 Q9 {/ D
- PWR_BackupAccessCmd( ENABLE ); //使能后备寄存器访问$ m& I2 p$ a( i
- . E R& C5 Q1 h! n
- if( BKP_ReadBackupRegister( BKP_DR1 ) != 0x5055 ) //检查是不是第一次配置时钟& G' O$ }; g1 t J6 i/ U5 ]
- {& R! e5 C& `. V
- BKP_DeInit(); //复位备份区域
* B# p# F Z) O- M& f# Z - RCC_LSEConfig( RCC_LSE_ON ); //设置外部低速晶振(LSE),使用外设低速晶振: g: N3 t4 d. j. l
- //检查指定的RCC标志位设置与否,等待低速晶振就绪
% T, L2 n% U1 } - while( RCC_GetFlagStatus( RCC_FLAG_LSERDY ) == RESET && temp < 250 )
! x7 W( h/ [4 U4 T! N5 j0 E - {
2 l7 K2 O7 U# P. }* Q, A3 c2 ^! y - temp++; e+ E, i2 H" T: w {" M4 Z- L( l2 }
- delay_ms( 10 );
+ x/ B/ Q. ]- f) D( L% t - }/ m3 `! N+ |. d2 M, d" k
- if( temp >= 250 )
r& h1 h5 I4 _+ c# I' a - return 1; //初始化时钟失败,晶振有问题
" x4 {; L! U7 O - RCC_RTCCLKConfig( RCC_RTCCLKSource_LSE ); //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟( o& |' I0 \8 x6 W
- RCC_RTCCLKCmd( ENABLE ); //使能RTC时钟1 A7 o0 G" }2 R9 d. O7 z/ E
- RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
0 J* i* f" t7 `, w) B - RTC_WaitForSynchro(); //等待RTC寄存器同步
/ S6 e6 t- I9 I - RTC_ITConfig( RTC_IT_SEC, ENABLE ); //使能RTC秒中断
* R$ ?8 |, |- I - RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
! z' q0 c3 ^, c& D. ^% o - RTC_EnterConfigMode(); // 允许配置
; q Q) n1 n! Z# F1 w$ ] - RTC_SetPrescaler( 32767 ); //设置RTC预分频的值: I0 w) Y( }1 `
- RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
Q* C" T* b8 a - RTC_ExitConfigMode(); //退出配置模式9 J! [9 x! @$ d$ K( N
- BKP_WriteBackupRegister( BKP_DR1, 0x5055 ); //向指定的后备寄存器中写入用户程序数据. {( c2 o) L. S5 v' A
- }
$ v3 _7 n) ?6 x" g9 \ - else //系统继续计时4 i- k2 H4 @1 Q; [' K8 V
- {
1 V0 _: _) X" D+ S- b( L - RTC_WaitForSynchro(); //等待最近一次对RTC寄存器的写操作完成
) e6 R4 U" k' a$ X$ W2 _# u; x7 }% B - RTC_ITConfig( RTC_IT_SEC | RTC_IT_ALR, ENABLE ); //使能RTC秒中断、闹钟中断9 A. U" a% J0 O) R
- RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
7 `% ~' m) I+ Y: f( D \ - }$ y! u9 x* u9 `" p W- K
- RTC_NVIC_Config(); //RCT中断分组设置
7 i8 f" [: _- {; s) P - return 0; //OK, y( T2 ^% B3 U) F
- }
$ Y7 \" W9 o7 I% @" ^ c6 Q
1 y6 r& _3 ~( [7 S' Q1 W* h$ n: n# e g- //RTC时钟中断# w3 T5 v/ ]' R8 d- g
- //每秒钟触发一次! x4 r% n$ g/ h9 z# `
- void RTC_IRQHandler( void )
5 c5 u2 C5 \1 H$ U- i - {
x7 T/ r; a( v) O0 U - if( RTC_GetITStatus( RTC_IT_SEC ) != RESET ) //秒钟中断
) ^$ K9 y6 F) F - { 5 G1 x+ q8 i8 {+ S/ p
- printf( "RTC INT!\r\n" ); 2 B' N2 u: z) \+ S6 x/ {/ K
- }. J4 H$ C3 @1 G' V3 \
' n i+ r2 M. S, _- //RTC_ClearITPendingBit(RTC_IT_SEC | RTC_IT_OW);( s, u" Z/ x: v0 P
- RTC_ClearITPendingBit( RTC_IT_SEC | RTC_IT_ALR );
+ U3 \: m9 c: M! g# s - RTC_WaitForLastTask();, o3 T# h& `6 @
- }
复制代码 , M! n, \' O# {# E0 _) K. G
在设置RTC时首先要判断一下RTC是否已经初始化过了,因为一般使用RTC时都会有电池供电,RTC的时候只需要设置一次就行。当系统关机后,只要电池有电,RTC就能正常工作,所以不需要每次开机都初始化一次时间,当没有初始化时初始化一次,初始化之后,以后开机就不需要再初始化了。为了标记当前设备是否已经初始化了,手动的给备份寄存器中写入特定的值。每次单片机启动后就会读取一次备份寄存器的值,当备份寄存器中的值不是写入的特定值,就说明当前设备还没有被初始化过,需要初始化一次。如果备份寄存器中的值是写入的特殊值,就说明当前设备已经被初始化过了,不需要再初始化了。- f, M( I; F( [# Q4 @
, l( |, [# C5 T! g; p' {! K 在初始化的时候,开启RTC的秒中断,这样RTC每一秒就会中断一次,在中断函数中通过串口打印数据。当程序运行后在串口工具中就可以看到每隔1秒,就会打印一个字符串。
4 e4 q/ ]: k7 E" k# J2 F) B8 D7 e9 A% M3 z$ r/ I6 X8 w- A
7 j- J9 K+ P5 Y+ h9 s
$ e) r+ c# @ J( q! n n0 K, r( A7 a& F |