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

STM32CubeIDE实时时钟(RTC)经验分享

[复制链接]
攻城狮Melo 发布时间:2023-4-6 16:45
一、RTC简介
+ {) ]/ k8 H1 l0 J* b# W  i        实时时钟的缩写是RTC(Real_Time Clock),核心是晶振,晶振频率一般为32768 Hz 。它为分频计数器提供精确的与低功耗的实基信号。它可以用于产生秒、分、时、日等信息。为了确保时钟长期的准确性,晶振必须正常工作,不能够受到干扰。RTC的晶振又分为:外部晶振和内置晶振。* f' C3 [7 M9 f8 w8 t  I1 z2 C/ i
: N! _; E/ T2 a& i8 C
        RTC时间信息存储在后备寄存器(RTC_BKUP)中,在STM32中,通常采用一个32位计数器来计时,而不是用年月日时分秒的分组寄存器,因此在处理STM32的时间信息时(设置或读取),通常要求先处理时分秒时间,再处理年月日时间,以确保时间和日期值之间的一致性。& r' j) g+ M# `5 j$ K/ ?; s, s( }& f

, R. d( U$ j7 u% v        RTC和后备寄存器一般不会被系统或电源复原源复位,当从待机模式唤醒时,也不会复位,但是要注意的是,停止供电会出现重置,数值恢复到初始设置,因此如果要保持长期有效,尤其是断电重启依然有效,需要配置独立供电电源。
/ u5 L7 x* R( s$ b. Q& D
" B: l2 P- Y( O( C% g7 h4 h" K9 v        RTC时间是以振荡频率来计算的。故它本质上是一个计数器,只是通过换算呈现出日历时间信息。而一般的计数器都是16位的。又因为时间的准确性很重要,故震荡次数越低,时间的准确性越低。所以必定是个高次数。2^15 = 32768 ,而 32768 Hz = 2^15 即分频15次后为1Hz,周期 = 1s,因此RTC的时间基准是1秒。16位计数器最大值0XFFFFFFFF,按秒计算,能支持136年时长计算。
8 R% D1 ]- V/ }( W& G5 M0 @" m  h- R, f- K0 o. V
, T3 [: W! L: \" F! G% \/ j
二、工程创建及配置6 _9 D/ T. E: u+ K
        本文采用STM32L496VGTx-ali开发板来实现,该开发板是没有给RTC独立供电的,因此断电后会出现时间重置,但功能模拟演示还是OK的。本文假设已经移植了前面工程,并实现了lpuart1串口通信与电脑端的调试输出以及按键、LED灯驱动! F* i7 M- s: t  `

' ?& T) U7 p2 h5 ?' D        假设已经实现了lpuart1串口通信及按键、LED灯功能,现在双击.ioc打开cubeMX配置界面,开启RTC功能。数据格式支持二进制格式和BCD格式,在HAL中采用RTC_FORMAT_BIN(0x00000000u)和RTC_FORMAT_BCD(0x00000001u)标识。* C3 K- R2 y% D% D5 f
3 Q/ s8 \! [) O( y! p" z& b7 q! K
1927c21adfaa4522bbf8ef9117ae2483.png % ?, ?8 C( @: |. w0 h. V  t0 v
% Z! s1 a' M. h) J6 X5 w& R, G1 Z
         BCD(Binary Coded Decimal),即二-十进制编码,是用四位二进制码的 10 种组合表示十进制数0-9。这种编码至少需要用四位二进制码元,而四位二进制码元可以有16种组合。当用这些组合表示十进制数0-9时,有六种组合不用。
& F! j# ~* `% k/ N, Q+ F# w

5 U& l) k- v% i- m        RTC功能开启后,时钟树视图可以关于RTC的选择器可以选择外部高速时钟、外部低速时钟、内部低速时钟,本文选择外部低速时钟(LSE)。% J& F; D/ o0 \: `) W' j
- F& b. ?4 U% }0 e
77fc485a203d4e63a71c21cde13eb335.png
. T+ R1 e7 T1 S5 ]) C) A/ t/ }9 a3 Q+ B5 f+ C
         完成配置保存输出按钮(本文是为每个外设生成独立.h/.c源码),在生成的rtc.c文件中可以看到RTC初始化函数关于年月日、时分秒的设值和cubeMX上配置信息一致。
! v: O% k5 Y# t, ]& z+ \

: i7 S9 O( J+ i' v( s1 h# ?        备注:如果在cubeMX配置界面没有选择数据格式,年日、时分秒默认是0X前缀的。8 d9 [2 l6 p9 C! l7 {# H
5 K# y9 V( U2 `4 J& Z3 T
629d8cf91bdc4be8b721bdc3bae4b3f7.png
% O. [9 h2 n) ?7 E; `9 C! W' S$ g$ v. M" [
三、驱动代码设计实现
. T$ }& `9 p4 G, T" q/ b7 E3 O8 B; Y        【1】在Core/Src/rtc.c文件中,调整RTC初始化函数,在RTC设置初始化时间、日期之前,加入后备寄存器的读取,判定是否已经进行过初始化,避免重复设置时间、日期而改写本该持续计数的RTC计数。
( ^, C/ x" e: U, \3 o- L
  1. void MX_RTC_Init(void)
    6 T$ U& m- n9 p% A
  2. {
    $ n. @7 m9 `4 O" X! i5 j* s  P

  3. 9 p7 W0 s, h8 I5 W/ e; [* R
  4.   /* USER CODE BEGIN RTC_Init 0 */
    8 u" |+ {, C; y) h
  5. . |" t$ d5 l+ A
  6.   /* USER CODE END RTC_Init 0 */' E* b; W  m' n: }

  7. . c( N0 Y3 c# @, j3 G" L' e
  8.   RTC_TimeTypeDef sTime = {0};) p) X! @; X6 J; I/ H
  9.   RTC_DateTypeDef sDate = {0};' k: J% B) B9 y

  10. & i' C! P4 s- ]* e
  11.   /* USER CODE BEGIN RTC_Init 1 */
    3 @) \- v5 J) _8 S8 N# Z% @
  12.     //新增. v: Z8 Z0 y) F2 @
  13.   __HAL_RCC_PWR_CLK_ENABLE();//使能电源时钟PWR
    0 Y& h3 r9 J3 [; Y
  14.    HAL_PWR_EnableBkUpAccess();//取消备份区域写保护5 W/ f& {1 w4 x& }
  15.   /* USER CODE END RTC_Init 1 *// [9 C$ U9 b/ L# Z" S

  16. $ r" Z. i: r4 O' v% w3 W
  17.   /** Initialize RTC Only. b1 [0 d( a4 L+ [, i1 |3 i6 S
  18.   */) F; n1 X8 v: J, Z1 C/ M$ `
  19.   hrtc.Instance = RTC;4 s, z5 a: C# q* v' x( \5 f
  20.   hrtc.Init.HourFormat = RTC_HOURFORMAT_24;( [! v% N* J4 n& I6 u1 W
  21.   hrtc.Init.AsynchPrediv = 127;
    ! V8 I$ M$ e1 L2 R
  22.   hrtc.Init.SynchPrediv = 255;
    " W2 m" I: V+ W# n3 p2 H
  23.   hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;1 O& h! {# {1 C$ @5 f
  24.   hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
    - n- [  R# N& J& s/ r% z0 D
  25.   hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
    " y7 u8 |) B: q# X) ?) [5 s
  26.   hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;, z, S6 w9 a! z, A1 x
  27.   if (HAL_RTC_Init(&hrtc) != HAL_OK)
    ! J  E8 m6 J' [( q
  28.   {
    8 b2 g8 J6 ~' p
  29.     Error_Handler();
    4 ^( Q" X* O: b7 Q& L2 [8 E
  30.   }
    + p8 b" y! s8 {. ^) {% Z' `# m* ^

  31. / [4 p# R$ b" p( ?' N6 \+ O! ?
  32.   /* USER CODE BEGIN Check_RTC_BKUP */
    ) d0 l1 _7 [+ n9 B3 r
  33.     //新增
    & M' ^1 h+ |/ J( l3 g" N
  34.   if(HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR1)!=0X8888)//判断是否首次上电
    1 L% l5 K) p) s$ f+ u. \
  35.   {2 n  |; |4 j$ h- L
  36.           HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR1,0X8888); //标记数值(写入上电检查数值)
    0 @3 p3 ~( m2 u. D& i) Z
  37.   /* USER CODE END Check_RTC_BKUP */' `( S# t( F2 m0 @  B: b* ^* B

  38. 0 T" v; e8 C. h
  39.   /** Initialize RTC and set the Time and Date) w7 _  n! m. I
  40.   */' I. S. l+ L  n/ _
  41.   sTime.Hours = 12;7 F6 Z6 s9 h$ L$ n, L
  42.   sTime.Minutes = 30;$ Q/ A; j# p- I! j1 e5 y
  43.   sTime.Seconds = 30;
    3 f2 t& H! j/ ]: @1 I
  44.   sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
    6 ]/ b5 V$ e4 v1 E+ |
  45.   sTime.StoreOperation = RTC_STOREOPERATION_RESET;
    + S: j0 E6 D$ [/ S* ]+ |
  46.   if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
      u) g4 ^, W( V" o0 z7 b
  47.   {
    ) {) K% G8 ~5 R, G$ Q2 G
  48.     Error_Handler();$ ^; I' R% R7 p- ~" {" ]
  49.   }: V& i  ^$ u2 @) Q9 j0 h9 J
  50.   sDate.WeekDay = RTC_WEEKDAY_TUESDAY;6 Y) y" h8 v6 j9 Z0 V
  51.   sDate.Month = RTC_MONTH_DECEMBER;
    8 ~! q, n) {" l9 {
  52.   sDate.Date = 20;4 _) D( D/ C% o% Z- h; N
  53.   sDate.Year = 22;
    . K) L' ^# j! C3 F9 y! g% ^7 u
  54. . X7 P. s5 N5 E; g/ S
  55.   if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD) != HAL_OK): }' k6 w. {$ @, u0 I: M
  56.   {
    & [0 ^) O3 o) y: q$ {$ W$ n; J/ L8 ?5 \
  57.     Error_Handler();2 I5 P: O: ^: Z% V
  58.   }9 N4 U% P* Z3 _- [7 W8 G8 a1 K9 [
  59.   /* USER CODE BEGIN RTC_Init 2 */8 L) p8 l1 _% _  ?' r; L
  60.     //新增
    . q( |0 ?: ^% K! X8 N. w" f
  61. //  printf("手动初始化测试!\r\n");//显示初始化信息0 E% u+ \2 ~6 F; P; y. _5 B  D/ h
  62.   }
    " m0 E6 S& q/ L( b" P8 _
  63.   /* USER CODE END RTC_Init 2 */
    8 f  f: k( V' v! W

  64. # P+ A5 e1 e# j
  65. }
复制代码
' d2 U. \, Y+ I! o. V" f: K
        在main.c文件中,加入按键、LED灯、串口外设驱动头文件,4 A& }( N6 ^% }( x
  1. /* USER CODE BEGIN Includes */
    % S8 ?4 D0 z$ x+ k1 G; A8 _
  2. #include "../../ICore/key/key.h"! A) q  O9 s5 s0 O6 \5 V7 s$ z- n/ E
  3. #include "../../ICore/led/led.h"
    ! N1 _; F0 m$ V* ^+ d/ Q+ @
  4. #include "../../ICore/print/print.h"
    / r# U# e7 {* d
  5. #include "../../ICore/usart/usart.h"& A( e) W$ E# f7 ?; G8 j: z
  6. /* USER CODE END Includes */
复制代码
$ ~) p4 D8 U+ W/ ~" i# g" U
        在main函数中声明RTC日期、时间读取缓存变量。6 W6 w$ M% T6 R* A
  1.   /* USER CODE BEGIN 1 */
    : l, ?* [3 i8 ^$ l; I
  2.   RTC_DateTypeDef RtcDate;0 b% k$ o3 v, q0 Z$ v3 r) r
  3.   RTC_TimeTypeDef RtcTime;7 @9 Y6 L7 i& F' e% }7 {, l
  4.   uint8_t time_set = 0;//用户设置时间标记
    * A* p) b: d6 a
  5.   /* USER CODE END 1 */
