STM32-笔记41-RTC(实时时钟)

一、什么是RTC?

实时时钟的缩写是RTC(Real_Time Clock)。RTC 是集成电路,通常称为时钟芯片。

实时时钟是一个独立的定时器。 RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
RTC模块和时钟配置系统(RCC_BDCR寄存器)处于后备区域,即在系统复位或从待机模式唤醒后, RTC的设置和时间维持不变。
复位后,对备份寄存器和RTC的访问被禁止,并且备份域被保护以防止可能存在的意外的写操作。执行以下操作可以使能对备份寄存器和RTC的访问:

  • 通过设置寄存器RCC_APB1ENR的PWREN和BKPEN位来打开电源和后备接口的时钟
  • 电源控制寄存器(PWR_CR)的DBP位来使能对后备寄存器和RTC的访问。

32位的可编程计数器,可对应Unix时间戳的秒计数器。

Unix时间戳是从120位的可编程预分频器,可适配不同频率的输入时钟。 可选择三种RTC时钟源: HSE时钟除以128(通常为8MHz/128) LSE振荡器时钟(通常为32.768KHz) LSI振荡器时钟(40KHz) 970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。

20位的可编程预分频器,可适配不同频率的输入时钟。

可选择三种RTC时钟源:

HSE时钟除以128(通常为8MHz/128)

LSE振荡器时钟(通常为32.768KHz)

LSI振荡器时钟(40KHz)

二、RTC框图

三、RTC寄存器及库函数

3.1 备份域控制寄存器(RCC_BDCR)

3.2 RTC控制寄存器高位(RTC_CRH)

3.3 RTC控制寄存器低位(RTC_CRL)

3.4 RTC预分频装载寄存器(RTC_PRLH/RTC_PRLL)

20位寄存器

3.5 RTC预分频器余数寄存器(RTC_DIVH / RTC_DIVL)

20位寄存器

3.6 RTC计数器寄存器 (RTC_CNTH / RTC_CNTL)

3.7 RTC闹钟寄存器(RTC_ALRH/RTC_ALRL)

库函数

HAL_RTC_Init(); //初始化函数

HAL_RTC_GetTime(); //获取时间

HAL_RTC_GetDate(); //获取日期

HAL_RTC_SetTime(); //设置时间

HAL_RTC_SetData(); //设置日期

HAL_RTC_GetAlarm();//获取设置的闹钟是多少

HAL_RTC_SetAlarm_IT(); //设置闹钟是多少,无IT就是设置一个闹钟,但是闹钟到时间不打开,有IT的就是,设置一个闹钟,到时间就会打开,会响

_HAL_RTC_ALARM_GET_FLAG(); //这个查看的标志位是配置标志(如下图所示)

四、读写RTC时间实验

  • 注意事项: 必须设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入RTC_PRL、RTC_CNT、 RTC_ALR寄存器
  • 对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器中的 RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是1时,才可以写入RTC寄存器

复制项目文件53-BKP读写

重命名为54-读写RTC时间实验

打开项目文件

main.c

#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "rtc.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    led_init();                         /* 初始化LED灯 */
    uart1_init(115200);
    rtc_init();
    printf("hello world!\r\n");
    
    if(rtc_read_bkr(1) != 0xA5A5)//防止复位影响:假如我们前面已经设定了bkr的值了,就不用在重新设定时间了
    {
        rtc_write_bkr(1, 0xA5A5);
        printf("读出来的值为:%X\r\n", rtc_read_bkr(1));
        
        struct tm time_data;
        time_data.tm_year = 2025;
        time_data.tm_mon = 1;
        time_data.tm_mday = 13;
        time_data.tm_hour = 12;
        time_data.tm_min = 28;
        time_data.tm_sec = 30;
        rtc_set_time(time_data);
    }
    while(1)
    { 
        rtc_get_time();
        delay_ms(1000);
    }
}

rtc.c

#include "rtc.h"
#include "stdio.h"

