stm32入门学习13-时钟RTC
(一)时钟RTC
stm32内部集成了一个秒计数器RTC,用于显示我们日常的时间,如日期年月日,时分秒等,RTC的主要原理就是进行每秒自增,如果我们知道开始记秒的开始时间,就可以计算现在的日期,但是这不需要我们计算,我们只要调用C语言库函数即可自动完成秒数到日常时间或日常时间到秒数的转换,如果只是实现秒自增的功能,那么内部任何一个计数器都可以实现,但是时钟RTC与其他计数器不同的是其可以使用备用电源保持工作,在最小板供电停止时如果有备用电源其会继续工作;这里的备用电源供电引脚为stm32最小板的1好引脚,标注为VBT;
(二)备份寄存器BKP
和时钟RTC一样,备份寄存器BKP可以在备用电源供电时保持数据不丢失,但是其却不能在备用电源和主电源同时断电时候还保持掉电不丢失,这里使用BKP主要是为了给时钟判断是否需要初始化,如果备用电源没有断开,RTC依旧在工作,那么在主电源重新供电的时候时钟就不需要初始化,直接读取其时钟值即可;
(三)RTC时钟和BKP备份寄存器
这里BKP主要是为了检测备用电源是否掉电的,我们的思路是在上电的时候在备份寄存器BKP上写入一个数据(不要写默认值0),如果备用电源没有断电,这个数据会一直保持,如果断电则会恢复为0,我们只要会初始化BKP和读写BKP即可,主要用到这几个函数
void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data);
// 写入备份寄存器uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR);
// 读出备份寄存器void RTC_WaitForLastTask(void);
// 等待上次任务完成,内部自带while循环和进入RTC的配置模式void RTC_WaitForSynchro(void);
// 等待系统同步,自带while循环和进入RTC的配置模式
(1)BKP初始化
BKP初始化要初始BKP的时钟,还有电源管理模块的时钟RCC_APB1Periph_PWR,以便在使用备用电源时stm32可以进行电源管理进入待机模式,这些时钟都是APB1上的外设,因此我们要调动APB1的初始化函数
void rtc_bkp_init()
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
}
(2)RTC初始化
RTC不想其他外设一样用初始化结构体来进行初始化,我们要对每一步手动初始化,初始化流程图如下

