本实验主要使用STM32CubeMX软件配置STM32F407开发板实现RTC周期唤醒、闹钟A/B事件功能,周期唤醒中输出RTC时间,闹钟A/B事件发生时利用串口输出闹钟A/B事件发生提示

1、准备材料

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

ST-LINK/V2驱动

STM32CubeMX软件(Version 6.10.0

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相关配置

请先阅读“STM32CubeMX教程1 工程建立”实验3.4.1小节配置RCC和SYS

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、外设参数配置

本实验需要需要初始化USART1作为输出信息渠道,具体配置步骤请阅读“STM32CubeMX教程9 USART/UART 异步通信”

单击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教程1 工程建立”实验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高效开发教程(基础篇)

更多内容请浏览 OSnotes的CSDN博客

STM32CubeMX教程10 RTC 实时时钟 - 周期唤醒、闹钟A/B事件和备份寄存器的更多相关文章

  1. 第43章 RTC—实时时钟

    第43章     RTC—实时时钟 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fireg ...

  2. 第43章 RTC—实时时钟—零死角玩转STM32-F429系列

    第43章     RTC—实时时钟 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/fireg ...

  3. stm32——RTC实时时钟

    stm32——RTC实时时钟 一.关于时间 2038年问题 在计算机应用上,2038年问题可能会导致某些软件在2038年无法正常工作.所有使用UNIX时间表示时间的程序都将将受其影响,因为它们以自19 ...

  4. RTC实时时钟驱动

    RTC(Real-Time Clock)实时时钟为操作系统提供了一个可靠的时间,并且在断电的情况下,RTC实时时钟也可以通过电池供电,一直运行下去. RTC通过STRB/LDRB这两个ARM指令向CP ...

  5. 教你在树莓派使用上RTC实时时钟,不用再担心断电后时间归零的问题,开机后自动同步RTC时钟!!!

    准备工作:1.系统建议使用官方最新的镜像文件 2.RTC时钟模块板(I2C接口)建议使用DS1307时钟模块,或者RTC时钟模块RTC时钟模块: 大家知道arduino的电平是5V,树莓派是3.3V, ...

  6. RTC实时时钟-备份区域BKP--原理讲解

    RTC(Real Time Clock):实时时钟 BCD码:用4位2进制来表示10以内的十进制的形式. RTC的时钟源:LSE(32.768KHZ).HSE_RTC.LSI.经过一个精密校准(RTC ...

  7. stm32 rtc 实时时钟

    STM32的实时时钟是一个独立的定时器 通常会在后备区域供电端加一个纽扣电池,当主电源没有电的时,RTC不会停止工作 若VDD电源有效,RTC可以触发秒中断.溢出中断和闹钟中断 备份寄存器BKP 备份 ...

  8. RTC实时时钟

    作者:宋老师,华清远见嵌入式学院讲师. 1.1 RTC介绍 在 一个嵌入式系统中,通常采用RTC 来提供可靠的系统时间,包括时分秒和年月日等,而且要求在系统处于关机状态下它也能够正常工作(通常采用后备 ...

  9. 【iCore3 双核心板】例程十:RTC实时时钟实验——显示日期和时间

    实验指导书及代码包下载: http://pan.baidu.com/s/1jHuZcnc iCore3 购买链接: https://item.taobao.com/item.htm?id=524229 ...

  10. 【iCore4 双核心板_ARM】例程十:RTC实时时钟实验——显示时间和日期

    实验现象: 核心代码: int main(void) { /* USER CODE BEGIN 1 */ RTC_TimeTypeDef sTime; RTC_DateTypeDef sDate; ; ...

随机推荐

  1. FreeSWITCH容器化问题之rtp端口占用

    操作系统 :CentOS 7.6_x64.debian 11 (bullseye,docker) FreeSWITCH版本 :1.10.9 Docker版本:23.0.6 FreeSWITCH容器化带 ...

  2. 《HelloGitHub》第 90 期

    兴趣是最好的老师,HelloGitHub 让你对编程感兴趣! 简介 HelloGitHub 分享 GitHub 上有趣.入门级的开源项目. https://github.com/521xueweiha ...

  3. MySQL系列之——SQL介绍、常用SQL分类、数据类型、表属性、字符集、DDL应用、DCL应用、DML应用(增删改)、DQL应用(select )、元数据信息、show命令

    文章目录 一 SQL介绍 二 常用SQL分类 2.1 客户端命令 三 数据类型.表属性.字符集 3.1 数据类型 3.1.1 作用 3.1.2 种类 3.2 表属性 3.2.1 列属性 3.2.2 表 ...

  4. TIPTOP GP ERP二次开发规范说明

    TIPTOP GP ERP二次开发规范说明 TIPTOP ERP共分三大区:topstd(标准区) + toppord(正式区) + toptest(测试区) 标准区                  ...

  5. A piece of cake

    1. A piece of cake(易事情)2. Break a leg(祝好运)3. Don't count your chickens before they hatch(不要过早乐观)4. D ...

  6. JS逆向实战24—— 补环境过某房地产瑞数4.0

    前言 瑞数就不过多介绍了,算是国内 2 线产品中的天花板了.4 代其实难度不高,但要弄出来 确实挺费时间和耐心的.今天就简单来讲讲如何用补环境轻松的过瑞数. 本文首发链接为: https://mp.w ...

  7. .Net中的内存泄露

    .Net中的内存泄露 说明: 虽然已经有GC垃圾回收器在工作,但是还是会出现内存泄露. 内存碎片 费托管内存泄露比托管内存泄露更加严重.GC可以移动托管内存,为其他对象腾空间.但是非托管内存将永远的卡 ...

  8. URL, URI 和 URN 之间的区别

    英文原文:What's the difference between a URI and a URL?  URI 标识一个事物 , URL 定位一个事物:然而,位置同样可以标识一个事物,所以,每个 U ...

  9. 拓展欧几里得 edgcd 模板+简易推论

    LL exgcd(LL a,LL b, LL &x, LL &y) { if(b == 0) { x=1,y=0; return a; } LL d = exgcd(b, a%b, x ...

  10. 题解 CF1004C

    题意描述: 给定 \(n\) 个数,从前往后找,看看能和后面的数组成多少个不同的数对. 若两个数对仅仅是位置不同,我们也认为是两个不同的数对. 题目分析: 阅读题目,我们不难看出,若前面有一个数已经出 ...