RTC_HandleTypeDef rtc_handle = {0};
//初始化rtc,因为bkp依赖于rtc 
void rtc_init(void)
{
    __HAL_RCC_PWR_CLK_ENABLE();//使能电源时钟
    __HAL_RCC_BKP_CLK_ENABLE();//使能bkp时钟
    HAL_PWR_EnableBkUpAccess();//允许访问备份域
    
    rtc_handle.Instance = RTC;
    rtc_handle.Init.AsynchPrediv = 32767;//分频系数32768-1 = 32767
    rtc_handle.Init.OutPut = RTC_OUTPUTSOURCE_NONE; //侵入脚:不使用侵入检测相关内容
    HAL_RTC_Init(&rtc_handle);
}
//在msp函数中设置使能RTC、设置时钟源、中断配置(这里没用)
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
    __HAL_RCC_RTC_ENABLE();
    
    RCC_OscInitTypeDef osc_initstruct = {0};
    RCC_PeriphCLKInitTypeDef periphclk_initstruct = {0};
    
    osc_initstruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;//LSE
    osc_initstruct.LSEState = RCC_LSE_ON;//LSE的状态:打开
    osc_initstruct.PLL.PLLState = RCC_PLL_NONE;//锁向环:none
    HAL_RCC_OscConfig(&osc_initstruct);//配置振荡器

    periphclk_initstruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;//配置RTC
    periphclk_initstruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;//RTC时钟选择LSE
    HAL_RCCEx_PeriphCLKConfig(&periphclk_initstruct);//配置RTC时钟源
    
}
//读rtc,读出来是2个字符16个字节,十六位寄存器,bkrx指定读的是哪个寄存器
uint16_t rtc_read_bkr(uint8_t bkrx)
{
    uint32_t data = 0;
    //读bkp寄存器
    data = HAL_RTCEx_BKUPRead(&rtc_handle, bkrx);
    return (uint16_t)data; 
}
//写rtc,写进哪个寄存器?bkrx,写进去什么?data
void rtc_write_bkr(uint8_t bkrx,uint16_t data)
{
    //写bkp寄存器
    HAL_RTCEx_BKUPWrite(&rtc_handle, bkrx, data);
}
void rtc_get_time(void)//获取时间
{
    RTC_TimeTypeDef rtc_time = {0};//获取时间
    RTC_DateTypeDef rtc_date = {0};//获取日期
    
    HAL_RTC_GetTime(&rtc_handle, &rtc_time, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&rtc_handle, &rtc_date, RTC_FORMAT_BIN);
    
    printf("rtc time: %d-%02d-%02d %02d:%02d:%02d\r\n", rtc_date.Year + 2000, rtc_date.Month, rtc_date.Date,
        rtc_time.Hours, rtc_time.Minutes, rtc_time.Seconds);
}

void rtc_set_time(struct tm time_data)//设置时间(年月日时分秒)
{
    RTC_TimeTypeDef rtc_time = {0};
    RTC_DateTypeDef rtc_date = {0};
    
    rtc_time.Hours = time_data.tm_hour;
    rtc_time.Minutes = time_data.tm_min;
    rtc_time.Seconds = time_data.tm_sec;
    HAL_RTC_SetTime(&rtc_handle, &rtc_time, RTC_FORMAT_BIN);
    
    rtc_date.Year = time_data.tm_year - 2000;
    rtc_date.Month = time_data.tm_mon;
    rtc_date.Date = time_data.tm_mday;
    HAL_RTC_SetDate(&rtc_handle, &rtc_date, RTC_FORMAT_BIN);
    
    while(!__HAL_RTC_ALARM_GET_FLAG(&rtc_handle, RTC_FLAG_RTOFF));
}

rtc.h

#ifndef __RTC_H__
#define __RTC_H__


#include "sys.h"
#include "time.h"

void rtc_init(void);
uint16_t rtc_read_bkr(uint8_t bkrx);
void rtc_write_bkr(uint8_t bkrx, uint16_t data);
void rtc_get_time(void);
void rtc_set_time(struct tm time_data);

#endif

五、RTC闹钟实验

复制项目文件夹54-读写RTC时间实验

重命名项目文件为55-RTC闹钟实验

打开项目文件

main.c

