STM32单片机-BKP和RTC

STM32单片机-BKP和RTC

  • 一、Unix时间戳
    • 1.1 时间戳转换
  • 二、BKP(备份寄存器)
  • 三、RTC(实时时钟)
    • 3.1 RTC工作原理
  • 四、代码部分
    • 4.1 BKP备份寄存器
    • 4.2 RTC实时时钟

一、Unix时间戳

  • Unix时间戳定义为从伦敦时间1970年1月1日0时0分0秒开始所经过的秒数,不考虑闰秒
  • 时间戳存储在一个秒计数器中,秒计数器为32位/64位整形变量
  • 世界上所有时区秒计数器相同,不同时区通过添加偏移来得到当地时间

在这里插入图片描述

1.1 时间戳转换

  • C语言的time.h模块提供了时间获取时间戳转换的相关函数,可以方便地进行秒计数器、时器时间和字符串之间的转换

在这里插入图片描述
  下图为时间戳转换图
  time_t是int32或者int64数据类型,存储时间戳中自增的秒数
  struct tm是一个封装的结构体类型,结构体成员是秒,分,时等
  char*指向一个表示时间的字符串

在这里插入图片描述

二、BKP(备份寄存器)

  • BKP用于存储用户应用程序数据,当VDD主电源被切断,它们仍然由VBRT备用电源维持供电,当系统在待机模式下被唤醒,或系统复位或电源复位时,它们也不会被复位
  • TAMPER引脚产生的侵入事件将所有备份寄存器内容清除
  • RTC引脚输出RTC校准时钟RTC闹钟脉冲或者秒脉冲
  • 存储RTC时钟校准寄存器
  • 用户数据存储容量20字节/84字节

  下图为BKP基本结构
  BKP由数据寄存器-存储数据、控制寄存器、状态寄存器和RTC时钟校准寄存器组成,TAMPER有上升沿或者下降沿出现时,清除寄存器内容,保证安全

在这里插入图片描述

三、RTC(实时时钟)

  • RTC是一个独立的定时器,可以为系统提供时钟日历的功能
  • RTC时钟配置处于系统的后备区域系统复位时数据不清零,VDD断电后可以借助VBRT供电继续走时
  • 32位的可编程计数器,可对应Unix时间戳秒计数器
  • 20位的可编程预分频器,可适配不同频率的输入时钟
  • 可选择三种RTC时钟源:HSE时钟除以128(8MHz/128)、LSE振荡器时钟(VBRT)(32.768KHz)和LSI振荡器时钟(40KHz)

3.1 RTC工作原理

  下图为RTC框图
  灰色填充部分属于后备区域,在主电源掉电后,可以使用备用电源维持工作。RTCCLK是时钟源,一般选择LSE振荡器时钟,经过分频器分频,重装载的值加1就是分频系数。32位可编程计数器RTC_CNT存放时间数据,RTC_ALR是闹钟寄存器
  进入中断有3个中断源,RTC_Second秒中断,每秒进一次中断。RTC_Overflow溢出中断,当32位计数器溢出进入中断。RTC_Alarm闹钟中断

在这里插入图片描述

  下图为RTC基本结构
  选择时钟,RTCCKL先通过预分频器对时钟进行预分频,重装寄存器是计数目标,决定分频值余数寄存器是一个自减计数器,存储当前的计数值,最终得到1Hz的秒计数信号通向32位计数器,1秒自增一次,同时可以设定闹钟,最终也可以进入中断

在这里插入图片描述

四、代码部分

  注意事项

  • RTC的时钟开启没有单独选项,需要开启PWR和BKP时钟,设置PWR_CR的DBP,使能对BKP和RTC的访问
  • 若在读取RTC寄存器时,RTC的APB1接口曾经处在禁止状态,则软件首先必须等待RTC_CRL寄存器中的RSF位(寄存器同步标志)被硬件置1
  • 必须设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入RTC_PRL(重装值)、RTC_CNT(计数器)、RTC_ALR(闹钟值)寄存器
  • 对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CR寄存器中的RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是1时,才可以写入RTC寄存器