复制代码
. S7 k( Y& f3 ~  L6 E1 p- e% E
        在main函数中开启串口lpuart1的中断接收。  O* j! q2 F& r+ ^5 d0 G& c) b
  1.   /* USER CODE BEGIN 2 */
    ' m) W5 n3 d! {4 Q
  2.   ResetPrintInit(&hlpuart1);
    8 N3 J+ K, n4 d3 U9 k8 B' p
  3.   HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
    ) T3 C) U" \$ }% T' ]
  4.   HLPUSART_RX_STA = 0;
    0 l- n; k# {- e6 B" h6 O( P
  5.   printf("APP reset now!\r\n");, }; X* ~0 E# g6 ?" |$ h9 w- Y7 p
  6.   /* USER CODE END 2 */
复制代码
1 [; `: r1 z/ @. z9 O. \
        在main函数循环体中,实现按键KEY1获取RTC时间以及按键KEY2设置RCT时间,注意RTC设置时间、日期和RTC读取时间、日期时,数据格式保持一致,最好和CubeMX设置时一致,例如读取、写入RTC时间日期都采用RTC_FORMAT_BCD格式。
3 T$ c& Y' l' c4 g) Y" m4 @: n
  1. /* USER CODE BEGIN WHILE */# c$ C( T3 b$ ~7 z' W
  2.   while (1). A# L: A0 W7 l
  3.   {1 l, W- w7 G) e% |$ p' ?7 h0 ^" G
  4.           if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
    # q0 U# M" c7 r( \+ @
  5.                     printf("lpuart1:%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);+ @; l4 T- ?' x- v" f% E
  6.                     if(time_set){. \, J4 a+ f  h. L5 g4 s3 q
  7.                             //将超级终端发过来的数据换算并写入RTC,读取数据为字符内容需要转换为数值$ K* o1 l& r  \
  8.                     RtcDate.Year =  (HLPUSART_RX_BUF[2]-0x30)*10+HLPUSART_RX_BUF[3]-0x30;//减0x30后才能得到十进制0~9的数据6 H6 q; S# v9 T# J
  9.                   RtcDate.Month =  (HLPUSART_RX_BUF[4]-0x30)*10+HLPUSART_RX_BUF[5]-0x30;  k: ?% t; Y0 b& t) ^! q% H
  10.                   RtcDate.Date =  (HLPUSART_RX_BUF[6]-0x30)*10+HLPUSART_RX_BUF[7]-0x30;" Y: E: L1 I2 x/ i5 [" P6 v
  11.                   RtcTime.Hours =  (HLPUSART_RX_BUF[8]-0x30)*10+HLPUSART_RX_BUF[9]-0x30;5 O  M5 E: d9 k: N
  12.                   RtcTime.Minutes =  (HLPUSART_RX_BUF[10]-0x30)*10+HLPUSART_RX_BUF[11]-0x30;0 V0 d, N' r. \' z, A/ `& p/ Y
  13.                   RtcTime.Seconds =  (HLPUSART_RX_BUF[12]-0x30)*10+HLPUSART_RX_BUF[13]-0x30;3 s/ t) s/ `8 b* u  @0 f: D1 I
  14.                   //写入格式RTC_FORMAT_BCD或RTC_FORMAT_BIN,确保写入及读取数据格式一致即可' `6 {! m, V" Q7 i# s( r
  15.                     if (HAL_RTC_SetTime(&hrtc,  &RtcTime, RTC_FORMAT_BCD) != HAL_OK)//将数据写入RTC程序3 s& [2 v. E; _! t, n- i* b4 g# [
  16.                     {& A" o$ e! O6 r8 L
  17.                         printf("写入时间失败!\r\n"); //显示写入失败+ T  |0 e* B' J' B- j0 C8 N( U
  18.                     }else if (HAL_RTC_SetDate(&hrtc,  &RtcDate, RTC_FORMAT_BCD) != HAL_OK)//将数据写入RTC程序8 \. z' Y9 T& j3 B4 y9 F
  19.                     {
    ; v  n' c% t8 ~/ ^0 U& m
  20.                         printf("写入日期失败!\r\n"); //显示写入失败
    ! X  |+ L" `  [# n6 ]9 ]. G
  21.                     }else printf("写入成功!\r\n");//显示写入成功
    : T/ f9 \$ q# u' A- n- [" b6 V
  22.                     time_set=0;
    9 i3 ^) u+ `0 `$ n
  23.                     }1 c! m/ @! ^3 t& R  z
  24.                     HLPUSART_RX_STA=0;//接收错误,重新开始2 V. l  X( `0 ~6 ]  G# D' U$ _
  25.                     HAL_Delay(100);//等待5 K, K3 X( x7 s- y) |' V
  26.             }7 [0 d! x; q6 h1 B, A' J
  27.             if(KEY_1())1 w& P6 E" \% s; w! S) _
  28.           {+ K# x; T- j$ h2 ^: [2 l
  29.                     HAL_RTC_GetTime(&hrtc, &RtcTime,  RTC_FORMAT_BCD);//读出时间值
    7 `2 B! P0 Z# T* _% S( d5 M
  30.           HAL_RTC_GetDate(&hrtc, &RtcDate,  RTC_FORMAT_BCD);//一定要先读时间后读日期& V8 e4 e* x0 G" X, t9 k4 @. g$ m! ]
  31.           printf(" RTC实时时钟测试\r\n");7 F2 w( o5 U4 c2 n' f$ v
  32.           printf(" 实时时间:%04d-%02d-%02d  %02d:%02d:%02d  \r\n",2000+RtcDate.Year,; `7 B! s4 M- a. h% |8 B
  33.                           RtcDate.Month, RtcDate.Date,RtcTime.Hours, RtcTime.Minutes, RtcTime.Seconds);//显示日期时间" Q0 x+ t+ N& G9 c$ e- f) X! F6 ^" V
  34.           }3 g0 ~. D' Q0 k/ B: ^
  35.             if(KEY_2())! N- E0 C" c, v
  36.           {* h( `+ Z3 F1 I: S/ ~
  37.                     printf(" 请输入设置时间,格式20221220183030,按回车键确定! \r\n");
    8 q! [5 c8 }: T& I. V
  38.                     time_set=1;
    5 |% G4 b  @) T2 L) O
  39.           }' ^6 `3 u/ }+ I9 K
  40.     /* USER CODE END WHILE */
复制代码

2 C5 E  h: _. h' w: c: }         注意和前面简介说过,确保时间和日期值之间的一致性,先处理时间(时分秒),在处理日期(年月日),在stm32l4xx_hal_rtc.c的HAL_RTC_GetTime函数注释中也有明确要求如下,“您必须在HAL_RTC_GetTime()之后调用HAL_RTC_GetDate()来解锁值以确保时间和日期值之间的一致性。读取RTC当前时间锁定日历阴影寄存器中的值,直到读取当前日期以确保时间和日期值之间的一致性。”:& l6 _4 ~0 j# z& U3 N6 j8 }
/ z1 p) @/ k; H5 m$ P
27887a24a62d46a1b27a1e4efe8855d6.png
5 R* J, C9 l7 Q+ ?; B/ Z/ C2 i' C, M3 S) F& E/ d* @' m
* B6 {9 g& X# ~
四、编译及测试
) x$ A7 x  l' u        和本专栏其他博文一样,设置好工程输出目标格式支持,配置好运行调试支持后,点击编译工程及运行按钮,完成工程编辑及下载。       - |, h* ], n! {# a2 A8 {' C  H

