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

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

[复制链接]
攻城狮Melo 发布时间:2023-4-6 16:45
一、RTC简介
- t  P9 [$ I8 N9 A        实时时钟的缩写是RTC(Real_Time Clock),核心是晶振,晶振频率一般为32768 Hz 。它为分频计数器提供精确的与低功耗的实基信号。它可以用于产生秒、分、时、日等信息。为了确保时钟长期的准确性,晶振必须正常工作,不能够受到干扰。RTC的晶振又分为:外部晶振和内置晶振。
' V/ i6 d3 C: W7 E
- r/ J$ w! b' ~3 e        RTC时间信息存储在后备寄存器(RTC_BKUP)中,在STM32中,通常采用一个32位计数器来计时,而不是用年月日时分秒的分组寄存器,因此在处理STM32的时间信息时(设置或读取),通常要求先处理时分秒时间,再处理年月日时间,以确保时间和日期值之间的一致性。5 j9 h* }# b1 y6 M* N  Z4 B& L

$ a+ J3 }0 B- y5 t4 o        RTC和后备寄存器一般不会被系统或电源复原源复位,当从待机模式唤醒时,也不会复位,但是要注意的是,停止供电会出现重置,数值恢复到初始设置,因此如果要保持长期有效,尤其是断电重启依然有效,需要配置独立供电电源。
% c/ N2 z9 J) o5 [! v9 Z- V# w9 I1 |. @& g% k( I$ _- u
        RTC时间是以振荡频率来计算的。故它本质上是一个计数器,只是通过换算呈现出日历时间信息。而一般的计数器都是16位的。又因为时间的准确性很重要,故震荡次数越低,时间的准确性越低。所以必定是个高次数。2^15 = 32768 ,而 32768 Hz = 2^15 即分频15次后为1Hz,周期 = 1s,因此RTC的时间基准是1秒。16位计数器最大值0XFFFFFFFF,按秒计算,能支持136年时长计算。: ?2 P; O* f) M# ^! n& s
