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

【经验分享】STM32实例-RTC实时时钟实验③-RTC设置日期时间函数

[复制链接]
STMCU小助手 发布时间:2022-6-28 18:34
  本实验使用到硬件资源如下:
(1)D1 指示灯
(2)串口 1
(3)RTC
    D1 指示灯、串口 1 电路在前面章节都介绍过,这里就不多说,至于 RTC 它属于 STM32F1 芯片内部的资源,只要通过软件配置好即可使用。D1 指示灯用来提示系统运行状态。串口1将读取的 RTC 时间日期信息打印出来。
    这里需要注意 RTC 不能断电,否则时间数据将会丢失,如果想让时间在断电后还可以继续走,那么必须确保开发板上的纽扣电池有电。
    实验所要实现的功能是:设置 RTC 时间日期初值,在 RTC 秒中断内使用串口打印出RTC 日期和时间,D1指示灯闪烁提示系统运行。程序框架如下:
(1)初始化 RTC,设置RTC 时间日期初值
(2)开启 RTC 的秒中断,编写 RTC 中断函数,
(3)在 RTC 中断内更新时间并打印输出
(4)编写主函数
    前面介绍 RTC 配置步骤时, 就已经讲解如何初始化 RTC。下面我们打开 “RTC 实时时钟实验”工程,在 APP 工程组中添加 rtc.c 文件(里面包含了 RTC 驱动程序),在 StdPeriph_Driver 工程组中添加 stm32f10x_rtc.c、stm32f10x_pwr.c 和 stm32f10x_bkp.c 库文件。RTC操作的库函数都放在 stm32f10x_rtc.c、stm32f10x_pwr.c 和 stm32f10x_bkp.c及对应的头文件中,所以使用到 RTC 就必须加入 stm32f10x_rtc.c、stm32f10x_pwr.c 和 stm32f10x_bkp.c 文件,同时还要包含对应的头文件路径。
    这里我们分析几个重要函数,其他部分程序大家可以打开工程查看。