#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "rtc.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    led_init();                         /* 初始化LED灯 */
    uart1_init(115200);
    rtc_init();
    printf("hello world!\r\n");
    
    if(rtc_read_bkr(1) != 0xA5A5)//防止复位影响:假如我们前面已经设定了bkr的值了,就不用在重新设定时间了
    {
        rtc_write_bkr(1, 0xA5A5);
        printf("读出来的值为:%X\r\n", rtc_read_bkr(1));
        
        struct tm time_data,alarm_data;
        time_data.tm_year = 2025;
        time_data.tm_mon = 1;
        time_data.tm_mday = 13;
        time_data.tm_hour = 13;
        time_data.tm_min = 29;
        time_data.tm_sec = 30;
        rtc_set_time(time_data);
        /*设置闹钟的时间*/
        alarm_data.tm_hour = 13;
        alarm_data.tm_min = 29;
        alarm_data.tm_sec = 45;
        rtc_set_alarm(alarm_data);
    }
    
    while(1)
    { 
        rtc_get_time();
        delay_ms(1000);
    }
}

rtc.c

#include "rtc.h"
#include "stdio.h"

RTC_HandleTypeDef rtc_handle = {0};
//初始化rtc,因为bkp依赖于rtc 
void rtc_init(void)
{
    __HAL_RCC_PWR_CLK_ENABLE();//使能电源时钟
    __HAL_RCC_BKP_CLK_ENABLE();//使能bkp时钟
    HAL_PWR_EnableBkUpAccess();//允许访问备份域
    
    rtc_handle.Instance = RTC;
    rtc_handle.Init.AsynchPrediv = 32767;//分频系数32768-1 = 32767
    rtc_handle.Init.OutPut = RTC_OUTPUTSOURCE_NONE; //侵入脚:不使用侵入检测相关内容
    HAL_RTC_Init(&rtc_handle);
}
//在msp函数中设置使能RTC、设置时钟源、中断配置
void HAL_RTC_MspInit(RTC_HandleTypeDef *hrtc)
{
    __HAL_RCC_RTC_ENABLE();
    
    RCC_OscInitTypeDef osc_initstruct = {0};
    RCC_PeriphCLKInitTypeDef periphclk_initstruct = {0};
    
    osc_initstruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;//LSE
    osc_initstruct.LSEState = RCC_LSE_ON;//LSE的状态:打开
    osc_initstruct.PLL.PLLState = RCC_PLL_NONE;//锁向环:none
    HAL_RCC_OscConfig(&osc_initstruct);//配置振荡器

    periphclk_initstruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;//配置RTC
    periphclk_initstruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;//RTC时钟选择LSE
    HAL_RCCEx_PeriphCLKConfig(&periphclk_initstruct);//配置RTC时钟源
    
    HAL_NVIC_SetPriority(RTC_Alarm_IRQn,2,2);//中断
    HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
    
}
void RTC_Alarm_IRQHandler(void)//中断处理函数
{
    HAL_RTC_AlarmIRQHandler(&rtc_handle);
}
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
    printf("闹钟响了。。。\r\n");
}

//读rtc,读出来是2个字符16个字节,十六位寄存器,bkrx指定读的是哪个寄存器
uint16_t rtc_read_bkr(uint8_t bkrx)
{
    uint32_t data = 0;
    //读bkp寄存器
    data = HAL_RTCEx_BKUPRead(&rtc_handle, bkrx);
    return (uint16_t)data; 
}
//写rtc,写进哪个寄存器?bkrx,写进去什么?data
void rtc_write_bkr(uint8_t bkrx,uint16_t data)
{
    //写bkp寄存器
    HAL_RTCEx_BKUPWrite(&rtc_handle, bkrx, data);
}
void rtc_get_time(void)//获取时间
{
    RTC_TimeTypeDef rtc_time = {0};//获取时间
    RTC_DateTypeDef rtc_date = {0};//获取日期
    
    HAL_RTC_GetTime(&rtc_handle, &rtc_time, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&rtc_handle, &rtc_date, RTC_FORMAT_BIN);
    
    printf("rtc time: %d-%02d-%02d %02d:%02d:%02d\r\n", rtc_date.Year + 2000, rtc_date.Month, rtc_date.Date,
        rtc_time.Hours, rtc_time.Minutes, rtc_time.Seconds);
}