& o  v$ s' n  j; N
. i3 m9 g7 y( K" m3 I3 t; G
二、工程创建及配置+ }7 @- D- V7 Z& g- K
        本文采用STM32L496VGTx-ali开发板来实现,该开发板是没有给RTC独立供电的,因此断电后会出现时间重置,但功能模拟演示还是OK的。本文假设已经移植了前面工程,并实现了lpuart1串口通信与电脑端的调试输出以及按键、LED灯驱动7 {0 E; r& B7 n  P" s  w

# s3 k3 e- t/ _0 j+ w6 W        假设已经实现了lpuart1串口通信及按键、LED灯功能,现在双击.ioc打开cubeMX配置界面,开启RTC功能。数据格式支持二进制格式和BCD格式,在HAL中采用RTC_FORMAT_BIN(0x00000000u)和RTC_FORMAT_BCD(0x00000001u)标识。
; i- F# F' p- C8 c! v% X# {7 H' I8 H
1927c21adfaa4522bbf8ef9117ae2483.png
% O3 p# G" X7 J  @$ Q; D
9 x% g, v# |* N8 y% a+ N! ?         BCD(Binary Coded Decimal),即二-十进制编码,是用四位二进制码的 10 种组合表示十进制数0-9。这种编码至少需要用四位二进制码元,而四位二进制码元可以有16种组合。当用这些组合表示十进制数0-9时,有六种组合不用。! `/ T$ c$ U- ]4 N+ y8 [+ @9 s6 I
0 e! _# r. \  P" l5 I* O8 o
        RTC功能开启后,时钟树视图可以关于RTC的选择器可以选择外部高速时钟、外部低速时钟、内部低速时钟,本文选择外部低速时钟(LSE)。8 ?  X! I6 f5 {: l- }

2 k4 u* R% [% i# Y% G6 a8 P
77fc485a203d4e63a71c21cde13eb335.png . a! z' ]. l% z, P- t0 b
( i2 \7 k) f  B6 f( w2 L
         完成配置保存输出按钮(本文是为每个外设生成独立.h/.c源码),在生成的rtc.c文件中可以看到RTC初始化函数关于年月日、时分秒的设值和cubeMX上配置信息一致。8 V0 W& n/ c% U5 B- g
* e& g; j$ V5 O5 F5 X" w
        备注:如果在cubeMX配置界面没有选择数据格式,年日、时分秒默认是0X前缀的。
2 k: H& w/ i- [5 h
( ]4 B/ {4 L) f( S
629d8cf91bdc4be8b721bdc3bae4b3f7.png
  Q' j- f0 @* j' l+ x* w; ]/ I% C  r8 I" p7 h' F# y* Z
三、驱动代码设计实现
  v& S# I# G, W2 Z0 K6 y8 r        【1】在Core/Src/rtc.c文件中,调整RTC初始化函数,在RTC设置初始化时间、日期之前,加入后备寄存器的读取,判定是否已经进行过初始化,避免重复设置时间、日期而改写本该持续计数的RTC计数。9 W, j! d- l8 D$ I9 M
  1. void MX_RTC_Init(void)
    ! k8 d# C% v, E  P
  2. {
    / L: n$ \' c5 ?* R4 T, b- q# i
  3. : p5 L2 z+ U9 N+ l' X1 t% ^  j. [8 M
  4.   /* USER CODE BEGIN RTC_Init 0 */
    ' ~# _7 U# l& R. s
  5. * W+ [8 G( H' r3 C# F! G+ ~
  6.   /* USER CODE END RTC_Init 0 */
    ) g9 i1 A% w0 Y0 q$ n

  7. 1 L( {# q. p9 V. @' l, h& p
  8.   RTC_TimeTypeDef sTime = {0};
    " a7 j( E/ ?# f) |
  9.   RTC_DateTypeDef sDate = {0};
    2 n, X8 b0 O7 }0 G2 Q

  10. + \, ]. d4 V  t; s0 x
  11.   /* USER CODE BEGIN RTC_Init 1 */
    ( k7 q" i: ]7 Q8 D
  12.     //新增+ C9 c$ f7 }' d4 L
  13.   __HAL_RCC_PWR_CLK_ENABLE();//使能电源时钟PWR
    7 W9 k% K) {5 F7 R* z
  14.    HAL_PWR_EnableBkUpAccess();//取消备份区域写保护
    : @/ n. F* K  s- f
  15.   /* USER CODE END RTC_Init 1 */
    , k# A' t# T" R$ v! H" [  |8 T, {+ r
  16. $ l$ ~: e! j; r* c  a# v( d
  17.   /** Initialize RTC Only
    . R+ x" v9 w* A- Z, x) ?; m
  18.   */
    . s8 r' T; e( z
  19.   hrtc.Instance = RTC;8 q2 @) H9 Q% r1 S' Y  V
  20.   hrtc.Init.HourFormat = RTC_HOURFORMAT_24;4 Q* {, U8 U% [7 C
  21.   hrtc.Init.AsynchPrediv = 127;
    ; L3 o, U9 u* s. b: X
  22.   hrtc.Init.SynchPrediv = 255;8 f* E) N  }* D3 b8 I; Z% t
  23.   hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
      U* L4 R' b* A& |- X5 d
  24.   hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;/ v- N; F6 x1 I+ x% m' I& r# `6 [
  25.   hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
    1 T/ `2 P  P; X% c+ g+ ^  E7 J
  26.   hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;. K6 c  Y3 w/ ?4 o/ M
  27.   if (HAL_RTC_Init(&hrtc) != HAL_OK)
      h& U1 T% j( c' U
  28.   {$ F$ Y! k0 [2 E
  29.     Error_Handler();
    / k7 P0 G9 g) c5 Z# V) t
  30.   }  x* k! d0 k- e% L3 V  n
  31. - ]: d( A  k$ j
  32.   /* USER CODE BEGIN Check_RTC_BKUP */8 f7 }  k! n2 \% ?- K, n' D
  33.     //新增4 a+ m5 _  F' [7 L- O6 F9 q/ ~) v
  34.   if(HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR1)!=0X8888)//判断是否首次上电
    - P7 ^) ~+ _  {9 x3 _5 I# f3 K6 X
  35.   {
    7 @/ J4 j" A+ s& N4 @# q
  36.           HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR1,0X8888); //标记数值(写入上电检查数值)
    7 z, T( s: |2 Q. P
  37.   /* USER CODE END Check_RTC_BKUP */
    ! x- |* F2 y0 g% D( k0 F
  38. & G) C8 w5 G( P$ S0 |) u' J% c
  39.   /** Initialize RTC and set the Time and Date
    ( x! E3 s+ z! j- i
  40.   */
    ; h$ D+ V; b) O! O
  41.   sTime.Hours = 12;
    % F- @) ]7 o' H" |" T
  42.   sTime.Minutes = 30;
    % S- W# W# V! E1 b+ c& {
  43.   sTime.Seconds = 30;! J' {1 I' I- L4 E0 G& N5 p
  44.   sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;. C: M4 t, t8 n
  45.   sTime.StoreOperation = RTC_STOREOPERATION_RESET;5 U) }* b$ ~0 B; x$ a
  46.   if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
    3 w' L: q  _! u8 y$ N' q
  47.   {3 Q/ q9 K4 m, C5 Y
  48.     Error_Handler();+ C& `6 }/ |$ B) J; b! l
  49.   }! @( d) k5 Z3 u2 W7 t
  50.   sDate.WeekDay = RTC_WEEKDAY_TUESDAY;
    ' U% K& _/ s! S& T( g& |
  51.   sDate.Month = RTC_MONTH_DECEMBER;
    0 n. j+ }2 F0 T) B8 B
  52.   sDate.Date = 20;, r1 A1 g; c; s& M2 h
  53.   sDate.Year = 22;
    3 Z) C/ Y" x" q
  54. ; p4 Z4 ~8 p3 L5 x! K/ }' q
  55.   if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD) != HAL_OK)
    # E  I$ ^! B# R8 t7 R
  56.   {& p/ v9 {+ L% x! {3 E3 q" L
  57.     Error_Handler();7 _3 [3 A6 h8 n/ ^
  58.   }
    - H! Y$ @+ Q- S* O7 Z
  59.   /* USER CODE BEGIN RTC_Init 2 */, V- H5 b8 G% f! {8 e$ C  \
  60.     //新增* r* r+ q. g& M3 E2 O, A
  61. //  printf("手动初始化测试!\r\n");//显示初始化信息
      w( m; O% b4 }- }$ m  Y
  62.   }
    4 R& S' y8 ~2 N9 M2 N
  63.   /* USER CODE END RTC_Init 2 */" W" |  i2 M. R  l) \" U6 L
  64. # X/ M& _( {+ |3 S& e
  65. }
