你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】HAL库 STM32CubeMX教程十三---RTC时钟

[复制链接]
STMCU小助手 发布时间:2022-3-23 15:00
前言:
4 F# C/ _( t& n% ~# D% w/ Q% c9 ~& B7 c+ }
本系列教程将 对应外设原理,HAL库与STM32CubeMX结合在一起讲解,使您可以更快速的学会各个模块的使用! |& k2 P" `6 a  W: e. E% s2 c

' Q, D+ u5 \; @$ a3 Q$ d8 m+ d% t所用工具:
- _2 V' j( Q" k; g+ G0 e! S  k6 \6 }6 e) c! m( O; U, H+ h1 a3 p
1、芯片: STM32F407ZET6/ STM32F103ZET68 Y! @2 j9 ~+ f+ ^) B

4 H/ z# [7 ?3 E  U. _0 M2、STM32CubeMx软件2 K$ H& N, O' _6 m0 U6 M) z0 x' U* c
6 S0 \& N0 ]. I* E! m' w
3、IDE: MDK-Keil软件( N9 c3 _( U* W. D$ _7 @
1 H5 u' n) c; t; z% O
4、STM32F1xx/STM32F4xxHAL库
2 w$ x' ]6 f! r  J! b2 R5 O% P
5 Q7 f6 g" F8 k+ S6 ]5 j知识概括:
$ ~1 x- L: v8 p7 |7 G* [9 ^* C4 [9 [; f, W7 c8 a8 A
通过本篇博客您将学到:3 I; D+ F2 G3 v! U; s
% t0 n  J, |6 T- E5 ^+ P
RTC时钟原理% s% r  \7 x9 o  o. j: W8 b" ?( l
2 y& V/ c7 D* E
STM32CubeMX创建RTC例程6 L) R& h: t6 @6 Z% K2 Z/ d

# q  G& I: N) F* @HAL库定时器RTC函数库
1 C8 B8 p/ O+ s: A# v% c/ ~
/ n+ k* b. N6 w" A! w: vPS: 这里的RTC讲解,我们只将原理,不讲寄存器
8 U) ?0 k' ~: K6 p8 j7 Q6 r) m7 S" K6 ~( I7 I- \; U
什么是RTC
/ F' r/ v0 J7 t9 ]* V; ^8 oRTC (Real Time Clock):实时时钟& l+ b( b6 e0 q

' O) z: J7 [7 v( w$ ~5 J' p3 WRTC是个独立的定时器。
RTC模块拥有一个连续计数的计数器,在相应的软件配置下,可以提供时钟日历的功能。修改计数器的值可以重新设置当前时间和日期 RTC还包含用于管理低功耗模式的自动唤醒单元。
0 D- }. q7 J: G# t' ]6 U+ F# E. ]: ?" e8 Y
{Y5MNB6$TJM~2G@SN(7WHRY.png
0 c4 c: ?* @+ o+ _2 Z2 l  v8 f2 |, `0 R
在断电情况下 RTC仍可以独立运行 只要芯片的备用电源一直供电,RTC上的时间会一直走。

; e* r3 w- G) a; R; b/ Z- W1 g# c! M
; [! C; ?- ?: j1 K+ r, j  d  [! xRTC实质是一个掉电后还继续运行的定时器,从定时器的角度来看,相对于通用定时器TIM外设,它的功能十分简单,只有计时功能(也可以触发中断)。但其高级指出也就在于掉电之后还可以正常运行。
7 H/ g4 v" y+ U) e) V* V* Y8 _, N8 f1 h/ A2 I- H5 ~; N2 V
两个 32 位寄存器包含二进码十进数格式 (BCD) 的秒、分钟、小时( 12 或 24 小时制)、星期几、日期、月份和年份。此外,还可提供二进制格式的亚秒值。系统可以自动将月份的天数补偿为 28、29(闰年)、30 和 31 天。
. o1 ^; w1 m) _6 {
/ a4 u! h4 f9 [, r: G. K' t上电复位后,所有RTC寄存器都会受到保护,以防止可能的非正常写访问。+ p7 Z/ x5 U1 A; t
* r0 U/ r0 V& P9 D
无论器件状态如何(运行模式、低功耗模式或处于复位状态),只要电源电压保持在工作范围内,RTC使不会停止工作。
. s3 r1 u1 w' ?# a2 ?
+ |& u! T4 Y& pRCT特征:1 `. h7 o, b) R  C
● 可编程的预分频系数:分频系数高为220。
9 g1 R8 D6 m& K● 32位的可编程计数器,可用于较长时间段的测量。
" p+ N0 B: b" q/ u# n& i● 2个分离的时钟:用于APB1接口的PCLK1和RTC时钟(RTC时钟的频率必须小于PCLK1时钟 频率的四分之一以上)。
0 h0 G$ P- ^. S8 L6 S● 可以选择以下三种RTC的时钟源:" O# U2 y) ?. G# O: v. b
     ● HSE时钟除以128;
$ ?. \4 x! p, i' Q4 T     ● LSE振荡器时钟;
# _3 k9 P9 j* n$ c     ● LSI振荡器时钟6 a. p9 v/ K  }  j3 {$ l0 g

8 S" U4 R: @( {; Y  i3 ?1 `1 m● 2个独立的复位类型:
$ k) v1 X9 J- q( J! e+ o* O     ● APB1接口由系统复位;
1 F9 k, ~+ b) _6 X* e8 ^9 E     ● RTC核心(预分频器、闹钟、计数器和分频器)只能由后备域复位
+ `4 u* N" T% n+ B; b$ Y4 W
7 I) \% S, C5 d" J6 o● 3个专门的可屏蔽中断:
. A% R7 F0 b& {  M( n- O/ U     ● 1.闹钟中断,用来产生一个软件可编程的闹钟中断。
& P9 N+ @# A' A     ● 2.秒中断,用来产生一个可编程的周期性中断信号(长可达1秒)。
$ i) [- C+ v$ R8 a# M; P- G# \     ● 3.溢出中断,指示内部可编程计数器溢出并回转为0的状态。. x0 C" C; m, N" E7 w
- b& n) r+ b4 t& v
RTC时钟源:
# c( U) C8 q, z% W; q; S7 _8 S! r三种不同的时钟源可被用来驱动系统时钟(SYSCLK):
+ R0 j. `" M! }1 c. F( D0 m  l6 a, b$ `: O
● HSI振荡器时钟' N9 Q% j7 d$ L( N: E" ~2 ~" I4 F
● HSE振荡器时钟
( h+ W) U0 b+ `3 E● PLL时钟
4 }& e0 D' C" h3 P: _# R) _. I7 z6 i' ?
: Q, h& f1 p7 W+ s这些设备有以下2种二级时钟源:" q9 u1 P/ ?! X$ w4 B- Y% [3 W7 B
● 40kHz低速内部RC,可以用于驱动独立看门狗和通过程序选择驱动RTC。 RTC用于从停机/待机模式下自动唤醒系统。
' F3 O4 X% f( b) [● 32.768kHz低速外部晶体也可用来通过程序选择驱动RTC(RTCCLK)。: |- u9 W/ F8 `! K. L( n& `

$ a2 i* k+ F  Z, {  X7 p) z4 vRTC原理框图
1 b2 a& i; c& |' \" \! Q
, P: p3 _3 e" m# ` MG$U7{O}1)FMJTPCOVCF8`V.png - m2 ~( @! J$ _1 J# q- H

$ \. e, z% C0 [' w( F2 oRTC时钟的框图还是比较简单的,这里我们把他分成 两个部分:% ^, C$ E5 ?8 H- H2 S. f. {/ @( q

/ c* s# l* M$ GAPB1 接口:用来和 APB1 总线相连。 此单元还包含一组 16 位寄存器,可通过 APB1 总线对其进行读写操作。APB1 接口由 APB1 总 线时钟驱动,用来与 APB1 总线连接。
) p" C' Z+ R% Q; l! Q
& A/ ~5 c) b2 l通过APB1接口可以访问RTC的相关寄存器(预分频值,计数器值,闹钟值)。, i" \6 I4 L6 z

/ B  m: y1 V$ ~8 V  A+ a  bRTC 核心接口:由一组可编程计数器组成,分成 两个主要模块 。) [: L( d- I; a0 H4 `! X
0 Q4 G, {# @# A
9VM)0T5~(6ULAZH063`Y41A.png " t( g: M" n" U7 V

% p2 F) M" E7 N; C第一个模块是 RTC 的 预分频模块,它可编程产生 1 秒的 RTC 时间基准 TR_CLK。RTC 的预分频模块包含了一个 20 位的可编程分频器(RTC 预分频器)。如果在 RTC_CR 寄存器中设置了相应的允许位,则在每个 TR_CLK 周期中 RTC 产生一个中断(秒中断)。
! ?: e/ L& w9 s8 A# N
1 X" J# r% |3 O, J, n K2U[6UER60[[1_Q2P9PMO9G.png
, k- A0 D: l; R- U/ [& R2 t1 F0 c3 i( a. O
第二个模块是一个 32 位的可编程计数器 (RTC_CNT),可被初始化为当前的系统时间,一个 32 位的时钟计数器,按秒钟计算,可以记 录 4294967296 秒,约合 136 年左右,作为一般应用,这已经是足够了的。
. [0 p  o1 B( L0 f3 ]
" ~- M0 }' ]: @; T6 t) o9 ^! NRTC具体流程:
& W+ y5 R7 ?" N/ g6 {! s) C3 t& oRTCCLK经过RTC_DIV预分频,RTC_PRL设置预分频系数,然后得到TR_CLK时钟信号,我们一般设置其周期为1s,RTC_CNT计数器计数,假如1970设置为时间起点为0s,通过当前时间的秒数计算得到当前的时间。RTC_ALR是设置闹钟时间,RTC_CNT计数到RTC_ALR就会产生计数中断,
% g- F* j1 ~, G5 |. Y, H2 xRTC_Second为秒中断,用于刷新时间,' J# |$ a2 O- W3 C8 E
RTC_Overflow是溢出中断。, F2 T- q$ A! e2 g$ u# s7 h, D3 {  I
RTC Alarm 控制开关机/ Z. G# C: n  X4 t& \
RTC时钟选择; {: y  r4 ?4 k9 h$ h
使用HSE分频时钟或者LSI的时候,在主电源VDD掉电的情况下,这两个时钟来源都会受到影响,因此没法保证RTC正常工作.所以RTC一般都时钟低速外部时钟LSE,频率为实时时钟模块中常用的32.768KHz,因为32768 = 2^15,分频容易实现,所以被广泛应用到RTC模块.(在主电源VDD有效的情况下(待机),RTC还可以配置闹钟事件使STM32退出待机模式).4 w, Q/ ?6 ?: ~8 A( y+ j
2 A# e% Y3 w4 S* R( f+ b0 D& L
RTC复位过程2 G4 G% g% ]" S8 \8 u+ d# M6 l+ a
除了RTC_PRL、RTC_ALR、RTC_CNT和RTC_DIV寄存器外,所有的系统寄存器都由系统复位或电源复位进行异步复位。2 g  ?* `. n7 u. R/ Z
RTC_PRL、RTC_ALR、RTC_CNT和RTC_DIV寄存器仅能通过备份域复位信号复位。" |+ H, q. g- b* q: ~; Y

( P. ?; |$ h% \, }6 g4 S系统复位后,禁止访问后备寄存器和RCT,防止对后卫区域(BKP)的意外写操作
0 Y! U" s) y+ ^( }2 _, {. @8 s
. F$ b" J- i, ~% n( f5 a7 J  oRTC中断
6 _9 [! N* x  ~* l" h+ k2 c8 _秒中断:
  u! L  t1 ~! ^, }2 B: f8 \) R# Q这里时钟自带一个秒中断,每当计数加一的时候就会触发一次秒中断,。注意,这里所说的秒中断并非一定是一秒的时间,它是由RTC时钟源和分频值决定的“秒”的时间,当然也是可以做到1秒钟中断一次。我们通过往秒中断里写更新时间的函数来达到时间同步的效果1 o! d( o! V% \* F! }, C
- A/ X) u, ~1 Q7 v
闹钟中断:8 {0 N  u) t0 Q4 l/ T' i
闹钟中断就是设置一个预设定的值,计数每自加多少次触发一次闹钟中断
8 j5 d7 Y. ?. {& F) O0 k; Q
# F+ m' H9 |1 TCubeMX配置RTC& }; Q. H9 a! Q
工程创建
. h; T$ X& E+ }9 Q4 d0 y1设置RCC