4.1 BKP备份寄存器

  STM32的VBRT引脚接在ST-Link的3.3V,PB1接一个按键
  步骤:开启RCC(PWR和BKP) — 使能BKP-PWR_BackupAccessCmd(ENABLE) — 写入和读取
  下面为main.c

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "Key.h"uint8_t KeyNum;
uint16_t ArrayWrite[] = {0x1234,0x5678};
uint16_t ArrayRead[2];int main(void)
{OLED_Init();Key_Init();//开启RCC-PWR和BKPRCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);//使能BKPOLED_ShowString(1,1,"W:");OLED_ShowString(2,1,"R:");while(1){KeyNum = Key_GetNum();if(KeyNum == 1){ArrayWrite[0] ++;ArrayWrite[1] ++;BKP_WriteBackupRegister(BKP_DR1,ArrayWrite[0]);BKP_WriteBackupRegister(BKP_DR2,ArrayWrite[1]);//写入OLED_ShowHexNum(1,3,ArrayWrite[0],4);OLED_ShowHexNum(1,8,ArrayWrite[1],4);}ArrayRead[0] = BKP_ReadBackupRegister(BKP_DR1);ArrayRead[1] = BKP_ReadBackupRegister(BKP_DR2);//读取OLED_ShowHexNum(2,3,ArrayRead[0],4);OLED_ShowHexNum(2,8,ArrayRead[1],4);}
}

4.2 RTC实时时钟

  下面是RTC相关的库函数

void RCC_LSEConfig(uint8_t RCC_LSE);//启动LSE时钟
void RCC_LSICmd(FunctionalState NewState);//配置LSI时钟
void RCC_RTCCLKConfig(uint32_t RCC_RTCCLKSource);//选择RTCCLK的时钟源
void RCC_RTCCLKCmd(FunctionalState NewState);//RTCCLK使能
FlagStatus RCC_GetFlagStatus(uint8_t RCC_FLAG);//等待RCC标志位,时钟启动完成void RTC_EnterConfigMode(void);//设置CNF位,使RTC进入配置模式
void RTC_ExitConfigMode(void);//退出配置模式
uint32_t  RTC_GetCounter(void);//获取CNT计数器的值
void RTC_SetCounter(uint32_t CounterValue);//写入计数器的值
void RTC_SetPrescaler(uint32_t PrescalerValue);//写入预分频器
void RTC_SetAlarm(uint32_t AlarmValue);//写入闹钟值
uint32_t  RTC_GetDivider(void);//读取余数寄存器值
void RTC_WaitForLastTask(void);//等待上一次操作完成
void RTC_WaitForSynchro(void);//等待同步
FlagStatus RTC_GetFlagStatus(uint16_t RTC_FLAG);//等待标志位

  步骤:开启PWRBKP时钟,使能BKP和RTC的访问 — 开启LSE时钟并等待LSE时钟启动完成 — 选择RCCCLK时钟源-LSE — 等待同步和等待上一次写入操作完成 — 设置预分频器初始时间写入数据读取数据
  其中想要实现复位不重置时间数据和主电源断开时不切断时间,需要使用BKP判断
  利用c语言库time.h中的函数,实现写入和读取时间,即计数器值和时间数据的相互转换
  下面为MyRTC.c和MyRTC.h

