STM32-BKP备份寄存器RTC实时时钟
一、原理
Unix:

一些系统是使用32bit有符号数存储,实际范围为-2,147,483,648到2,147,483,647即~
经过计算int32数据会在2038年1月19日溢出,可以看到转换的为北京时间。


STM32的时间戳为无符号时间戳。
我们需要把秒计数器的时间通过计算得到秒技术其对应的时间,然后根据时区进行偏移(考虑到品平年闰年,大月小月闰月等)。
可以根据c语言官方函数直接计算:

UTC、GMT
GMT是之前的时间标准,UTC是计算了偏移量的现行标准。中国一般使用GMT+8/UTC+8。Unix时间戳没有闰秒,即协调世界时间的功能,所以可能秒数会偏差。
时间戳和日期进行转换(数据类型):

time_t实际上是int64类型,用来存储秒计数值

tm类型为定义日期的结构体:struct tm
其中year为从1900年的第几年(最小应该为70);mon月份从0开始;wday表示周几;yday表示每年的第几天;isdst是否使用夏令时,1表示用,0不用,-1表示不知道。

夏令时为在夏天的某段时间将时间提前一个小时。
实际使用:

mktime函数原理,通过输入的年月日时分秒计算,其他参数会自动计算回填,可以通过此函数自动计算星期。


strftime函数参数(char *c,size_length,const *char,const struct tm*),其中const *char为格式字符串。函数使用为,将const struct tm*的内容通过const *char格式化字符存入长度为size_length的数组char *c中。
其他函数:

二、 STM32的BKP备份寄存器&RTC实时时钟
1、BKP原理:
BKP寄存器数据需要VBT保持供电来进行掉电不丢失,实际使用方式和Flash类似。手册建议VBT无外部供电时接到VDD并上100nf的滤波电容。

TAMPER在STM32F103C8T6中在PC13。可以外接上拉电阻和开关接地,做保护措施,接收到低电平清除寄存器内容。主电源断电后,侵入检测仍然有效。RTC校准时钟可以对RTC时钟进行校准。存储RTC时钟校准寄存器可以配合RTC校准时钟对RTC进行校准。

2、BKP的基本结构:

3、STM32的RTC外设

STM32的RTC类似DS1302外置实时时钟。RTC输入时钟具有20bit的分配器,即可分配1-的分频。
RTC框图:

灰色部分为VBT断电供电部分选择RTC时钟-RTCCLK提供时钟-RTC_DIV(余数寄存器,自减)计数溢出后产生TR_CLK,并且通过RTC_PRL(重装载寄存器)进行重装载(预分频器原理)-通过TR_CLK的RTC_CNT进行计数(为无符号32bit),
- RTC_CNT的计满溢出中断为RTC_Overflow。
- 其中RTC_ALR为闹钟,和RTC_CNT一样的uint32寄存器,当RTC_ALR和RTC_CNT计数相同,会产生RTC_Alarm信号,前往中断系统(或唤醒芯片,退出睡眠模式,WKUP-PA0引脚也可以唤醒设备)。
- RTC_Sencond中断来自TR_CLK的秒计数。
- 中断选项中,IE结尾的是中断使能,F结尾的是中断标志位。
晶振选择:
一般可以选择三个时钟源。根据STM32RTC时钟树可以看到,包括2高速、2低俗、2内部、2外部共4个晶振作为晶振源,详细可见定时器文章。高速时钟一般为内部运行和主要外设使用,低速时钟一般供RTC、看门狗等使用。可以看到LSE OSC指向RTCCLK。且RTC有三个来源时钟。


32.768=可直接经过分频1Hz。硬件电路计数器也方便进行计数溢出得到频率信号。一般使用LSE。
4、RTC基本结构

5、电路



CR2032纽扣电池,印制面为正极。
6、操作注意事项

