【STM32学习】实时时钟 —— RTC

  • 零、参考
  • 一、工作原理
    • 1、RTC介绍
    • 2、工作过程
  • 二、相关寄存器
  • 三、代码说明
    • 1、rtc初始化
    • 2、关于中断
    • 3、中断配置代码(仅供参考)
      • 3.1 秒中断+普通闹钟功能
      • 3.2 待机模式唤醒
      • 3.3 停机模式唤醒

零、参考

STM32RTC实时时钟实验讲解,从入门到放弃
【STM32】RTC休眠唤醒(停机模式)、独立看门狗开启状态下
关于STM32使用RTC唤醒停止模式的设置

一、工作原理

1、RTC介绍

RTC(Real Time Clock):实时时钟,是指可以像时钟一样输出实际时间的电子设备,一般会是集成电路,因此也称为时钟芯片。总之,RTC只是个能靠电池维持运行的32位定时器,并不像实时时钟芯片,读出来就是年月日。
RTC就只一个定时器而已,掉电之后所有信息都会丢失,因此我们需要找一个地方来存储这些信息,于是就找到了备份寄存器(BKP)。因为它掉电后仍然可以通过纽扣电池供电,继续工作,所以能时刻保存这些数据。
stm32F103使用外部晶体的32.768kHz的振荡器,产生一个1秒长的时间基准。
RTC 模块和时钟配置系统(RCC_BDCR 寄存器)是在后备区域,即在系统复位或从待机模式唤醒后 RTC 的设置和时间维持不变。但是在系统复位后,会自动禁止访问后备寄存器和 RTC,以防止对后备区域(BKP)的意外写操作。所以在要设置时间之前, 先要取消备份区域(BKP)写保护

2、工作过程

RTC只是一个时钟,但与RTC相连的有两个系统时钟:一个是APB1接口的PCLK1,另一个是RTC时钟。这样,RTC功能也就分为两个部分:第一部分,APB1接口部分,与APB1总线相连,MCU也就是通过这条总线对其进行读写操作。另一部分,RTC核心,其整个核心部分位于后备区域,所以只要有VBAT引脚供电,就能一直工作,由一系列可编程计数器组成,这部分又再细分为两个组件:20位的预分频模块与32位可编程计数器。预分频模块用来产生最长为1秒的RTC时间基准,而32位的可编程的计数器可被初始化为当前的系统时间。

RTC_PRL:预分频装载寄存器
RTC_DIV:预分频器余数寄存器
RTC_CNT:计数器寄存器
RTC_ALR:闹钟寄存器

首先,在初始化时候,DIV和PRL都会装载同样的数。RTCCLK频率为32.768kHz,来驱动DIV工作,DIV为自减寄存器。当DIV减少到0时,会产生一个TR_CLK信号,该信号会触发三件事情:①将PRL的数重装在到DIV中;②触发CNT计数器+1;③触发SECF,进而产生秒中断。我们会设置一个合理的预分频数,使得TR_CLK触发时间刚好是1s。ALR中存储的是我们设置的闹钟秒数,当CNT和ALR中的数据相同时,就会触发闹钟中断或者闹钟唤醒。(关于CNT和ALR中存放的数据,为时间戳。)

1s的时间如何计算?

我们的RTC_CLK为32.768kHz,所以只要PRL中写入32767即可,即为2的15次方。
我们只需要在PRL的低16位寄存器中写入0x7FFF即可。

DIV寄存器存在的意义?
从上述能知道TR_CLK输出必须为1s。那么我们想要的到0.15s,应该怎么实现呢?
道理很简单,因为DIV是自减的,并且从32768一直减少到0,所需要的时间为1s。所以减少一个数,所用的时间为1/32768s,那么减少多少个数,就计时了0.15s呢?
1/32768s * n = 0.15
n = 0.15 * 32768
又因为DIV寄存器是可读的,所以我们就能实现0.15s的计时。

从框图中,还可以看到,只有秒中断和闹钟中断能触发中断,溢出是没有中断的

二、相关寄存器

具体的寄存器讲解与设置,就不做讲解了,不是很复杂。
只说几处需要注意的:

1、RTC的寄存器都是分了高16位和低16位使用的
2、
需要注意的是RTOFF位,该位为只读位,表示最后一次寄存器的写状态。当我们需要对PRL、CNT、ALR寄存器写入数据时,需要先判断该位是否为1。只有上一次写完了,我们才能继续写。
3、关于备份数据寄存器

可以把它看作是一个EEPROM,掉电不丢失的存储器。
DRx的x(x=1…10)什么意思呢?就是有10个这样的16位寄存器。
例如,RTC程序中,我们向DR1写入0x5050,开机检测是否已经配置过RTC。

三、代码说明

1、rtc初始化

2、关于中断

rtc中断服务函数有两个:RTC_IRQHandler(全局中断)和RTCAlarm_IRQHandler(闹钟中断)
给我们的印象,好像是全局中断就是用来处理秒中断的;闹钟中断就是用来处理闹钟中断的。
但是,这样的说法并不是全面的。

先说结论:
① 如果只需要闹钟作为一个定时的功能,而不是用于低功耗的唤醒,那么只需要在NVIC设置秒中断(如果不用可以不设置)和闹钟中断,秒中断和闹钟中断任何一个都能触发RTC_IRQHandler,我们只需要根据读取相应标志位,判断是秒还是闹钟中断即可。


② 如果该闹钟中断用于低功耗唤醒——待机模式。那么闹钟中断会触发RTCAlarm_IRQHandler函数。我们需要在中断函数中,清除调闹钟中断标志位。
③ 如果该闹钟中断用于低功耗唤醒——停机模式。单纯的闹钟中断是无法唤醒停机模式的,我们需要将RTC闹钟中断链接到外部中断EXIT_17(也不用链接,默认的,就是使能一下子),用来唤醒停机模式。

3、中断配置代码(仅供参考)

3.1 秒中断+普通闹钟功能

记得开启闹钟中断以及设置闹钟时间