复制代码
4 d2 S3 o7 O, y# y
        在main.c文件中,加入按键、LED灯、串口外设驱动头文件,
) n$ p6 {- A# b0 m% B' D, W
  1. /* USER CODE BEGIN Includes */
    . M; q& f3 R7 }7 Y' \% J
  2. #include "../../ICore/key/key.h"
    % a& j$ `& w0 \" {
  3. #include "../../ICore/led/led.h"
    * p- S7 h( h5 p; t" \! Q+ m. x
  4. #include "../../ICore/print/print.h"
    9 U  M4 ]4 j) Q9 Z
  5. #include "../../ICore/usart/usart.h"
    7 u: n) S/ U% ?5 R4 c* b* r
  6. /* USER CODE END Includes */
复制代码

! W4 x- h9 @, p3 Z9 l        在main函数中声明RTC日期、时间读取缓存变量。
  s" |8 H8 e5 t
  1.   /* USER CODE BEGIN 1 */
    0 d% r# q" R" a
  2.   RTC_DateTypeDef RtcDate;1 x/ X6 A7 s* z" l3 _/ d3 |, f, F
  3.   RTC_TimeTypeDef RtcTime;. {& o8 G8 U2 @' X9 V0 u
  4.   uint8_t time_set = 0;//用户设置时间标记
    3 v( u% U" Q. M9 Q/ c
  5.   /* USER CODE END 1 */
复制代码

+ y: t% d6 T: ^        在main函数中开启串口lpuart1的中断接收。
5 C1 }6 J: \9 i0 m. q- i( T
  1.   /* USER CODE BEGIN 2 */
    . O- h% f6 E% c6 j  ]4 S3 Z
  2.   ResetPrintInit(&hlpuart1);
    2 _- V3 n2 z. y2 g
  3.   HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
    ( ]+ D' ]7 s' H9 f( o
  4.   HLPUSART_RX_STA = 0;
    ! h) S' D: [! p- R+ G! W& J
  5.   printf("APP reset now!\r\n");; \' U0 _! R9 n( d& j3 b4 B
  6.   /* USER CODE END 2 */
复制代码

5 T/ {: t) T6 U- U8 P& W+ l        在main函数循环体中,实现按键KEY1获取RTC时间以及按键KEY2设置RCT时间,注意RTC设置时间、日期和RTC读取时间、日期时,数据格式保持一致,最好和CubeMX设置时一致,例如读取、写入RTC时间日期都采用RTC_FORMAT_BCD格式。! \+ P6 R% ]. I1 U8 S
  1. /* USER CODE BEGIN WHILE */
    7 H, }6 I7 {7 J& T! M- c
  2.   while (1)- R8 Z1 S8 |% o, N4 ^1 f5 C
  3.   {
    : O7 B9 g" w+ `6 h. L* v/ O0 d
  4.           if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始1 W8 I: ]/ T5 M, J) y0 N
  5.                     printf("lpuart1:%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);/ u5 T" K! w7 j+ M
  6.                     if(time_set){
    0 [# g, E3 z% d
  7.                             //将超级终端发过来的数据换算并写入RTC,读取数据为字符内容需要转换为数值
    8 h. y/ C/ ^6 n' W
  8.                     RtcDate.Year =  (HLPUSART_RX_BUF[2]-0x30)*10+HLPUSART_RX_BUF[3]-0x30;//减0x30后才能得到十进制0~9的数据
    . m% s  l3 C# j& o+ x! b
  9.                   RtcDate.Month =  (HLPUSART_RX_BUF[4]-0x30)*10+HLPUSART_RX_BUF[5]-0x30;
    . o$ O1 ?  M! a0 J
  10.                   RtcDate.Date =  (HLPUSART_RX_BUF[6]-0x30)*10+HLPUSART_RX_BUF[7]-0x30;! H, w- n6 W& `1 a
  11.                   RtcTime.Hours =  (HLPUSART_RX_BUF[8]-0x30)*10+HLPUSART_RX_BUF[9]-0x30;& S8 T" X$ g& Q1 c  F2 Y- c
  12.                   RtcTime.Minutes =  (HLPUSART_RX_BUF[10]-0x30)*10+HLPUSART_RX_BUF[11]-0x30;
      `! U: C9 q! X  _/ b
  13.                   RtcTime.Seconds =  (HLPUSART_RX_BUF[12]-0x30)*10+HLPUSART_RX_BUF[13]-0x30;- l3 D1 ]3 }3 p. G: t
  14.                   //写入格式RTC_FORMAT_BCD或RTC_FORMAT_BIN,确保写入及读取数据格式一致即可
    % W$ h8 G* ^& [( i. |. O# l1 \7 C
  15.                     if (HAL_RTC_SetTime(&hrtc,  &RtcTime, RTC_FORMAT_BCD) != HAL_OK)//将数据写入RTC程序" e: G4 C. f! e6 }- A, q
  16.                     {
    # N+ D! e: j; `# `
  17.                         printf("写入时间失败!\r\n"); //显示写入失败
    6 h5 u( o# e" a* t  r, F6 S
  18.                     }else if (HAL_RTC_SetDate(&hrtc,  &RtcDate, RTC_FORMAT_BCD) != HAL_OK)//将数据写入RTC程序
    2 `% ?8 _, S9 p4 z
  19.                     {4 t* z) O+ h; H
  20.                         printf("写入日期失败!\r\n"); //显示写入失败
    ' x+ {$ r+ \- Z% P2 [4 E
  21.                     }else printf("写入成功!\r\n");//显示写入成功. x; e, h0 T" Y8 ~+ T
  22.                     time_set=0;+ d* M4 j1 U, o
  23.                     }% z0 @0 H" w- u5 i6 H4 m3 O2 i
  24.                     HLPUSART_RX_STA=0;//接收错误,重新开始
    / H% P6 n" z3 D8 C' F1 a
  25.                     HAL_Delay(100);//等待9 T9 E, X+ D+ o
  26.             }7 d. a& g/ U6 ]" X2 s
  27.             if(KEY_1())" G/ d& K. O" |$ |" h
  28.           {' [# e$ c7 ]" I: H! w1 c
  29.                     HAL_RTC_GetTime(&hrtc, &RtcTime,  RTC_FORMAT_BCD);//读出时间值
    7 l+ d7 B+ Z; |6 M6 m8 _5 c) Z& l
  30.           HAL_RTC_GetDate(&hrtc, &RtcDate,  RTC_FORMAT_BCD);//一定要先读时间后读日期
    2 K7 K- t  |2 @+ @- N7 {8 Y
  31.           printf(" RTC实时时钟测试\r\n");3 K. O! N3 W9 c  G
  32.           printf(" 实时时间:%04d-%02d-%02d  %02d:%02d:%02d  \r\n",2000+RtcDate.Year,
    . `0 p1 l8 b# R$ g) r$ {3 [
  33.                           RtcDate.Month, RtcDate.Date,RtcTime.Hours, RtcTime.Minutes, RtcTime.Seconds);//显示日期时间- M) j7 p0 N  s! z9 Y' G( X3 T9 Q
  34.           }6 {$ x5 j1 A! ^) M
  35.             if(KEY_2())4 g3 q0 Q0 I9 ]1 X3 V+ g
  36.           {% P& N0 q- E+ f5 V' E
  37.                     printf(" 请输入设置时间,格式20221220183030,按回车键确定! \r\n");" b+ k8 Q$ }7 b# d# o! d) Q
  38.                     time_set=1;
    3 K5 ^3 P) n( r3 l* z) |
  39.           }1 \3 V4 ~4 _! O0 ~+ W
  40.     /* USER CODE END WHILE */