- PWR是电源管理
- 第二点寄存器同步操作的原因:因为PCLK1(APB1总线时钟,36MHz)在主电源掉电时会停止。为了保证RTC掉电不丢失,RTC都是在RTCCLK(32.768Hz)同步下变更的。所以用APB1总线读取RTC寄存器内容,存在时钟不同步问题。时钟不同步会导致读取到错误数据。所以在APB总线刚开启时要进行时钟同步。
- RTC_CRL为时钟配置使能标志位,使用时需要先配置。库函数自动进行了配置。
- RTC的RTOFF为等待结束标志位。等待即可,当RTOFF=1才可写入。主要还是因为时钟频率不一样,不能立即更新。
三、程序实例
问题1:VBT供电导致STM32系统供电指示灯和OLED下电后还会有一些微弱显示。
问题2:有些芯片RTC晶振不起振。会导致程序卡死在晶振等待起振的地方。
1、写入BKP备份寄存器和从备份寄存器读出,显示到OLED。
将STM32断电、VBT不断电,STM32上电查看BKP数据是否掉电保存。(保存数据)
将VBT断电、STM32断电,然后STM32在上电查看BKP数据是否掉电保存。(不保存数据)


main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"uint16_t Data[4]={0x01,0x02,0x03,0x04};//写入的数据
uint16_t GetData[4];//BKP读出的数据
int main(void){OLED_Init();RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);//开启PWR时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);//开启BKP时钟PWR_BackupAccessCmd(ENABLE);//开启RTC和BKP的访问使能BKP_WriteBackupRegister(BKP_DR1,Data[0]);//数据写入,在做STM32下电测试时,写入代码注释BKP_WriteBackupRegister(BKP_DR2,Data[1]);BKP_WriteBackupRegister(BKP_DR3,Data[2]);BKP_WriteBackupRegister(BKP_DR4,Data[3]);OLED_ShowString(1,1,"BKP:");GetData[0] = BKP_ReadBackupRegister(BKP_DR1);//数据读出GetData[1] = BKP_ReadBackupRegister(BKP_DR2);GetData[2] = BKP_ReadBackupRegister(BKP_DR3);GetData[3] = BKP_ReadBackupRegister(BKP_DR4);OLED_ShowHexNum(2,1,GetData[0],2);OLED_ShowHexNum(2,4,GetData[1],2);OLED_ShowHexNum(2,7,GetData[2],2);OLED_ShowHexNum(2,10,GetData[3],2);while(1){Delay_ms(200);}return 0;
}
2、RTC时钟

