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

【经验分享】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. /****************************************************************
    * w) s+ W1 v( e! J1 L
  2. * 函 数 名 : RTC_Init
    & ?4 a# e5 _2 h: Z; x# _
  3. * 函数功能 : RTC 初始化
    0 o5 d! c) W% q' n6 S
  4. * 输 入 : 无
    1 `6 w9 l( _* e! Z; V5 E9 I
  5. * 输 出 : 0,初始化成功
    / S/ n" [5 l2 ]: k
  6. 1,LSE开启失败
    & r' p/ u% B) A" a) B& H: J3 h
  7. *****************************************************************/6 y( T/ d/ s; _& N* ?  h& [. R& m
  8. u8 RTC_Init(void)
    : n- z) f$ C; E3 c9 D7 P
  9. {+ i5 ]5 u8 X3 w7 f* R
  10. //检查是不是第一次配置时钟. w" ?0 Q9 j! v
  11. u8 temp=0;& z+ @' P7 X/ N/ k& [1 |# R# P
  12. RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP,1 h, O3 K" G2 v, ]. ~/ f
  13. ENABLE); //使能 PWR 和BKP外设时钟
    ; W$ P3 D+ ~3 O9 ~
  14. PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
    5 E' |" x2 T+ O1 M: b
  15. if (BKP_ReadBackupRegister(BKP_DR1) != 0xA0A0) // 从 指 定的后备寄存器中读出数据:读出了与写入的指定数据不相乎
    6 q4 Q! Z" F3 ^2 g" E' q$ i6 s- f
  16. {
    & V" }+ t+ {7 ?8 g6 F! C; G
  17. BKP_DeInit(); //复位备份区域, ?9 g' a* l: `! i
  18. RCC_LSEConfig(RCC_LSE_ON); //设置外部低速晶振(LSE),使用外设低速晶振5 O! E& U8 n8 e. V9 O
  19. while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250)
    % A3 B0 R9 H# s
  20. //检查指定的 RCC 标志位设置与否,等待低速晶振就绪) c  I4 {, r3 C- ~
  21. {4 y; M, @) o% D0 Q# J8 u/ ?
  22. temp++;
    " O: P% d1 @8 K' [
  23. delay_ms(10);' i: m- X9 L: m( U) H, R( Y- A
  24. }
    6 ^( D7 h$ g0 E( E7 A
  25. if(temp>=250)return 1;//初始化时钟失败,晶振有问题% C" x' H. f5 j; C( ~. v1 |# G4 [
  26. RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置 RTC 时钟(RTCCLK),选择 LSE 作为RTC时钟' G2 V* b! {5 n+ H
  27. RCC_RTCCLKCmd(ENABLE); //使能 RTC 时钟
    # b% v: E$ u2 j- n1 a- j
  28. RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成
    % }' m. v" u5 n9 `$ V2 X
  29. RTC_WaitForSynchro(); //等待 RTC 寄存器同步* a) ?1 w2 I! A7 `9 |' ?  @1 o: i
  30. RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能 RTC秒中断
    9 u3 H8 o/ F9 @5 a7 ?8 Y4 e
  31. RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成$ A# S9 H1 ~( q( U" X7 d
  32. RTC_EnterConfigMode();// 允许配置
    % ^% [$ y0 n0 m+ }) p4 b
  33. RTC_SetPrescaler(32767); //设置 RTC 预分频的值
    " V1 t8 j( l# w, b$ _& T( I
  34. RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成
    ; _( D3 q+ z( N, ~
  35. RTC_Set(2017,3,22,17,34,55); //设置时间
    3 T9 O3 W+ r& r! }' v, g
  36. RTC_ExitConfigMode(); //退出配置模式
    6 N5 q( D0 k" e" p: B
  37. BKP_WriteBackupRegister(BKP_DR1, 0XA0A0); //向指定的后备寄存器中写入用户程序数据
    ' Y( j3 ^; V: O
  38. }
    1 V- {3 @# M+ W/ F1 X, t
  39. else//系统继续计时
    , g0 X2 k  p3 ]3 ~5 H
  40. {
    8 A1 t1 v" U- ~* P! }0 G8 p! W) y
  41. RTC_WaitForSynchro(); //等待最近一次对 RTC 寄存器的写操作完成
    . C7 u/ `  J! n, [- {
  42. RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能 RTC秒中断$ l+ F1 k9 T5 O9 Q+ f" Y5 F2 O( V
  43. RTC_WaitForLastTask(); //等待最近一次对 RTC 寄存器的写操作完成
    6 {% @/ Z. ~; n- v7 B
  44. }
    9 m5 f  h% O/ D7 f+ Q, P$ U/ M
  45. RTC_NVIC_Config();//RCT 中断分组设置
    " t+ p$ i. }6 Q: H- K: c; r( K
  46. RTC_Get();//更新时间4 _2 E) g) I' E' k2 C9 ~
  47. return 0; //ok
    3 A$ y" w1 a% W0 Y; G
  48. }
复制代码
; H* S) S# _# I9 h8 i. W9 [
    在 RTC_Init()函数中,首先使能电源 PWR 和后备域时钟,打开后备寄存器
. a( `" U# C$ ]# x
写访问,因为 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 U8 @* p0 Y* m. e# h
    参数 RTC_BKP_DR 可以选择是 RTC_BKP_DR1-RTC_BKP_DR42。4 I& P7 K, t- y% U3 F; A
读取备份域寄存器函数RTC_ReadBackupRegister 原形是:
  1. uint32_t RTC_ReadBackupRegister(uint32_t RTC_BKP_DR);
复制代码
* O/ o& Q) K3 ^3 M+ R  I" T
    参数 RTC_BKP_DR 可以选择是 RTC_BKP_DR1-RTC_BKP_DR42。
6 g" q3 w" @0 @2 G& y( i; ~0 a
    最后我们的 RTC 初始化函数 RTC_Init 带有一个返回值,如果返回值为 1 表示 RTC初始化失败,否则成功。
& {7 L+ v2 B) o! q) A% W8 [
RTC设置日期时间函数
    在RTC 初始化函数内调用了一个 RTC_Set 函数设置日期和时间, 具体代码如下
  1. /****************************************************************, I& ]) W3 N. |- K! G3 ?
  2. * 函 数 名 : RTC_Set
    0 l- A6 ~: V0 Z# x" H& w+ _# W; o
  3. * 函数功能 : RTC 设置日期时间函数(以 1970 年 1 月 1 日为基准,把输入的时钟转换为秒钟)1970~2099 年为合法年份3 q2 s( {1 n- j- A: g/ y
  4. * 输 入 : syear:年 smon:月 sday:日hour:时 min:分 sec:秒: A2 P7 _! R0 L: W
  5. * 输 出 : 0,成功1,失败
    5 x* f' d+ ?; b5 I7 s' ?
  6. *****************************************************************/' I. k1 }- |* |
  7. u8 RTC_Set(u16 syear,u8 smon,u8 sday,u8 hour,u8 min,u8 sec)
    ) l; e; @9 O- i
  8. {
      L( F# L2 e4 t& F+ J3 k7 C! F
  9. u16 t;
    ' }4 @* r2 ~" l; ]2 b+ g! X+ A
  10. u32 seccount=0;& ^% U$ q/ v" H
  11. if(syear<1970||syear>2099)return 1;
    # v, b  L0 n1 W3 X% B
  12. for(t=1970;t<syear;t++) //把所有年份的秒钟相加) n  @5 ]8 Z4 d' E0 j
  13. {
    " m. {  n2 U- \# D; ]& |. }* `
  14. if(Is_Leap_Year(t))seccount+=31622400;//闰年的秒钟数0 [/ C/ X5 R  _! ~
  15. else seccount+=31536000; //平年的秒钟数/ h* v' M/ m9 p$ Y9 ]8 a
  16. }* Q4 a; b; {, P" S  P- z
  17. smon-=1;
    " U% a$ _9 {% ]' V
  18. for(t=0;t<smon;t++) //把前面月份的秒钟数相加) M4 X$ l2 m. M; c) f/ R# ^
  19. {
    & Z) u+ d6 B0 U- c: E
  20. seccount+=(u32)mon_table[t]*86400;//月份秒钟数相加
    2 W) F; r7 U1 T, K
  21. if(Is_Leap_Year(syear)&&t==1)seccount+=86400;//闰年2月份增加一天的秒钟数5 g8 }/ H6 o. T4 w
  22. }
      I: \5 H) w; `
  23. seccount+=(u32)(sday-1)*86400;//把前面日期的秒钟数相加, S* L: a3 D) f+ c7 f/ }
  24. seccount+=(u32)hour*3600;//小时秒钟数% u9 v7 K! c0 k' V
  25. seccount+=(u32)min*60; //分钟秒钟数
    3 q. I, B4 ^, c: I
  26. seccount+=sec;//最后的秒钟加上去8 [0 z( p6 d( D5 N3 p: b
  27. RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP,ENABLE); //使能 PWR 和BKP外设时钟
    3 a5 l+ m3 w& ~$ r7 E( G* z
  28. PWR_BackupAccessCmd(ENABLE); //使能 RTC 和后备寄存器访问
    # Z" @' ?; R! I6 S# F
  29. RTC_SetCounter(seccount); //设置 RTC 计数器的值
    $ [  U  L& G4 |
  30. RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成; ^+ x6 V* k# o8 B. R5 ~
  31. return 0;
    0 _7 Z: @  `  a7 b+ \" S
  32. }
复制代码
/ E: j' w# i/ M
    该函数用于设置日期时间,把我们输入的时间,转换为以 1970 年 1 月 1
, F/ i: J6 x  V# q( |
日 0 时 0 分 0 秒当做起始时间的秒钟信号,后续的计算都以这个时间为基准
的,由于 STM32 的秒钟计数器可以保存 136 年的秒钟数据,这样我们可以计时到 2106 年。
    Rtc.c 文件内还有一个闹钟设置函数 RTC_Alarm_Set,此函数与设置时间函数完全一样,只是函数名不同而已。
3 {# w3 E! G4 {! [8 q
收藏 评论0 发布时间:2022-6-28 18:34

举报

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