什么是RTC RTC (Real Time Clock):实时时钟 RTC是个独立的定时器。RTC模块拥有一个连续计数的计数器,在相应的软件配置下,可以提供时钟日历的功能。修改计数器的值可以重新设置当前时间和日期 RTC还包含用于管理低功耗模式的自动唤醒单元。
" M2 {3 Y& L3 V7 ^- o
& Z7 B0 s0 Y4 o+ M在断电情况下 RTC仍可以独立运行 只要芯片的备用电源一直供电,RTC上的时间会一直走。 : b6 e$ V/ P- h
RTC实质是一个掉电后还继续运行的定时器,从定时器的角度来看,相对于通用定时器TIM外设,它的功能十分简单,只有计时功能(也可以触发中断)。但其高级指出也就在于掉电之后还可以正常运行。 7 V5 k! A+ [( A. |+ m8 B4 T0 X
两个 32 位寄存器包含二进码十进数格式 (BCD) 的秒、分钟、小时( 12 或 24 小时制)、星期几、日期、月份和年份。此外,还可提供二进制格式的亚秒值。系统可以自动将月份的天数补偿为 28、29(闰年)、30 和 31 天。 上电复位后,所有RTC寄存器都会受到保护,以防止可能的非正常写访问。 无论器件状态如何(运行模式、低功耗模式或处于复位状态),只要电源电压保持在工作范围内,RTC使不会停止工作。
/ }- m \8 W, @5 r' M9 R
6 l2 w# s. S) Z0 U! ^ RCT特征:● 可编程的预分频系数:分频系数高为220。* i; m0 a. c( }$ @9 ]
● 32位的可编程计数器,可用于较长时间段的测量。
# ^$ P- S' `# y$ K6 |● 2个分离的时钟:用于APB1接口的PCLK1和RTC时钟(RTC时钟的频率必须小于PCLK1时钟 频率的四分之一以上)。
4 t x7 t2 F3 S' H+ [, H) t● 可以选择以下三种RTC的时钟源:
$ E2 B8 }1 Q1 G8 c/ B- ]. v ● HSE时钟除以128;0 F9 d0 Y1 f$ [; O* ^5 S) S. C9 m
● LSE振荡器时钟;
* G* I$ |3 \4 _' I+ d ● LSI振荡器时钟 ● 2个独立的复位类型:4 D1 k' P i# l( S+ P3 G& x2 v# z% f* h
● APB1接口由系统复位;; w. G+ R. i4 n8 P
● RTC核心(预分频器、闹钟、计数器和分频器)只能由后备域复位 ● 3个专门的可屏蔽中断:
9 V$ V/ f9 J; o* Z8 v ● 1.闹钟中断,用来产生一个软件可编程的闹钟中断。 ● 2.秒中断,用来产生一个可编程的周期性中断信号(长可达1秒)。 ● 3.溢出中断,指示内部可编程计数器溢出并回转为0的状态。 9 T# _( q C7 L2 v8 S
RTC时钟源:
7 @" B6 b4 i4 u5 Y三种不同的时钟源可被用来驱动系统时钟(SYSCLK): ● HSI振荡器时钟0 S/ J- h0 I% b9 M& L3 L
● HSE振荡器时钟
0 f8 ^& q3 F7 L4 P2 C8 y! S● PLL时钟 这些设备有以下2种二级时钟源: ● 40kHz低速内部RC,可以用于驱动独立看门狗和通过程序选择驱动RTC。 RTC用于从停机/待机模式下自动唤醒系统。
- [$ D( ^5 H9 K5 C● 32.768kHz低速外部晶体也可用来通过程序选择驱动RTC(RTCCLK)。
# ?6 f3 p( b+ E& U+ o
% l( C( i& F9 ]: H: A0 Y
RTC原理框图
/ P7 l3 `; z# ~( M4 y0 E7 |; _( f$ A& t7 T/ Y
) V1 ?' m a" S
5 R1 e- B7 R) i4 _6 o5 L
RTC时钟的框图还是比较简单的,这里我们把他分成 两个部分:+ F5 l7 G8 \) X2 q: Y
APB1 接口:用来和 APB1 总线相连。 此单元还包含一组 16 位寄存器,可通过 APB1 总线对其进行读写操作。APB1 接口由 APB1 总 线时钟驱动,用来与 APB1 总线连接。 通过APB1接口可以访问RTC的相关寄存器(预分频值,计数器值,闹钟值)。 RTC 核心接口:由一组可编程计数器组成,分成 两个主要模块 。 ; s n0 F7 n! \! p: Q6 ~
! Y' p' H) B) f! D
g) 6 q. }/ p( D! Q B3 i4 M7 Q0 u
. G" l0 n# C. o
; k3 A: l# B5 t1 D& N. f$ X3 F% P3 a4 R0 y" i1 O
5 V3 m; H# X2 k* H2 a5 g6 c; K/ d% `$ ?
第一个模块是 RTC 的 预分频模块,它可编程产生 1 秒的 RTC 时间基准 TR_CLK。RTC 的预分频模块包含了一个 20 位的可编程分频器(RTC 预分频器)。如果在 RTC_CR 寄存器中设置了相应的允许位,则在每个 TR_CLK 周期中 RTC 产生一个中断(秒中断)。
6 X% `6 C/ v3 b* K$ a
- N; ]9 p. l& y- ]
7 q0 f. R6 g& x& o7 ^
1 ?& @2 @& p, \9 ]$ u# S
, w0 R5 t# f! J2 ]! k % D/ f1 G; g* b% P6 Y
/ _2 w2 f/ s+ d2 J" f" J
: U) `* ]. {; B第二个模块是一个 32 位的可编程计数器 (RTC_CNT),可被初始化为当前的系统时间,一个 32 位的时钟计数器,按秒钟计算,可以记 录 4294967296 秒,约合 136 年左右,作为一般应用,这已经是足够了的。. w/ X+ a1 ?! s! g4 W' b3 ?# _! }
9 [8 |% S6 L q7 w8 |
& L& g( r Z" y1 H- {) S RTC具体流程:RTCCLK经过RTC_DIV预分频,RTC_PRL设置预分频系数,然后得到TR_CLK时钟信号,我们一般设置其周期为1s,RTC_CNT计数器计数,假如1970设置为时间起点为0s,通过当前时间的秒数计算得到当前的时间。RTC_ALR是设置闹钟时间,RTC_CNT计数到RTC_ALR就会产生计数中断, - RTC_Second为秒中断,用于刷新时间,
- RTC_Overflow是溢出中断。
- RTC Alarm 控制开关机
A" y9 L7 e! y& I' t( s# W) K. u6 ~4 |2 @
" k0 g ]4 X7 F; C! S( E
" X1 ]. M/ U4 V3 J% ^0 C9 R* O
' E, E# f9 y: [* O0 F4 v
3 ~) j8 t3 ?, xRTC时钟选择使用HSE分频时钟或者LSI的时候,在主电源VDD掉电的情况下,这两个时钟来源都会受到影响,因此没法保证RTC正常工作.所以RTC一般都时钟低速外部时钟LSE,频率为实时时钟模块中常用的32.768KHz,因为32768 = 2^15,分频容易实现,所以被广泛应用到RTC模块.(在主电源VDD有效的情况下(待机),RTC还可以配置闹钟事件使STM32退出待机模式).
: o% u3 W2 d! h& r3 N1 n& W3 [4 h
) s2 |0 r! r. N& X0 h$ I RTC复位过程除了RTC_PRL、RTC_ALR、RTC_CNT和RTC_DIV寄存器外,所有的系统寄存器都由系统复位或电源复位进行异步复位。1 u M$ S+ w6 o3 U" Q
RTC_PRL、RTC_ALR、RTC_CNT和RTC_DIV寄存器仅能通过备份域复位信号复位。 系统复位后,禁止访问后备寄存器和RCT,防止对后卫区域(BKP)的意外写操作 " z: i9 i; w' x' r6 R
, g8 H1 A) ^ U6 T 读RTC寄存器RTC内核完全独立于APB1接口,软件通过APB1接口对RTC相关寄存器访问。但是相关寄存器只在RTC APB1时钟进行重新同步的RTC时钟的上升沿被更新。所以软件必须先等待寄存器同步标志位(RTC_CRL的RSF位)被硬件置1才读。 4 E& J/ a; O: i& X
% T( g2 l& G1 A. y, s3 Y% }8 G+ b
配置RTC寄存器必须设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入RTC_PRL、
! R' ?: e9 G6 S+ D/ d1 |RTC_CNT、RTC_ALR寄存器。 另外,对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询3 h2 U0 m1 y0 {) ~" N) Q7 |
RTC_CR寄存器中的RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是’1’
+ H! N% z: S( o6 Y( ?: v7 I; k时,才可以写入RTC寄存器。
: y! s6 J! T' ]
$ {# s& K& z* {7 `3 \& X RTC时钟源RTC是一个独立的时钟源
0 K* m+ ]/ |1 N( T 4 N5 p( g" X; V9 f$ D" q
x1 u. z" p3 R4 X" O
& H1 R: `; n& e: k- L
) j& e0 W$ ^5 Z% s' Y
RTC寄存器- RTC控制寄存器 (RTC_CRH, RTC_CRL)
- RTC预分频装载寄存器 (RTC_PRLH, RTC_PRLL)
- RTC预分频余数寄存器 (RTC_DIVH, RTC_DIVL)
- RTC计数器寄存器 (RTC_CNTH, RTC_CNTL)
- RTC闹钟寄存器 (RTC_ALRH ,RTC_ALRL)
$ B& a/ _6 `1 r$ S) p& [ M
$ T( l! w( `6 p; w. A+ o
n0 ?3 N( T2 T( u3 A: [3 G: _1 \$ B# w
RTC控制寄存器高位——RTC_CRH 寄存器
; n' r1 _* d5 X: o+ [: K3 _/ V2 a: H5 x4 d# m/ ~ O* |; G# H
. B( N* F& n% H2 W
作用:配置3个专门的可屏蔽中断(溢出中断、闹钟中断、秒中断)使能。% ~5 f. _- |8 }) [' ^4 R
注意:系统复位后所有的中断被屏蔽,因此可通过写RTC寄存器来1 N6 X, ]1 s/ \
确保在初始化后没有挂起的中断请求。当外设正在完成前一次写操作时(标志位RTOFF=0),不' I, b9 m% r9 g, R1 N+ J* R
能对RTC_CRH寄存器进行写操作。
) U) N5 `; u- R# r8 o7 ]% ~4 y4 M
1 R9 F/ W' U4 [8 a5 @9 A
RTC控制寄存器低位——RTC_CRL 寄存器
/ \; q) Q" w& a0 D% m
. M O6 a6 T8 t* ~0 G
6 a- [: _+ g/ q1 g
/ i+ S0 p5 O& S9 C! U6 [7 |: I6 ?# p9 u7 Z/ T
一般用到该寄存器的 3,4,5位( f4 }& Q% m4 S# ]
- 第 3 位为寄存器同步标志位,我们在修改控制寄存器 RTC_CRH/CRL 之前,必须先判断该位,是否已经同步了,如果没有则等待同步
- 第 4 位为配置标位,在软件修改 RTC_CNT/RTC_ALR/RTC_PRL 的值的时候,必须先软件置位该位,以允许进入配置模式
- 第 5 位为 RTC 操作位,该位由硬件操作,软件只读。通过该位可以判断上次对 RTC 寄存器的操作是否完成,如果没有,我们必须等待上一次操作结束才能开始下一次,也就是判断RTOFF位是否置位。. a$ }! w M i! l, }
5 d/ z" t+ ~8 M5 z/ _
& w, B5 i# i9 \$ ~6 |* [0 r# j9 a
9 y Z$ V) u) z; W T& q) S R三个位总结如下: ① 修改CRH/CRL寄存器,必须先判断RSF位,确定已经同步。2 Z3 s9 p$ @( Y2 F9 V
② 修改CNT,ALR,PRL的时候,必须先配置CNF位进入配置模式,修改完之后,设置CNF位为0退出配置模式2 ]) R1 m! b6 B/ C- }2 h0 t4 b
③ **同时在对RTC相关寄存器写操作之前,必须判断上一
% R- q& ?2 `) X: u Q) R* z) d
6 r# w' N* H! X" J- S
3 H1 u& G2 Q% E9 c/ A8 K8 [ d( N' E0 x J {4 s) d* m
4 Z2 f+ U- k0 n- G
Q1 U" q7 u) L0 ?& }( MRTC 预分频装载寄存器——(RTC_PRLH/RTC_PRLL) 寄存器$ @/ J- V# G2 | x' }" L/ m2 j1 i
作用:配置 RTC 时钟的分频数, 比如我们使用外部 32.768K 的晶振作为时钟的输入频率,那么我们要设置这两个寄存器的值为 7FFFh(32767),就可获得周期为1秒钟的信号。6 E' T$ V0 I& y4 j8 w; x' }
7 h' `% g) T B/ [$ N$ H' e5 w' U" v4 i! `1 ~2 X8 w% L4 s+ h
7 Z, \+ x; g" w: k. f# j& Q, _7 f: m3 {: q; _/ U# J2 Q. c; d9 d
RTC预分频器余数寄存器(RTC_DIVH、RTC_DIVL)0 q" }3 {5 `4 s; Y _7 M$ r
作用: 和他的名字一样,获得余数,也就是获取更精确的计时,比如:0.1s ,0.01 s等 寄存器是只读寄存器,其值在RTC_PRL或RTC_CNT寄存器中的值发生改变后,由硬件重新装载。 ! y4 T! X" ]" N/ f: _4 E0 }
7 @. s3 ?- ^" e' L" \6 f. i; f ?; z
' a! T8 T- I. u+ c! u 4 [$ ?. _9 E( c0 S2 E0 o [6 r
0 b! H' Y3 q6 z* ]5 o! u- k
3 U# S, L* n- q% g' [
3 x" w" g7 n9 bRTC 计数器寄存器——RTC_CNTX 寄存器
3 X: @- l& G. V4 L/ y作用:存放计数器内的计数值。也就是用来记录时钟时间 该寄存器由 2 个 16 位的寄存器组成 RTC_CNTH 和 RTC_CNTL,总共 32 位,当进行读操作时,直接返回计数器内的计数值(系统时间)) D8 f4 ]3 F/ J
! n: Q: X! q% \, m0 D
$ V& m& F+ [; }8 ~" L& a. h/ L( P+ U
. b/ G# Z% }3 J5 l, u1 D$ r5 c% m- j, V2 E2 n' O
- r; z9 c; S; a3 Q1 W
+ @% J% v$ @; K% yRTC 计数器寄存器——RTC 闹钟寄存器(RTC_ALRH、RTC_ALRL)
7 k1 g& j8 U- V7 M" l r7 H \+ t0 v% B) b
作用: RTC时钟中断控制寄存器 该寄存器也是由 2 个 16 位的寄存器组成 RTC_ALRH 和 RTC_ALRL,也就是32位,当可编程计数器的值与RTC_ALR中的32位值相等时,即触发一个闹钟事件,并且产生RTC闹钟中断。
( `; v+ t' u& {, i) @
( a! k% b6 R; _2 \4 p. {! c3 Y" y$ d6 \$ z1 W
# c9 a3 g' a r4 E- t& g0 d# E: W5 E3 t5 ?' g( ?0 G0 x
2 k ~6 f5 X Z
+ Y+ I8 w5 A. T5 X* fBKP备份寄存器
/ U; D) j- o( k备份寄存器是42个16位的寄存器。可用来存储84个字节数据。3 {! z6 ?" l# ~5 w5 X
它们处在备份区域,当VDD电源切断,仍然由VBAT维持供电。 当系统在待机模式下被唤醒,或者系统复位或者电源复位,它们也不会复位。
5 }1 F- n v2 B6 w I执行以下操作将使能对后备寄存器和RTC访问: - 设置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能电源和后备时钟。
- 设置寄存器PWR_CR的DBP位,使能对RTC和后备寄存器的访问$ L0 [: v- w. ]
) \; x2 R8 J6 }+ d _, @! @7 [
一般用 BKP 来存储 RTC 的校验值或者记录一些重要的数据, 0 u/ p5 `& m1 Y+ t' \+ {7 r+ `5 N
$ ?' q$ C9 j7 u: K& v
9 Z! b4 T: Y$ V, v/ l+ l% q9 x
配置RTC寄存器:1.查询RTOFF位,知道RTOFF的值为1. 2.置CNF值为1,进入配置模式。 3.对一个或者多个RTC寄存器进行写操作。 4.清除CNF标志位,退出配置模式。 5.查询RTOFF,直到RTOFF位变1,已确认写操作已经完成。 仅当CNF标志位被清除时,写操作才能进行,这个操作至少需要3个RTCCLK周期。 9 ~6 F5 t& |4 A8 j
: _$ f' m2 ?3 t$ N# q9 O- I
RTC相关库函数- RTC时钟源和时钟操作函数:
' E- Q: S2 ~* `, U3 s a1 D8 l* I8 ?8 Y/ y; H" ]
- void RCC_RTCCLKConfig(uint32_t CLKSource);//时钟源选择$ O) P5 s- U$ u7 G0 U
- void RCC_RTCCLKCmd(FunctionalState NewState)//时钟使能
复制代码 * L; g! M4 N5 k9 t: D* B
% u/ r$ @0 T) M6 k6 c2 b: [- RTC配置函数(预分频,计数值):
; S( x5 L; x6 z+ u
- void RTC_SetPrescaler(uint32_t PrescalerValue);//预分频配置:PRLH/PRLL n3 p& {+ q6 m8 B+ @0 E% U4 U
- void RTC_SetCounter(uint32_t CounterValue);//设置计数器值:CNTH/CNTL
5 _: q4 w5 A6 m/ P+ j5 W9 ^2 f7 p - void RTC_SetAlarm(uint32_t AlarmValue);//闹钟设置:ALRH/ALRL
复制代码
0 f) F2 U* Z; N$ y U8 B
* }' f! t7 G. s- RTC中断设置函数:
) x) {$ o4 A5 _) e* ^2 j: Q0 w$ D" b, v$ y' }, T: m# l B! W
2 w0 j& D3 t- S: j
- void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState);//CRH
复制代码
" N3 [. l: Y% i: O6 f( y- RTC配置函数:
2 q. V/ t* R- G/ X
5 R! h) l7 m$ @( m9 e9 [" }$ L
- void RTC_EnterConfigMode(void);//允许RTC配置 :CRL位 CNF7 y) X! x7 Q; @9 X
- void RTC_ExitConfigMode(void);//退出配置模式:CRL位 CNF
复制代码
+ K6 a7 }" e% `# @# l- RTC同步函数:
F1 n) V9 w1 p3 \% `
- void RTC_WaitForLastTask(void);//等待上次操作完成:CRL位RTOFF
7 _, M6 S) e% Z4 G( T1 R( U - void RTC_WaitForSynchro(void);//等待时钟同步:CRL位RSF
复制代码
& ^5 V: t$ g" Q U4 o+ J
- { i; ~0 J4 M1 A2 N; N6 r0 x; \- RTC相关状态位获取清除函数:
8 S2 S/ j: ?* T, n ^8 J% e1 f
- FlagStatus RTC_GetFlagStatus(uint16_t RTC_FLAG);# i5 v! p c) w( E' _ D
- void RTC_ClearFlag(uint16_t RTC_FLAG);
( Z5 ]9 v- f( c, H H - ITStatus RTC_GetITStatus(uint16_t RTC_IT);
- a# X+ |# K# e - void RTC_ClearITPendingBit(uint16_t RTC_IT);
复制代码
. N) S0 {3 ?1 T$ A) ~
5 i. |& B( I1 x) g9 q- 其他相关函数(BKP等)
" `. a) ^3 I7 W5 J' p" z
- PWR_BackupAccessCmd();//BKP后备区域访问使能7 O$ d0 t/ F5 R# a: m* J0 B6 d
- RCC_APB1PeriphClockCmd();//使能PWR和BKP时钟
" o; D" A+ m. |5 R8 P2 | - RCC_LSEConfig();//开启LSE,RTC选择LSE作为时钟源 4 S$ o, x/ B6 Z5 ~/ {
- PWR_BackupAccessCmd();//BKP后备区域访问使能
/ X7 F' N& V+ z5 h - uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR);//读BKP寄存器. r% O$ E7 H1 w; L
- void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data);//写BKP
- _+ a, ^3 ?. L& A) J
复制代码
* T `& Y D; Y7 B; j6 D' w$ m
* g; x* p2 [0 ^ S$ \% n4 G5 [3 P% S配置RTC步骤; h: `- D9 {; T9 i
- ①使能PWR和BKP时钟:
1 F! A! v) w3 r. J) \! t N6 ^
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
复制代码
5 h* X& c- u6 Y1 @, U/ ~# ?
$ B( o* u0 g+ g- ② 使能后备寄存器访问:
' `3 ?! r6 r/ i
. m% s0 ~" u5 L6 H, X5 u Y; s
- PWR_BackupAccessCmd(ENABLE); //使能 RTC 和后备寄存器访问
复制代码
3 F. `0 @2 {8 M/ C$ T ?0 w- ③复位备份区域,开启外部低速振荡器。7 J( m9 k: |+ r5 |0 {/ x
' z$ w4 n3 M( |! P" { & D# D9 M: O0 @ |
+ ?+ A8 j( t5 J* j
7 E/ t D2 o4 ]9 k& R5 Q4 T- ④ 配置RTC时钟源,使能RTC时钟:
( N& a5 `: k' ^# q3 |) g7 P2 U( C7 Z7 B4 w6 L
- RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //选择 LSE 作为 RTC 时钟(RCC_RTCCLKSource_LSI 和 RCC_RTCCLKSource_HSE_Div128)
8 W1 a0 R+ X) A4 c0 U8 G - RCC_RTCCLKCmd(ENABLE); //使能 RTC 时钟
复制代码
' r6 t) r3 J( G- ⑤ 设置RTC预分频系数:RTC_SetPrescaler();* P' a- ]3 F% I7 s% g
- RTC_EnterConfigMode();/// 允许配置 1 P' i2 c( Q; S. A
- RTC_SetPrescaler(32767); //设置RTC预分频的值4 T- I+ O' D# N& u# G
- RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
复制代码 # I* B! p; k. K6 ~* ]5 q
9 {4 p( {+ L9 J( k$ E3 e5 L
- ⑥ 设置时间:RTC_SetCounter();
! r$ ^ c( W* n# x6 A+ t3 ?0 s' x+ [& R7 }/ K+ Y- @" L
- RTC_EnterConfigMode();/// 允许配置7 U* q$ }$ ]/ B$ o( j0 c
- void RTC_SetCounter(uint32_t CounterValue);4 d+ O+ ^6 d! I' ^( W
- RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
复制代码 3 f. n. r( D) |( D
6 L* u) T. ~& B& Y( Y5 j
$ f: |% b8 w' G' e3 Q
- ⑦开启相关中断(可选):$ [! \3 z( F* w+ Z+ p
$ a3 x& e8 }6 q- _
- void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState);//RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能 RTC 秒中断
复制代码
6 z( F5 t) r, o* t/ {- ⑧编写中断服务函数:
; Q j) f( I- f6 f, U( g) T+ V' x9 H% w
) P# j8 X+ E& Z" D7 P6 U
2 ?" s: B; V" x- ⑨部分操作要等待写操作完成和同步。! D. f+ ~$ @3 I! I5 d
8 g Z. A" U% i1 T) d
- RTC_WaitForLastTask();//等待最近一次对RTC寄存器的写操作完成4 F0 Y4 Z- i# b- Y: [
- RTC_WaitForSynchro(); //等待RTC寄存器同步
复制代码 0 J; c$ M" u, Q, V' D' p8 G
0 y0 R* W& E0 L/ R) k, k# v) }' t0 X8 T; o f% }7 P7 n% s
|