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高效开发教程(基础篇)
相关文章:

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、常用函数 …...

HarmonyOS4.0系统性深入开发08服务卡片架构
服务卡片概述 服务卡片(以下简称“卡片”)是一种界面展示形式,可以将应用的重要信息或操作前置到卡片,以达到服务直达、减少体验层级的目的。卡片常用于嵌入到其他应用(当前卡片使用方只支持系统应用,如桌…...

002文章解读与程序——中国电机工程学报EI\CSCD\北大核心《计及源荷不确定性的综合能源生产单元运行调度与容量配置两阶段随机优化》已提供下载资源
👆👆👆👆👆👆👆👆👆👆👆👆👆👆👆👆👆👆下载资源链接Ǵ…...

Typora快捷键设置详细教程
文章目录 一、快捷键设置步骤二、设置快捷键简单案例参考资料 一、快捷键设置步骤 在typora软件中,快捷键的设置步骤主要为: 打开【文件】–>【偏好设置】,找到【通用】–>【打开高级设置】,找到 conf.user.json 文件。 然…...

《异常检测——从经典算法到深度学习》25 基于深度隔离林的异常检测算法
《异常检测——从经典算法到深度学习》 0 概论1 基于隔离森林的异常检测算法 2 基于LOF的异常检测算法3 基于One-Class SVM的异常检测算法4 基于高斯概率密度异常检测算法5 Opprentice——异常检测经典算法最终篇6 基于重构概率的 VAE 异常检测7 基于条件VAE异常检测8 Donut: …...
第7章 1 异常处理
bug的由来及分类 p81 字符串形式表示的数字之间也可以比较大小 import re ageinput(年龄:) if age>18:print(age)列表的append操作每次只能添加一个元素: lst[] lst.append(A) lst.append(B) # lst.append(A,B) 错误python中的异常处理机制 p82 t…...

昇腾910平台安装驱动、固件、CANN toolkit、pytorch
本文使用的昇腾910平台操作系统是openEuler,之前没了解过,不过暂时感觉用起来和centOS差不多。系统架构是ARM,安装包基本都是带aarch64字样,注意和x86_64区别开,别下错了。 安装依赖 cmake 通过yum安装的cmake版本较…...
【数据挖掘】模型融合
模型融合是指将多个不同的机器学习模型组合起来,通过综合多个模型的预测结果来得到更准确的预测结果。模型融合可以提高模型的鲁棒性,减小模型的方差,提高模型的泛化能力。 常见的模型融合方法包括平均法、投票法和堆叠法。 平均法(Averagin…...

DM、Oracle、GaussDB、Kingbase8(人大金仓数据库)和HIVE给列增加注释
DM数据库给列增加注释 1、创建表 CREATE TABLE test222 ( id int NOT NULL PRIMARY KEY, name varchar(1000) DEFAULT NULL, email varchar(1000) DEFAULT NULL, phone varchar(1000) DEFAULT NULL ) 2、给列添加注释 comment on column TEST222.NAME is 这是一个列注释; 例如…...

C语言实例_stdlib.h库函数功能及其用法详解
一、前言 C语言作为一种高效、灵活的编程语言,标准库的使用对于开发人员来说是不可或缺的。其中,stdlib.h是C语言中一个重要的标准库头文件,提供了许多常用的函数和工具,以便开发人员能够更加便捷地进行内存管理、字符串处理、随…...
Error in onLoad hook: “URIError: URI malformed“ found in…报错处理以及完善uniapp针对对象传参
使用uniapp传参的过程中遇到这么一个问题,当我们需要传整个对象作为参数时,我会先将这个对象先编码,然后再解码,从而获取到怎么参数,平常实操的时候也没有遇到过问题,但是今天测试的时候,刚好一…...

c语言-位操作符练习题
文章目录 前言一、n&(n-1)的运用场景(n为整数)二、&1 和 >>的应用场景总结 前言 本篇文章介绍利用c语言的位操作符解决一些练习题,目的是掌握各个位操作符的使用和应用场景。 表1.1为c语言中的位操作符 操作符含义&按位与|按位或^按位异或~按位…...

园林机械部件自动化三维测量检测形位公差-CASAIM自动化三维检测工作站
随着园林机械的广泛应用,对其机械部件的精确测量需求也日益增加。传统的测量方法不仅效率低下,而且精度难以保证,因此,自动化三维测量技术成为了解决这一问题的有效途径。本文将重点介绍CASAIM自动化三维检测工作站在园林机械部件…...

o2o生活通全开源尊享版+多城市切换+企业付款+交友IM+平台快报
搭建教程 1.把 pigo2ov282.sql 文件里面的网址 test.souho.net 全部批量替换为你的自己的 2.使用 phpmyadmin 导入 pigo2ov282.sql 到你的数据库(直接访问/phpmyadmin 即可) 3.修改数据库文件/conf/db.php 里的数据库连接信息(请勿使用记事本…...

UE4开发BIM程序 的 流程
某机构BIM设计研究中心主任马晓龙,他对编程颇有研究。今天他会用通俗易懂的语言来讲解基于游戏引擎UE4的BIM技术可视化应用。对于想要自己开发程序的设计师一定要读一下! 1)关于UE4——UE4是什么? 可以简单的理解为,一…...

【AI大语言模型】ChatGPT在地学、GIS、气象、农业、生态、环境等领域中的应用
以ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diffusion、星火大模型、文心一言、千问为代表AI大语言模型带来了新一波人工智能浪潮,可以面向科研选题、思维导图、数据清洗、统计分析、高级编程、代码调试、算法学习、论文检索、写作、翻译、润色、文献辅助…...
【面试题】写一个睡眠函数
题目要求 请你编写一个异步函数,它接收一个正整数参数 millis ,并休眠 millis 毫秒。要求此函数可以解析任何值。 示例 1: 输入:millis 100 输出:100 解释: 在 100ms 后此异步函数执行完时返回一个 Pro…...

4. 云原生之kubesphere基础服务搭建
文章目录 安装kubesphere插件服务暴露NodePort方式LoadBalancer方式安装 OpenELB部署eip资源配置网关启动网关创建路由测试网关路由ingress高级功能在服务中配置LoadBalancer 基础设施部署服务部署建议helm仓库添加helm仓库 运维相关部署gitlab部署nexus3部署harbor 研发相关 安…...

思福迪运维安全管理系统 任意文件读取漏洞
产品简介 思福迪运维安全管理系统是思福迪开发的一款运维安全管理堡垒机 漏洞概述 由于思福迪运维安全管理系统 GetCaCert路由存在任意文件读取漏洞,攻击者可通过该漏洞在服务器端读取任意文件敏感内容,可能导致攻击者后续获取到相关的服务器权限 资…...

OCR在审核应用落地
本文字数:6686字 预计阅读时间:35分钟 01 背景 1、业务背景 在传统视频审核场景中,审核人员需要对进审视频中的文字内容进行逐一审核,避免在文字上出现敏感词、违禁词或者广告等相关词汇。这种人工审核费时费力,并且由…...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...

C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...

mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...

c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...