当前位置: 首页 > news >正文

STM32CubeMX教程10 RTC 实时时钟 - 周期唤醒、闹钟A/B事件和备份寄存器

目录

1、准备材料

2、实验目标

3、实验流程

3.0、前提知识

3.1、CubeMX相关配置

3.1.1 、时钟树配置

3.1.2、外设参数配置

3.1.3 、外设中断配置

3.2、生成代码

3.2.1、外设初始化函数调用流程

3.2.2、外设中断函数调用流程

3.2.3、添加其他必要代码

4、常用函数

5、烧录验证

5.1、具体步骤

5.2、实现现象

6、注释详解

参考资料


1、准备材料

开发板(正点原子stm32f407探索者开发板V2.4)

STM32CubeMX软件(Version 6.10.0)

野火DAP仿真器

keil µVision5 IDE(MDK-Arm)

CH340G Windows系统驱动程序(CH341SER.EXE)

XCOM V2.6串口助手

2、实验目标

使用STM32CubeMX软件配置STM32F407开发板实现RTC周期唤醒、闹钟A/B事件功能,具体为在周期唤醒时利用串口输出当前RTC记录时间,当闹钟A/B事件发生时利用串口输出闹钟A/B事件发生提示

3、实验流程

3.0、前提知识

RTC的时钟可以由外部低速时钟LSE、外部高速时钟HSE经过2-31分频和内部RC振荡LSI三种时钟来源提供,但是一般我们都选择使用32.768kHz的LSE作为RTC的时钟源,因为32.768kHz的时钟频率可以经过128次分频,然后再经过256次分频得到一个较为精确的1Hz信号,此信号1s脉动一次,可以方便的用于更新日历,如下图所示(注释1)

另外RTC还有两个可编程的闹钟A/B,如果设置了闹钟A/B的时间,则闹钟A/B设定时间会和当前日历时间对比,如果时间相等,会产生ALRA/BF事件

周期唤醒可以使用RTC内部一个16位唤醒自动重载寄存器来实现,周期唤醒的时钟信号可以来自于更新日历的1Hz(ck_spre)信号,也可以使用RTC时钟的2/4/8/16分频后的时钟,设置该自动重载寄存器的值,根据时钟频率向上计数,当计数溢出时发生周期唤醒事件

闹钟A/B,周期唤醒产生的 ALRAF、 ALRBF和WUTF事件均可以输出到复用引脚RTC_AF1(PC13)

STM32F407的RTC还有20个32位的备份寄存器,其名字从RTC_BKP_DR0到RTC_BKP_DR19,定义在stm32f4xx_hal_rtc_ex.h文件中,RTC和备份寄存器均由单片机的备用电源VBAT提供,主电源VDD/VDDA断开不影响备份寄存器内容存储及RTC的正常运行

3.1、CubeMX相关配置

3.1.1 、时钟树配置

本文实验中RTC时钟信号源选择为外部32.768kHz的低速时钟LSE,与之前使用的STM32F407G-DISC1开发板在RCC及Clock Configuration页面中对LSE的设置不同,首先需要在Pinout & Configuration页面左边System Core/RCC中将原来Disable状态的Low Speed Clock(LSE)选择为Crystal/Ceramic Resonator,表示外部低速时钟LSE由32.768kHz的晶振提供,如下图所示

然后还是在这个页面,在Timers/RTC中单击Activate Clock Source,激活时钟源之后才可以对Clock Configuration页面的时钟修改,如下图所示

最后在Clock Configuration页面将输出到RTC时钟的时钟源选择为LSE,此时就已经配置好了RTC的输入时钟为32.768kHz的LSE,如下图所示

3.1.2、外设参数配置

单击Pinout & Configuration页面左边Timers/RTC

在该页面中间RTC Mode and Configuration中单击Activate Calendar激活日历,这里Alarm A、Alarm B和WakeUp均有Disable、Internal Alarm/WakeUp和Routed to AF1三个选项,分别表示不使用、单纯内部使用和输出到复用引脚AF1(PC13),注意由于AF1只有一个所以一旦某一个选择输出到了复用引脚AF1,其他便不可以设置

配置如下图所示

然后对启用的日历、Alarm A、Alarm B和WakeUp参数做不同的配置,这里比较通俗易懂,具体配置请看下图

3.1.3 、外设中断配置

在Pinout & Configuration页面左边System Core/NVIC中勾选闹钟A/B中断及周期唤醒中断,然后选择合适的中断优先级即可,另外串口中断可以不打开,本节实验输出采用阻塞传输数据的方式输出RTC时间

3.2、生成代码

请先阅读“STM32CubeMX STM32F4 HAL库 工程建立”实验3.4.3小节配置Project Manager

单击页面右上角GENERATE CODE生成工程

3.2.1、外设初始化函数调用流程

主函数中调用MX_RTC_Init()函数对RTC基本参数及日历时间、日历日期、闹钟A定时时间、闹钟B定时时间和周期唤醒等参数初始化/使能