时间显示,如果VBT供电,那么STM32复位或下电RTC时钟不会丢失(RTC和BKP都可通过VBT供电)。
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyRTC.h"
#include "time.h"
Unixdate GetTime;
time_t CNT;
time_t DIVData;int main(void){OLED_Init();MyRTC_Init();OLED_ShowString(1,1,"Date: - - ");OLED_ShowString(2,1,"Time: : : ");OLED_ShowString(3,1,"CNT :");OLED_ShowString(4,1,"DIV :");while(1){GetTime = GetNowTime();//获取RTC内的时间CNT = GetCounter();DIVData = GetDIV();OLED_ShowNum(1,6,GetTime.years,4);OLED_ShowNum(1,11,GetTime.months,2);OLED_ShowNum(1,14,GetTime.day,2);OLED_ShowNum(2,6,GetTime.hours,2);OLED_ShowNum(2,9,GetTime.minutes,2);OLED_ShowNum(2,12,GetTime.second,2);OLED_ShowNum(3,6,CNT,10);OLED_ShowNum(4,6,DIVData,10);}return 0;
}
MyRTC.c
#include "stm32f10x.h" // Device header
#include "time.h"
#include "MyRTC.h"
Unixdate SetTime;
void MyRTC_Init(void){//时钟配置RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);//使能RTC和BKP访问PWR_BackupAccessCmd(ENABLE);//开启LSE/LSI,并等待启动完成RCC_LSEConfig(RCC_LSE_ON);while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)!=SET);
// RCC_LSICmd(ENABLE);//备用配置LSI为内部时钟,并启动
// while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY)!=SET);//等待启动完成//使用BKP来判断是否断电,若断电则进行初始化,相当于用BKP做了一个标志位if(BKP_ReadBackupRegister(BKP_DR1)!=0xA5A5){//选择LSE为时钟源,并使能时钟RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);// RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);//备用选择LSI作为时钟源RCC_RTCCLKCmd(ENABLE);//等待时钟同步,等待RTC上一次操作完成RTC_WaitForSynchro();RTC_WaitForLastTask();//配置预分频器,LSE=32768Hz,分频32768后为1Hz,LSI=40000HzRTC_SetPrescaler(32768-1);//函数内置写CNF=1/=0进入了配置模式/退出配置模式,只有配置模式可以写入寄存器// RTC_SetPrescaler(40000-1);//备用使用LSI作为时钟源 RTC_WaitForLastTask();Time_Init(&SetTime);SetNowTime(SetTime);BKP_WriteBackupRegister(BKP_DR1,0xA5A5);}else{//若BKP不断电则不初始化//等待时钟同步,等待RTC上一次操作完成RTC_WaitForSynchro();RTC_WaitForLastTask();}}/*** @brief 获取当前CNT* @param * @arg * @param * @arg * @retval None*/
uint32_t GetCounter(void){return RTC_GetCounter();
}/*** @brief 获取当前余数值计数值* @param * @arg * @param * @arg * @retval None*/
uint32_t GetDIV(void){return RTC_GetDivider();
}/*** @brief 设置当前时间* @param 输入为Unixdate自定义日期类型* @arg * @param * @arg * @retval None*/
void SetNowTime(Unixdate UnixdataStructure){struct tm NowTime;time_t count;NowTime.tm_min = UnixdataStructure.minutes;NowTime.tm_hour = UnixdataStructure.hours;NowTime.tm_mday = UnixdataStructure.day;NowTime.tm_mon = UnixdataStructure.months;NowTime.tm_year = UnixdataStructure.years;NowTime.tm_sec = UnixdataStructure.second;count = mktime(&NowTime)-8*60*60;//设置时间到RTC,输入东八区时间,偏移到0时区RTC_SetCounter(count);RTC_WaitForLastTask();//等待完成
}/*** @brief 获取RTC当前时间* @param * @arg * @param * @arg * @retval 返回当前RTC对应的日期时间*/
Unixdate GetNowTime(void){struct tm NowTime;Unixdate UnixdataStructure;time_t count;count = RTC_GetCounter()+8*60*60;//获取当前计数,偏移到东八区(STM32默认函数为0区时间)RTC_WaitForLastTask();//等待完成NowTime = *localtime(&count);//根据计数值换算成日期时间,将值传给NowTimeUnixdataStructure.years = NowTime.tm_year+1900;UnixdataStructure.months = NowTime.tm_mon+1;UnixdataStructure.day = NowTime.tm_mday;UnixdataStructure.hours = NowTime.tm_hour;UnixdataStructure.minutes = NowTime.tm_min;UnixdataStructure.second = NowTime.tm_sec;return UnixdataStructure;
}/*** @brief 日期变量初始化* @param 输入为日期变量结构体地址,直接对其进行改变* @arg * @param * @arg * @retval None*/
void Time_Init(Unixdate *UnixdataStructure){UnixdataStructure->years = 2025-1900;UnixdataStructure->months = 1-1;UnixdataStructure->day = 3;UnixdataStructure->hours = 23;UnixdataStructure->minutes = 59;UnixdataStructure->second = 56;
}
MyRTC.h
#ifndef __MYRTC_H
#define __MYRTC_H
#include "stm32f10x.h" // Device header//#pragma pack(n)可修改编译器字节对齐数
typedef struct{uint8_t second;//(0-60)suint8_t minutes;//(0-59)minuint8_t hours;//(0-23)huint8_t months;//月(1-12)uint8_t day;//月中第几天(1-31)uint16_t years;//年
}Unixdate;void MyRTC_Init(void);
uint32_t GetCounter(void);
uint32_t GetDIV(void);
Unixdate GetNowTime(void);
void Time_Init(Unixdate *UnixdataStructure);
void SetNowTime(Unixdate UnixdataStructure);
#endif
其他
数据范围原理:
int32范围为~
,数据在计算机中为补码存储,
即int32范围:
在最大值情况下,符号位为 0,其余 31 位均为 1
0111 1111 1111 1111 1111 1111 1111 1111
在最小值情况下,符号位为 1,其余 31 位全为 0
1000 0000 0000 0000 0000 0000 0000 0000
最高位表示符号位,1为负,第32bit为,如上,所以正数可以达到
,负数可以达到
。
- 最大值:(2^{31} - 1 = 2147483647)
- 最小值:(-2^{31} = -2147483648)
同理int16范围为2^15-1 ~ -2^15 (32767~-32768)
int8_t范围为2^7-1 ~ -2^7 (127~-128)
相关文章:
STM32-BKP备份寄存器RTC实时时钟
一、原理 Unix: 一些系统是使用32bit有符号数存储,实际范围为-2,147,483,648到2,147,483,647即~ 经过计算int32数据会在2038年1月19日溢出,可以看到转换的为北京时间。 STM32的时间戳为无符号时间戳。 我们需要把秒计数器的时间通过计算…...
HTML-文本标签
历史上,网页的主要功能是文本展示。所以,HTML 提供了大量的文本处理标签。 1.<div> <div>是一个通用标签,表示一个区块(division)。它没有语义,如果网页需要一个块级元素容器,又没…...
香橙派5plus单独编译并安装linux内核无法启动的原因分析与解决记录
1 说明 我依照官方手册编译单独编译linux内核,安装后重启出现内核启动失败的问题,编译和安装步骤如下:# 1. 克隆源码 git clone --depth1 -b orange-pi-6.1-rk35xx https://github.com/orangepi-xunlong/linux-orangepi# 2 配置源码 make rockchip_linu…...
嵌入式应用软件开发中C语言方向面试题
嵌入式应用软件开发中C语言方向面试题随笔 前言一、C语言基础二、嵌入式开发相关三、硬件相关知识五、实际编程问题前言 做嵌入式开发这么多年了,简单记录下C语言方向常见面试题,这里是应用软件方向的。 一、C语言基础 C语言的指针与数组的区别是什么?指针:指针是一个变量…...
linux 系统配置ip
最常见的配置方法是通过 DHCP 获取动态 IP 地址,或者手动配置一个静态 IP 地址。下面我将详细说明这两种常见的配置方法。 通过 DHCP 自动获取 IP 地址 如果你的开发板连接到网络(比如通过网线或者 Wi-Fi),并且网络环境支持 DHCP…...
低代码开发:开启企业数智化转型“快捷键”
一、低代码开发浪潮来袭,企业转型正当时 在当今数字化飞速发展的时代,低代码开发已如汹涌浪潮,席卷全球。从国际市场来看,诸多企业巨头纷纷布局低代码领域,像微软的 PowerApps、OutSystems 等平台,凭借强大…...
Tailwind CSS 实战:性能优化最佳实践
在现代网页开发中,性能优化就像是一场精心策划的马拉松。记得在一个电商项目中,我们通过一系列的性能优化措施,让页面加载时间减少了 60%,转化率提升了 25%。今天,我想和大家分享如何使用 Tailwind CSS 进行性能优化。 优化理念 性能优化就像是在打磨一块璞玉。我们需要通过各…...
[redux] useDispatch的两种用法
先重写2个方法先, 方便ts类型推导,如果你看不懂为什么这么写, 先看我这篇 [redux] ts声明useSelector和useDispatch-CSDN博客 export type RootState ReturnType<typeof store.getState>; export type AppDispatch typeof store.dispatch; export const useAppDispat…...
Postgresql 命令还原数据库
因为PgAdmin打不开,但是数据库已经安装成功了,这里借助Pg命令来还原数据库 C:\Program Files\PostgreSQL\15\bin\psql.exe #链接数据库 psql -U postgres -p 5432#创建数据库 CREATE DATABASE "数据库名称"WITHOWNER postgresENCODING UTF8…...
电脑找不到mfc110.dll文件要如何解决?Windows缺失mfc110.dll文件快速解决方法
一、mfc110.dll文件的重要性 mfc110.dll,全称Microsoft Foundation Class Library 110,是Microsoft Visual C Redistributable for Visual Studio 2012的一部分。这个动态链接库(DLL)文件对于支持基于MFC(Microsoft F…...
Elasticsearch与数据库数据一致性:最佳实践与解决方案
在现代应用程序中,Elasticsearch(ES)作为一个高效的分布式搜索引擎,常常与数据库一同使用,以提供强大的搜索、分析和数据可视化功能。然而,数据库和Elasticsearch之间的同步与一致性常常成为一个挑战。如何…...
vue导入导出excel、设置单元格文字颜色、背景色、合并单元格(使用xlsx-js-style库)
npm i xlsx-js-style <template><button click"download">下载 Excel 表格</button><el-table :data"tableData" style"width: 100%"><el-table-column prop"date" label"日期" width"180…...
电子电气架构 --- 中央处理器HPC及软件架构
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 所谓鸡汤,要么蛊惑你认命,要么怂恿你拼命,但都是回避问题的根源,以现象替代逻辑,以情绪代替思考,把消极接受现实的懦弱,伪装成乐观面对不幸的…...
代码实战:基于InvSR对视频进行超分辨率重建
Diffusion Models专栏文章汇总:入门与实战 前言:上一篇博客《使用Diffusion Models进行图像超分辩重建》中讲解了InvSR的原理,博主实测的效果是非常不错的,和PASD基本持平。这篇博客就讲解如何利用InvSR对视频进行超分辨率重建。 目录 环境准备 代码讲解 环境准备...
一文读懂主成分分析法(PCA)
主成分分析法(PCA) 主成分分析法(PCA)主成分分析的基本思想主成分的计算主成分分析的原理主成分分析的特点主成分分析的应用 主成分分析法(PCA) 主成分分析的基本思想 PCA是1901 年Pearson在研究回归分析…...
Redis(基础篇 + 实践篇 )
01 | 基本架构:一个键值数据库包含什么? Redis 作为一个内存数据存储系统,它的架构设计非常简洁,但功能非常强大。理解其核心架构对高效使用 Redis 至关重要。 客户端与服务器架构: 客户端通过 TCP 协议连接到 Redis …...
高质量C++小白教程:2.10-预处理器简介
当你在编译项目时,你可能希望编译器完全按照你编写的方式编译每一个代码文件,当事实并非如此。 相反,在编译之前,每一个.cpp文件都会经历一个预处理的阶段,在此阶段中,称为预处理器的程序对代码文件的文本进行各种更改. 预处理器实际上不会以任何方式修改原始代码文件,预处理…...
一、二极管(模电理论篇)
导论:PN结(结电容)是构成二极管,三极管,场效应管的原理基础 1.二极管特性(单向导电性) 1.1 P型半导体与N型半导体 在单晶体硅(原子核为正四价电子,可以形成四条共价键&…...
JAVA学习笔记_JVM
文章目录 初识jvm内存结构程序计数器(寄存器) 栈问题辨析内存溢出 线程诊断本地方法栈Heap堆内存溢出内存诊断 方法区内存溢出常量池 stringTable直接内存垃圾回收 初识jvm JRE JVM 基础类库 JDK JRE 编译工具 JavaSE JDK IDE工具 JavaEE JDK 应用服务器 IDE工具 jvm是…...
SQL 中复杂 CASE WHEN 嵌套逻辑优化
目标:优化复杂的 CASE WHEN 逻辑,提升 SQL 语句的可读性与执行效率,减少多层嵌套带来的复杂性。 1. CASE WHEN 的常见问题 嵌套过深:多个条件判断嵌套,难以阅读和维护。重复逻辑:相似逻辑在多个分支中重复…...
IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...