RTC初始化函数
要使用 RTC,我们必须先对它进行配置。初始化代码如下:
  1. /****************************************************************7 X5 G$ x! e# `) E4 T9 \, X2 {
  2. * 函 数 名 : RTC_Init
    - y7 R# B5 f6 ~% p8 Z6 q% g$ z6 a+ t
  3. * 函数功能 : RTC 初始化
    4 F$ H6 `+ G8 ^4 F& l
  4. * 输 入 : 无
      d4 j* t; s8 v2 Z- H' M; `$ G
  5. * 输 出 : 0,初始化成功
    ) Y2 x3 ~4 X6 [, Q; r
  6. 1,LSE开启失败
    " l) d* {  }: M4 Y
  7. *****************************************************************/
    " H7 [! L5 j" s* }/ n
  8. u8 RTC_Init(void)
    8 E3 x' t' F" j% e! c
  9. {" R! J8 _0 m% _- [. Q. k
  10. //检查是不是第一次配置时钟
    3 F" W$ ~0 b7 f; x" _
  11. u8 temp=0;; v) j! X& P" e# s( x! J
  12. RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP,$ j6 u4 \; t, f/ _* t! F
  13. ENABLE); //使能 PWR 和BKP外设时钟
    ! @5 A1 d* x4 p/ d' A" |
  14. PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问' z* K9 `7 A" Z1 j  M
  15. if (BKP_ReadBackupRegister(BKP_DR1) != 0xA0A0) // 从 指 定的后备寄存器中读出数据:读出了与写入的指定数据不相乎" q, |' w7 H) ?3 B( q- L: V$ v( O
  16. {! W7 n: r. I* X( M3 T
  17. BKP_DeInit(); //复位备份区域
    . E4 h1 o+ U0 P* d" }
  18. RCC_LSEConfig(RCC_LSE_ON); //设置外部低速晶振(LSE),使用外设低速晶振- a  C# t' G- _$ H* f2 C
  19. while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250)
    0 i+ K# {. t& J2 S0 @, J
  20. //检查指定的 RCC 标志位设置与否,等待低速晶振就绪  T% q4 U/ L! i. J8 F' B
  21. {# f( o* |* x7 K. z. I) j7 X
  22. temp++;( Z* K# @* n' U: \
  23. delay_ms(10);; ^% r1 k. w& A7 l; |0 Y
  24. }' Q  {% V/ m7 r% W
  25. if(temp>=250)return 1;//初始化时钟失败,晶振有问题
    5 L8 I/ p+ `; R0 t+ y- x. e5 Z
  26. RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置 RTC 时钟(RTCCLK),选择 LSE 作为RTC时钟
    . b% Z4 k0 v/ B
  27. RCC_RTCCLKCmd(ENABLE); //使能 RTC 时钟
    3 X$ Y) T. k" I- e$ J# H
  28. RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成
    , l; D# t) Q* e$ a
  29. RTC_WaitForSynchro(); //等待 RTC 寄存器同步
    " ?4 m3 i3 O" B* ?+ K, M  n; P
  30. RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能 RTC秒中断
    % Q- P0 o8 @  C' N
  31. RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成
    4 E: P' e9 X$ s
  32. RTC_EnterConfigMode();// 允许配置: i: A4 y3 O2 \6 |; u
  33. RTC_SetPrescaler(32767); //设置 RTC 预分频的值
    . O, d8 W7 Z2 g0 ?6 ?/ p
  34. RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成
    ( Z. W. V. M2 U) l8 C' \7 k0 T
  35. RTC_Set(2017,3,22,17,34,55); //设置时间' K& t' z7 v/ W9 i
  36. RTC_ExitConfigMode(); //退出配置模式
    5 C4 q' ]6 H7 i+ E$ R* s1 G
  37. BKP_WriteBackupRegister(BKP_DR1, 0XA0A0); //向指定的后备寄存器中写入用户程序数据5 \0 m8 V/ G* ~& o8 i7 g
  38. }. |9 u* u  E) m6 I* u
  39. else//系统继续计时
    3 }( n  X# e  P9 z" `# [8 q- U
  40. {
    2 c1 [4 s3 R8 N4 j
  41. RTC_WaitForSynchro(); //等待最近一次对 RTC 寄存器的写操作完成  H* b3 G& m1 X) K+ b$ C6 f, a! U$ f
  42. RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能 RTC秒中断/ `& V, i6 B& g9 R
  43. RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成: y  K+ h/ T! I0 [2 S. ]
  44. }
    : F$ R3 {9 F* [) e- M. u
  45. RTC_NVIC_Config();//RCT 中断分组设置' J  f, V5 l9 R& o8 f% H
  46. RTC_Get();//更新时间
    " n7 G& C/ O6 H4 p
  47. return 0; //ok6 U! x& B8 t. X: C
  48. }
复制代码

5 M7 Y: h0 T& R) Y- v- j5 z9 y2 f
    在 RTC_Init()函数中,首先使能电源 PWR 和后备域时钟,打开后备寄存器2 J* v) N# ^4 r$ z+ T
写访问,因为 RTC 初始化和初值的设置只需执行一次即可,第一次初始化 RTC,我们只需按照前面介绍的RTC配置步骤完成, 在设置 RTC 时间和日期初值时是通过 RTC_Set 函数完成,其实里面还是调用 RTC_SetCounter 完成的,这里单独写这个函数是为了方便设置RTC时间和日期。默认我们将RTC初值日期设置为2017年 3月22 日,初值时间设置为17 点 34 分 55 秒。
    设置好时间后我们调用函数 RTC_WriteBackupRegister 向 RTC 的 BKR 寄存器(地址 0)写入标志字 0XA0A0,用于 标 记 时 间 已 经 被 设 置 了 。
    这 样 ,再 次 发 生 复 位 的 时 候 , 通 过 调 用 函 数RTC_ReadBackupRegister 判断 RTC 对应 BKR 地址的值,来决定是不是需要重新设置时间,如果不需要设置,则跳过时间设置,这样就不会重复设置时间,使得我们设置的时间不会因复位或者断电而丢失。
    写备份域寄存器函数RTC_WriteBackupRegister 原形是:
  1. void RTC_WriteBackupRegister(uint32_t RTC_BKP_DR, uint32_t Data);
复制代码

7 \: l- y9 G4 M( \8 w) v$ R6 h
    参数 RTC_BKP_DR 可以选择是 RTC_BKP_DR1-RTC_BKP_DR42。& q# W) y% a! S6 X, G4 C% r% `
读取备份域寄存器函数RTC_ReadBackupRegister 原形是:
  1. uint32_t RTC_ReadBackupRegister(uint32_t RTC_BKP_DR);
复制代码
+ q& `( a, _7 e  C9 L
    参数 RTC_BKP_DR 可以选择是 RTC_BKP_DR1-RTC_BKP_DR42。. [0 Y  [+ J1 G) g  x( H
    最后我们的 RTC 初始化函数 RTC_Init 带有一个返回值,如果返回值为 1 表示 RTC初始化失败,否则成功。
& i8 f5 N# O6 N" f# s" t8 K. K3 H
RTC设置日期时间函数
    在RTC 初始化函数内调用了一个 RTC_Set 函数设置日期和时间, 具体代码如下
  1. /****************************************************************2 X$ N' F9 J' q1 A6 s, u7 x
  2. * 函 数 名 : RTC_Set* g& s# a/ R4 m( q
  3. * 函数功能 : RTC 设置日期时间函数(以 1970 年 1 月 1 日为基准,把输入的时钟转换为秒钟)1970~2099 年为合法年份% `7 _: ^3 O  r) Y8 Y: k
  4. * 输 入 : syear:年 smon:月 sday:日hour:时 min:分 sec:秒8 m7 ?( Y# k+ ^8 s+ H
  5. * 输 出 : 0,成功1,失败& f. c/ E% h, ?1 Z) e
  6. *****************************************************************/+ D6 R8 k- F$ ]
  7. u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)/ M1 }! S7 Q1 {4 w! E
  8. {
    ; x4 N4 a3 s  {' ~! a7 F
  9. u16 t;
    # X. i3 j/ M- B% P0 L7 M
  10. u32 seccount=0;1 h; s8 U! `2 C9 K8 @
  11. if(syear<1970||syear>2099)return 1;+ c9 j9 \# k) k# Q, h
  12. for(t=1970;t<syear;t++) //把所有年份的秒钟相加9 j% v3 ]6 O9 M& a* u+ D
  13. {. d- e. H8 g9 T3 D+ k
  14. if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数
    : \. ]) d( o5 J8 Q* @
  15. else seccount+=31536000; //平年的秒钟数; b! M8 ~8 w5 Z+ |$ J$ @
  16. }
    ' n3 F9 ~) d+ F, A* C5 x# Z# D
  17. smon-=1;$ w. [- r! Z. A7 h0 g+ c( _
  18. for(t=0;t<smon;t++) //把前面月份的秒钟数相加
    8 a; ]. W6 L8 N: x1 u0 B
  19. {5 F# H5 I1 A, ^1 k) X
  20. seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加7 ?  m2 s0 k0 k2 r
  21. if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数
    ; J9 v, A' R' a1 a  `
  22. }
    $ k7 @8 f( h- U% f7 Q% C6 e2 [
  23. seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加- ~0 H+ p) A* O: E. `) ^- J9 _
  24. seccount+=(u32)hour*3600;//小时秒钟数5 s# M" }- f  T- Y5 {6 d" v
  25. seccount+=(u32)min*60; //分钟秒钟数
    2 |- e* c/ [( n$ @. b6 _$ m. i
  26. seccount+=sec;//最后的秒钟加上去
    7 ~7 P9 c% k! d
  27. RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP,ENABLE); //使能 PWR 和BKP外设时钟
    7 K* z4 _& d; q
  28. PWR_BackupAccessCmd(ENABLE); //使能 RTC 和后备寄存器访问
    . f. Y" h6 b; K& L/ C: ~! x+ w
  29. RTC_SetCounter(seccount); //设置 RTC 计数器的值
    8 p4 B' m/ }$ {3 }- ]5 R
  30. RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成  C7 S/ \$ y5 [$ I* m5 F3 v3 c2 s
  31. return 0;
    9 M3 F0 |* }: N; p( p) `; m
  32. }
复制代码

( X, K3 Z- @# e+ d5 j
    该函数用于设置日期时间,把我们输入的时间,转换为以 1970 年 1 月 12 h1 }: A! D) `
日 0 时 0 分 0 秒当做起始时间的秒钟信号,后续的计算都以这个时间为基准
的,由于 STM32 的秒钟计数器可以保存 136 年的秒钟数据,这样我们可以计时到 2106 年。
    Rtc.c 文件内还有一个闹钟设置函数 RTC_Alarm_Set,此函数与设置时间函数完全一样,只是函数名不同而已。
8 R8 L: N  B0 Z
收藏 评论0 发布时间:2022-6-28 18:34

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版