void rtc_set_time(struct tm time_data)//设置时间(年月日时分秒)
{
    RTC_TimeTypeDef rtc_time = {0};
    RTC_DateTypeDef rtc_date = {0};
    
    rtc_time.Hours = time_data.tm_hour;
    rtc_time.Minutes = time_data.tm_min;
    rtc_time.Seconds = time_data.tm_sec;
    HAL_RTC_SetTime(&rtc_handle, &rtc_time, RTC_FORMAT_BIN);
    
    rtc_date.Year = time_data.tm_year - 2000;
    rtc_date.Month = time_data.tm_mon;
    rtc_date.Date = time_data.tm_mday;
    HAL_RTC_SetDate(&rtc_handle, &rtc_date, RTC_FORMAT_BIN);
    
    while(!__HAL_RTC_ALARM_GET_FLAG(&rtc_handle, RTC_FLAG_RTOFF));
}
void rtc_set_alarm(struct tm alarm_data)//设置闹钟
{
    RTC_AlarmTypeDef alarm = {0};
    
    alarm.Alarm = RTC_ALARM_A;//闹钟
    alarm.AlarmTime.Hours = alarm_data.tm_hour;//时间的设置:时分秒
    alarm.AlarmTime.Minutes = alarm_data.tm_min;
    alarm.AlarmTime.Seconds = alarm_data.tm_sec;
    
    HAL_RTC_SetAlarm_IT(&rtc_handle, &alarm, RTC_FORMAT_BIN);
}

rtc.h

#ifndef __RTC_H__
#define __RTC_H__


#include "sys.h"
#include "time.h"

void rtc_init(void);
uint16_t rtc_read_bkr(uint8_t bkrx);
void rtc_write_bkr(uint8_t bkrx, uint16_t data);
void rtc_get_time(void);
void rtc_set_time(struct tm time_data);
void rtc_set_alarm(struct tm alarm_data);

#endif

执行结果如下

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/953973.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

51c自动驾驶~合集46

我自己的原文哦~ https://blog.51cto.com/whaosoft/13050104 #世界模型会是L3自动驾驶的唯一解吗 三维空间占有率(3D Occupancy)预测的目的是预测三维空间中的每个体素是否被占有,如果被占有,则对应的体素将被标记。3D Semant…

mybatis-spring @MapperScan走读分析

接上一篇文章:https://blog.csdn.net/qq_26437925/article/details/145100531, 本文注解分析mybatis-spring中的MapperScan注解,则将容易许多。 目录 MapperScan注解定义ConfigurationClassPostProcessor扫描注册beanDefinitionorg.mybatis.s…

Apache PAIMON 学习

参考:Apache PAIMON:实时数据湖技术框架及其实践 数据湖不仅仅是一个存储不同类数据的技术手段,更是提高数据分析效率、支持数据驱动决策、加速AI发展的基础设施。 新一代实时数据湖技术,Apache PAIMON兼容Apache Flink、Spark等…

SQL面试题1:连续登陆问题

引言 场景介绍: 许多互联网平台为了提高用户的参与度和忠诚度,会推出各种连续登录奖励机制。例如,游戏平台会给连续登录的玩家发放游戏道具、金币等奖励;学习类 APP 会为连续登录学习的用户提供积分,积分可兑换课程或…

电商系统,核心通用架构案例设计方案浅析

文章目录 一、用户系统案例设计1、用户信息的存储方案2、用户注册确保唯一3、用户数据合并方案4、用户敏感信息加密存储5、数据传输安全性6、多用户数据隔离性7、防止恶意注册8、用户好友关系存储方案9、用户登录token方案10、会员优先处理设计 二、网关系统设计1、网关的功能2…

【EI 会议征稿】第四届材料工程与应用力学国际学术会议(ICMEAAE 2025)

2025 4th International Conference on Materials Engineering and Applied Mechanics 重要信息 大会官网:www.icmeaae.com 大会时间:2025年3月7-9日 大会地点:中国西安 截稿时间:2025年1月24日23:59 接受/拒稿通知&#xf…

SQL面试题2:留存率问题

引言 场景介绍: 在互联网产品运营中,用户注册量和留存率是衡量产品吸引力和用户粘性的关键指标,直接影响产品的可持续发展和商业价值。通过分析这些数据,企业可以了解用户行为,优化产品策略,提升用户体验…

【Rust自学】11.7. 按测试的名称运行测试

喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 11.7.1. 按名称运行测试的子集 如果想要选择运行的测试,就将测试的名称(一个或多个)作为cargo test的…