u8 RTC_Init(void)
{//检查是不是第一次配置时钟u8 temp=0;RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);  //使能PWR和BKP外设时钟   PWR_BackupAccessCmd(ENABLE);  //使能RTC和后备寄存器访问  if (BKP_ReadBackupRegister(BKP_DR1) != 0x5555)        //从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎{                BKP_DeInit();   //复位备份区域    RCC_LSEConfig(RCC_LSE_ON);  //设置外部低速晶振(LSE),使用外设低速晶振while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250)  //检查指定的RCC标志位设置与否,等待低速晶振就绪{temp++;delay_ms(10);}if(temp>=250)return 1;//初始化时钟失败,晶振有问题  RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);        //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟    RCC_RTCCLKCmd(ENABLE);    //使能RTC时钟  RTC_WaitForLastTask();   //等待最近一次对RTC寄存器的写操作完成RTC_WaitForSynchro();      //等待RTC寄存器同步  RTC_ITConfig(RTC_IT_SEC, ENABLE);     //使能RTC秒中断RTC_WaitForLastTask();    //等待最近一次对RTC寄存器的写操作完成RTC_WaitForSynchro();      //等待RTC寄存器同步  RTC_ITConfig(RTC_IT_ALR, ENABLE);     //使能RTC闹钟中断RTC_WaitForLastTask();   //等待最近一次对RTC寄存器的写操作完成RTC_WaitForSynchro();      //等待RTC寄存器同步    RTC_EnterConfigMode();/// 允许配置  RTC_SetPrescaler(32767); //设置RTC预分频的值RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成RTC_Set(2023,4,4,19,10,10);  //设置时间    RTC_WaitForLastTask(); RTC_SetAlarm(10+RTC_GetCounter());    //闹钟值设定为当前时间的10秒后RTC_WaitForLastTask();           RTC_ExitConfigMode(); //退出配置模式  BKP_WriteBackupRegister(BKP_DR1, 0X5555);   //向指定的后备寄存器中写入用户程序数据}else//系统继续计时{RTC_WaitForSynchro(); //等待最近一次对RTC寄存器的写操作完成RTC_ITConfig(RTC_IT_SEC, ENABLE);  //使能RTC秒中断RTC_WaitForLastTask();    //等待最近一次对RTC寄存器的写操作完成RTC_ITConfig(RTC_IT_ALR, ENABLE);      //使能RTC闹钟中断RTC_WaitForLastTask();   //等待最近一次对RTC寄存器的写操作完成RTC_WaitForSynchro();      //等待RTC寄存器同步 }RTC_NVIC_Config();//RCT中断分组设置                              RTC_Get();//更新时间   return 0; //ok}
static void RTC_NVIC_Config(void)
{   NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;     //RTC全局中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级1位,从优先级3位NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //先占优先级0位,从优先级4位NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;        //使能该通道中断NVIC_Init(&NVIC_InitStructure);        //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}
void RTC_IRQHandler(void)
{        if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒钟中断{                          RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);        //清秒中断RTC_Get();   //更新时间   RTC_WaitForLastTask();  //等待写入完成}if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断{RTC_ClearITPendingBit(RTC_IT_ALR);      //清闹钟中断     RTC_Get();      //更新时间  RTC_WaitForLastTask();       }                                               }

3.2 待机模式唤醒

void RTC_NVIC_Config(void)
{   NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQn;        //RTC闹钟中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级1位NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;    //先占优先级0位NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;       //使能该通道中断NVIC_Init(&NVIC_InitStructure);        //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}
void RTCAlarm_IRQHandler(void)
{if(RTC_GetITStatus(RTC_IT_ALR) != RESET){RTC_ClearITPendingBit(RTC_IT_ALR);RTC_WaitForLastTask();if(PWR_GetFlagStatus(PWR_FLAG_WU) != RESET){PWR_ClearFlag(PWR_FLAG_WU);RTC_WaitForLastTask();//处理业务}}RTC_ITConfig(RTC_IT_ALR, DISABLE);  //关闭闹钟中断,用的时候在开启
}

3.3 停机模式唤醒

void RTC_NVIC_Config(void)
{   EXTI_InitTypeDef EXTI_InitStructure;  NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = RTCAlarm_IRQn;      //RTC闹钟中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级1位NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;    //先占优先级0位NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;       //使能该通道中断NVIC_Init(&NVIC_InitStructure);        //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器//闹钟中断接到第17线外部中断EXTI_ClearITPendingBit(EXTI_Line17);EXTI_InitStructure.EXTI_Line = EXTI_Line17;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure); }
void RTCAlarm_IRQHandler(void)
{EXTI_ClearITPendingBit(EXTI_Line17);RTC_ClearFlag(RTC_FLAG_ALR);RTC_WaitForLastTask();SystemInit();//重要,由于停机下对所有时钟关闭,所以唤醒需要重新配置时钟!!if(PWR_GetFlagStatus(PWR_FLAG_WU) != RESET){PWR_ClearFlag(PWR_FLAG_WU);// 清除唤醒标志//处理业务}RTC_ITConfig(RTC_IT_ALR, DISABLE);            //失能RTC闹钟中断,用的时候在开启
}

【STM32学习】实时时钟 —— RTC相关推荐

  1. stm32之实时时钟RTC(掉电计时保持、秒中断、闹钟中断、溢出中断)

    前言:stm32系列产品普遍都有实时时钟RTC模块,它提供一个掉电保持计时功能,掉电后由后备供电区域供电.除了提供时间和日期之外,还可以设置闹钟提醒,且可以在待机模式下设置闹钟唤醒系统.在一些小容量. ...

  2. STM32学习心得二十一:实时时钟RTC和备份寄存器BKP特征、原理及相关实验代码解读

    记录一下,方便以后翻阅~ 主要内容 1) RTC特征与原理: 2) BKP备份寄存器特征与原理: 3) RTC常用寄存器+库函数介绍: 4) 相关实验代码解读. 实验内容: 因为没有买LCD屏,所以计 ...

  3. 【嵌入式】MSP430系统实时时钟RTC学习日志(完善中)

    目录 MSP430系统实时时钟RTC [时钟初始化]系统时钟初始化需要注意的问题 MSP430F149时钟源选择(部分转) MSP430 系统时钟 ACLK.MCLK.SMCLK [MSP430时钟] ...

  4. 【STM32学习】时钟配置详解

    [STM32学习]时钟配置详解 看懂时钟图 结合代码 外部高速时钟修改 看懂时钟图 在刚开始学习32的时候,并不会在意这些,或者即使看了也看的不是很明白.随着学习的深入,我们发现看门狗.定时器.ADC ...

  5. linux设备树例程,iTOP-iMX6-设备树内核-实时时钟RTC以及Linux-c测试例程

    当 Linux 开发者谈论一个实时时钟,他们通常指的是某种能记录墙上时间,并且有备用电 池,以至于在系统关机的时候仍然可以工作的器件. Linux 有两个系列广泛兼容的用户空间 RTC 设备节点: • ...

  6. ESP32设备驱动-DS3231实时时钟(RTC)驱动

    DS3231实时时钟(RTC)驱动 1.DS3231介绍 DS3231 是一款低成本.极其精确的 I2C 实时时钟 (RTC),具有集成的温度补偿晶体振荡器 (TCXO) 和晶体. 该设备包含电池输入 ...

  7. ESP8266-Arduino编程实例-PCF8563实时时钟(RTC)驱动

    PCF8563实时时钟(RTC)驱动 1.PCF8563介绍 PCF8563 是针对低功耗优化的 CMOS 实时时钟 (RTC) 和日历. 还提供了可编程时钟输出.中断输出和低电压检测器. 所有地址和 ...

  8. imx6 linux 时钟,迅为-iMX6开发板-驱动-实时时钟RTC以及Linux-c测试例程

    当Linux开发者谈论一个实时时钟,他们通常指的是某种能记录墙上时间,并且有备用电 池,以至于在系统关机的时候仍然可以工作的器件. Linux 有两个系列广泛兼容的用户空间 RTC 设备节点: /de ...

  9. 了解实时时钟RTC的原理并通过stm32实现STM32的日历读取、设置和输出

    文章目录 前言 一.RTC是什么? 1.定义 2.原理 二.配置项目 三.配置代码 1.重定向printf函数 2.效果(1) 3.添加星期 4.效果(2) 四.总结 五.参考资料 前言 硬件:stm ...

最新文章

  1. 如何用简单易懂的例子解释隐马尔可夫模型?
  2. 科大讯飞2019年创下历史最佳业绩,员工涨薪27%,营收来源妥妥「安徽之光」
  3. mysql数据库导入后莫名丢失,oracle导入丢失数据库
  4. java的for循环的几种写法
  5. 推荐:周志华《机器学习》西瓜书精炼版笔记来了!
  6. 如何去学会黑客技术,黑客电脑技术学习必备知识
  7. iis端口号 linux,Linux 6 修改ssh默认远程端口号的操作步骤
  8. win32调用系统颜色对话框
  9. c语言程序与实验系统,C/C ++程序设计学习与实验软件系统v2019 最新版下载_云间下载...
  10. 深入理解数组与指针——原因在于”退化“
  11. Liunx 常用命令 学习备份
  12. python菜鸟教程 | for 循环
  13. java jconsole_jconsole与jvisualvm
  14. 实现百度地图marker平滑移动
  15. 赵小楼《天道》《遥远的救世主》深度解析(35)你觉得叶晓明、冯世杰、刘冰是底层吗?
  16. android 4.4以上sd卡,怎样无根绕过Android 4.4(KitKat)外部SD卡限制
  17. [GXYCTF 2019]Ping Ping Ping
  18. 厉害!95后2万月薪程序员,背着电脑送外卖,途中还要改bug!
  19. 如何运用3Dmax制作游戏武器模型
  20. windows切换窗口,取消edge窗口为多个

热门文章

  1. html5+css3中的background: -moz-linear-gradient 用法
  2. 微信小程序-长按图片保存到相册
  3. SQL语句中的“@”符号的作用
  4. 蓝桥杯 第十三届蓝桥杯省赛C++组真题 修剪灌木 Python
  5. 浪潮云分布式云ICP加速千行百业羽化创新
  6. 对抗生成网络GAN系列——GANomaly原理及源码解析
  7. wifi模组论坛_未来3年5G模组价格下降80%,这对5G产业来说意味着什么?|物联网|芯片...
  8. 使用Stream流实现数组与集合的相互转换
  9. C语言实现求字符串的长度
  10. CC1310空中升级笔记05 BIM适配工程处理