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

江科大STM32入门——读写备份寄存器(BKP)实时时钟(RTC)笔记整理

wx:嵌入式工程师成长日记

ddd39e6b19e14e33897aa6213919c759.png

https://mp.weixin.qq.com/s/hDk7QaXP8yfYIj1gUhtMrw?token=1051786482&lang=zh_CNicon-default.png?t=O83Ahttps://mp.weixin.qq.com/s/hDk7QaXP8yfYIj1gUhtMrw?token=1051786482&lang=zh_CN

RTC是一个独立的定时器,BKP并不能完全掉电不丢失,其可以完成一些主电源掉电时,保存少量数据的任务。而RTC在主电源掉电的时候保证掉电不丢失的关键就是BKP。

(一)时间戳

1、简介

  • Unix时间戳(UnixTimestamp)定义为从UTC/GMT的1970年1月1日0时0分0秒开始所经过的秒数(只用秒来计数,永不进位)
  • 时间戳存储在一个秒计数器中,秒计数器为32位/64位的整型变量(32位2038年到头,无符号是2106年)
  • 世界上所有时区的秒计数器相同,不同时区通过添加偏移来得到当地时间

图片

对于计算器来说一个永不进位的数据,无论是存储还是计算,都是非常方便的,因此在计算程序的底层,应用非常广泛。需要显示当前时间时,直接转换成年月日时分秒这个的格式就行了。

【使用好处】:

①简化硬件电路:在设计RTC硬件电路的时候,直接弄一个很大的秒寄存器就行了,不需要考虑年月日进位大小月平年论润,非常友好。

②进行时间间隔的计算非常方便。

③存储方便,只需要一个很大的变量表示秒数。

2、GMT/UTC

图片

3、时间戳转换

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

图片

图片

(二)BKP外设

  • BKP(备份寄存器)

  • BKP可用于存储用户应用程序数据。当VDD(2.0~3.6V)电源被切断,他们仍然由VBAT(1.8~3.6V)维持供电。当系统在待机模式下被唤醒,或系统复位或电源复位时,他们也不会被复位。

  • (VBAT:当使用电池或其他电源连接到VBAT脚上时,当VDD断电时,可以保存备份寄存器的内容和维持RTC的功能。如果应用中没有使用外部电池,VBAT引脚应接到VDD引脚上)

  • TAMPER引脚产生的侵入事件将所有备份寄存器内容清除

    1. TAMPER引脚是用于引入检测信号(可以是或上升沿/下降沿)的,当发生入侵时,将清除BKP所有内容,并申请中断。

    2. 并且是由备用电源供电,主电源断电后侵入检测仍然有效,以保证数据安全

  • RTC引脚输出RTC校准时钟、RTC闹钟脉冲或者秒脉冲

  • 存储RTC时钟校准寄存器

  • 用户数据存储容量:20字节(中容量和小容量)/84字节(大容量和互联型)

  • 如果备用电源VBAT和主电源VCC都断电了,就会清除数据,因为BKP本质是RAM存储器,掉电丢失数据

2、BKP基本结构

橙色的是后背区域,除了BKP还有RTC电路。STM32后备区的特性是的那个VDD主电源掉电时,后备区仍然可以由VBAT的备用电池供电。当VDD主电源上电时候,后背区域会由VBAT切换到VDD,可以节省电池电量。

图片

(三)RTC外设

  • RTC(Real Time Clock)实时时钟

  • RTC是一个独立的定时器,可为系统提供时钟和日历的功能

  • RTC和时钟配置系统处于后备区域,系统复位时数据不清零,VDD(2.0~3.6V)断电后可借助VBAT(1.8~3.6V)供电继续走时。

  • 32位的可编程计数器

  • 20位的可编程预分频器,可适配不同频率的输入时钟(确保给到计数器的是1Hz的频率)

  • 可选择三种RTC时钟源:(LSE(低速外部时钟)主要就是供RTC的,只有这一路时钟可以通过VBAT备用电池供电)
    • HSE时钟除以128(通常为8MHz/128)

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

    • LSI振荡器时钟(40KHz)

