

适用平台 :STM32F103C8T6
开发环境:Keil5 + 标准库函数
前言
做项目的时候,你有没有遇到过这种需求:单片机断电重启后,想要记住上一次设置的某个参数?比如密码、校准值、或者一个计数器的值?用Flash存当然可以,但Flash有擦写次数限制,而且操作麻烦。这时候,BKP备份寄存器就派上用场了。
再说另一个场景:你想做一个电子时钟,要求断电后时间还能继续走,下次上电显示的是当前真实时间,而不是从0开始。这时候就需要RTC实时时钟。
BKP和RTC这两个模块在STM32里是"绑"在一起的,它们都工作在后备区域,都有独立的电池供电。这篇文章我就结合江科大的课程笔记,把这两个东西从原理到应用彻底讲清楚。
一、BKP备份寄存器
1.1 BKP是什么?
BKP(Backup Registers) 就是备份寄存器,说白了,它就是单片机里一小块特殊的RAM,用来存用户数据。但它和普通RAM最大的区别是:掉电不丢失。
| 特性 | 说明 |
|---|---|
| 供电方式 | VDD(2.0~3.6V)正常供电;VDD掉电后自动切换为VBAT(1.8~3.6V)电池供电 |
| 抗复位能力 | 系统复位、电源复位、待机唤醒后,数据不清零 |
| 数据清除条件 | 只有TAMPER引脚检测到侵入事件,或软件手动清除,才会清空 |
| 存储容量 | 中/小容量:20字节(10个16位寄存器 DR1~DR10);大容量/互联型:84字节(DR1~DR42) |
| 附加功能 | RTC时钟校准、闹钟脉冲/秒脉冲输出 |
我的理解:你可以把BKP想象成单片机的"记事本小纸条"。主电源断了,它靠纽扣电池续命;你按复位键重启了,它上面的字还在。只有你把TAMPER引脚(PC13)接到一个检测电路上,有人拆机打开外壳触发了侵入检测,它才会"销毁证据"把数据清空。这个设计在安防领域很有用。
1.2 BKP的基本结构

BKP基本结构框图
从框图里可以清楚地看到BKP的三大外部接口:
-
VBAT:电池供电入口,接3V纽扣电池(CR2032)
-
TAMPER:侵入检测引脚,对应PC13。当这个引脚检测到有效边沿时,所有备份寄存器内容被硬件自动清除
-
RTC:可以输出RTC校准时钟、闹钟脉冲或秒脉冲
内部核心是数据寄存器(DR),每个DR都是16位的。中容量芯片有DR1~DR10共10个,能存20个字节。对于STM32F103C8T6这种中容量芯片来说,20个字节存个密码、存个设备ID、存几个标志位,完全够用了。
二、RTC实时时钟
2.1 RTC是什么?
RTC(Real Time Clock) 实时时钟,就是一个独立的定时器,专门用来给系统提供时间和日历功能。
RTC的几个关键特性:
-
独立工作:RTC和它的时钟配置寄存器都在后备区域,有自己独立的时钟源
-
掉电保持:VDD(2.0~3.6V)断电后,VBAT(1.8~3.6V)继续给RTC供电,时钟继续走
-
32位计数器:RTC_CNT是一个32位可编程计数器,可以把它当成Unix时间戳的秒计数器来用
-
20位预分频器:RTC_PRL是预分频重装寄存器,可以把不同频率的输入时钟分频成1Hz
-
闹钟功能:RTC_ALR是32位闹钟值,当CNT == ALR时产生闹钟中断
我的理解 :RTC本质上就是一个"超级定时器"。普通定时器(TIM)主电源断了就停了,RTC有"备用电池"这个外挂,所以能一直跑。32位计数器最大能计到 0xFFFFFFFF,换算成秒大约是136年,所以你根本不用担心它溢出。预分频器的作用就是把32.768KHz这种"高频"时钟,分频成1Hz(每秒一个脉冲),这样CNT每加1就代表过了1秒。
2.2 RTC的三种时钟源
RTC的时钟源可以选择三种:
| 时钟源 | 频率 | 特点 | 适用场景 |
|---|---|---|---|
| HSE/128 | 8MHz/128 = 62.5KHz | 精度高,但主电源断了HSE就停了 | 不推荐用于RTC |
| LSE | 32.768KHz(外部晶振) | 精度高,有独立供电,频率正好是2^15 | 强烈推荐 |
| LSI | 约40KHz(内部RC) | 不用外接晶振,但温漂大,精度差 | 对时间精度要求不高的场景 |
重点说一下LSE :32.768KHz这个频率选得特别巧妙,因为 32768 = 2^15。RTC的20位预分频器设置重装值为 32768-1 = 32767 时,正好分频得到1Hz。而且外部低速晶振(如下图的圆柱形银色晶振)频率稳定,温漂小,是做时钟的最佳选择。
2.3 RTC框图与基本结构