复制代码
" y( u% `: |8 _- ^# j/ m4 a
         注意和前面简介说过,确保时间和日期值之间的一致性,先处理时间(时分秒),在处理日期(年月日),在stm32l4xx_hal_rtc.c的HAL_RTC_GetTime函数注释中也有明确要求如下,“您必须在HAL_RTC_GetTime()之后调用HAL_RTC_GetDate()来解锁值以确保时间和日期值之间的一致性。读取RTC当前时间锁定日历阴影寄存器中的值,直到读取当前日期以确保时间和日期值之间的一致性。”:  T+ l' V  }0 |* l
2 U9 @' w9 Y& a7 s8 a5 b
27887a24a62d46a1b27a1e4efe8855d6.png
! h3 l, v9 }1 r8 a% x4 H1 p* {* g  @5 w2 ^

7 P! H' z2 l# a& y, z( @! Y; h- q$ V- M四、编译及测试
+ Z  ?$ u' J% ?- f& p, O9 M' n        和本专栏其他博文一样,设置好工程输出目标格式支持,配置好运行调试支持后,点击编译工程及运行按钮,完成工程编辑及下载。      
6 u1 J# i( `4 w  ~7 T' \6 N
! _3 U& c; v( {' R
e451e4c7dd9b4c5fa03817fca447c916.png . E* ~  ]- J: s/ x

  c* |) ]8 e4 z: g/ s- s. |

  b& ~0 z' q3 O        打开串口助手工具,连接到开发板,测试4 j: A2 Q  e- k& E