2、RTC框图

图片

图片

3、RTC基本结构

图片

4、硬件电路

如果没有外部电池,建议VBAT引脚接到VDD,就是VBAT和主电源接到一起,并且再连接一个100nF的滤波电容

图片

图片

(四)RTC常用函数

void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState);//RTC 中断使能:通过传入指定的中断类型 RTC_IT 和状态 NewState,实现对 RTC 中断的使能或失能控制。
void RTC_EnterConfigMode(void);//进入 RTC 配置模式:用于进入 RTC 的配置状态,以便进行相关参数的修改。
void RTC_ExitConfigMode(void);//退出 RTC 配置模式:在完成 RTC 配置操作后,使用此函数退出配置模式。
uint32_t RTC_GetCounter(void);//获取 RTC 计数器的值:返回 RTC 计数器的当前数值。
void RTC_SetCounter(uint32_t CounterValue);//设置 RTC 计数器的值:将 RTC 计数器设置为指定的数值 CounterValue。
void RTC_SetPrescaler(uint32_t PrescalerValue);//设置 RTC 预分频的值:为 RTC 预分频设置特定的值 PrescalerValue。
void RTC_SetAlarm(uint32_t AlarmValue);//设置 RTC 闹钟的值:设定 RTC 闹钟的触发值为 AlarmValue。
uint32_t RTC_GetDivider(void);//获取 RTC 预分频分频因子的值:获取当前 RTC 预分频分频因子的数值。
void RTC_WaitForLastTask(void);//等待最近一次对 RTC 寄存器的写操作完成:确保之前对 RTC 寄存器的写入操作已经完成。
void RTC_WaitForSynchro(void);//等待 RTC 寄存器与 RTC 的 APB 时钟同步:等待 RTC 相关寄存器(如 RTC_CNT、RTC_ALR 和 RTC_PRL)与 APB 时钟完成同步。
FlagStatus RTC_GetFlagStatus(uint16_t RTC_FLAG);//检查指定的 RTC 标志位设置与否:通过传入标志位 RTC_FLAG,返回其状态。
void RTC_ClearFlag(uint16_t RTC_FLAG);//清除 RTC 的待处理标志位:清除指定的 RTC 标志位。
ITStatus RTC_GetITStatus(uint16_t RTC_IT);//检查指定的 RTC 中断发生与否:根据传入的中断类型 RTC_IT,判断中断是否发生。
void RTC_ClearITPendingBit(uint16_t RTC_IT);//清除 RTC 的中断待处理位:清除指定 RTC 中断的待处理位。

读写RTC实时时钟步骤