简化的RTC框图

RTC基本结构
把这两张图结合起来看,RTC的工作流程就很清晰了:
第一步:时钟源选择
HSE/128、LSE、LSI 三选一,经过多路选择器输出 RTCCLK。
第二步:预分频
RTCCLK 进入预分频器,RTC_PRL 存放分频重装值,RTC_DIV 是余数寄存器(当前分频计数)。当DIV减到0时,输出一个 TR_CLK(通常是1Hz)。
第三步:32位计数器计数
TR_CLK 的每个上升沿让 RTC_CNT 加1。CNT就是当前的秒计数器。
第四步:中断与闹钟
-
秒中断:每个TR_CLK周期产生一次(每秒一次)
-
溢出中断:CNT从0xFFFFFFFF回到0时产生
-
闹钟中断:当 CNT == ALR 时产生,同时可以唤醒待机模式
第五步:NVIC中断管理
三种中断信号经过RTC_CR控制寄存器中的中断使能位(SECIE、OWIE、ALRIE),最终汇总到NVIC。
注意框图中的两个小字:"待机时不供电" 和 "待机时维持供电"。APB1接口和NVIC在主电源断电后是不工作的,但预分频器、计数器、闹钟值这些核心模块由VBAT维持供电,所以时间不会停。
我的理解:RTC的设计非常像"沙漏"。预分频器就是那个控制沙流速率的阀门(把32.768KHz变成1Hz),CNT就是下面接沙子的容器(每秒钟多一粒沙)。ALR闹钟就是你设定的目标重量,到了就响铃。而APB1总线只是你"看"沙漏的窗口,你断电了窗口黑了,但沙子还在流。
三、硬件电路
3.1 备用电池供电
硬件电路连接
看左边的"备用电池供电"部分:
-
简单连接:VBAT直接接3V电池正极,电池负极接地。这种接法有个问题:主电源3.3V和电池同时存在时,可能会有电流倒灌。
-
推荐连接:通过二极管D1、D2做"或门"供电。VDD(3.3V)经过D2降压后到VBAT,电池B2经过D1到VBAT。谁电压高谁供电。C3是滤波电容。这样主电源有电时电池不耗电,主电源断了电池无缝接管。
3.2 外部低速晶振电路
晶振X1(32.768KHz)跨接在PC14(OSC32_IN)和PC15(OSC32_OUT)之间,两个引脚各接一个10pF的负载电容到地。这是标准的皮尔斯振荡电路。
我的理解:VBAT这个设计真的很妙。你想想看,如果没有VBAT,每次断电重启时间都要重新设置,那电子时钟就没法用了。纽扣电池一般能用好几年,因为RTC功耗极低(微安级)。我建议大家做项目时一定用推荐连接的二极管方案,不要直接把电池焊在VBAT上,否则电池寿命会大打折扣。
四、RTC操作注意事项(重点!)
4.1 使能访问权限(第一步必须做)
要对BKP和RTC进行任何操作,必须先完成以下两步:
-
开启PWR和BKP的时钟:这两个外设都挂在APB1总线上,使用前必须先开时钟,否则寄存器写不进去。
-
设置PWR_CR的DBP位:DBP(Disable Backup Domain Write Protection)默认是0,后备区域被锁死;置1后才能访问RTC和BKP寄存器。
为什么需要这两步? 因为后备区域是"敏感地带",为了防止程序跑飞时误改写RTC和BKP,STM32默认把它们锁起来了。你必须先开时钟,再设置DBP位,才能解锁。
4.2 等待寄存器同步
如果APB1接口之前被禁用过,在读取RTC寄存器前,必须等待**RSF位(Register Synchronized Flag)**被硬件置1。
由于RTC时钟(32.768KHz)和APB1时钟(36MHz)是异步的,APB1读RTC寄存器时需要等待同步,否则读到的可能是旧数据。
4.3 进入配置模式
必须设置RTC_CRL寄存器中的CNF位(Configuration Flag),使RTC进入配置模式后,才能写入RTC_PRL、RTC_CNT、RTC_ALR这三个核心寄存器。
这是硬件层面的保护机制。CNF=1时,RTC核心暂停计数,允许修改配置;CNF=0时,RTC正常运行。
4.4 等待写操作完成
对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询RTC_CRL寄存器中的**RTOFF位(RTC Operation OFF)**来判断RTC寄存器是否处于更新中。
仅当RTOFF状态位是1时,才可以写入RTC寄存器。
因为RTC时钟很慢(32.768KHz),而APB1时钟很快(36MHz),写RTC寄存器需要好几个RTC时钟周期才能真正生效。如果连续快速写入,后面的数据会覆盖前面的,导致配置错误。
风险说明:这四步如果漏了任何一步,RTC要么初始化失败,要么时间不准,要么写不进去。特别是解除写保护(DBP)和等待RTOFF,是很多新手最容易踩的坑。
五、总结表格
| 项目 | BKP备份寄存器 | RTC实时时钟 |
|---|---|---|
| 全称 | Backup Registers | Real Time Clock |
| 本质 | 16位数据寄存器组(DR1~DRn) | 32位秒计数器 + 20位预分频器 |
| 供电区域 | 后备区域(VBAT维持) | 后备区域(VBAT维持) |
| 掉电保持 | ✅ 数据不丢失 | ✅ 时钟继续走 |
| 系统复位保持 | ✅ 不清零 | ✅ 不清零 |
| 容量/范围 | 20字节(中容量)/ 84字节(大容量) | CNT可计136年 |
| 时钟源 | 无(纯寄存器) | HSE/128、LSE(32.768KHz)、LSI(40KHz) |
| 推荐时钟源 | - | LSE外部低速晶振 |
| 清除条件 | TAMPER侵入事件、软件复位 | 仅VBAT断电才停 |
| 中断类型 | 无 | 秒中断、溢出中断、闹钟中断 |
| 关键寄存器 | DRx、CSR、CR | PRL、DIV、CNT、ALR、CRL、CRH |
| 访问前提 | 开PWR/BKP时钟 + DBP=1 | 开PWR/BKP时钟 + DBP=1 + CNF=1 + RTOFF=1 |
写这篇文章的时候,我把江科大的视频又回顾了一遍,对照着《STM32F10xxx参考手册》把RTC的框图重新梳理了一次。说实话,RTC这个章节知识点比较杂,BKP和RTC的关系、三种时钟源的选择、预分频器的计算、那四个操作注意事项,刚开始学的时候很容易搞混。
但只要你抓住一条主线------"RTC是一个用32.768KHz晶振驱动、经过32768分频得到1Hz、用32位计数器记录秒数的独立定时器"------其他的细节就都能串起来了。BKP则是RTC的"好邻居",共享后备区域和VBAT供电,用来存一些掉电不能丢的小数据。
📌 开发环境 :Keil5 + STM32F103C8T6最小系统板 + ST-Link
📌 参考资料 :江科大STM32入门教程、《STM32F10xxx参考手册》
📌 前置文章 :江科大STM32入门:Unix时间戳详解
如果本文对你有帮助,欢迎点赞 👍 收藏 ⭐ !有任何问题欢迎在评论区交流,一起学习进步!