#include "stm32f10x.h"                  // Device header
#include "MyRTC.h"                  // Device header
#include <time.h>uint16_t MyRTC_Time[] = {2024,1,1,1,1,1};//时间数据void MyRTC_Init()
{//开启RCC-BKP、PWR和使能访问RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);PWR_BackupAccessCmd(ENABLE);//第一次上电或者完全断电,BKPDR1默认为0时,才会初始化时间if(BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5){//开启LSE时钟,等待LSE时钟启动完成(标志位)RCC_LSEConfig(RCC_LSE_ON);while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);//选择RCCCLK时钟源-LSERCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);RCC_RTCCLKCmd(ENABLE);//等待同步和等待上一次写入操作完成RTC_WaitForSynchro();RTC_WaitForLastTask();//配置预分频器 =  32768MHz /32768 = 1HzRTC_SetPrescaler(32768-1);RTC_WaitForLastTask();//等待写入操作//设置初始时间MyRTC_SetTime();BKP_WriteBackupRegister(BKP_DR1,0xA5A5);//DR1写入A5A5}else{//等待同步和等待上一次写入操作完成RTC_WaitForSynchro();RTC_WaitForLastTask();}
}
/*
@brief:写入时间数据
*/
void MyRTC_SetTime()
{time_t time_cnt;//计数器值struct tm time_data;//时间数据time_data.tm_year = MyRTC_Time[0] - 1900;time_data.tm_mon = MyRTC_Time[1] - 1;time_data.tm_mday = MyRTC_Time[2];time_data.tm_hour = MyRTC_Time[3];time_data.tm_min = MyRTC_Time[4];time_data.tm_sec = MyRTC_Time[5];//写入数据time_cnt = mktime(&time_data)- 8*60*60;//时间数据--计数器(北京时间)RTC_SetCounter(time_cnt);RTC_WaitForLastTask();//等待写入操作
}
/*
@brief:读取时间数据
*/
void MyRTC_ReadTime()
{time_t time_cnt;//计数器值struct tm time_data;//时间数据time_cnt = RTC_GetCounter()+ 8*60*60;//读取时间存到计数器(北京时间)time_data = *localtime(&time_cnt);//将时间数据转到结构体MyRTC_Time[0] = time_data.tm_year + 1900;MyRTC_Time[1] = time_data.tm_mon + 1;MyRTC_Time[2] = time_data.tm_mday;MyRTC_Time[3] = time_data.tm_hour;MyRTC_Time[4] = time_data.tm_min;MyRTC_Time[5] = time_data.tm_sec;//读取数据
}
#ifndef __MYRTC_H
#define __MYRTC_Hextern uint16_t MyRTC_Time[];void MyRTC_Init();
void MyRTC_SetTime();
void MyRTC_ReadTime();#endif

  下面为main.c

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "MyRTC.h"int main(void)
{MyRTC_Init();OLED_Init();OLED_ShowString(1,1,"Data:XXXX-XX-XX");OLED_ShowString(2,1,"Time:XX:XX:XX");OLED_ShowString(3,1,"CNT:");while(1){MyRTC_ReadTime();OLED_ShowNum(1,6,MyRTC_Time[0],4);OLED_ShowNum(1,11,MyRTC_Time[1],2);OLED_ShowNum(1,14,MyRTC_Time[2],2);OLED_ShowNum(2,6,MyRTC_Time[3],2);OLED_ShowNum(2,9,MyRTC_Time[4],2);OLED_ShowNum(2,12,MyRTC_Time[5],2);OLED_ShowNum(3,6,RTC_GetCounter(),10);}
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3使用echarts简单教程~~概念篇

没写过 写着玩玩 不足的地方还望小伙伴补充~~ 概念篇 文档奉上&#xff1a;数据集 - 概念篇 - 使用手册 - Apache EChartshttps://echarts.apache.org/handbook/zh/concepts/dataset <template><div id"main" style"width: 600px; height: 400px&…

集合进阶:增强for循环和lambda表达式

一.增强for遍历 1.增强for的底层是迭代器,为了简化迭代器的代码书写的。 2.他是JDK5之后出现的,其内部原理就是一个lterrator迭代器。 3.所有的单列集合和数组才能用增强for进行遍历 二.格式 for(元素的数据类型 变量名;数组或者集合){} 三.代码 Collection<String>…

72-UDP协议工作原理及实战

#ifndef UDPCOMM_H #define UDPCOMM_H#include <QMainWindow> #include <QUdpSocket> // 用于发送和接收UDP数据报 #include <QtNetwork>QT_BEGIN_NAMESPACE namespace Ui { class udpComm; } QT_END_NAMESPACEclass udpComm : public QMainWindow {Q_OBJECT…

定个小目标之刷LeetCode热题(23)

今天写这道题&#xff0c;背过八股文的都应该知道LRU算法缓存的基本原理&#xff0c;在 Java 语言中&#xff0c;同样有类似的数据结构 LinkedHashMap&#xff0c;本题我们采用自己创建哈希表双链表的形式简单实现一下 对于get操作&#xff1a;通过cache.get(key)获取&#xff…

格雷母线定位与控制系统:确保机车平稳运行的关键

微深节能的格雷母线高精度位移检测系统是一种高精度的位置检测设备&#xff0c;它通过发射和接收信号来确定移动物体的实时位置。在机车定位系统中&#xff0c;格雷母线被安装在固定的轨道上&#xff0c;而机车上的检测装置则负责读取这些信号&#xff0c;从而准确计算出机车的…

一文弄懂 Python os.walk(),轻松搞定文件处理和目录遍历

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ Python os 模块的 walk() 方法以自顶向下或自底向上的方式遍历指定的目录树&#xff0c;从而显示目录树中的文件名。对于目录树中的每个目录&#xff0c;os.walk() 方法都会产生一个包含目录路径、当前…

Java17 --- redis7缓存双写一致性

一、缓存双写一致性 如果redis中有数据&#xff1a;需要和数据库中的值相同。如果redis中没有数据&#xff1a;数据库中的值要是最新值&#xff0c;且准备回写redis。只读缓存。读写缓存&#xff1a;①、同步直写策略&#xff1a;写数据库后也同步写redis缓存&#xff0c;缓存…

光纤三维布里渊温度和应变分布matlab模拟与仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 光纤三维布里渊温度和应变分布matlab模拟与仿真。其中 , 布里渊散射是光波与声波在光纤中传播时相互作用而产生的光散射过程 , 在不 同的条件下 , 布里渊散射又分…

数据结构与算法-字符出现的次数

问题描述 以下是这个找出字符串中字符串出现频率最多的字符。大家可以自行研究一下&#xff0c;题目不难&#xff0c;我今天尝试使用C语言来完成解答&#xff0c;但是在解答过程居然出现了一个意想不到的问题。可能是高级语言用多了&#xff0c;C语言某些函数的限制和风险忘记管…

C++之std::type_identity

目录 1.简介 2.C20的std::type_identity 3.使用 type_identity 3.1.阻止参数推导 3.1.1.模板参数推导过程中的隐式类型转换 3.1.2.强制显式实例化 3.2.阻止推断指引 3.3.类型保持 3.4.满足一些稀奇古怪的语法 4.示例 5.总结 1.简介 std::type_identity 是 C17 引入的…

【Python/Pytorch - 网络模型】-- 手把手搭建E3D LSTM网络

文章目录 文章目录 00 写在前面01 基于Pytorch版本的E3D LSTM代码02 论文下载 00 写在前面 测试代码&#xff0c;比较重要&#xff0c;它可以大概判断tensor维度在网络传播过程中&#xff0c;各个维度的变化情况&#xff0c;方便改成适合自己的数据集。 需要github上的数据集…

这些数据可被Modbus采集,你还不知道???

为什么要用Modbus采集模块 Modbus采集模块之所以被广泛使用&#xff0c;是因为它提供了标准化的通信协议&#xff0c;确保了不同设备间的兼容性。它支持多种通信方式&#xff0c;易于实现&#xff0c;并且能够适应不同的网络环境。Modbus模块能够收集和传输各种工业数据&#x…

061、Python 包:模块管理

包&#xff08;Package&#xff09;是一种用于组织模块的层次结构。包实际上就是一个包含了__init__.py文件的目录&#xff0c;该文件可以为空或包含包的初始化代码。通过使用包&#xff0c;可以更好地组织和管理大型项目中的模块&#xff0c;避免命名冲突&#xff0c;并提高代…

kettle从入门到精通 第七十一课 ETL之kettle 再谈http post,轻松掌握body中传递json参数

场景&#xff1a; kettle中http post步骤如何发送http请求且传递body参数&#xff1f; 解决方案&#xff1a; http post步骤中直接设置Request entity field字段即可。 1、手边没有现成的post接口&#xff0c;索性用python搭建一个简单的接口&#xff0c;关键代码如下&#…

深度学习模型的生命周期与推理系统架构

目录 深度学习模型的生命周期 ​编辑 深度学习模型的生命周期 推理相比训练的新特点与挑战 推理系统架构 推理系统 vs 推理引擎 顶层:API接口和模型转换 中层:运行时(计算引擎) 底层:硬件级优化 边缘设备计算 主要问题 边缘部署和推理方式 方式1:边缘设备计…

可提供实习证明/实习鉴定报告,企业项目试岗实训开营啦

在数字化转型的浪潮中&#xff0c;大数据和人工智能等前沿技术已成为推动经济发展和科技进步的关键动力。当前&#xff0c;全球各行各业都在积极推进数字化转型&#xff0c;不仅为经济增长注入新活力&#xff0c;也对人才市场结构产生了深刻影响&#xff0c;尤其是对数字化人才…

在 KubeSphere 上快速安装和使用 KDP 云原生数据平台

作者简介&#xff1a;金津&#xff0c;智领云高级研发经理&#xff0c;华中科技大学计算机系硕士。加入智领云 8 余年&#xff0c;长期从事云原生、容器化编排领域研发工作&#xff0c;主导了智领云自研的 BDOS 应用云平台、云原生大数据平台 KDP 等产品的开发&#xff0c;并在…

联邦学习周记|第四周

论文&#xff1a;Active Federated Learning 链接 将主动学习引入FL&#xff0c;每次随机抽几个Client拿来train&#xff0c;把置信值低的Client概率调大&#xff0c;就能少跑几次。 论文&#xff1a;Active learning based federated learning for waste and natural disast…

“Git之道:掌握常用命令,轻松管理代码“

目录 1. 初始化和配置 2. 提交和更新 3. 分支和合并 4. 查看和比较 5. 远程仓库 6. 文件操作命令 1. 初始化和配置 git init&#xff1a;在当前目录初始化一个新的Git仓库git config&#xff1a;配置Git的全局或局部选项git clone&#xff1a;从远程仓库克隆一个本地副本…

vue3第四十节(pinia的用法注意事项解构store)

pinia 主要包括以下五部分&#xff0c;经常用到的是 store、state、getters、actions 以下使用说明&#xff0c;注意事项&#xff0c;仅限于 vue3 setup 语法糖中使用&#xff0c;若使用选项式 API 请直接查看官方文档&#xff1a; 一、前言&#xff1a; pinia 是为了探索 vu…