引言
本文主要从BKP备份寄存器和RTC实时时钟的原理,特性及应用三个方面展开讨论,解析它们在STM32中的独特价值,助力开发者更好的掌握和运用它们。
BKP备份寄存器的定义
STM32的BKP备份寄存器是一种特殊的存储单元,它位于备份区域,即使在系统复位或电源关闭时,也能通过电池备份保持数据不丢失。它通常包含多个寄存器,用于存储关键数据,如配置信息、状态标志等。这些寄存器通过低功耗设计和独立的备份电源,确保数据在主电源失效时仍能安全保存,为系统恢复和数据完整性提供可靠保障。
简单来讲,BKP备份寄存器可用于存储用户的程序数据,当VDD(也就是芯片电源)被切断后,仍然可以通过VBAT引脚外接一个纽扣电池(通常在1.8v到3.6v)向芯片供电,让系统处于低功耗模式下维持一些功能的运行,当系统或电源复位,它也不会被复位。
硬件设计
BKP备份寄存器与RTC实时时钟的关系
首先,它们两个是紧密相关的功能模块,它们共同为系统提供数据部分和时间管理的功能,它们都是共享VBAT备用电源。
其次,BKP备份寄存器是用于数据的备份,而RTC实时时钟模块本身用于时间计数,所以RTC实时时钟需要BKP备份寄存器来存储一些数据。
BKP备份寄存器的特性及应用
我认为BKP备份寄存器最大的特性就是断电不丢失数据,当然,如果VDD和VBAT同时断电的话,那它就发挥不出它的作用了。很显然,它的特性决定着它的应用,如低功耗模式下的数据保持,防拆功能(TAMPER)等。
配置TAMPER(防拆功能)的示例代码
首先是使能PWR和BKP时钟,开启PWR时钟是因为访问备份域是由PWR模块进行控制的,所以必须要使能它
void BKP_Init(void)
{// 使能PWR和BKP时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);// 使能访问备份寄存器PWR_BackupAccessCmd(ENABLE);
}
其次是配置PC13引脚,因为大部分的TAMPER引脚都是PC13,可根据实际需求更改
void TAMPER_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;// 配置TAMPER引脚(PC13)为输入模式GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(GPIOC, &GPIO_InitStructure);// 配置TAMPER引脚为高电平有效BKP_TamperPinLevelConfig(BKP_TamperPinLevel_High);// 使能TAMPER引脚功能BKP_TamperPinCmd(ENABLE);
}
接着封装一下写入数据和读出数据的函数
void BKP_WriteData(uint16_t Data)
{// 写入数据到备份寄存器1BKP_WriteBackupRegister(BKP_DR1, Data);
}uint16_t BKP_ReadData(void)
{// 从备份寄存器1读取数据return BKP_ReadBackupRegister(BKP_DR1);
}
最后是检查TAMPER事件标志位,建议放在while循环中不断查询,当然,用户可以在里面调用一些TAMPER事件发生后执行的函数,如通过串口打印输出等,这里就不调用了。
void BKP_CheckTamper(void)
{// 检查TAMPER事件标志位if (BKP_GetFlagStatus(BKP_FLAG_TAMP) != RESET){// 清除TAMPER事件标志位BKP_ClearFlag(BKP_FLAG_TAMP);}
}
总结一下
该示例代码的逻辑思路是初始化BKP和TAMPER引脚,接着把想写的数据写入到备份寄存器中,然后再把写入的数据读出来,再关掉VDD同时保持VBAT连接备用电源,如果写入的数据没有被删除,那这个BKP备份寄存器实验就是成功的。同时将TAMPER引脚置于低电平,如果TAMPER事件发生,那么这个防拆功能的实验也是成功的。
RTC实时时钟定义
RTC(Real-Time Clock,实时时钟)是一种硬件电路,用于为电子设备提供精确的时间和日期信息。它通常独立于主处理器运行,即使在系统关闭或断电时,也能通过备用电池继续计时。RTC的主要功能是保持时间的连续性,提供秒、分、时、日、月、年的准确记录,并支持闹钟和周期性唤醒功能。它广泛应用于嵌入式系统、智能设备和低功耗应用中,确保设备在任何情况下都能准确地跟踪时间。
RTC实时时钟特性与应用
RTC实时时钟具有高精度,低功耗,掉电保持的特性,广泛应用于时钟,日历,数据采集等。
RTC实时时钟示例代码
1,设置实时时钟时间函数
#include "time.h"uint32_t MyRTC_Time[] = {2025,8,18,10,41,00}; //定义全局的时间数组,数组内容分别为年、月、日、时、分、秒void MyRTC_SetTime(void)
{time_t time_cnt; //定义秒计数器数据类型struct tm time_date; //定义日期时间数据类型// 将数组的时间赋值给日期时间结构体time_date.tm_year = MyRTC_Time[0] - 1900; // 年份,从1900开始计算time_date.tm_mon = MyRTC_Time[1] - 1; // 月份,从0开始(0表示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]; // 秒// 调用mktime函数,将日期时间转换为秒计数器格式// - 8 * 60 * 60为东八区的时区调整time_cnt = mktime(&time_date) - 8 * 60 * 60;// 将秒计数器写入到RTC的CNT中RTC_SetCounter(time_cnt);// 等待上一次操作完成RTC_WaitForLastTask();
}
从全局数组 MyRTC_Time[]中读取时间值,将读取的时间变量赋值给struct tm类型的变量time_data,再使用mktime函数将struct tm转换为秒计数器格式,并调整时区。
2,RTC初始化函数
void MyRTC_Init(void)
{/*开启时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); //开启PWR的时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE); //开启BKP的时钟/*备份寄存器访问使能*/PWR_BackupAccessCmd(ENABLE); //使用PWR开启对备份寄存器的访问if (BKP_ReadBackupRegister(BKP_DR1) != 0x0000) //通过写入备份寄存器的标志位,判断RTC是否是第一次配置//if成立则执行第一次的RTC配置{RCC_LSEConfig(RCC_LSE_ON); //开启LSE时钟while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET); //等待LSE准备就绪RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //选择RTCCLK来源为LSERCC_RTCCLKCmd(ENABLE); //RTCCLK使能RTC_WaitForSynchro(); //等待同步RTC_WaitForLastTask(); //等待上一次操作完成RTC_SetPrescaler(32768 - 1); //设置RTC预分频器,预分频后的计数频率为1HzRTC_WaitForLastTask(); //等待上一次操作完成MyRTC_SetTime(); //设置时间,调用此函数,全局数组里时间值刷新到RTC硬件电路BKP_WriteBackupRegister(BKP_DR1, 0x0000); //在备份寄存器写入自己规定的标志位,用于判断RTC是不是第一次执行配置}else //RTC不是第一次配置{RTC_WaitForSynchro(); //等待同步RTC_WaitForLastTask(); //等待上一次操作完成}
}
首先判断写入BKP备份寄存器里面的BKP_DR1是不是等于0x0000,这个是提前写入的值,当然,这个值可以自定义,这个判断操作是检查VBAT是否断开,如果断开就需要重新配置,选择外部低速晶振,也就是我们常见的32.768khz,再进行预分频为1hz,最后再调用刷新时间的函数,就初始化完成了。
3,RTC读取时间函数
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;
}
最后在需要的地方调用此函数,就可以获取时间了。
总结
本文介绍了STM32的BKP备份寄存器与RTC实时时钟功能。BKP寄存器可在系统复位后保存关键数据,确保数据的连续性。RTC模块则提供精确的时间管理,支持时钟设置、时间更新等功能,二者结合可实现低功耗、高可靠性的数据备份与时间记录,适用于多种嵌入式应用场景。