3 `8 v9 C0 X/ b6 t
e451e4c7dd9b4c5fa03817fca447c916.png
0 J2 W" s0 O0 f+ v2 Z, {& L1 O% h: a* G$ E+ M

6 |5 @: w4 f" d" e% s+ m        打开串口助手工具,连接到开发板,测试
1 E* F9 u* y7 L& L3 C3 g( Q! B
$ E2 T9 l6 ~. H/ u' G5 M. {
        【1】按键KEY1,读取RTC时间,读取出的时间就去前面在cubeMX 设置的时间,并开始计数,因此分秒有差异。
+ G0 A' x: T" R# G1 G
5 E. G* H: {( ?, X9 ]2 D
fb4cb2634b1c469cabfdac88376d1c3b.png 0 ], G3 t* I- w- ]$ a7 @9 W

+ g, r3 U5 j) j% @  _         【2】按键KEY2设置时间,并按格式输入时间,然后在按键KEY1读取新的RTC时间,如下图所示,设置时间生效。  f0 o+ @: i) P- x1 v' ]
9 T# j5 s; ?6 |$ I5 i
30dc865389204e619affac8dc1687db7.png
( i4 I8 W! O: z" k/ E; Q
+ r! M6 n- D4 r) s6 ?) Y         【3】按开发板上的Reset按键,系统复位,再按键KEY2读取RTC时间,时间依然正常计数6 w0 C+ Q' u3 L4 r) H, U1 i

* M1 O1 \$ _. S# {' g- o% H
1862d58464bb469ab8f231cf549be7c0.png
) ?& z; p3 r  {* l1 G1 R( t5 A! q9 E4 c' i0 q0 j2 h. n
/ K. O4 H9 @6 b3 m2 J  b' ?
         【4】断电一会,然后重新上电,再按键KEY2读取RTC时间,时间恢复到CubeMX设置的初始时间(即为断电后,原来写入的标识无法保持,HAL_RTCEx_BKUPRead读取标记失败重新初始化)。) u) Q; Z8 U2 E  }9 w3 U
' h+ j$ J+ d% v, y2 j
6d8f19e2a6ac4eaf959ccae14e9e980d.png
0 S  ~- L& {" I0 A6 J
% j* m5 c. ^& ^/ v6 z8 h% @& o
————————————————* d; e  O6 ]1 Q2 v% G9 r7 d% T. o8 e0 E
版权声明:py_free-物联智能如有侵权请联系删除
7 x7 V( z0 D. |0 Q( G& _2 Q, l  n/ b- o! V# O- J

2 A+ B: e5 ^3 o! @5 U( T. V9 X6 m* U$ G+ {
收藏 评论0 发布时间:2023-4-6 16:45

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版