首先要开启RTC的时钟并选择对应通道,然后要写入重装寄存器和预分频值以使得计数器实现每秒自增(即频率为1Hz),当然我们还要打开对应的电源控制
如果在主电源断开但备用电源没有断开时,RTC仍然会工作,但是主电源开始供电时,程序会重启,这时会再次调用RTC的初始化函数,这时不希望的,因此我们在BKP中写入一个标志位,如果该标志位没有清零,表示备用电源和主电源没有同时断电,这时我们就不需要重新对RTC进行初始化,我们在RTC初始化中加入if判断即可
void rtc_init()
{PWR_BackupAccessCmd(ENABLE);if (BKP_ReadBackupRegister(BKP_DR1) != 1){RCC_LSEConfig(RCC_LSE_ON);while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET);RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);RCC_RTCCLKCmd(ENABLE);RTC_WaitForSynchro(); // wait synchronizationRTC_WaitForLastTask(); // wait writeRTC_SetPrescaler(32768-1);RTC_WaitForLastTask();rtc_set_time(2024, 8, 9, 16, 7, 0);BKP_WriteBackupRegister(BKP_DR1, 1);}
}
由于RTC的时钟和系统时钟不是一个频率,在开启时钟后要等待时钟同步(synchronization),每次进行写操作时要等待上次任务完成;
这里选择的是外部低速时钟,其晶振为32.768KHz,因此要进行32768分频使其频率为1Hz
(3)设置和读取时间
我们可以从该时钟读取到的只有秒值,且要修改时间也只有输入对应时间的秒数才能修改,但是我们可以借助time库来计算我们常见的时间格式和对应秒数的转换,主要有两个函数和一个结构体
struct tm {int tm_sec; /* seconds after the minute, 0 to 60(0 - 60 allows for the occasional leap second) */int tm_min; /* minutes after the hour, 0 to 59 */int tm_hour; /* hours since midnight, 0 to 23 */int tm_mday; /* day of the month, 1 to 31 */int tm_mon; /* months since January, 0 to 11 */int tm_year; /* years since 1900 */int tm_wday; /* days since Sunday, 0 to 6 */int tm_yday; /* days since January 1, 0 to 365 */int tm_isdst; /* Daylight Savings Time flag */union { /* ABI-required extra fields, in a variety of types */struct {int __extra_1, __extra_2;};struct {long __extra_1_long, __extra_2_long;};struct {char *__extra_1_cptr, *__extra_2_cptr;};struct {void *__extra_1_vptr, *__extra_2_vptr;};};
};
// 时间结构体,对应秒、分、时、日、月、年、星期、天extern _ARMABI time_t mktime(struct tm * /*timeptr*/) __attribute__((__nonnull__(1)));
// 将时间结构体转换为秒数,输入一个时间结构体指针输出其到1990年的秒数,结构体只要年月日即可extern _ARMABI struct tm *localtime(const time_t * /*timer*/) __attribute__((__nonnull__(1)));
// 输入到1990年的秒数输出一个时间结构体指针
这里有一些规定与日常不合,这里的月是从0开始的,年是如今到1900年的年数,星期从星期天(0)开始
这里转换的是伦敦时间,要计算北京时间还要加上8小时(东八区),对应秒数加8*60*60
void rtc_set_time(unsigned int year, unsigned int month, unsigned int day, unsigned int hour, unsigned int min, unsigned int sec)
{struct tm timer;unsigned int counter;timer.tm_year = year-1900;timer.tm_mon = month-1;timer.tm_mday = day;timer.tm_hour = hour;timer.tm_min = min;timer.tm_sec = sec;counter = mktime(&timer);counter -= 8*60*60;RTC_SetCounter(counter);RTC_WaitForLastTask();
}
struct tm* rtc_read_time()
{struct tm* timer;unsigned int counter = RTC_GetCounter() + 8*60*60;timer = localtime(&counter);timer->tm_year += 1900;timer->tm_mon += 1;return timer;
}
(4)封装与声明
最终.c 和 .h文件如下
#include "stm32f10x.h" // Device header
#include <time.h>void rtc_set_time(unsigned int year, unsigned int month, unsigned int day, unsigned int hour, unsigned int min, unsigned int sec);void rtc_bkp_init()
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
}void rtc_init()
{PWR_BackupAccessCmd(ENABLE);if (BKP_ReadBackupRegister(BKP_DR1) != 1){RCC_LSEConfig(RCC_LSE_ON);while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET);RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);RCC_RTCCLKCmd(ENABLE);RTC_WaitForSynchro(); // wait synchronizationRTC_WaitForLastTask(); // wait writeRTC_SetPrescaler(32768-1);RTC_WaitForLastTask();rtc_set_time(2024, 8, 9, 16, 7, 0);BKP_WriteBackupRegister(BKP_DR1, 1);}
}void rtc_set_time(unsigned int year, unsigned int month, unsigned int day, unsigned int hour, unsigned int min, unsigned int sec)
{struct tm timer;unsigned int counter;timer.tm_year = year-1900;timer.tm_mon = month-1;timer.tm_mday = day;timer.tm_hour = hour;timer.tm_min = min;timer.tm_sec = sec;counter = mktime(&timer);counter -= 8*60*60;RTC_SetCounter(counter);RTC_WaitForLastTask();
}struct tm* rtc_read_time()
{struct tm* timer;unsigned int counter = RTC_GetCounter() + 8*60*60;timer = localtime(&counter);timer->tm_year += 1900;timer->tm_mon += 1;return timer;
}
#ifndef __RTC_H__
#define __RTC_H__void rtc_bkp_init(void);
void rtc_init(void);
void rtc_set_time(unsigned int year, unsigned int mon, unsigned int day, unsigned int hour, unsigned int min, unsigned int sec);
struct tm* rtc_read_time(void);#endif
(四)主函数
只要在主函数进行对应初始化和调用读取函数即可,初始化已经包含了开始时间的设定,如果要重新设定开始时间,可以在初始化中更改,也可以在主函数中使用rtc_set_time函数来修改,但是要注意上电后stm32复位问题,应该和初始化一样加入备用电源是否断电的判断代码
#include "stm32f10x.h" // Device header
#include "rtc.h"
#include "OLED.h"
#include <time.h>int main()
{struct tm* time_inf;OLED_Init();rtc_bkp_init();rtc_init();OLED_ShowString(1, 1, "time:");OLED_ShowString(2, 1, "xxxx-xx-xx");OLED_ShowString(3, 1, "xx:xx:xx");while(1){time_inf = rtc_read_time();OLED_ShowNum(2, 1, time_inf->tm_year, 4);OLED_ShowNum(2, 6, time_inf->tm_mon, 2);OLED_ShowNum(2, 9, time_inf->tm_mday, 2);OLED_ShowNum(3, 1, time_inf->tm_hour, 2);OLED_ShowNum(3, 4, time_inf->tm_min, 2);OLED_ShowNum(3, 7, time_inf->tm_sec, 2);}return 0;
}
(五)总结
通过实现一个时钟,我们学习了RTC和BKP两个可以使用备用电源供电工作的外设,了解了计数器RTC的原理和备份寄存器BKP的原理,通过备份寄存器实现了RTC的掉电继续计时和数据不丢失
相关文章:
stm32入门学习13-时钟RTC
(一)时钟RTC stm32内部集成了一个秒计数器RTC,用于显示我们日常的时间,如日期年月日,时分秒等,RTC的主要原理就是进行每秒自增,如果我们知道开始记秒的开始时间,就可以计算现在的日…...
vuex properties of undefined (reading ‘getters‘)
前言: 最近打算用vue 写个音乐播放器,在搞 vuex 的时候遇到一个很神奇报错;vuex 姿势练了千百次了,刚开始的时候我一直以为是代码问题,反复检查了带了,依旧报错。 Error in mounted hook: "TypeError:…...
再谈表的约束
文章目录 自增长唯一键外键 自增长 auto_increment:当对应的字段,不给值,会自动的被系统触发,系统会从当前字段中已经有的最大值1操作,得到一个新的不同的值。通常和主键搭配使用,作为逻辑主键。 自增长的…...
认识一下测试策略与测试方案
目录 测试方案 测试策略 测试策略的内容主要包括 测试技术和工具 测试启动、停止和完成标准 风险分析和应对方案 测试范围 测试角色和职责 测试方法和类型 测试工具 测试层级 测试指标 测试可交付成果 测试方案的内容包括 测试目标 测试范围 测试环境 测试策略…...
Gradle 查看包的依赖关系
在 Terminal 中可以通过 gradle 的命令查看项目中使用的依赖库及其版本,并且可以更加直观的看到各个模块中库之间的依赖关系。同时也可以跟踪并解决与库版本冲突有关的问题。 工具查看 在 Android Studio 中选择 View > Tool Windoors > Gradle 或者直接选择…...
虚幻5|给攻击添加特效
一,打开武器蓝图 选择武器网格体,在细节处找到组件开始重叠,点击 写下以下蓝图,这是最终蓝图,后面会分讲要点 二,actor拥有标签,就是被击打的敌人,我们给actor添加标签 到主界面&am…...
Delphi包管理与依赖:掌握GetIt与DelphiPI的艺术
标题:Delphi包管理与依赖:掌握GetIt与DelphiPI的艺术 在Delphi的广袤生态中,包管理和依赖解决方案是构建大型项目不可或缺的工具。本文将深入探讨Delphi中的两种主要包管理工具:GetIt包管理器和DelphiPI,通过实际代码…...
如何使用unittest和pytest进行python脚本的单元测试
1. 关于unittest和pytest unittest是python内置的支持单元测试的模块,他提供了核心类,TestCase,让单元测试 代码的编写不再是从0开始,不再是作坊式,而是标准化,模板化,工厂化。 pytest是第三方…...
Java中的值传递与引用传递
Java中的值传递与引用传递 在Java编程中,理解值传递与引用传递的概念是编写无误代码的关键。这两个概念有时会让人感到困惑,特别是当它们与对象有关时。现在,我们将一步步地解释这两个概念,帮助你彻底理解它们。 1. 值传递与引用…...
Seaborn库
目录 主要功能和特点 使用方法 实例应用 Seaborn库的最新版本有哪些新功能和改进? 如何在Seaborn中实现复杂的数据预处理步骤,例如数据清洗和转换? Seaborn与其他数据可视化库(如Matplotlib、Plotly)相比有哪些优…...
openresty整合modsecurity
安装依赖包 安装依赖 yum -y install gcc-c flex bison yajl yajl-devel curl-devel curl GeoIP-devel doxygen zlib-devel libtool libxml2-devel libxslt-devel安装依赖包 ftp://ftp.icm.edu.pl/vol/rzm7/linux-centos-vault/7.8.2003/sclo/x86_64/rh/Packages/d/devtools…...
结构体structure、共用体union
目录 结构体 结构体类型的定义形式 结构体类型的大小 内存计算例子 共用体union 用共用体判断大小端 结构体和共用体对比 qsort() 结构体 结构体类型——用来描述复杂数据的一种数据类型 构造类型(用户自定义类型) struc…...
Spring自动注册-<bean>标签和属性解析
xml文件中最常见也最核心的就是<bean>,<Import>,<beans>,<alias>标签,关于它们的解析主要是BeanDefinitionParserDelegate类中.<bean>标签的解析最为复杂和重要. <bean>标签 processBeanDefinition(ele, delegate)方法中,主要是是对…...
【仿RabbitMQ消息队列】基于C++11中packaged_tack异步线程池
目录 什么是同步和异步? future 使用future和async配合管理异步任务 使用promise和future配合管理异步任务 使⽤std::packaged_task和std::future配合 C11异步线程池 什么是同步和异步? 同步(Synchronous) 同步编程是指程…...
免费下载专利
给大家提供一个可以免费下载专利的地方 链接:https://www.drugfuture.com/cnpat/cn_patent.asp...
CentOS7安装流程步骤详细教程
1. 简介 1.1. 概述 CentOS 7是一款基于Red Hat Enterprise Linux(RHEL)的开源Linux发行版,它提供了一个稳定、安全、高效的操作系统,适用于企业级应用、服务器、云计算等领域。CentOS 7以其高度的稳定性和安全性而闻名,它广泛应用于各种行业和领域,包括政府、金融、医疗、…...
【大模型从入门到精通17】openAI API 构建和评估大型语言模型(LLM)应用5
这里写目录标题 理论问题:实践问题:理论实践 理论问题: 1.描述评估LLM应用程序输出的重要性,并提及至少三个维度,这些输出应该在这几个维度上被评估。 2.解释在评估LLM应用程序时开发稳健的性能指标的作用,…...
苹果手机无iCloud备份下“最近删除”照片的恢复策略
iPhone最近删除清空了照片还能恢复吗?大家都知道,照片对于我们来说是承载着美好回忆的一种形式。它记录着我们的平淡生活,也留住了我们的美好瞬间,因此,具有极其重要的纪念价值。但由于我们的失误操作导致照片被删除&a…...
Docker搭建Minio容器
Docker搭建Minio容器 前言 在上一集我们介绍了分布式文件存储行业解决方案以及技术选型。最终我们决定选用Minio作为分布式文件存储。 那么这集我们就在Docker上搭建Minio容器即可。 Docker搭建Minio容器步骤 创建Minio文件目录 我们选择创建/minio/data目录 修改目录权…...
【C++】多源BFS问题和拓扑排序
目录 多源BFS介绍 单源BFS和多源BFS的区别 SO如何解决多源BFS问题 多源之核心 矩阵 算法思路 代码实现 飞地的数量 算法思路 代码实现 地图中的最高点 算法思路 代码实现 地图分析 算法思路 代码实现 拓扑排序介绍 有向无环图 编辑 如何解决这类问题 课…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
