BKP

BKP简介

BKP(Backup Registers)备份寄存器

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

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

RTC引脚输出RTC校准时钟、RTC闹钟脉冲或者秒脉冲 存储RTC时钟校准寄存器

用户数据存储容量:     

                20字节(中容量和小容量)/ 84字节(大容量和互联型)

BKP基本结构

RTC

RTC简介

RTC(Real Time Clock)实时时钟 RTC是一个独立的定时器,可为系统提供时钟和日历的功能 RTC和时钟配置系统处于后备区域,系统复位时数据不清零,VDD(2.0~3.6V)断电后可借助VBAT(1.8~3.6V)供电继续走时

32位的可编程计数器,可对应Unix时间戳的秒计数器

20位的可编程预分频器,可适配不同频率的输入时钟

可选择三种RTC时钟源:     

HSE时钟除以128(通常为8MHz/128)   

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

 LSI振荡器时钟(40KHz)

RCT时钟树

简化的RTC框图

APB1 总线(上层):用于 CPU 通过软件读写 RTC 的寄存器(如设置时间、读取闹钟值)。

后备区域(Back-up Domain):图中灰色高亮部分,这是 RTC 的核心供电域。

  • 特性:当系统主电源掉电时,VBAT 引脚(电池供电)会继续为该区域供电。因此,**RTC_CNT(计数器)、RTC_ALR(闹钟)、RTC_PRL(预分频)** 等关键寄存器的值在断电后仍能保持,实现了精准计时

待机时不供电:标注了 “待机时不供电” 的模块(如 APB1 接口、RTC_CR 控制寄存器、NVIC),说明这些寄存器仅在系统运行时有效,进入待机模式后会丢失数据。

RTC 预分频器 (RTC_PRL / RTC_DIV)

RTC_PRL 重装载值,重装载值赋给RTC_DIV 余数计数器  当递减到0时,RTCCLK再产生一个1HZ秒脉冲并且RTC_CNT+1

32 位可编程计数器 (RTC_CNT):秒脉冲每来一次,计数器就加 1。

溢出(Overflow):当 32 位计数器从最大值(0xFFFFFFFF)计数到下一个数时变为 0,会产生RTC_Overflow信号。

闹钟寄存器 (RTC_ALR):存放用户设定的闹钟时间。

RTC_CNT(当前时间)RTC_ALR(设定时间)相等时,产生RTC_Alarm信号并进行中断,可用于定时采集数据等

基本结构

RTC操作注意事项

执行以下操作将使能对BKP和RTC的访问:     

设置RCC_APB1ENR的PWREN和BKPEN,使能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寄存器

BKP读写备份寄存器相关代码

1.初始化(PWR和BKP)

注意:必须先开 PWR/BKP 时钟,再解锁后备区域,顺序不能反

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
//解锁 STM32 的 “后备区域(Backup Domain)” 访问权限。
	PWR_BackupAccessCmd(ENABLE);

2.写BKP的DR寄存器

// BKP_DRx where x:[1, 42]:寄存器编号
// Data	要写入的 16 位数据  0x0000 ~ 0xFFFF
BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data);

RTC时钟相关代码

1.开启PWR和BKP时钟再解锁后备区域

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);
	PWR_BackupAccessCmd(ENABLE);

2.开启LSE时钟,并等待LSE时钟启动完成

	RCC_LSEConfig( RCC_LSE_ON);
	while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)!=SET);

3.选择RTCCLK时钟源,并使能时钟

	RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
	RCC_RTCCLKCmd(ENABLE);

4.等待同步和等待上一次操作完成(防止时钟不同步,造成BUG)

	RTC_WaitForSynchro();
	RTC_WaitForLastTask();

5.配置预分频器再调用一次等待上一次操作完成函数

	RTC_SetPrescaler(32768-1);//时钟源频率(32768) - 1
	RTC_WaitForLastTask();

6.设置初始时间(可以不写,会使用默认时间1970)再调用一次等待上一次操作完成函数

	RTC_SetCounter(1771946317);
	RTC_WaitForLastTask();

7.写两个函数设置和获取时间

设置时间函数

//将自定义的时间数组 MyRTC_Time 转换为时间戳,写入 RTC 计数器
void MyRTC_SetTime(void){
	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();
}

读取时间函数

//从 RTC 计数器读取时间戳,转换为日期时间,存回 MyRTC_Time 数组
void MyRTC_ReadTime(void){
	time_t time_cnt;//秒时间戳
	struct tm time_data;
	time_cnt = RTC_GetCounter()+8*60*60;
	time_data = *localtime(&time_cnt);//将时间戳转换为tm结构体
	MyRTC_Time[0] = time_data.tm_year+1900;//还原为实际年份(如126→2026)
	MyRTC_Time[1] =time_data.tm_mon +1;//还原为实际月份(如1→2月)
	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 ;
	
}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