深入浅出 Android AES 加密解密:从理论到实战

深入浅出 Android AES 加密解密:从理论到实战 在现代移动应用中,数据安全是不可忽视的一环。无论是用户隐私保护,还是敏感信息的存储与传输,加密技术都扮演着重要角色。本文将以 AES(Advanced Encryption Standard&am…

jupyter notebook练手项目:线性回归——学习时间与成绩的关系

线性回归——学习时间与学习成绩的关系 第1步:导入工具库 pandas——数据分析库,提供了数据结构(如DataFrame和Series)和数据操作方法,方便对数据集进行读取、清洗、转换等操作。 matplotlib——绘图库,p…

JVM虚拟机的组成 笼统理解 六大部分 类加载子系统 运行时数据区 执行引擎 本地接口 垃圾回收器 线程工具

目录 JVM虚拟机的组成:概述 JVM虚拟机的组成:详细解析 1. 类加载子系统 2. 运行时数据区 3. 执行引擎 4. 本地接口 5. 垃圾回收器 6. 线程管理与调试工具 概述 JVM(Java Virtual Machine)是一个虚拟计算机,执行…

单细胞组学大模型(8)--- scGenePT,scGPT和GenePT的结合,实验数据和文本数据的交融模型

–https://doi.org/10.1101/2024.10.23.619972 研究团队和单位 Theofanis Karaletsos–Head Of AI - Science at Chan Zuckerberg Initiative (Chan Zuckerberg Initiative是扎克伯格和他妻子Chan成立的科研&教育机构) 研究简介 研究背景&…

kafka原理和实践

Kafka是当前分布式系统中最流行的消息中间件之一,凭借着其高吞吐量的设计,在日志收集系统和消息系统的应用场景中深得开发者喜爱。本篇就聊聊Kafka相关的一些知识点。主要包括以下内容: Kafka简介 Kafka特点Kafka基本概念Kafka架构Kafka的几…

CSS | 实现三列布局(两边边定宽 中间自适应,自适应成比)

目录 示例1 (中间自适应 示例2(中间自适应 示例3(中间自适应 示例4 (自适应成比 示例5(左中定宽,右边自适应 示例6(中间自适应 示例7(中间自适应 示例8(中间定宽…

【大数据】机器学习------神经网络模型

一、神经网络模型 1. 基本概念 神经网络是一种模拟人类大脑神经元结构的计算模型,由多个神经元(节点)组成,这些节点按照不同层次排列,通常包括输入层、一个或多个隐藏层和输出层。每个神经元接收来自上一层神经元的输…

docker一张图理解

1、push 将本地的镜像上传到镜像仓库,要先登陆到镜像仓库。参数说明: –disable-content-trust : 忽略镜像的校验,默认开启 # 上传本地镜像myapache:v1到镜像仓库中。 docker push myapache:v1 1.2、search 从Docker Hub查找镜像。参数说明: –…

Unity shader中真的可以动态关闭Stencil Test吗?

这个问题很多年前就有人问了: https://discussions.unity.com/t/how-to-disable-the-stencil-block-via-shader-properties/600273/1 最后的答案是: set [_StencilComp] to CompareFunction.Disabled to disable the Stencil Op completely. 但是我测试…

Python----Python高级(函数基础,形参和实参,参数传递,全局变量和局部变量,匿名函数,递归函数,eval()函数,LEGB规则)

一、函数基础 1.1、函数的用法和底层分析 函数是可重用的程序代码块。 函数的作用,不仅可以实现代码的复用,更能实现代码的一致性。一致性指的是,只要修改函数的代码,则所有调用该函数的地方都能得到体现。 在编写函数时&#xf…

win10电脑 定时关机

win10电脑 定时关机 https://weibo.com/ttarticle/p/show?id2309405110707766296723 二、使用任务计划程序设置定时关机打开任务计划程序: 按下“Win S”组合键,打开搜索框。 在搜索框中输入“任务计划程序”,然后点击搜索结果中的“任务…

初识JAVA-面向对象的三大特征之多态

1. 重温面向对象 面向对象是一种解决问题的思想,它把计算机程序看作是各种对象组合起来的。每个对象都有自己的数据(属性)和行为(方法),主要依靠对象之间的交互来解决和实现问题。Java是一门纯面向对象的语…