0 I# u. O/ i. M2 d' [8 ?( @! o5 n2 M+ H, E2 K
9523)HOJID95N7EVK0TPE~7.png
0 [3 I. p+ e% l4 l, ]& u8 w+ x3 e. i  J- p8 h
设置高速外部时钟HSE 选择外部时钟源+ R  h5 \0 g. l* O, d
使能外部晶振LSE& ]. o2 a# s6 {) V) X0 _) T3 P, `# ?
. m  N+ [6 X4 P7 E( [0 }+ E) P
RTC设备因为其独特的运行方式(即掉电依旧运行)使用HSE分频时钟或者LSI的时候,在主电源VDD掉电的情况下,这两个时钟来源都会受到影响,资源消耗太大,小小的纽扣电池根本吃不消。没法保证RTC正常工作.所以RTC一般都时钟低速外部时钟LSE
* |+ r: N# k1 ?2 Q
. J7 O7 W3 o3 }  |5 u2.配置RTC% T9 B* g/ Z' E! m$ S
6 ?; u0 F, U; I2 O& C2 e
]3KRAQH0[GZN5C6VRT)7G2D.png
! ~& @4 Q: ?7 k6 L% w4 L2 {$ [. P$ }, [4 W
Activate Clock Source 激活时钟源
+ V1 E/ `$ t/ R5 ?' ~' Y+ XActivate calendar激活日历+ A1 P- {2 a0 }' o3 M; i
" k4 `$ L. x; M4 o0 A) O
这两个都要点,作用也很明显,先是使能时钟源,再使能RTC日历
  Y" P/ }. q  q3 D' |3 ORTC_OUT: Not RTC_OUT
# F6 S& [$ X4 M/ q, `2 KTamper: ×
- `" |  x# z% R+ n$ `+ g" K3 Z* A( H+ P6 ~0 p1 d
第一个是是否使能 tamper(PC13)引脚上输出校正的秒脉冲时钟,' ^6 C' G; K4 K# ~1 d5 R

2 T; R. [  `, i& r/ c+ n& b第二个: RTC入侵检测校验功能
( V1 [/ {. s4 C1 o: d. P2 h6 p
4 B# N; ?$ \4 `: R3 X: M1 nRTC校验功能,使能侵入检测功能。RTC时钟经64分频输出到侵入检测引脚TAMPER上
' D: X7 ]- |  s1 V当 TAMPER引脚上的信号从 0变成1或者从 1变成 0(取决于备份控制寄存器BKP_CR的 TPAL位),会产生一个侵入检测事件。侵入检测事件将所有数据备份寄存器内容清除。+ j; a8 f/ M8 w* Y  x4 r0 X
* @5 |7 X' R/ T4 L! I' w4 w
1.也就是第一个是使能tamper(PC13)引脚作为时钟脉冲输出
. X! x# F! i$ i0 ~7 `: h2.第二个是使能tamper(PC13)引脚作为入侵检测功能
) c& ^' [' t% r! E/ t; T0 j! D8 C3 T, [& s5 v2 y) b

1 L# k& n' V7 J下面是两个RTC的中断:+ T7 \, C9 x7 \# T- I7 @4 L2 J
RTC全局中断RTC_IRQHandler()# s( e( d/ c. H8 c! Z3 I) A
闹钟中断函数RTCAlarm_IRQHandler()5 b1 y, R6 S, \5 Y3 i+ `' H0 w. z

# z2 U& d* C6 d. E1 f3 M 09507@T~XZT4O(JJA4OQ~UD.png . G1 q! K3 @: D$ p/ c

4 [( L# q; w+ O9 Q0 X. C2 b- m此处设置时间为2020/04/25 13:30:00
+ M9 Q8 j* M# A# g* JData Format: 日期格式# Z) H8 E4 U/ [  ^
) R4 }5 L/ F" y2 ]  f; G
Binary data format 十六进制
4 y+ A6 \( q# K' ]3 ]6 J3 SBCD data format BCD码进制
; V8 [2 X3 i7 C: Y9 E; k1 Q  o" M- q
+ X/ [* @  N! r) ]6 j使用自动配置,初始化时间必须使用BCD data format,原因是库函数存在bug,如果使用Binary data format,月份配置会出错,比如说11月,配置时会赋值为RTC_MONTH_NOVEMBER,而此宏定义值为0x11,也就是说其十进制值为17
+ F5 _# c/ @5 e. a( m1 V# \, h4 ~' e0 C# s- B
Hours: 小时, u: L- ~; N% ^, p' R% X" }
" |- H+ O# t( M6 M) B/ Y
Minutes: 分钟
) D: k7 X/ e6 `' `/ A
! G" ]6 F# k# j4 p+ y' vSeconds: 秒" S' m8 G9 Z. n4 k. ^. R

! `6 g" I  |  T6 i" dWeek Day: 星期
8 {# z; \8 R0 G
2 u( U- [8 X* |  H7 z5 GMonth 月份* @! w7 }5 l  {7 H* i" Z8 @
5 Q2 N' F3 z# s; A0 ]% l
Date: 日期
" C4 X2 Z9 K( z6 a9 b8 V) S. e
+ Y/ e5 z+ B/ \' p' ?7 LYear: 年份! S. \4 e. ^9 X* p2 k- _/ g' o
3 n) J6 k7 G+ ]& ?
3 使能串口
9 n: m3 q3 A: z2 ?  {& n! ]6 `$ Y& f$ }
$QZ4])UE4XDW){Z`)~LVH.png
( x8 f0 [2 D$ d& @! k2 O0 W# A7 C2 Z
使能一下串口,因为发送日期到上位机) R/ m' ~2 L& Y
9 O5 @% _" {4 f, Q3 ]0 Y# g; x
4时钟源设置
- {3 o3 c/ q' T5 l' b- ~, G) `- ^1 O, y+ d; q% A9 W# u
U_S]%_(4Q}GR[39F((ZVA6O.png
* @7 x# x, ~. _" P: N$ v$ y6 a/ V0 S- }  R+ s7 ~
我的是 外部晶振为8MHz
3 z: r4 S% n1 v7 J/ R2 W. `1选择外部时钟HSE 8MHz
+ C7 {9 C. [% S2 P0 H# p& g2PLL锁相环倍频9倍
; B3 c" s# H6 \8 M3 q) ]$ N3系统时钟来源选择为PLL
8 [; y8 n1 s! A, b+ A& O% t4设置APB1分频器为 /2
- n, c& `- j" O3 C5 A5 使能CSS监视时钟4 f6 j! [* \1 ~! i
6 设置RTC时钟为LSE. j; e* H  S8 n( ~' D% Z1 V

  u4 R2 ?7 ~( z  E$ j5项目文件设置% Z" v- C/ ]; s

' y6 ~4 a  d5 z/ q FSG]%CL3~6TP)R9AA[84.png 9 y3 g6 Y( A2 s! L9 v7 }% Y

" T  s$ y: s: ~2 O! s* |1 设置项目名称
0 B6 a: b0 F  X$ t5 E2 设置存储路径
- b" R# q* \' m$ Q, w5 ^3 a" [( M3 选择所用IDE
1 |/ T& w+ Z" ~
% W6 S& c: [+ h @3BLF`]NM7`0GP{674FL2%L.png * I9 u, R- k1 O: K, S; ?* T3 m, B

2 E$ D- o: \  h- _6创建工程文件5 Z6 ]9 \* x0 w6 H
! Q/ {) {6 K- v7 D6 N
然后点击GENERATE CODE 创建工程
' F* z$ R+ ?. s; J. Y, d4 Y( M/ @7 V- v  g9 c
配置下载工具' H" j7 d+ g3 V7 j
新建的工程所有配置都是默认的 我们需要自行选择下载模式,勾选上下载后复位运行
' f3 N; F7 ]# l$ f# O" K
5 Z& i* }. @. f0 l QYRVOZJ9OXE0YFH`WZK1(EF.png & f5 t. a) ]5 r1 f; G

, x+ M# \4 [0 l% R# L- c$ Y% aRTC_HAL库函数
; v) ~" B6 S  ~! u# x
  1. /*设置系统时间*/2 h0 J9 u# ], B( ^! k, j: c
  2. HAL_StatusTypeDef HAL_RTC_SetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format) * _" r9 q" [0 B+ p  P0 G  ^
  3. /*读取系统时间*/! k4 \# F/ ]2 ]9 a: z. G. c" P' q
  4. HAL_StatusTypeDef HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format)( Y. Q" k4 T0 _3 w# f% ]3 g0 K, U
  5. /*设置系统日期*/- S" b9 d. B1 s1 b  X8 f$ P3 d
  6. HAL_StatusTypeDef HAL_RTC_SetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format)" u* R! O) H0 `3 [$ N
  7. /*读取系统日期*/
    0 j  \- K8 p- R
  8. HAL_StatusTypeDef HAL_RTC_GetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format)
    3 z- h! F% Z& b! _% V" q
  9. /*启动报警功能*// V& z- E5 X5 U8 p5 U  o4 z, r
  10. HAL_StatusTypeDef HAL_RTC_SetAlarm(RTC_HandleTypeDef *hrtc, RTC_AlarmTypeDef *sAlarm, uint32_t Format)5 I" c. E5 b" n2 i$ }: i$ A
  11. /*设置报警中断*/
      j: ]2 K6 ^& O
  12. HAL_StatusTypeDef HAL_RTC_SetAlarm_IT(RTC_HandleTypeDef *hrtc, RTC_AlarmTypeDef *sAlarm, uint32_t Format)
    ( f  `% }. y* T" G/ `
  13. /*报警时间回调函数*/
    % p* W  {2 Q+ ?! u3 ]
  14. __weak void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
    # K/ J5 g4 `" M
  15. /*写入后备储存器*/
    " P* A4 ~* ]$ ?$ n$ _1 U
  16. void HAL_RTCEx_BKUPWrite(RTC_HandleTypeDef *hrtc, uint32_t BackupRegister, uint32_t Data)/ o% ^& d2 A/ B  m0 G$ l2 {. f
  17. /*读取后备储存器*/
    $ K' |& T9 x* o5 R) V4 d9 E5 b
  18. uint32_t HAL_RTCEx_BKUPRead(RTC_HandleTypeDef *hrtc, uint32_t BackupRegister  0 Z& D" t; P4 u1 w0 r
复制代码

. m4 a5 N! c9 R2 t) l) k我们可以看到前面的四个函数,分别是4 q# Z  v0 e8 G  x( `
设置系统时间:HAL_RTC_SetTime();3 E* y) W+ ^; E5 [) r) ?3 }
读取系统时间: HAL_RTC_GetTime();/ S$ m4 O1 G# Q5 a  Z: N5 ]
设置系统日期: HAL_RTC_SetDate();
2 n! {- I) I* E! ^8 C) [1 a读取系统日期: HAL_RTC_GetDate();  D+ J* K8 j( T' Y9 a

& {5 ]4 ?% n6 D6 Z  }# k+ Z3 R+ L因为系统的时间和日期开始的时候已经设置过了,所以我们这里只用两个读取函数
' E! y4 N  `- K
  {+ R, d( X8 O读取系统时间函数
) n  W1 ?7 i  T. l- j# r5 b9 l
  1. /*读取系统时间*/
    0 q; S' F, S9 l: w
  2. HAL_StatusTypeDef HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format)
复制代码
, O' U& r; Z9 W3 T
功能: 获取RTC时钟的时间8 o/ `" z* @; V$ f! c$ G- z
/ z% h( `1 O, ?( l# t
参数:
9 o2 L, \2 S/ \0 J$ ~# {8 R! M
. x$ E( n# k4 U% k$ C" J% s: Y*hrtc RTC结构体参数 例:&hi2c2+ B$ l8 G6 |  d! ?. z8 [9 S: F

8 I2 u2 l8 V5 R* x! u7 J, mRTC_TimeTypeDef *sTime: 获取RTC时间的结构体,! Z) f! r$ A" J! {! y+ `. k
8 W8 U. H' X. P" }" x! x  O* d
Format: 获取时间的格式6 M: R' ~5 Y/ C9 M' q8 `$ P
RTC_FORMAT_BIN 使用16进制
) t1 P$ j( Y2 a- x) K0 QRTC_FORMAT_BCD 使用BCD进制  B9 \8 f, U" l% r  m

7 Q; ?$ d4 C; I0 x读取系统日期函数
- f7 Y5 V, \* @9 I1 l  m
$ D$ g5 \# U5 h6 r/ w9 U( A2 Y' A
  1. /*读取系统日期*/
    . n  O( g  b+ i( ~3 J+ G0 k
  2. HAL_StatusTypeDef HAL_RTC_GetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format)
复制代码
% V( o7 {/ m: s0 U+ m
功能: 获取RTC时钟的日期
% N- E% D* Q( k) P3 A6 n0 }$ T; Z, _  `7 |) p
参数:' `2 m0 C4 U8 B
* J) K% M( w, z' {7 t+ `
*hrtc RTC结构体参数 例:&hi2c2  w* e, f% O; S5 ~$ c( I2 [

" G2 ]: f, m$ c8 H& }: K8 R6 aRTC_DateTypeDef *sTime: 获取RTC日期的结构体,
* P( _' T7 \  S4 V$ i0 ~& j0 |! |; i4 w, X
Format: 获取日期的格式* S7 a1 W7 F( ]- v
RTC_FORMAT_BIN 使用16进制
9 g/ T9 r: |* x& `. YRTC_FORMAT_BCD 使用BCD进制4 ?3 c3 {6 h$ C) L0 I- R" h- W
  ?5 |& v# D5 L( ?: O
在stm32f1xx_hal_rtc.h头文件中,可以找到RTC_TimeTypeDef,RTC_DateTypeDef这两个结构体的成员变量。* s0 m6 G6 Z) I2 r( {

  |3 ]8 e8 c( Q* O3 R3 g
  1. /**
    4 B7 C  D  O' l2 j
  2.   * @brief  RTC Time structure definition
    9 n4 P3 E/ ^, R6 Z0 D4 y0 W% O, |
  3.   */
    . f7 R! \: t$ o, ?* r
  4. typedef struct6 O4 U6 l: i# u) m: x! h
  5. {1 ~/ e: {, Y. i( Y# L
  6.   uint8_t Hours;            /*!< Specifies the RTC Time Hour." J  v2 K4 H, A6 {/ ?; s7 D
  7.                                  This parameter must be a number between Min_Data = 0 and Max_Data = 23 */6 B/ \0 i9 `4 p4 x8 u" L! z1 A

  8. 1 h  e# p' u  y2 [  P+ W
  9.   uint8_t Minutes;          /*!< Specifies the RTC Time Minutes.
    2 |4 e; P! ~! l5 i: M! V1 P
  10.                                  This parameter must be a number between Min_Data = 0 and Max_Data = 59 */
    1 o6 O, ?  q& x; P1 _% _' n) f

  11. 7 Z; }7 K( p( u* `% C: L* R
  12.   uint8_t Seconds;          /*!< Specifies the RTC Time Seconds.: n7 a$ Q  g1 {) t- q* @; P  U
  13.                                  This parameter must be a number between Min_Data = 0 and Max_Data = 59 */1 s4 i0 v% A3 C: R9 @8 R2 K( Y
  14. ( A+ ?6 j9 L. I9 y
  15. } RTC_TimeTypeDef;
复制代码
  1. /**
    % u' l2 y0 u  U8 K$ d
  2.   * @brief  RTC Date structure definition
    ; X5 Y1 ^1 U5 l; b+ `
  3.   */( T' G5 n5 ~( A- ?- W8 G" `; x8 p
  4. typedef struct
    . e% l7 ]3 W8 e  [4 H1 I
  5. {: g' d$ D; {6 F) S- I" Z/ [  |
  6.   uint8_t WeekDay;  /*!< Specifies the RTC Date WeekDay (not necessary for HAL_RTC_SetDate).+ _8 `' f3 q& o) X
  7.                          This parameter can be a value of @ref RTC_WeekDay_Definitions */# b' N  Q. N  U) @* C( M& ]5 n

  8. 0 C, w# }4 J. C
  9.   uint8_t Month;    /*!< Specifies the RTC Date Month (in BCD format).
    $ }( a( Y+ B1 Z
  10.                          This parameter can be a value of @ref RTC_Month_Date_Definitions */2 m- Z4 {+ H/ w+ S% ]

  11. 6 V. [* `: a! l' n1 T* ]. r
  12.   uint8_t Date;     /*!< Specifies the RTC Date.. u  I; p8 K/ h$ ?7 @9 h
  13.                          This parameter must be a number between Min_Data = 1 and Max_Data = 31 */) a! g# k% a( W* R
  14. 5 o7 P: g2 j- d5 v7 w$ K
  15.   uint8_t Year;     /*!< Specifies the RTC Date Year.
    2 {7 q% t- T0 m0 U  f
  16.                          This parameter must be a number between Min_Data = 0 and Max_Data = 99 */
    9 R, L+ w; f% [7 i

  17.   T+ \/ ^! j( W2 b* P
  18. } RTC_DateTypeDef;
复制代码

1 |" t' D; |/ L4 G程序代码:* ]/ g+ b8 J0 J8 D! [
4 M$ h# W; J* {* @8 e" K; @' a# `; Z
main.c8 v: X. [6 H% |

3 G( A" `4 }. M' W! C; c% J3 w, v在main.c中重写fputc函数,使得能够使用printf函数
+ ^1 h0 {0 A+ g6 D' V6 ~
# [6 N2 c9 f+ k. G6 L7 t" }: Z- s6 x
  1. #include "stdio.h"" m1 j* ?: E/ A. i& S/ T& P" O
  2. , M% h" U) S, b' W( V6 I
  3. $ Y/ \4 y( K9 D* O5 {1 ~+ Z7 H
  4. int fputc(int ch,FILE *f){: i3 _0 v% o9 W/ f& I( ?  L' P
  5. uint8_t temp[1]={ch};( d, |0 `+ S( X, i" Y
  6. HAL_UART_Transmit(&huart1,temp,1,2);* h. W: L# H, a$ ^6 Q
  7. return ch;9 X) S& x2 G! J0 T) o+ Y
  8. }
复制代码
+ ]4 ^0 j, x5 A  R8 ^# I1 N; v
定义两个结构体来获取日期和时间:9 G- C# A! M) Z  U7 P0 c
8 I" D* V- n2 a" Q$ g: ^; q
  1. RTC_DateTypeDef GetData;  //获取日期结构体" g3 ~" M/ h  H' L! q. {

  2.   Y5 |& R* K4 q/ f3 B
  3. RTC_TimeTypeDef GetTime;   //获取时间结构体
复制代码

* }8 Q* @3 }3 e' K* ~5 R在while循环中添加:
* d) N+ a; u/ ]. l% D$ B- ^  N0 X
  1.          /* Get the RTC current Time */( k" g3 [* K4 E3 j& F& O( c
  2.           HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN);5 M9 m( Y: _+ k  I( R" x
  3.       /* Get the RTC current Date */
    . }5 S+ N8 _' g5 a; \8 r8 x+ \7 G
  4.       HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN);
    ! L  ~! B6 d, u& e8 z
  5. . q: R. \8 ?) N: a2 I1 }
  6.       /* Display date Format : yy/mm/dd */1 ^* z) Q# p" ?7 P  D7 W
  7.       printf("%02d/%02d/%02d\r\n",2000 + GetData.Year, GetData.Month, GetData.Date);
    6 w* B  O) m4 d, `) V
  8.       /* Display time Format : hh:mm:ss */
    + R4 Z* Q8 g) l. Q, f' z- g* `
  9.       printf("%02d:%02d:%02d\r\n",GetTime.Hours, GetTime.Minutes, GetTime.Seconds);7 h! }6 v" S* |
  10. ( j) e$ |4 O* \- u4 b$ p
  11.       printf("\r\n");5 g7 j5 o7 p) {; e1 O2 V7 [% G

  12. . }9 {+ W4 c1 [  ]& s! ^: H
  13.       HAL_Delay(1000);
复制代码

3 J+ O' E$ G& F* G' B+ h. ~程序中使用HAL_RTC_GetTime(),HAL_RTC_GetDate()读取时间和日期,并保存到结构体变量中,然后通过串口输出读取的时间和日期。3 `3 U1 u' x/ C7 P
9 K1 v, U7 Y2 p2 W# S; T, s; @; ]
例程测试正常:
; G6 r  q6 r1 T# r( Q) @& b3 \( d& ~/ z" G
9%9S%~1X4K2K`XWJFA9]P57.png
7 C. j1 w1 t; ]* K& l0 m
- ]7 F" @8 V" E. f; jRTC掉电重置
  E) O% U6 a6 r9 r5 l# P9 H但是呢,在hal库中生成的代码,每次断电就RTC时间会重置,每次上电都会重新初始化时间
5 P' O4 Y+ L2 \: S% d4 ]5 P  ^. P5 f' Z- m9 x" ~  z
因为HAL库设置了一个BKP寄存器保存一个标志。每次单片机启动时都读取这个标志并判断是不是预先设定的值:如度果不是就初始化RTC并设置时间,再设置标志为预期值;如果是预期值就跳过初始化和时间设置,继续执行后面的程序# J, i" i1 h& T  u) X3 P: m+ L
3 \$ E% T$ h& L9 u% }# a
所以这里我们只需要每次上电执行RTC初始化之前,将标志设置为预期值即可+ r3 N0 ^9 u% Y% ?3 h; \, q; g# b

+ b; \- b  O" j& r( _; a( h在rtc.c中的RTC_Init修改为以下内容即可

+ o9 V4 t; Z) |" [6 h1 V- W
+ |% F  Q+ @$ h. d
  1. void MX_RTC_Init(void)
    6 l8 W7 B7 G5 u- c4 m2 A
  2. {
    " G! ]' `7 u$ B/ A

  3. + I# H7 K4 t2 q& D6 i* V+ `
  4.   /* USER CODE BEGIN RTC_Init 0 */1 C' m/ \+ j! x  B) c8 F9 C& z
  5.                 RTC_TimeTypeDef time;   //时间结构体参数
    ( E7 V0 `  D8 Z9 u( [4 D( `5 o
  6.                 RTC_DateTypeDef datebuff;   //日期结构体参数$ c4 e' s! }8 b3 O3 C! ?
  7.   /* USER CODE END RTC_Init 0 */
    ; f& @; r( _$ a0 u; z
  8. * Z6 a) N: X; t5 j; o$ K8 U- G
  9.   RTC_TimeTypeDef sTime = {0};" v" }& i9 k: E, P. V
  10.   RTC_DateTypeDef DateToUpdate = {0};( F! }3 n$ w/ Z+ s/ G" r0 |
  11. " n' D! v$ T  h0 z% J
  12.   /* USER CODE BEGIN RTC_Init 1 *// Z, @; J% }$ e7 |2 |
  13.         __HAL_RCC_BKP_CLK_ENABLE();       //开启后备区域时钟2 h6 a; ?% d' _1 z9 A* e  h; B
  14.         __HAL_RCC_PWR_CLK_ENABLE();                  //开启电源时钟
    % E6 _3 z5 V# [. c7 i% Z
  15.   /* USER CODE END RTC_Init 1 */
    9 C0 Z$ z! ]' f% r% K5 @
  16.   /**Initialize RTC Only + q/ ~" p5 `4 Z
  17.   */- x$ z# Y3 ]: {, x6 Q
  18.   hrtc.Instance = RTC;2 ~/ ~, @( `3 Z* d. O8 w
  19.   hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
    ( C5 t' R9 \. ?) e0 T* i
  20.   hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE;$ M8 i* l6 V7 f2 D9 \
  21.   if (HAL_RTC_Init(&hrtc) != HAL_OK)
    + e& X' b7 F# i( o! A
  22.   {4 C8 f& y1 k% r" K* s7 h: ?
  23.     Error_Handler();
    # ?  L' Z( P+ }
  24.   }$ `8 {8 H2 d8 w' K5 B" {5 f7 d
  25. ' [3 ]$ D( [0 ^
  26.   /* USER CODE BEGIN Check_RTC_BKUP */3 a  M9 I3 w( c: u" g' k$ \
  27.         if(HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR1)!= 0x5051)
    " L  H4 s: l3 H6 d+ Y
  28.         {8 q* o$ k9 }5 G1 O
  29.                
    5 x  b$ S3 e+ N5 p" l
  30.   /* USER CODE END Check_RTC_BKUP */
    9 Y! T4 l- m% d" B1 y

  31. " j2 t( M% z* j* ]' H
  32.   /**Initialize RTC and set the Time and Date 7 Y7 F1 h! j8 W3 H& U& f
  33.   */
    1 e' E  N( W+ {
  34.   sTime.Hours = 0x14;! L9 K7 f6 b, U. z8 ~1 \: T/ T
  35.   sTime.Minutes = 0x30;
    ( h  C, L& z8 a) v1 F
  36.   sTime.Seconds = 0x0;
    5 j' u& J" O% G/ Z! i* u
  37. ' ~$ a, x, K+ Q3 w) F0 d- V
  38.   if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK): R% D( T  z+ z* i
  39.   {
    # {$ m* S$ K2 M, k, b$ D
  40.     Error_Handler();% Z$ k! o. e% V' P5 |9 G3 t
  41.   }
    , m5 |6 Z6 M; N+ I- K
  42.   DateToUpdate.WeekDay = RTC_WEEKDAY_SATURDAY;! _. @$ F3 p% d% T/ `) G5 t
  43.   DateToUpdate.Month = RTC_MONTH_APRIL;
    4 u0 X) ^" r4 ~; X( K
  44.   DateToUpdate.Date = 0x25;/ J9 b+ T: |1 C$ e  H7 w( Y
  45.   DateToUpdate.Year = 0x20;
    " D8 m- h( q$ M/ Q/ v* D$ h

  46. & n. Y4 Y7 M  ~; C5 @1 O5 o
  47.   if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BCD) != HAL_OK)
    % S% x/ N/ g1 ]
  48.   {' [1 y  A; L; J8 w
  49.     Error_Handler();, H) t+ g+ W/ q2 E2 W: z' G8 |) `4 B; c! r  z
  50.   }
    8 X! ?2 G2 R5 Q) Z# z/ P$ C7 f
  51.   /* USER CODE BEGIN RTC_Init 2 */
    , {9 s. R2 X7 K/ V6 Q, t
  52. 2 R$ j; I( G8 t* Z7 ]
  53.         __HAL_RTC_SECOND_ENABLE_IT(&hrtc,RTC_IT_SEC);         //开启RTC时钟秒中断7 P2 j$ t; I! o9 w
  54.         datebuff = DateToUpdate;  //把日期数据拷贝到自己定义的data中
    * Z0 z1 J: T8 r* g9 e7 }
  55.         HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x5051);//向指定的后备区域寄存器写入数据/ U! A$ b, y4 i2 t. G
  56.         HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR2, (uint16_t)datebuff.Year);' O  |- `$ ^# v- \- S0 S3 S
  57.         HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR3, (uint16_t)datebuff.Month);% k$ m5 l6 x( d2 d4 \
  58.         HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR4, (uint16_t)datebuff.Date);
    3 `5 Q+ f" ~& n& y# H) V
  59.         HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR5, (uint16_t)datebuff.WeekDay);  ]+ N+ k' C+ G: _/ R
  60.         0 \1 r8 W: B6 @+ }" Y
  61.   }
    0 `% b( X* m0 H
  62.         else
    3 j0 r# {: U8 @: x: t1 M& y
  63.         {
    % D% Y1 s0 x6 n# s2 z' Z: Y: s" }5 H
  64.                 datebuff.Year    = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2);
    $ ~% Y- |. w5 W6 |
  65.                 datebuff.Month   = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR3);
    / L7 g4 P) T* B- W8 S$ e; F6 A
  66.                 datebuff.Date    = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR4);2 B; }: O, Y% Y: E0 h) p
  67.                 datebuff.WeekDay = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR5);' D. ~; M+ f; k7 c
  68.                 DateToUpdate = datebuff;
    9 n" N0 s% S4 w+ J# @
  69.                 if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN) != HAL_OK), ~2 h1 b/ _7 g. S  i, u+ r9 B: U+ t
  70.                 {
    6 B5 Z/ Y' w: c. [8 q. T
  71.                         Error_Handler();6 Z4 j+ d( K4 y
  72.                 }0 C7 \& R4 Y, c4 }3 n4 R* S. Q
  73.                 __HAL_RTC_SECOND_ENABLE_IT(&hrtc,RTC_IT_SEC);         //开启RTC时钟秒中断               
    ) Q( d7 j9 r# Z- s. H) S' y4 T) }
  74.         }
    8 {6 b) M; _/ c# Q
  75.         * z1 l. o  F  A3 I; q+ S

  76. , B. z. \8 M( E3 h2 M$ X
  77. : p" K, [5 k" w1 S; P
  78. }
    " T  K2 K* M3 S" Z/ g
复制代码

+ C) q  x# h$ m/ V) V2 L( s5 k# P2 O0 G
赞 收藏 评论0 发布时间:2022-3-23 15:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版