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

【经验分享】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. /****************************************************************$ Z( j- |" q& G. f! d
  2. * 函 数 名 : RTC_Init
    ' o; h/ {* i0 i
  3. * 函数功能 : RTC 初始化
    * X% e! T- D! g( m; D
  4. * 输 入 : 无
    ' W, P, x( {/ p. t: _0 |- Q7 p
  5. * 输 出 : 0,初始化成功/ o7 L& t2 M+ w( B" e8 g7 S
  6. 1,LSE开启失败8 ]  B" |5 Q  M% @
  7. *****************************************************************/
    + I0 W4 k3 F% g4 z7 Z
  8. u8 RTC_Init(void)# C5 f* x6 z% l0 {  g
  9. {
      v3 g+ y- D# c- K" E* u
  10. //检查是不是第一次配置时钟6 N& J3 F+ ~. ]8 [+ d, w
  11. u8 temp=0;
    ) ~* ?' ^- r" P- q7 v! ]$ t: A1 Z
  12. RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP,& p3 R; Y, W7 C
  13. ENABLE); //使能 PWR 和BKP外设时钟
    ( I9 V! }' ^: W/ Y/ l
  14. PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
    " q) J3 I3 a+ Z6 `
  15. if (BKP_ReadBackupRegister(BKP_DR1) != 0xA0A0) // 从 指 定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
    # H4 _2 o; F/ M" Y" L( }4 p6 Q
  16. {
    9 n) L4 d4 u1 P5 j  ^  ?( @0 Z
  17. BKP_DeInit(); //复位备份区域
    9 [; P. R, e! ]. C- t- Q
  18. RCC_LSEConfig(RCC_LSE_ON); //设置外部低速晶振(LSE),使用外设低速晶振* a- @5 o( }6 Q9 N+ S! b
  19. while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250)2 ~/ f0 z3 G. W" }
  20. //检查指定的 RCC 标志位设置与否,等待低速晶振就绪
    , ^( K: ~/ z, W7 b( a' H
  21. {6 A7 d- g2 Z+ a5 [& L5 d
  22. temp++;
    . G; X7 Z( u, F( m0 L+ [
  23. delay_ms(10);
    / Y# R7 \1 W2 N
  24. }  R) W6 \/ W) K. A3 H
  25. if(temp>=250)return 1;//初始化时钟失败,晶振有问题
    $ u+ W* S9 B  i; P) C1 s$ G3 O
  26. RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置 RTC 时钟(RTCCLK),选择 LSE 作为RTC时钟1 V6 H- y0 t1 |; h, s8 N- a
  27. RCC_RTCCLKCmd(ENABLE); //使能 RTC 时钟
    1 ?3 V" S7 a. E. m) b* I
  28. RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成
    , i: u4 ?9 a: e0 {
  29. RTC_WaitForSynchro(); //等待 RTC 寄存器同步0 k: b$ r2 t! e  S# V
  30. RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能 RTC秒中断
    8 ?  {  r; d0 P6 l8 W3 c
  31. RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成6 Y7 c: q" i5 O. F3 \9 B
  32. RTC_EnterConfigMode();// 允许配置# |$ L& L. f" n
  33. RTC_SetPrescaler(32767); //设置 RTC 预分频的值
    ' Z! {# A# i7 y7 F6 O$ A+ d  h
  34. RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成3 \6 L" r- K5 [9 q" C
  35. RTC_Set(2017,3,22,17,34,55); //设置时间4 L) o" A3 n  C% ^8 f4 h
  36. RTC_ExitConfigMode(); //退出配置模式
      y% q1 k8 m2 F. u
  37. BKP_WriteBackupRegister(BKP_DR1, 0XA0A0); //向指定的后备寄存器中写入用户程序数据. _, G5 f1 s/ o8 Q6 C
  38. }# i0 S9 e1 s8 A3 Q* R
  39. else//系统继续计时0 t7 n4 c) H& h8 n% T
  40. {" p% m' o; t, e* U; Z, r
  41. RTC_WaitForSynchro(); //等待最近一次对 RTC 寄存器的写操作完成! q9 w7 g7 A3 t7 D( P+ W. Y2 V
  42. RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能 RTC秒中断
    , |0 C3 b8 G  c
  43. RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成
    " v2 c* o/ o: V' R
  44. }: f1 U3 X: w5 q, M$ a8 q* Y
  45. RTC_NVIC_Config();//RCT 中断分组设置3 G: B! a/ T# @0 a) f/ b% L! V
  46. RTC_Get();//更新时间' W5 c/ U' K1 \) ]
  47. return 0; //ok5 U5 K% A# r1 f4 w& j
  48. }
复制代码

2 d1 x) K" j. f* N  F
    在 RTC_Init()函数中,首先使能电源 PWR 和后备域时钟,打开后备寄存器# m* ?: D$ D0 w5 g/ {. y6 r, _
写访问,因为 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);
复制代码

1 u! u- _% k; v$ t: s0 L
    参数 RTC_BKP_DR 可以选择是 RTC_BKP_DR1-RTC_BKP_DR42。
. z/ \, Z9 Z* f' @  ^
读取备份域寄存器函数RTC_ReadBackupRegister 原形是:
  1. uint32_t RTC_ReadBackupRegister(uint32_t RTC_BKP_DR);
复制代码

5 ?+ g( S% g7 M& E5 j  E; W, a
    参数 RTC_BKP_DR 可以选择是 RTC_BKP_DR1-RTC_BKP_DR42。
3 H+ ?. W, A9 o2 v  W
    最后我们的 RTC 初始化函数 RTC_Init 带有一个返回值,如果返回值为 1 表示 RTC初始化失败,否则成功。
( w, u- {7 p$ J% y
RTC设置日期时间函数
    在RTC 初始化函数内调用了一个 RTC_Set 函数设置日期和时间, 具体代码如下
  1. /****************************************************************
    , p$ O; s5 B6 m# |, Z9 p4 r+ B9 t$ c
  2. * 函 数 名 : RTC_Set
    5 E0 Q8 a7 Q* q
  3. * 函数功能 : RTC 设置日期时间函数(以 1970 年 1 月 1 日为基准,把输入的时钟转换为秒钟)1970~2099 年为合法年份, i  b" b# p+ {# |3 ]+ @
  4. * 输 入 : syear:年 smon:月 sday:日hour:时 min:分 sec:秒
    5 z* W& z2 v# n6 Y
  5. * 输 出 : 0,成功1,失败
    ' {+ w/ E0 q4 T7 Y
  6. *****************************************************************/' h3 k9 k" {' \7 b8 C9 j9 F2 w
  7. u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
    5 L( r- R( T8 m* r( \
  8. {; X5 W! B$ X: D* q( h/ V5 O6 g
  9. u16 t;
    ' n8 F$ B; ]3 u+ a' m1 I
  10. u32 seccount=0;( R/ S; P1 E3 R% K2 ]
  11. if(syear<1970||syear>2099)return 1;6 j/ P0 \! g2 J/ L, ]0 x
  12. for(t=1970;t<syear;t++) //把所有年份的秒钟相加
    % n  d! _; p- |4 ^; s) J
  13. {
    ) e! }8 P' s/ ]9 S* \9 r
  14. if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数0 L* x' u* K. k( K
  15. else seccount+=31536000; //平年的秒钟数$ z$ ?% T; S; ?* x% k
  16. }
    8 g! t7 z9 ?+ \; L( o4 {- Y9 L
  17. smon-=1;5 K: B0 F! n. Z% K1 z; s0 h. A
  18. for(t=0;t<smon;t++) //把前面月份的秒钟数相加9 p" E1 ~  T9 `5 ~
  19. {
    9 h. t' \5 {* [3 M; U
  20. seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加0 A* R1 W* f  G) Q
  21. if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数
    ; i, ^  z" x3 g
  22. }+ D% n  j& s( a5 p* v$ t
  23. seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加
    $ d( ~/ M8 F9 d2 ]
  24. seccount+=(u32)hour*3600;//小时秒钟数
    " \0 _+ [8 J6 G* }. V
  25. seccount+=(u32)min*60; //分钟秒钟数6 v, f/ R0 ?, S
  26. seccount+=sec;//最后的秒钟加上去
    2 m8 s+ |. p8 s* s3 x; Q& }
  27. RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP,ENABLE); //使能 PWR 和BKP外设时钟5 s( S$ W, n8 \+ }4 t( b) n3 p
  28. PWR_BackupAccessCmd(ENABLE); //使能 RTC 和后备寄存器访问' Q' z: [$ Z- n( Z& W% V
  29. RTC_SetCounter(seccount); //设置 RTC 计数器的值
    5 a4 i& D: V5 t
  30. RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成2 {4 }) e: z$ Y4 y( g
  31. return 0;
    2 H& }: f, w1 c0 K% r; ~
  32. }
复制代码
  U6 c. G2 ?2 P% Y. b( v
    该函数用于设置日期时间,把我们输入的时间,转换为以 1970 年 1 月 1
* w, f7 o1 ]! T) n+ j8 w
日 0 时 0 分 0 秒当做起始时间的秒钟信号,后续的计算都以这个时间为基准
的,由于 STM32 的秒钟计数器可以保存 136 年的秒钟数据,这样我们可以计时到 2106 年。
    Rtc.c 文件内还有一个闹钟设置函数 RTC_Alarm_Set,此函数与设置时间函数完全一样,只是函数名不同而已。
, d) y9 x6 {" b: I+ L! \8 ]
收藏 评论0 发布时间:2022-6-28 18:34

举报

0个回答

所属标签

相似分享

官网相关资源

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