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

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

&#xff08;一&#xff09;时钟RTC stm32内部集成了一个秒计数器RTC&#xff0c;用于显示我们日常的时间&#xff0c;如日期年月日&#xff0c;时分秒等&#xff0c;RTC的主要原理就是进行每秒自增&#xff0c;如果我们知道开始记秒的开始时间&#xff0c;就可以计算现在的日…...

vuex properties of undefined (reading ‘getters‘)

前言&#xff1a; 最近打算用vue 写个音乐播放器&#xff0c;在搞 vuex 的时候遇到一个很神奇报错&#xff1b;vuex 姿势练了千百次了&#xff0c;刚开始的时候我一直以为是代码问题&#xff0c;反复检查了带了&#xff0c;依旧报错。 Error in mounted hook: "TypeError:…...

再谈表的约束

文章目录 自增长唯一键外键 自增长 auto_increment&#xff1a;当对应的字段&#xff0c;不给值&#xff0c;会自动的被系统触发&#xff0c;系统会从当前字段中已经有的最大值1操作&#xff0c;得到一个新的不同的值。通常和主键搭配使用&#xff0c;作为逻辑主键。 自增长的…...

认识一下测试策略与测试方案

目录 测试方案 测试策略 测试策略的内容主要包括 测试技术和工具 测试启动、停止和完成标准 风险分析和应对方案 测试范围 测试角色和职责 测试方法和类型 测试工具 测试层级 测试指标 测试可交付成果 测试方案的内容包括 测试目标 测试范围 测试环境 测试策略…...

Gradle 查看包的依赖关系

在 Terminal 中可以通过 gradle 的命令查看项目中使用的依赖库及其版本&#xff0c;并且可以更加直观的看到各个模块中库之间的依赖关系。同时也可以跟踪并解决与库版本冲突有关的问题。 工具查看 在 Android Studio 中选择 View > Tool Windoors > Gradle 或者直接选择…...

虚幻5|给攻击添加特效

一&#xff0c;打开武器蓝图 选择武器网格体&#xff0c;在细节处找到组件开始重叠&#xff0c;点击 写下以下蓝图&#xff0c;这是最终蓝图&#xff0c;后面会分讲要点 二&#xff0c;actor拥有标签&#xff0c;就是被击打的敌人&#xff0c;我们给actor添加标签 到主界面&am…...

Delphi包管理与依赖:掌握GetIt与DelphiPI的艺术

标题&#xff1a;Delphi包管理与依赖&#xff1a;掌握GetIt与DelphiPI的艺术 在Delphi的广袤生态中&#xff0c;包管理和依赖解决方案是构建大型项目不可或缺的工具。本文将深入探讨Delphi中的两种主要包管理工具&#xff1a;GetIt包管理器和DelphiPI&#xff0c;通过实际代码…...

如何使用unittest和pytest进行python脚本的单元测试

1. 关于unittest和pytest unittest是python内置的支持单元测试的模块&#xff0c;他提供了核心类&#xff0c;TestCase&#xff0c;让单元测试 代码的编写不再是从0开始&#xff0c;不再是作坊式&#xff0c;而是标准化&#xff0c;模板化&#xff0c;工厂化。 pytest是第三方…...

Java中的值传递与引用传递

Java中的值传递与引用传递 在Java编程中&#xff0c;理解值传递与引用传递的概念是编写无误代码的关键。这两个概念有时会让人感到困惑&#xff0c;特别是当它们与对象有关时。现在&#xff0c;我们将一步步地解释这两个概念&#xff0c;帮助你彻底理解它们。 1. 值传递与引用…...

Seaborn库

目录 主要功能和特点 使用方法 实例应用 Seaborn库的最新版本有哪些新功能和改进&#xff1f; 如何在Seaborn中实现复杂的数据预处理步骤&#xff0c;例如数据清洗和转换&#xff1f; Seaborn与其他数据可视化库&#xff08;如Matplotlib、Plotly&#xff09;相比有哪些优…...

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&#xff08;&#xff09; 结构体 结构体类型——用来描述复杂数据的一种数据类型 构造类型&#xff08;用户自定义类型&#xff09; struc…...

