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

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

[复制链接]
STMCU小助手 发布时间:2022-3-23 15:00
前言:4 |/ n, X1 R% ]$ H% g+ U# X
, J" Y2 `( q2 O0 y& X2 F6 g% v
本系列教程将 对应外设原理,HAL库与STM32CubeMX结合在一起讲解,使您可以更快速的学会各个模块的使用. @" n8 ^1 D" ?. u
3 N# }# |/ X/ G2 L# L" F
所用工具:$ ]5 k8 P/ n  i
, R" M$ I: T/ O' o4 y/ i' G7 A1 D
1、芯片: STM32F407ZET6/ STM32F103ZET6% z! O" ~' j, Z) X. \0 z
- m) `% O5 p6 n7 T
2、STM32CubeMx软件( E% i- J8 R1 n9 Z! @) T/ J+ N+ A; E

- {1 c/ `) \# f, I5 o3 J3、IDE: MDK-Keil软件5 ^7 D2 G3 j. \+ o* s+ e5 e8 h
0 L+ q% K2 c8 X) C7 x) G
4、STM32F1xx/STM32F4xxHAL库
) u- t, f+ @- {+ s8 I5 F- M
% ^# v% k: Q& q, I+ m9 T+ X( ]3 P知识概括:
/ G; t# C2 V$ H; ~0 t
- @1 i) {9 l  n+ e4 _7 e/ C+ z通过本篇博客您将学到:2 A& ?& J2 F: ?" O& \3 Y/ K
; \, n. h3 N( R8 C' X5 F
RTC时钟原理
* m/ q7 T, T1 z% A. E5 T2 L1 l9 }; S$ W9 z4 K, M  N1 \
STM32CubeMX创建RTC例程/ ]1 |, R7 t; l, f$ x% K9 q
) M% T: _5 x6 q$ |+ F# C
HAL库定时器RTC函数库& t: H3 p: h  {1 W5 k

9 E3 Y# n0 c9 c; lPS: 这里的RTC讲解,我们只将原理,不讲寄存器) k8 T) p0 O2 E) b* n

+ R- F# J) \. t4 u; Y3 }0 n什么是RTC
/ b- j3 d5 h) y6 I6 b; H; _( HRTC (Real Time Clock):实时时钟( }. a( ?, Z) `: {6 D* G
) `2 P! _3 i+ s2 f( v  X+ s3 V+ G
RTC是个独立的定时器。
RTC模块拥有一个连续计数的计数器,在相应的软件配置下,可以提供时钟日历的功能。修改计数器的值可以重新设置当前时间和日期 RTC还包含用于管理低功耗模式的自动唤醒单元。
5 O; f7 c- o6 }& p
4 C1 r  z/ w) w8 k; T3 t; D {Y5MNB6$TJM~2G@SN(7WHRY.png 6 ~8 y  U3 _/ o" R/ I7 G
& \. N% E4 B0 F, F
在断电情况下 RTC仍可以独立运行 只要芯片的备用电源一直供电,RTC上的时间会一直走。
% e! z  q! B( R3 [! n
; X  m" y# M) N5 N1 g* J
RTC实质是一个掉电后还继续运行的定时器,从定时器的角度来看,相对于通用定时器TIM外设,它的功能十分简单,只有计时功能(也可以触发中断)。但其高级指出也就在于掉电之后还可以正常运行。
. _  A7 B3 `( v
3 m; c  G  I0 a6 M' |8 d两个 32 位寄存器包含二进码十进数格式 (BCD) 的秒、分钟、小时( 12 或 24 小时制)、星期几、日期、月份和年份。此外,还可提供二进制格式的亚秒值。系统可以自动将月份的天数补偿为 28、29(闰年)、30 和 31 天。/ e1 A" x6 `9 L8 W3 d) W- G
7 |! h4 J  G% ?% H- b# K
上电复位后,所有RTC寄存器都会受到保护,以防止可能的非正常写访问。
! l  a6 X/ F# N5 F% e0 A
4 P* g( P! y" D7 K! g- G( H无论器件状态如何(运行模式、低功耗模式或处于复位状态),只要电源电压保持在工作范围内,RTC使不会停止工作。/ ]2 @4 E5 z# o5 P( i) ^

' q, k/ i& {, Z1 |4 q! l# z9 b+ ARCT特征:
+ s+ @( G1 c6 H# T● 可编程的预分频系数:分频系数高为220。
8 n3 @1 Y% ^% |% D$ o: |● 32位的可编程计数器,可用于较长时间段的测量。
$ X+ @+ A0 m# m  U8 S● 2个分离的时钟:用于APB1接口的PCLK1和RTC时钟(RTC时钟的频率必须小于PCLK1时钟 频率的四分之一以上)。+ F/ P: g+ B. }0 V9 e" S4 W
● 可以选择以下三种RTC的时钟源:
2 \4 c& v# r: K" x     ● HSE时钟除以128;5 A- |4 K5 o$ r2 o2 V( K0 [+ I! a
     ● LSE振荡器时钟;8 q' @$ l) a! L% K+ k6 [3 x8 N" |5 M! L
     ● LSI振荡器时钟/ c  E8 D6 N0 k7 V

1 q8 b. x4 e: G; b/ Y● 2个独立的复位类型:2 R0 ~: T, L8 \$ a2 I
     ● APB1接口由系统复位;9 Y4 \; c4 S/ C7 R
     ● RTC核心(预分频器、闹钟、计数器和分频器)只能由后备域复位* W! Z# z: W. m6 ^; {
3 Z* U3 c- I, `% U4 Q4 y1 k
● 3个专门的可屏蔽中断:
3 p& L$ u& A0 m' Z, A. U8 @& t! N5 Y8 x     ● 1.闹钟中断,用来产生一个软件可编程的闹钟中断。) f: L; k4 o/ d, S
     ● 2.秒中断,用来产生一个可编程的周期性中断信号(长可达1秒)。1 e. v7 \: J3 P$ k) `8 i# s
     ● 3.溢出中断,指示内部可编程计数器溢出并回转为0的状态。* M  S' h+ H- D4 {. E
6 Z/ q4 g( j& A" I2 m# ~
RTC时钟源:
( j* L6 I* B" }" C三种不同的时钟源可被用来驱动系统时钟(SYSCLK):
3 C& A3 Y1 ~8 n0 m. U( ~+ r% M0 E1 m0 z  D% {4 q0 A6 n
● HSI振荡器时钟  a3 l  ]8 Y- @; |% m
● HSE振荡器时钟& o5 J- O. @1 C1 w( b4 y
● PLL时钟
# _& k" M( R4 b" O6 ]8 H
7 I( C4 A0 i& y" f" m* j+ L: p这些设备有以下2种二级时钟源:
* }( U# ?. o, P● 40kHz低速内部RC,可以用于驱动独立看门狗和通过程序选择驱动RTC。 RTC用于从停机/待机模式下自动唤醒系统。3 k( W' Z5 F3 P% W  q  V( C
● 32.768kHz低速外部晶体也可用来通过程序选择驱动RTC(RTCCLK)。
9 t" C  ^; _! f8 @1 z8 C3 m- g, c$ e* U# n2 S6 }5 [' d
RTC原理框图
( |- H$ i! E5 P; Y. A# D4 C4 j7 [# X3 g6 |
MG$U7{O}1)FMJTPCOVCF8`V.png
  A: f' X' N, |( w3 M% n2 A' e
$ b. {) f: Q. u0 D+ {- l: C' {# hRTC时钟的框图还是比较简单的,这里我们把他分成 两个部分:: n% ]' d. N. m6 T
% s" B$ l5 K' v- k+ N  _4 u
APB1 接口:用来和 APB1 总线相连。 此单元还包含一组 16 位寄存器,可通过 APB1 总线对其进行读写操作。APB1 接口由 APB1 总 线时钟驱动,用来与 APB1 总线连接。3 O5 }% u& d! B$ j) m- ?
  i0 G' }4 R! M7 ~2 k2 r# M+ G
通过APB1接口可以访问RTC的相关寄存器(预分频值,计数器值,闹钟值)。
: y. i+ Z! I. n
5 o9 }  W8 v$ z, i* A3 S/ tRTC 核心接口:由一组可编程计数器组成,分成 两个主要模块 。- Y- Z& O  c7 i2 V+ m; b7 t3 i, e
" H. T0 i3 R1 ^6 C6 ~
9VM)0T5~(6ULAZH063`Y41A.png 3 J- `6 W- l$ E6 s- k1 |9 B+ ?

& _* F5 v/ P8 W8 A- o  \第一个模块是 RTC 的 预分频模块,它可编程产生 1 秒的 RTC 时间基准 TR_CLK。RTC 的预分频模块包含了一个 20 位的可编程分频器(RTC 预分频器)。如果在 RTC_CR 寄存器中设置了相应的允许位,则在每个 TR_CLK 周期中 RTC 产生一个中断(秒中断)。+ x; e2 Q7 j2 v9 y8 E9 ]$ ^. w
! s0 e2 Y1 u3 P5 q
K2U[6UER60[[1_Q2P9PMO9G.png 3 b. r# K+ E/ ~! A
) Q; e4 `; Q+ D( H% W1 l
第二个模块是一个 32 位的可编程计数器 (RTC_CNT),可被初始化为当前的系统时间,一个 32 位的时钟计数器,按秒钟计算,可以记 录 4294967296 秒,约合 136 年左右,作为一般应用,这已经是足够了的。$ b# |. i6 o8 I  t8 H- M! T

) b* y" m" I& Q# D) p  I7 ?RTC具体流程:* a. r# Y& N% t% N
RTCCLK经过RTC_DIV预分频,RTC_PRL设置预分频系数,然后得到TR_CLK时钟信号,我们一般设置其周期为1s,RTC_CNT计数器计数,假如1970设置为时间起点为0s,通过当前时间的秒数计算得到当前的时间。RTC_ALR是设置闹钟时间,RTC_CNT计数到RTC_ALR就会产生计数中断,* d) q* ~5 }0 x# F2 P/ Q1 c& H
RTC_Second为秒中断,用于刷新时间,4 @: P$ N$ c1 V: @  Z
RTC_Overflow是溢出中断。% z: `( ]. u  O' Z- P
RTC Alarm 控制开关机
& j6 h# Q' p) {RTC时钟选择2 L# r; b$ G0 i
使用HSE分频时钟或者LSI的时候,在主电源VDD掉电的情况下,这两个时钟来源都会受到影响,因此没法保证RTC正常工作.所以RTC一般都时钟低速外部时钟LSE,频率为实时时钟模块中常用的32.768KHz,因为32768 = 2^15,分频容易实现,所以被广泛应用到RTC模块.(在主电源VDD有效的情况下(待机),RTC还可以配置闹钟事件使STM32退出待机模式).
, P' o5 @4 B& d& R, b3 U! q. C0 }2 [7 N( I1 D, j$ d
RTC复位过程% k/ M7 ]8 B2 Q7 J' S
除了RTC_PRL、RTC_ALR、RTC_CNT和RTC_DIV寄存器外,所有的系统寄存器都由系统复位或电源复位进行异步复位。6 O. t7 T3 `- k# l
RTC_PRL、RTC_ALR、RTC_CNT和RTC_DIV寄存器仅能通过备份域复位信号复位。
6 \- m+ m1 b; m) e
: }0 n. M/ ]2 J6 L系统复位后,禁止访问后备寄存器和RCT,防止对后卫区域(BKP)的意外写操作) e0 |* P" P) w3 K

* H$ x5 }  x  \6 D2 eRTC中断& d+ g2 K# e; ?& X
秒中断:0 G1 i$ y2 R3 M7 y( }2 f
这里时钟自带一个秒中断,每当计数加一的时候就会触发一次秒中断,。注意,这里所说的秒中断并非一定是一秒的时间,它是由RTC时钟源和分频值决定的“秒”的时间,当然也是可以做到1秒钟中断一次。我们通过往秒中断里写更新时间的函数来达到时间同步的效果7 y' g4 |8 B! B7 D* m" _: ]' D
+ h% q- J8 z/ L, O. p( Y
闹钟中断:
8 E/ j* Z8 ]+ X" x闹钟中断就是设置一个预设定的值,计数每自加多少次触发一次闹钟中断' q6 |7 X+ s! k/ M8 D7 E

2 {" V. G! c) g) tCubeMX配置RTC2 @/ f  s" I/ v7 ?  P* f8 U& R# ^
工程创建; J' Y4 E7 G' [5 G' Y: F' ^( ?
1设置RCC

& Y+ h5 M( A0 H. a; E$ d. X
- C. e) J' m" z; u7 ` 9523)HOJID95N7EVK0TPE~7.png
( [2 h- Z" D/ u  g& J
; }$ v0 W$ ~; z& X设置高速外部时钟HSE 选择外部时钟源. V! v+ h  |) A6 y
使能外部晶振LSE1 P/ J1 ?' ?1 K: \1 U. _0 c' ]" e' H

/ e, Q2 G3 ?  _' cRTC设备因为其独特的运行方式(即掉电依旧运行)使用HSE分频时钟或者LSI的时候,在主电源VDD掉电的情况下,这两个时钟来源都会受到影响,资源消耗太大,小小的纽扣电池根本吃不消。没法保证RTC正常工作.所以RTC一般都时钟低速外部时钟LSE3 j. n& f! I5 ^

0 z9 r, R) @9 N5 v( i4 b% L! W2.配置RTC, J6 A. }2 y# v

7 v$ {$ a6 b! s9 V& p( y' B) c ]3KRAQH0[GZN5C6VRT)7G2D.png ; K0 A0 K2 ^* ]9 x
) w  M0 h2 b+ Y: t  T
Activate Clock Source 激活时钟源1 Q9 Y9 t+ E5 S& [
Activate calendar激活日历2 H- X) Z6 B" |1 B# S- P- s

! ]' u+ b) \  @& O这两个都要点,作用也很明显,先是使能时钟源,再使能RTC日历4 W* M% Q4 j% Q
RTC_OUT: Not RTC_OUT
6 ?  O* y! E0 {6 v! oTamper: ×) s9 M7 `0 M. v2 W2 C
5 y3 F$ [4 v" ?8 x0 S0 T
第一个是是否使能 tamper(PC13)引脚上输出校正的秒脉冲时钟,
# s4 e$ v, M$ ]- m: l) d/ L7 c$ Q6 [8 ~: g( k
第二个: RTC入侵检测校验功能
$ d0 g* `0 ]1 c4 D0 A+ j6 b  o; Y3 D8 k% V# \8 M( Z
RTC校验功能,使能侵入检测功能。RTC时钟经64分频输出到侵入检测引脚TAMPER上
  W$ i( |# d& D. u3 _当 TAMPER引脚上的信号从 0变成1或者从 1变成 0(取决于备份控制寄存器BKP_CR的 TPAL位),会产生一个侵入检测事件。侵入检测事件将所有数据备份寄存器内容清除。+ y- E' z6 c$ `  J" |+ {( U0 I4 d

2 ^, t: l+ a' C2 c$ j: r1.也就是第一个是使能tamper(PC13)引脚作为时钟脉冲输出
+ j& E7 r5 \' h7 u7 M# p1 H0 \3 I/ }2.第二个是使能tamper(PC13)引脚作为入侵检测功能7 @3 s  e+ Y. n# u4 n, G  P
6 X- o: ?8 x: N
$ F4 m. X, V# A6 w) s9 r
下面是两个RTC的中断:9 n9 p0 B4 Y8 n: b' G
RTC全局中断RTC_IRQHandler()
+ Z6 J, @5 _, W- G" M& x% D闹钟中断函数RTCAlarm_IRQHandler()% R" \( \. L5 z) g* p% c
8 F: H+ v$ C! \( f: H: ?( u
09507@T~XZT4O(JJA4OQ~UD.png : {; U& _8 o' J

9 }5 d' ~: y* f4 r; D$ I此处设置时间为2020/04/25 13:30:00
! n8 n" P( r" {; Q+ MData Format: 日期格式+ G  y0 D" h2 o$ p, p/ z9 Z5 X

/ A& p5 O8 O, C1 x' r* H; ]7 y- zBinary data format 十六进制
9 H' W% j+ ?/ SBCD data format BCD码进制
6 S- y7 F! f7 t, \9 X
9 }( w+ \' A3 ^使用自动配置,初始化时间必须使用BCD data format,原因是库函数存在bug,如果使用Binary data format,月份配置会出错,比如说11月,配置时会赋值为RTC_MONTH_NOVEMBER,而此宏定义值为0x11,也就是说其十进制值为17
* D' G( Y7 Y7 p% M4 o" S2 j7 `; W' x' z
Hours: 小时. T8 m9 L: p' o9 H1 w8 h  m# D

$ j/ E3 L5 o! b" t: L6 OMinutes: 分钟
# q1 Q$ D3 Q9 Z
7 l4 M/ M! U3 QSeconds: 秒+ `2 ?! n' J8 f# w

" K, ]8 s' q; B! z4 b8 TWeek Day: 星期
6 v" U. N% ?# U- d- j2 U5 g# A" l8 S3 o# k) E
Month 月份/ Q5 r" G( z) t% S# b. o; i" S% Q3 ?
8 k2 Z2 K( C3 w; L  j
Date: 日期
) q1 u6 r  m! C; ^
! k+ E1 R: e2 P- {, [Year: 年份
) N) F3 c  H7 Y; l# f# P! i; m; q* y0 r+ K& z, H6 d3 I0 W, I& Y
3 使能串口
8 _& ]3 k) m8 i& A4 N( ?# B0 g$ |' D" y; m7 k
$QZ4])UE4XDW){Z`)~LVH.png ) K: i2 r  Z" P; ?& {$ A( m3 r

( n- C% f# v9 N% @1 Z% w使能一下串口,因为发送日期到上位机" L, b7 `- S: m# R/ i

/ g. N7 b% R6 J/ J  c4时钟源设置
0 @; \9 m  r; S& X* v! l
9 q* [' _, s; h2 N; A9 A  q U_S]%_(4Q}GR[39F((ZVA6O.png
  b0 ]; ?% O( M! g8 \/ C) Y& e
" H! a1 L# `/ S" \* W" z我的是 外部晶振为8MHz/ u% s' u$ D: B7 W4 V
1选择外部时钟HSE 8MHz/ w" T2 C8 t+ m/ \$ `
2PLL锁相环倍频9倍4 V$ D1 t& h8 w5 `. [0 ^' Q; r$ g
3系统时钟来源选择为PLL
, c! l  z$ r# d. W# ~' |6 `9 v4设置APB1分频器为 /2
' ^4 d" O; O6 A: A$ d3 b5 使能CSS监视时钟# f6 j7 @7 ^/ W: {, F; P  n; y
6 设置RTC时钟为LSE
7 X2 y  q7 ?0 y! q6 |8 y: `' p
0 e% P1 X# }4 J) j5项目文件设置
% c4 U- @- \9 i% k! G8 g0 [; I. v) g
FSG]%CL3~6TP)R9AA[84.png + \  P: ~0 }4 [/ J3 V
3 e/ s% d; H4 q& [. Y+ i
1 设置项目名称
' ~. n, {1 G2 T( I0 d2 设置存储路径
) O7 a* n' d: O& [' K& [$ K3 选择所用IDE
/ w: C0 f& |0 A! o
- N8 a& Q" y& t8 _$ ?4 E* S @3BLF`]NM7`0GP{674FL2%L.png 4 D+ d0 O/ h+ O, T, C+ G. ]6 ]
  v8 V* P2 a' O9 n/ O! K+ k; B
6创建工程文件& g) _+ T3 U& z) y8 I2 R

' e4 h+ F  O6 P, @8 w% X然后点击GENERATE CODE 创建工程
# }2 C* ?4 I  I; m6 r" v1 F8 w8 Z( q& F0 s) T: J
配置下载工具) H3 Q. Z% I: Q
新建的工程所有配置都是默认的 我们需要自行选择下载模式,勾选上下载后复位运行
% p. f4 L  R) F6 h: }/ |" g# B6 K0 U
' b, O0 ~  Q, x% m QYRVOZJ9OXE0YFH`WZK1(EF.png ! A' y& B+ q! q/ E
9 x3 R1 O* Z& j, t
RTC_HAL库函数# Z/ E( q& n  |& d) h8 J! m
  1. /*设置系统时间*/& e5 n2 A. m; `9 ?  A" C) ^) A1 J
  2. HAL_StatusTypeDef HAL_RTC_SetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format)
    9 @. t, w6 T2 W
  3. /*读取系统时间*/
    , Y! g# N, z8 C* j2 L; V. {
  4. HAL_StatusTypeDef HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format). _' H: x, i# F5 P8 z  R
  5. /*设置系统日期*/" p3 u# I$ T$ X) j% o
  6. HAL_StatusTypeDef HAL_RTC_SetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format)  V+ ~9 E# b/ e2 Y5 {
  7. /*读取系统日期*/
    - B" P) s. f1 U5 \0 S$ u* N! u
  8. HAL_StatusTypeDef HAL_RTC_GetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format)/ m, _9 S4 q, k# l$ r
  9. /*启动报警功能*/- f6 ~! E! f. z
  10. HAL_StatusTypeDef HAL_RTC_SetAlarm(RTC_HandleTypeDef *hrtc, RTC_AlarmTypeDef *sAlarm, uint32_t Format)
    & X2 P7 {* n  l$ I
  11. /*设置报警中断*/7 f! ^6 C2 c$ y" w3 U" Q
  12. HAL_StatusTypeDef HAL_RTC_SetAlarm_IT(RTC_HandleTypeDef *hrtc, RTC_AlarmTypeDef *sAlarm, uint32_t Format)
    $ y8 k% i( b  E2 n0 w4 u7 Y
  13. /*报警时间回调函数*/: @$ x( i2 r3 K' x; W5 C- L
  14. __weak void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)/ W/ p6 I8 E! k9 o, q" n- g
  15. /*写入后备储存器*/  Q" A2 o( _% q5 B. s) _
  16. void HAL_RTCEx_BKUPWrite(RTC_HandleTypeDef *hrtc, uint32_t BackupRegister, uint32_t Data)$ L/ P5 I8 ?" q: c( r% O, r/ v) f
  17. /*读取后备储存器*/
    $ W4 t- b* r5 B" t! X, V: p# U  H
  18. uint32_t HAL_RTCEx_BKUPRead(RTC_HandleTypeDef *hrtc, uint32_t BackupRegister  $ G/ l8 g. ^" f  M2 r
复制代码
; a5 j# H8 A% @$ b
我们可以看到前面的四个函数,分别是
' l' c7 h0 L6 |( z# e1 ?6 j设置系统时间:HAL_RTC_SetTime();3 @# h8 n) G6 o7 s2 _- L+ y
读取系统时间: HAL_RTC_GetTime();2 i) j( V2 u2 H
设置系统日期: HAL_RTC_SetDate();
' T2 z2 _. b( J1 D* z4 k读取系统日期: HAL_RTC_GetDate();1 B& V' I- _9 v* c4 p9 i" L
. I0 u% z( X7 A2 q: r
因为系统的时间和日期开始的时候已经设置过了,所以我们这里只用两个读取函数2 I6 D- j. j' I  @# b9 Z' B' T
7 S$ U1 v# u9 i3 e" w) S/ E
读取系统时间函数# \' h' t7 O9 O" F
+ F( e! o. S7 ]! R: g2 h
  1. /*读取系统时间*/& t# q  D7 M- u7 q, y1 m3 |
  2. HAL_StatusTypeDef HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format)
复制代码
+ c. ^+ |8 b3 H8 f0 _- v
功能: 获取RTC时钟的时间
: _  m" x- ?/ N) {7 s0 ~$ ^
% P0 B6 ^' S1 q0 u( A参数:
2 ~" }2 q4 E; e9 S/ ?' t5 Y
2 y! e: T; B- v8 ~* h) J*hrtc RTC结构体参数 例:&hi2c27 H6 t& H4 M0 C  D" X9 G
1 c* }- D! l5 I% m
RTC_TimeTypeDef *sTime: 获取RTC时间的结构体,0 ^9 a: O0 n* l. v' Q" |2 P* n
" y! H- ]7 o) l8 p9 ?( |) q
Format: 获取时间的格式
& l: G. \. E  ^" C7 o0 T& PRTC_FORMAT_BIN 使用16进制3 y. G3 E8 W, [0 ^  I( P
RTC_FORMAT_BCD 使用BCD进制
0 r5 q+ R+ ?6 d+ z+ S; W) Z
3 j9 ~" R- y" V# p/ M. x: g读取系统日期函数
  ~( E% ~2 p9 ?8 [1 B% K2 U. m3 ]1 V) H/ W5 N* ~
  1. /*读取系统日期*/8 Y% j; Y/ U8 F, \; T
  2. HAL_StatusTypeDef HAL_RTC_GetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format)
复制代码

8 o3 N' N2 Y+ I+ H/ P' R+ b. Z功能: 获取RTC时钟的日期3 [% z; d9 o9 [5 W  o8 T1 t

6 q) o( o2 D4 a4 l参数:
( F! f' S2 N7 @2 x
1 {7 z4 [& i: {& d6 \*hrtc RTC结构体参数 例:&hi2c2
. R8 q/ B( q6 ^9 G: e/ L$ h& o# t( ?; V
RTC_DateTypeDef *sTime: 获取RTC日期的结构体,9 C# ]" i' P$ L( C

( W9 ^, x- g& CFormat: 获取日期的格式: J  z, y( S5 r; n- J# n: J& ~# @# _
RTC_FORMAT_BIN 使用16进制; T4 j) q! R' r" B
RTC_FORMAT_BCD 使用BCD进制
# M* K9 `1 K7 b
1 `% F5 Y, ?! {& A, ~' `, k在stm32f1xx_hal_rtc.h头文件中,可以找到RTC_TimeTypeDef,RTC_DateTypeDef这两个结构体的成员变量。  p4 E3 [! Q0 b$ h3 @' ~' `
2 u8 \# a( Y. e1 v9 K
  1. /**
    - a: k( _! T4 T1 ~+ L7 E! i
  2.   * @brief  RTC Time structure definition
    9 J; Z* R( a$ r6 Z
  3.   */( r0 h* I. g: O
  4. typedef struct  X  z) j$ [5 l6 x2 s
  5. {" N- E0 D5 B) F' a1 I  m# w
  6.   uint8_t Hours;            /*!< Specifies the RTC Time Hour.
    8 A9 e) S) {; ]( Y0 n1 ~) B
  7.                                  This parameter must be a number between Min_Data = 0 and Max_Data = 23 */
    7 O5 F4 c9 s/ k/ r. g2 T0 T* [6 u

  8. * F5 U2 h+ [1 T9 w+ C
  9.   uint8_t Minutes;          /*!< Specifies the RTC Time Minutes.
    / Z0 y# @. u7 G7 W% H! @9 p0 y2 I
  10.                                  This parameter must be a number between Min_Data = 0 and Max_Data = 59 */
    * P& P5 e2 W% g# U/ G

  11. 2 w+ S; p; J/ |0 _& m
  12.   uint8_t Seconds;          /*!< Specifies the RTC Time Seconds.1 S/ i+ _* ~$ P; a
  13.                                  This parameter must be a number between Min_Data = 0 and Max_Data = 59 */& W, o! O9 a, ^% N3 U* l

  14. 0 x  V* ~8 c+ l# M; u1 h
  15. } RTC_TimeTypeDef;
复制代码
  1. /**
    8 t' }- |2 c; c. \3 K8 o5 q7 q, F
  2.   * @brief  RTC Date structure definition
    : M; R# s5 q7 P+ W$ s8 u; j% b
  3.   */
    " b+ w% y2 C( Z# ?  O
  4. typedef struct$ q% |8 Z  m$ Q/ O3 ?* z
  5. {, \% p0 X$ C$ I/ r8 k5 d
  6.   uint8_t WeekDay;  /*!< Specifies the RTC Date WeekDay (not necessary for HAL_RTC_SetDate).9 H+ Z' v$ L% h2 Q: V9 w/ M" [
  7.                          This parameter can be a value of @ref RTC_WeekDay_Definitions */
    . i6 M7 b9 S6 Y6 G

  8. 8 g" E7 |* v3 s2 n7 `
  9.   uint8_t Month;    /*!< Specifies the RTC Date Month (in BCD format).
    0 t. O2 H4 n% Q/ ?. c$ v
  10.                          This parameter can be a value of @ref RTC_Month_Date_Definitions */* i0 x8 y" [. p4 ~. e

  11. 6 G4 \8 Q( m( v" K- |0 j
  12.   uint8_t Date;     /*!< Specifies the RTC Date.4 \- z; e. y& y
  13.                          This parameter must be a number between Min_Data = 1 and Max_Data = 31 */
    0 S  y$ J* @% N6 z  K% Q3 F
  14. ! m. v8 r0 |: c2 [4 |( a: e" s& q/ K. ~2 f
  15.   uint8_t Year;     /*!< Specifies the RTC Date Year., e+ g' g. g+ z; u9 T
  16.                          This parameter must be a number between Min_Data = 0 and Max_Data = 99 */
      x+ w* c9 W! C- ]! `; O
  17. 2 P( V4 ]4 _- T# {, ^( b$ g. n
  18. } RTC_DateTypeDef;
复制代码
4 o& @# o8 o7 ]/ Y
程序代码:7 O2 ~- L. t2 n9 I
# Q! `+ i3 Q1 b& u4 t
main.c
; ]# h' w  N0 p$ E' H3 X% `
) i. N7 M1 {* _: ?; t在main.c中重写fputc函数,使得能够使用printf函数4 w$ a5 v1 {! k3 R7 w

" m1 Y! U! ~, R! I/ l
  1. #include "stdio.h"
    + N# l1 c" H2 `, Y/ j. s

  2. / I; g% k9 r* W# S
  3. . b; t7 z0 ^  j1 W2 e, Y4 j+ j& D
  4. int fputc(int ch,FILE *f){. ^$ F9 r' O) x
  5. uint8_t temp[1]={ch};& y8 E3 S4 i0 x8 O9 c
  6. HAL_UART_Transmit(&huart1,temp,1,2);0 M" H7 a2 T' X5 j2 A
  7. return ch;
    & h" t6 x8 d  r  |( z& `" ?0 I
  8. }
复制代码
( u- B6 ]2 u4 ]! b2 \
定义两个结构体来获取日期和时间:
+ W3 J$ ^5 a+ s. e& B+ K$ x+ u* z; ~+ `& i: }* d$ f$ o! w+ R
  1. RTC_DateTypeDef GetData;  //获取日期结构体
    9 }7 \: T0 F% R: k" b5 z# {
  2. - T$ ]" L7 ?$ S9 W
  3. RTC_TimeTypeDef GetTime;   //获取时间结构体
复制代码

2 c: @8 T( ~! L  v4 R7 A0 {在while循环中添加:
5 ~6 X+ }; r/ ]
  1.          /* Get the RTC current Time */
    1 T& \4 h  l# m5 e
  2.           HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN);6 C& X8 }6 ^. P4 l1 y* u
  3.       /* Get the RTC current Date */7 x- h* R; Q9 S6 \) s5 g
  4.       HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN);' Y# J, X1 q9 {5 H8 P; u

  5. 7 G. R; B9 n  E. V7 N- P
  6.       /* Display date Format : yy/mm/dd */
    . r& }7 t% c2 v4 O( t
  7.       printf("%02d/%02d/%02d\r\n",2000 + GetData.Year, GetData.Month, GetData.Date);# {* R" B: Y3 e  i7 {4 g2 H4 N
  8.       /* Display time Format : hh:mm:ss */# L9 M) `, p/ |' M
  9.       printf("%02d:%02d:%02d\r\n",GetTime.Hours, GetTime.Minutes, GetTime.Seconds);; x" r# R! F) [5 \: l

  10. ! o, p( x( Q# e
  11.       printf("\r\n");8 [% a7 Z4 C( q# x. T% M9 J
  12. , d- D4 b8 d3 U, n9 K% U- t
  13.       HAL_Delay(1000);
复制代码

9 K5 o' P) I$ _8 ?0 `% v3 v9 Q6 w5 o程序中使用HAL_RTC_GetTime(),HAL_RTC_GetDate()读取时间和日期,并保存到结构体变量中,然后通过串口输出读取的时间和日期。
- v7 E3 b& @7 R/ _% Y8 p$ _1 t" a" F3 _$ G3 m! b+ l5 E5 v) O) @
例程测试正常:
" J1 X, q1 Z0 O7 T: w. U7 `* t3 G5 \
9%9S%~1X4K2K`XWJFA9]P57.png
- R/ s  r5 x8 Z5 O8 }9 ]% P# [* H
$ w2 d6 P) x  uRTC掉电重置
" l/ d: ?3 Q# I4 j  Q7 g但是呢,在hal库中生成的代码,每次断电就RTC时间会重置,每次上电都会重新初始化时间
& Q0 ~- @6 K4 s. u) K
) V6 E) M3 m! h3 O% E因为HAL库设置了一个BKP寄存器保存一个标志。每次单片机启动时都读取这个标志并判断是不是预先设定的值:如度果不是就初始化RTC并设置时间,再设置标志为预期值;如果是预期值就跳过初始化和时间设置,继续执行后面的程序
8 B4 b1 U4 S4 k
) x  z( b! ^$ R# ]. F9 i2 v所以这里我们只需要每次上电执行RTC初始化之前,将标志设置为预期值即可
* H: G/ J" ~3 l% P; ~' v% X$ d; N% f( O/ C9 G
在rtc.c中的RTC_Init修改为以下内容即可

8 i: H8 N' v4 X; }
: L! z, L0 F- f* n+ v8 }
  1. void MX_RTC_Init(void)
    ' ?4 U8 I# b6 ?: s" l; m9 T
  2. {
    + ~+ Z# M- Y6 p" B

  3. 4 @/ t' R$ J3 P. s
  4.   /* USER CODE BEGIN RTC_Init 0 */; E1 c+ R; Q7 E
  5.                 RTC_TimeTypeDef time;   //时间结构体参数  T6 w  r# ]1 {
  6.                 RTC_DateTypeDef datebuff;   //日期结构体参数' \, v0 ^( v8 l- |
  7.   /* USER CODE END RTC_Init 0 */2 L" f( S1 T% E

  8. , n* w: U$ M% D' E" ?
  9.   RTC_TimeTypeDef sTime = {0};
    3 x8 @2 C" h% b5 c6 p$ Z3 H
  10.   RTC_DateTypeDef DateToUpdate = {0};
    # `5 B7 s( |1 R# z  l2 k
  11. * u' T+ K3 d* W2 c4 O  E* e, f/ E
  12.   /* USER CODE BEGIN RTC_Init 1 */0 ]7 O& t! V0 E$ t: M' e
  13.         __HAL_RCC_BKP_CLK_ENABLE();       //开启后备区域时钟
    + K8 J- G5 B+ k4 x. K) n
  14.         __HAL_RCC_PWR_CLK_ENABLE();                  //开启电源时钟2 S8 C2 m8 l4 c7 ]/ s/ I2 O
  15.   /* USER CODE END RTC_Init 1 */! e5 O1 O7 O5 i$ M2 e$ P2 c: q0 n
  16.   /**Initialize RTC Only $ o6 B6 q$ O2 J- _7 a: \, Y: M; @
  17.   */
    + |& F5 }, A, N# f, w% w' ^5 q
  18.   hrtc.Instance = RTC;
    $ ~" B2 |" O4 a, q
  19.   hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;+ `: R8 K: Q+ L6 r
  20.   hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE;5 i+ h3 o& h! z8 p& D
  21.   if (HAL_RTC_Init(&hrtc) != HAL_OK)
    & F* D8 X$ t: C8 \
  22.   {
    1 E% F. f  ~* q/ W! Y& L
  23.     Error_Handler();  N: |+ t, n. W. a6 [2 B
  24.   }
    0 u4 ?' l7 E' S: \6 x! J- e
  25. 6 X( T# f- u+ {+ P5 A
  26.   /* USER CODE BEGIN Check_RTC_BKUP */
    ; r/ V7 J0 ~! K
  27.         if(HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR1)!= 0x5051)
    : @$ v8 N" F5 B$ Q1 b
  28.         {- G2 q# R1 S8 m
  29.                 . w4 c2 n2 J$ Y6 f
  30.   /* USER CODE END Check_RTC_BKUP */
    ; G7 r9 b; T$ J2 l
  31. ) _3 ^; F( v3 n3 u' o
  32.   /**Initialize RTC and set the Time and Date
    4 C) e/ q1 L9 k% a8 s) U/ S9 f
  33.   */& P) F& B% q% p0 B7 E
  34.   sTime.Hours = 0x14;9 D0 Y+ b( U5 U2 k
  35.   sTime.Minutes = 0x30;6 C. X0 ?; t. L
  36.   sTime.Seconds = 0x0;, x. M9 s6 Y8 R5 c1 D4 h
  37. " Q9 F* i" M: t: g+ o7 s
  38.   if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
    : h: T2 L3 Z2 K% i3 \9 `
  39.   {
    6 t) R7 i* h3 j; g
  40.     Error_Handler();1 n' ]8 Y2 [' {) E$ Q, |
  41.   }
    ' `( F, h  ?" R
  42.   DateToUpdate.WeekDay = RTC_WEEKDAY_SATURDAY;8 ?  z% k5 t* R: `" T
  43.   DateToUpdate.Month = RTC_MONTH_APRIL;
    6 W6 Z! ~; G9 |) B7 i
  44.   DateToUpdate.Date = 0x25;
    # x/ ]: W- N4 Y2 M7 V1 ]! W; _
  45.   DateToUpdate.Year = 0x20;* C  `6 T6 Q$ w

  46. % L  F4 ?: e. {8 G/ a- x  u
  47.   if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BCD) != HAL_OK)
    / ]2 u9 d& k( r
  48.   {
    0 x6 K: E: c- d5 L4 _6 @$ e4 v+ z6 N
  49.     Error_Handler();* v) z, Z7 K( I% k0 h. Q0 {3 h
  50.   }8 h( P: m! ^' A
  51.   /* USER CODE BEGIN RTC_Init 2 */
    % k, q  q% w( P" a  i9 q8 g  P, o
  52. + R  B6 w. _/ L0 U) P' |0 n2 O
  53.         __HAL_RTC_SECOND_ENABLE_IT(&hrtc,RTC_IT_SEC);         //开启RTC时钟秒中断
    + a& c% Y  \8 s/ Z7 q; E: E( D; n  v
  54.         datebuff = DateToUpdate;  //把日期数据拷贝到自己定义的data中1 p) g; F# W; ?0 W
  55.         HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x5051);//向指定的后备区域寄存器写入数据5 B1 p2 ~; p1 D( T9 I
  56.         HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR2, (uint16_t)datebuff.Year);
    % O8 p9 d8 @9 Q" `5 p
  57.         HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR3, (uint16_t)datebuff.Month);  |0 F# W' @; g1 b7 A
  58.         HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR4, (uint16_t)datebuff.Date);
    " |, t- l% t+ s6 x* p
  59.         HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR5, (uint16_t)datebuff.WeekDay);& [- Q8 D9 `) a, M+ Y/ o% h
  60.         
    # _; |5 G! _6 i
  61.   }
    ; t. ]2 i! }( y6 T1 F
  62.         else% B6 H/ O( }9 C
  63.         {
    $ @; d8 ~! p2 z3 J. F! }# }4 \# \5 b
  64.                 datebuff.Year    = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2);
    + e; z; `3 X9 e3 z, z1 u/ c
  65.                 datebuff.Month   = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR3);- ~) ]9 I8 f! o3 {1 s
  66.                 datebuff.Date    = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR4);! T& n$ i8 N7 ?# W  H' v
  67.                 datebuff.WeekDay = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR5);
      H+ g; g$ O" Q: M  R2 C( E/ @' N
  68.                 DateToUpdate = datebuff;- Y) t) T1 o8 Z' ^5 x& ]: d2 r
  69.                 if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN) != HAL_OK)0 b6 O1 S6 n( T: v: D7 k, n7 d
  70.                 {
    ! U" [/ R2 I: B* p
  71.                         Error_Handler();) a- n4 [3 T( x5 w
  72.                 }  j* v3 N# \' r% @! F6 r( H+ W# x
  73.                 __HAL_RTC_SECOND_ENABLE_IT(&hrtc,RTC_IT_SEC);         //开启RTC时钟秒中断               
      }. F( }2 b5 a% T3 [  A/ v
  74.         }
    , v" O1 w6 L/ u
  75.         
    . j8 ]5 e% F2 \9 q! }% P

  76. % A$ F5 R* @; a' k7 |8 i

  77. 8 m. Y; v4 n6 w% b* M
  78. }
    - p, t) y' Z, b2 [. [" p* R1 Y
复制代码

5 D0 b6 _5 n8 }% Z( ?* O& N8 @. T2 ]  s2 ^/ y+ k
赞 收藏 评论0 发布时间:2022-3-23 15:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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