1.开启PWR和BKP的时钟  PWR的开启函数为RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); 开启后备电源的时钟  BKP的开启函数RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE); 开启BKP外设的时钟
2.使能备份区域的访问  在STM32中,想访问RTC和BKP,就要先开启备份区域的访问权限。  函数:PWR_BackupAccessCmd(ENABLE);
3.判断是否需要初始化RTC
4.开启LSE时钟    开启LSE时钟 RCC_LSEConfig(RCC_LSE_ON);    等待LSE时钟开启完毕 while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET);    选择RTCCLK时钟为LSERCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);    使能RTCCLK时钟 RCC_RTCCLKCmd(ENABLE);
5.等待时钟同步    因为可能在恢复主电源之后,APB1总线刚刚恢复震荡频率,但是RTCCLK需要经过外部震荡源分频后才能有一次输出。    如果直接读取,会导致读取不准确。     所以需要等待RTCCLK产生上升沿来激活更新一下时间戳计数器,这时APB1直接读取。 所以软件读取时必须等待RTCCLK来一个上升沿。    函数:RTC_WaitForSynchro(); (等待时钟同步)
6.等待写入完成    对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。    可以通过查询RTC_CR寄存器中的RTOFF状态位,判断RTC寄存器是否处于更新中。 当RTOFF状态位是1时,才可以写入RTC寄存器    函数:RTC_WaitForLastTask();
7.设置RTC预分频器    设置预分频器RTC_SetPrescaler(32768 - 1);    等待写入完成RTC_WaitForLastTask();
8.写入前其实是需要设置RTC_CRL寄存器中的CNF位    RTC进入配置模式后,才能写入RTC_PRL(预分频器)、RTC_CNT(时间戳计数器)、RTC_ALR(闹钟寄存器)寄存器
9.设置CNT时间戳计数器时间    利用C语言中time.h来转换时间戳并写入
在BKP备份寄存器中写入特定数据。为下次复位或仅主电源断电后上电时判断是否初始化打下基础
初始RTC:void MyRTC_Init(void){    /*开启时钟*/    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);     //开启PWR的时钟    RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);     //开启BKP的时钟    /*备份寄存器、RTC访问使能*/    PWR_BackupAccessCmd(ENABLE);                            //使用PWR开启对备份寄存器和RTC的访问    if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)          //通过写入备份寄存器的标志位,判断RTC是否是第一次配置                                                            //if成立则执行第一次的RTC初始化    {        RCC_LSEConfig(RCC_LSE_ON);                          //开启LSE时钟        while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET);  //等待LSE准备就绪        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);             //选择RTCCLK来源为LSE        RCC_RTCCLKCmd(ENABLE);                              //RTCCLK使能        RTC_WaitForSynchro();                               //等待同步        RTC_WaitForLastTask();                              //等待上一次操作完成        RTC_SetPrescaler(32768 - 1);                        //设置RTC预分频器,预分频后的计数频率为1Hz        RTC_WaitForLastTask();                              //等待上一次操作完成        MyRTC_SetTime();                                    //设置时间,调用此函数,全局数组里时间值刷新到RTC硬件电路        BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);           //在备份寄存器写入自己规定的标志位,用于判断RTC是不是第一次执行配置    }    else                                                    //RTC不是第一次配置    {        RTC_WaitForSynchro();                               //等待同步        RTC_WaitForLastTask();                              //等待上一次操作完成    }}
时钟设置:void MyRTC_SetTime(void){    time_t time_cnt = 0;    //定义秒计数器数据类型    struct tm time_date;    //定义日期时间数据类型
    time_date.tm_year = MyRTC_Time[0] - 1900;       //将数组的时间赋值给日期时间结构体    time_date.tm_mon = MyRTC_Time[1] - 1;    time_date.tm_mday = MyRTC_Time[2];    time_date.tm_hour = MyRTC_Time[3];    time_date.tm_min = MyRTC_Time[4];    time_date.tm_sec = MyRTC_Time[5];
    time_cnt = mktime(&time_date) - 8 * 60 * 60;    //调用mktime函数,将日期时间转换为秒计数器格式                                                    //- 8 * 60 * 60为东八区的时区调整    RTC_SetCounter(time_cnt);                       //将秒计数器写入到RTC的CNT中    RTC_WaitForLastTask();                          //等待上一次操作完成}
读取时间:void MyRTC_ReadTime(void){    time_t time_cnt;        //定义秒计数器数据类型    struct tm time_date;    //定义日期时间数据类型
    time_cnt = RTC_GetCounter() + 8 * 60 * 60;      //读取RTC的CNT,获取当前的秒计数器                                                    //+ 8 * 60 * 60为东八区的时区调整
    time_date = *localtime(&time_cnt);              //使用localtime函数,将秒计数器转换为日期时间格式
    MyRTC_Time[0] = time_date.tm_year + 1900;       //将日期时间结构体赋值给数组的时间    MyRTC_Time[1] = time_date.tm_mon + 1;    MyRTC_Time[2] = time_date.tm_mday;    MyRTC_Time[3] = time_date.tm_hour;    MyRTC_Time[4] = time_date.tm_min;    MyRTC_Time[5] = time_date.tm_sec;}

(五)BKP常用函数

void BKP_DeInit(void);//恢复缺省配置(用于清空BKP所有BKP寄存器)
void BKP_TamperPinLevelConfig(uint16_t BKP_TamperPinLevel);//配置TAMPER引脚的有效电平
void BKP_TamperPinCmd(FunctionalState NewState);//是否开启侵入检测功能
void BKP_ITConfig(FunctionalState NewState);//是否开启中断
void BKP_RTCOutputConfig(uint16_t BKP_RTCOutputSource);//时钟输出功能配置(在RTC引脚上输出时钟信号、RTC校准时钟、RTC闹钟脉冲、秒脉冲)
void BKP_SetRTCCalibrationValue(uint8_t CalibrationValue);//设置RTC校准值
void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data); //写BKP备份寄存器 
uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR); //读BKP寄存器 
FlagStatus BKP_GetFlagStatus(void);//查看标志位
void BKP_ClearFlag(void);//清除标志位
ITStatus BKP_GetITStatus(void);//查看中断标志位
void BKP_ClearITPendingBit(void);//清除中断标志位