在初始化RTC的函数HAL_RTC_Init()中调用了HAL_RTC_MspInit()函数完成了对RTC时钟使能,NVIC使能,NVIC优先级设置

如下图所示为上述的函数调用流程

3.2.2、外设中断函数调用流程

在stm32f4xx_it.c文件中新增了周期唤醒中断服务函数RTC_WKUP_IRQHandler()

在该RTC_WKUP_IRQHandler()函数中调用了HAL_RTCEx_WakeUpTimerIRQHandler()函数处理周期回调事件

最终调用了虚函数HAL_RTCEx_WakeUpTimerEventCallback(),该函数需要用户重新实现

如下图所示为周期唤醒中断函数调用流程

同时在stm32f4xx_it.c文件中新增了RTC闹钟A/B事件中断服务函数RTC_Alarm_IRQHandler()

在该RTC_Alarm_IRQHandler()函数中调用了HAL_RTC_AlarmIRQHandler()函数处理闹钟A/B事件

最后在该函数中调用了虚函数HAL_RTC_AlarmAEventCallback()处理闹钟A事件,调用虚函数HAL_RTCEx_AlarmBEventCallback()处理闹钟B事件

如下图所示为RTC闹钟A/B事件中断函数调用流程

3.2.3、添加其他必要代码

重新实现周期唤醒中断回调函数HAL_RTCEx_WakeUpTimerEventCallback()在rtc.c中,具体实现代码如下图所示

源代码如下

/*周期唤醒回调函数*/
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{RTC_TimeTypeDef sTime;RTC_DateTypeDef sDate;if(HAL_RTC_GetTime(hrtc, &sTime, RTC_FORMAT_BIN) == HAL_OK){HAL_RTC_GetDate(hrtc, &sDate,  RTC_FORMAT_BIN);char str[22];sprintf(str,"RTC Time= %2d:%2d:%2d\r\n",sTime.Hours,sTime.Minutes,sTime.Seconds);printf("%s", str);}HAL_GPIO_TogglePin(RED_LED_GPIO_Port,RED_LED_Pin);
}

重新实现闹钟A/B事件中断回调函数HAL_RTC_AlarmAEventCallback()和HAL_RTCEx_AlarmBEventCallback()在rtc.c中,具体代码如下所示

源代码如下

/*闹钟A事件回调函数*/
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{char infoA[]="Alarm A(xx:xx:15) trigger: \r\n";printf("%s", infoA);HAL_GPIO_TogglePin(GREEN_LED_GPIO_Port,GREEN_LED_Pin);
}/*闹钟B事件回调函数*/
void HAL_RTCEx_AlarmBEventCallback(RTC_HandleTypeDef *hrtc)
{char infoB[]="Alarm B(xx:0:30) trigger: \r\n";printf("%s", infoB);HAL_GPIO_TogglePin(GREEN_LED_GPIO_Port,GREEN_LED_Pin);
}

此时的代码可以正常运行,但存在一个问题,复位后重新执行RTC初始化函数会对RTC时间强制初始化为0:0:0,日期也会强制初始化,而我们想要设定的是当我们需要其初始化时就初始化,当一次初始化完毕之后,我不希望每次单片机复位时重新初始化

因此我们可以通过上述介绍的备份寄存器实现此功能,我们在RTC通用初始化结束之后,RTC日期和时间初始化之前处,添加判断RTC备份寄存器是否已被写入1来决定是否需要初始化时间和日期,如果已被写入1,则表示之前已完成日期和时间初始化,不需要再次重新初始化,因此启动周期唤醒后直接退出函数,如下图代码所示

源代码如下

//读取备份寄存R0
uint32_t iniRTC=HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0);	
//非零
if((iniRTC & 0x01))  
{//使能周期唤醒if(HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0, RTC_WAKEUPCLOCK_CK_SPRE_16BITS) != HAL_OK)Error_Handler();//提前退出函数,不初始化时间和日期return;  
}

何时改变/写入RTC备份寄存器中的值呢?

这里笔者使用按键来控制,当按下WK_UP按键时,就翻转备份寄存器RTC_BKP_DR0中存储的值,也就是说按下一次WK_UP按键,备份寄存器RTC_BKP_DR0中的值会在0/1之间改变,如下图所示为主循环中的按键扫描程序

源代码如下

uint32_t iniRTC = HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR0);
iniRTC = !iniRTC;if(HAL_GPIO_ReadPin(WK_UP_GPIO_Port,WK_UP_Pin) == 1)
{HAL_Delay(50);if(HAL_GPIO_ReadPin(WK_UP_GPIO_Port,WK_UP_Pin) == 1){HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR0, iniRTC);printf("Write RTC_BKP_DR0 %d\r\n", iniRTC);while(HAL_GPIO_ReadPin(WK_UP_GPIO_Port,WK_UP_Pin));}
}

4、常用函数