Spring自动注册-<bean>标签和属性解析

xml文件中最常见也最核心的就是<bean>,<Import>,<beans>,<alias>标签,关于它们的解析主要是BeanDefinitionParserDelegate类中.<bean>标签的解析最为复杂和重要. <bean>标签 processBeanDefinition(ele, delegate)方法中,主要是是对…...

【仿RabbitMQ消息队列】基于C++11中packaged_tack异步线程池

目录 什么是同步和异步&#xff1f; future 使用future和async配合管理异步任务 使用promise和future配合管理异步任务 使⽤std::packaged_task和std::future配合 C11异步线程池 什么是同步和异步&#xff1f; 同步&#xff08;Synchronous&#xff09; 同步编程是指程…...

免费下载专利

给大家提供一个可以免费下载专利的地方 链接&#xff1a;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

这里写目录标题 理论问题&#xff1a;实践问题&#xff1a;理论实践 理论问题&#xff1a; 1.描述评估LLM应用程序输出的重要性&#xff0c;并提及至少三个维度&#xff0c;这些输出应该在这几个维度上被评估。 2.解释在评估LLM应用程序时开发稳健的性能指标的作用&#xff0c…...

苹果手机无iCloud备份下“最近删除”照片的恢复策略

iPhone最近删除清空了照片还能恢复吗&#xff1f;大家都知道&#xff0c;照片对于我们来说是承载着美好回忆的一种形式。它记录着我们的平淡生活&#xff0c;也留住了我们的美好瞬间&#xff0c;因此&#xff0c;具有极其重要的纪念价值。但由于我们的失误操作导致照片被删除&a…...

Docker搭建Minio容器

Docker搭建Minio容器 前言 在上一集我们介绍了分布式文件存储行业解决方案以及技术选型。最终我们决定选用Minio作为分布式文件存储。 那么这集我们就在Docker上搭建Minio容器即可。 Docker搭建Minio容器步骤 创建Minio文件目录 我们选择创建/minio/data目录 修改目录权…...

【C++】多源BFS问题和拓扑排序

目录 多源BFS介绍 单源BFS和多源BFS的区别 SO如何解决多源BFS问题 多源之核心 矩阵 算法思路 代码实现 飞地的数量 算法思路 代码实现 地图中的最高点 算法思路 代码实现 地图分析 算法思路 代码实现 拓扑排序介绍 有向无环图 ​编辑 如何解决这类问题 课…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

听写流程自动化实践,轻量级教育辅助

随着智能教育工具的发展&#xff0c;越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式&#xff0c;也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建&#xff0c;…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...

Modbus RTU与Modbus TCP详解指南

目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...

阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)

cd /home 进入home盘 安装虚拟环境&#xff1a; 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境&#xff1a; virtualenv myenv 3、激活虚拟环境&#xff08;激活环境可以在当前环境下安装包&#xff09; source myenv/bin/activate 此时&#xff0c;终端…...

spring boot使用HttpServletResponse实现sse后端流式输出消息

1.以前只是看过SSE的相关文章&#xff0c;没有具体实践&#xff0c;这次接入AI大模型使用到了流式输出&#xff0c;涉及到给前端流式返回&#xff0c;所以记录一下。 2.resp要设置为text/event-stream resp.setContentType("text/event-stream"); resp.setCharacter…...

用 FFmpeg 实现 RTMP 推流直播

RTMP&#xff08;Real-Time Messaging Protocol&#xff09; 是直播行业中常用的传输协议。 一般来说&#xff0c;直播服务商会给你&#xff1a; ✅ 一个 RTMP 推流地址&#xff08;你推视频上去&#xff09; ✅ 一个 HLS 或 FLV 拉流地址&#xff08;观众观看用&#xff09;…...

Ray框架:分布式AI训练与调参实践

Ray框架&#xff1a;分布式AI训练与调参实践 系统化学习人工智能网站&#xff08;收藏&#xff09;&#xff1a;https://www.captainbed.cn/flu 文章目录 Ray框架&#xff1a;分布式AI训练与调参实践摘要引言框架架构解析1. 核心组件设计2. 关键技术实现2.1 动态资源调度2.2 …...