读写BKP备份寄存器步骤

1.开启PWR(电源控制)和BKP的时钟    PWR的开启函数为RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); (开启后备电源VBAT)    BKP的开启函数RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE); 开启BKP外设的时钟,都在APB1总线下
2.使能备份区域的访问    因为RTC实时时钟和BKP备份寄存器,都处于备份区域中。在STM32中,想访问RTC和BKP,就要先开启备份区域的访问权限。    函数:PWR_BackpAccessCmd(ENABLE);
3.读写操作    写入:BKP_WriteBackupRegister(BKP_DR1,Data);    读出:Data = BKP_ReadBackupRegister(BKP_DR1);
uint16_t WriteArr[] = {0x0000, 0x0001};uint16_t ReadArr[2] = { 0 };
int main(){    //使能时钟电源和后备接口时钟    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);    RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);
    //使能备份访问控制    PWR_BackupAccessCmd(ENABLE);    while(1)    {        if(KEY_Get() == 1)        {            //写入BKP            BKP_WriteBackupRegister(BKP_DR1,WriteArr[0]);            BKP_WriteBackupRegister(BKP_DR2,WriteArr[1]);
            WriteArr[0]++;            WriteArr[1]++;        }        //读取BKP        ReadArr[0] = BKP_ReadBackupRegister(BKP_DR1);        ReadArr[1] = BKP_ReadBackupRegister(BKP_DR2);    }}

http://www.kler.cn/a/503124.html

相关文章:

  • 图解Git——分支开发工作流《Pro Git》
  • 计算机视觉算法实战——步态识别(主页有源码)
  • Linux第二课:LinuxC高级 学习记录day01
  • 基于单片机的智能花卉浇水系统的设计与实现
  • MixDehazeNet:用于图像去雾网络的混合结构块
  • JAVA安全编码规范
  • 【RAG检索增强生成】MaxKB:构建企业级知识库问答系统(Ollama+Qwen2)
  • Vue.js组件开发-实现图片裁剪
  • Scala语言的软件开发工具
  • Redis动态热点数据缓存策略设计
  • nvm安装详细教程(安装nvm、node、npm、cnpm、yarn及环境变量配置)
  • 【JAVA 基础 第(18)课】HashSet 使用方法详解
  • 重回C语言之老兵重装上阵(一)vscode编译.C文件
  • 2024年华为OD机试真题-判断一组不等式是否满足约束并输出最大差-Python-OD统一考试(E卷)
  • PowerBuilder中调用Excel OLE对象的方法
  • 【Ubuntu与Linux操作系统:十、C/C++编程】
  • 前端开发:CSS背景属性
  • 内网穿透的应用-Ubuntu本地Docker部署Leantime项目管理工具随时随地在线管理项目
  • 集成工作流的后台管理系统,springboot集成activiti,Java集成工作流审批流,vue后台管理系统(源码)
  • java项目启动时,执行某方法
  • nacos环境搭建以及SpringCloudAlibaba脚手架启动环境映射开发程序
  • 对React的高阶组件的理解?应用场景?
  • 农业4.0背后的智慧引擎:机器学习助力精准农事决策
  • 版本控制器Git:时间机器与备份系统
  • ASP.NET Core与GraphQL集成
  • Excel数据叠加生成新DataFrame:操作指南与案例