! |/ L. A6 K$ `+ ~" l        【1】按键KEY1,读取RTC时间,读取出的时间就去前面在cubeMX 设置的时间,并开始计数,因此分秒有差异。
! c  n/ p4 W$ J' D$ W' ~$ g% g! N2 W$ y0 I- ^* N1 F- _
fb4cb2634b1c469cabfdac88376d1c3b.png ! ^  _# x& d- J1 Q& n
; }( S+ C( N& O8 I) ?
         【2】按键KEY2设置时间,并按格式输入时间,然后在按键KEY1读取新的RTC时间,如下图所示,设置时间生效。! p0 ?6 \, u& w
% ~5 i1 l9 Z0 \
30dc865389204e619affac8dc1687db7.png 0 ~- y6 Q2 g( V6 j3 W; y

% q# Y; h0 o$ E5 R3 L1 T( G         【3】按开发板上的Reset按键,系统复位,再按键KEY2读取RTC时间,时间依然正常计数( J& B% |- S$ m0 p1 I, O$ \' y
- {3 D9 m2 X4 M. [4 e+ I  E+ A
1862d58464bb469ab8f231cf549be7c0.png
; L2 t2 s# m2 X4 d, Y/ w6 `: ^' f- X# w. R+ Z  _* z; B

: K# O/ e3 J- ?, h. r         【4】断电一会,然后重新上电,再按键KEY2读取RTC时间,时间恢复到CubeMX设置的初始时间(即为断电后,原来写入的标识无法保持,HAL_RTCEx_BKUPRead读取标记失败重新初始化)。
8 D8 M1 |; q. r$ N4 I. F6 ?, F' V, z
6d8f19e2a6ac4eaf959ccae14e9e980d.png
& l8 S9 l# S7 S
% q7 z, j3 d9 W2 m
————————————————3 z* t& m* C- {8 L0 R; }& f! w
版权声明:py_free-物联智能如有侵权请联系删除
9 ]* I& e4 W% b8 D- u: B. `
6 t- U7 D+ L, k
8 t* @9 Y# M% h9 a

7 p' k2 }1 w5 h$ X" M9 S3 t: ?
收藏 评论0 发布时间:2023-4-6 16:45

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版