/*RTC周期回调中断服务函数*/
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)/*RTC闹钟A中断服务函数*/
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)/*RTC闹钟B中断服务函数*/
void HAL_RTCEx_AlarmBEventCallback(RTC_HandleTypeDef *hrtc)/*查询RTC时间*/
HAL_StatusTypeDef HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format)/*查询RTC日期*/
HAL_StatusTypeDef HAL_RTC_GetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format)/*读RTC备份寄存器的值*/
uint32_t HAL_RTCEx_BKUPRead(RTC_HandleTypeDef *hrtc, uint32_t BackupRegister)/*写RTC备份寄存器的值*/
void HAL_RTCEx_BKUPWrite(RTC_HandleTypeDef *hrtc, uint32_t BackupRegister, uint32_t Data)

5、烧录验证

5.1、具体步骤

“RCC中启用LSE -> RTC中激活时钟源 -> Clock Configuration配置RTC时钟来源为LSE -> RTC中激活日历 -> 选择闹钟A/B、唤醒模式 -> 配置RTC、闹钟A/B和周期唤醒参数 -> NVIC中启动RTC闹钟A/B、周期唤醒中断 -> rtc.c中重新实现周期唤醒回调函数HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc) -> 重新实现闹钟A事件回调函数HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc) -> 重新实现闹钟B事件回调函数HAL_RTCEx_AlarmBEventCallback(RTC_HandleTypeDef *hrtc) -> 在三个回调函数中利用串口编程实现输出信息提示”

5.2、实现现象

烧录程序,通过串口助手观察串口输出信息,每隔1秒,串口助手收到开发板传来的RTC时间信息,并且红色LED每一秒状态翻转一次,当时间到达0:0:15时,闹钟A触发,此时绿色LED灯状态翻转被点亮,随着时间继续流逝,当时间到达0:0:30时,闹钟B触发,此时此时绿色LED灯状态翻转被熄灭,此后每分钟的第15秒闹钟A会触发一次,每小时的0分30秒闹钟B会触发一次

按下WK_UP按键可以翻转备份寄存器RTC_BKP_DR0内存储的值,当备份寄存器RTC_BKP_DR0的值为1时,复位之后RTC的时间不会重置为0;

而当备份寄存器RTC_BKP_DR0的值为0时,复位之后RTC的时间会被重新初始化为0:0:0,串口输出信息如下图所示

细心的小伙伴可能发现0:0:15时刻的闹钟A没有响应,这是因为备份寄存器RTC_BKP_DR0的值为1时,我们在MX_RTC_Init初始化函数中初始化完毕RTC之后直接启动了周期唤醒然后整个函数就退出了,并没有对RTC的闹钟A/B进行初始化,如果你想兼顾两者功能,也可以编写程序不直接退出,而是绕过RTC时间和日期赋初值的代码,然后执行RTC的闹钟A/B的初始化

6、注释详解

注释1:图片来源STM32F4xx中文参考手册 RM0090

参考资料

STM32Cube高效开发教程(基础篇)

http://www.lryc.cn/news/269474.html

相关文章:

  • HarmonyOS4.0系统性深入开发08服务卡片架构
  • 002文章解读与程序——中国电机工程学报EI\CSCD\北大核心《计及源荷不确定性的综合能源生产单元运行调度与容量配置两阶段随机优化》已提供下载资源
  • Typora快捷键设置详细教程
  • 《异常检测——从经典算法到深度学习》25 基于深度隔离林的异常检测算法
  • 第7章 1 异常处理
  • 昇腾910平台安装驱动、固件、CANN toolkit、pytorch
  • 【数据挖掘】模型融合
  • DM、Oracle、GaussDB、Kingbase8(人大金仓数据库)和HIVE给列增加注释
  • C语言实例_stdlib.h库函数功能及其用法详解
  • Error in onLoad hook: “URIError: URI malformed“ found in…报错处理以及完善uniapp针对对象传参
  • c语言-位操作符练习题
  • 园林机械部件自动化三维测量检测形位公差-CASAIM自动化三维检测工作站
  • o2o生活通全开源尊享版+多城市切换+企业付款+交友IM+平台快报
  • UE4开发BIM程序 的 流程
  • 【AI大语言模型】ChatGPT在地学、GIS、气象、农业、生态、环境等领域中的应用
  • 【面试题】写一个睡眠函数
  • 4. 云原生之kubesphere基础服务搭建
  • 思福迪运维安全管理系统 任意文件读取漏洞
  • OCR在审核应用落地
  • 借贷协议 Tonka Finance:铭文资产流动性的新破局者
  • Python+Yolov5+Qt交通标志特征识别窗体界面相片视频摄像头
  • 浅谈高并发以及三大利器:缓存、限流和降级
  • 深入ArkUI:深入实战组件text和text input
  • WPF 基础(Binding 二)
  • 限制el-upload组件的上传文件大小
  • 什么是爬虫,为什么爬虫会导致服务器负载跑满
  • 线上隐私保护的未来:分布式身份DID的潜力
  • 服务器被入侵后如何查询连接IP以及防护措施
  • 【开源】基于Vue+SpringBoot的公司货物订单管理系统
  • 2023-12-29 服务器开发-Centos部署LNMP环境