STM32 RTC 实时时钟:掉电还走时、闹钟唤醒与时间漂移的工程配置
做带时间戳的数据记录、定时唤醒采集、低功耗待机,第一反应往往是“用个定时器加个计数器”。但系统一掉电,时间就丢了;进 STOP 模式省电,普通定时器又停了。这时候该 RTC 出场了。本文把 STM32 RTC 从时钟源选型、预分频、日历初始化到闹钟唤醒和时间漂移校准的工程做法一次讲清楚。
一、RTC 靠什么“掉电还走时”
RTC 有独立的供电域。主电源 VDD 断掉后,只要 VBAT 引脚还有电(纽扣电池或超级电容),RTC 和备份寄存器(BKP)就能继续走时。代价是 VBAT 域的功耗极低,CR2032 顶几年没问题。
工程上第一个坑就在这里:VBAT 悬空。很多最小系统板只把 VBAT 接了个 0.1μF 电容滤波,没接电池,掉电后 RTC 每次上电都从 2000 年 1 月 1 日开始。检查方法很简单:断电 10 秒再上电,读一次 RTC 时间,如果回到默认值,先查 VBAT 有没有电池,而不是怀疑代码。
第二个坑是写保护。RTC 寄存器默认有写保护,改时间、改闹钟前要先按固定顺序写两个解锁键(0xCA、0x53)。HAL 库的 HAL_RTC_SetTime 内部已经处理了,但如果直接操作寄存器,漏了这一步,写进去的值读出来还是旧的,现象就是“设了时间没生效”。
二、时钟源:LSE 还是 LSI,先定精度
RTC 的时钟源有三选一:LSE(外部 32.768kHz 晶振)、LSI(内部约 32kHz RC)、HSE 分频。选型只看一个指标:你要的时间准不准。
LSE 是首选。32.768kHz 恰好是 2 的 15 次方,分频到 1Hz 干净;晶振温漂小,普通 20ppm 的晶振一天漂移约 1.7 秒。LSI 是内部 RC 振荡器,频率随温度和电压漂,标称 32kHz 实测可能偏几百 Hz,一天能差几分钟,只能用在“有个大概时间就行”的场合,比如唤醒周期不敏感的采集。
但 LSE 有个代价:起振慢。32.768kHz 晶振从上电到稳定要 1 秒甚至更久,代码里等 LSE 就绪(RCC_FLAG_LSERDY)必须加超时,超时后要降级处理——要么切 LSI 先跑起来并在日志里打标记,要么直接报错停住。最忌讳的是死等,板子一换晶振批次起振变慢,程序就卡在启动。
负载电容也要算。晶振手册给的是负载电容 CL,板上两个外接电容 C = 2×CL − Cstray,杂散电容按 3~5pF 估。电容配错,晶振要么不起振,要么频偏变大,日积月累就是时间越走越偏。量产前用示波器看一次 LSE 波形,比事后调校准值靠谱得多。
三、预分频:异步 127、同步 255 的由来
RTC 内部是两级分频:异步预分频器 + 同步预分频器。时钟进来到秒脉冲的公式是:
f_CK_SPRE = f_RTCCLK / ((PREDIV_A + 1) × (PREDIV_S + 1))
LSE 是 32768Hz,取 PREDIV_A = 127、PREDIV_S = 255:(127+1)×(255+1) = 128×256 = 32768,刚好分到 1Hz。异步分频系数越大,功耗越低,所以规范写法是异步取最大值 127,剩下的给同步。LSI 时频率不是 32768,就按实测频率反推两组系数,原则不变:异步尽量大。
HAL 库的初始化结构体里这两个值就是 AsynchPrediv 和 SynchPrediv,CubeMX 生成的代码默认就是 127 和 255,用的就是 LSE。换 LSI 时记得改,否则 1 秒不再是 1 秒,时间越走越快或越走越慢。
四、日历初始化:用备份寄存器防重复
RTC 上电后要不要重新设时间,是个必须显式处理的问题。做法是利用备份寄存器存一个魔数:
- 第一次上电:备份寄存器是默认值(掉电后 VBAT 没电也会回到默认值),说明 RTC 没初始化过,设时间、写魔数 0xA5A5;
- 以后每次上电:读到魔数还在,说明 RTC 一直在走,直接用,不再设时间。
不加这个判断的后果:每次上电都把时间重设成编译时刻,设备断电重启后时间倒退,数据记录的时间戳全乱。反过来,魔数判断也有坑:换电池后第一次上电,魔数丢了,程序会重设时间,这是对的;但如果你的代码在魔数丢失时默认设成固件编译时间,而设备出厂后放了半年才第一次上电,那时间就是错的——量产设备应该在第一次联网或第一次 GPS 授时后校准,而不是依赖编译时间。
读时间的顺序也有讲究:先读时间寄存器再读日期寄存器,或者用 HAL_RTC_GetTime 后紧跟 HAL_RTC_GetDate。RTC 的影子寄存器机制是:读时间会锁住日期,读日期才解锁。如果只读时间不读日期,跨日子夜那一刻可能读到“23:59:59 配明天日期”这种错配组合。HAL 的 GetTime/GetDate 成对调用就是为了避开这个坑。
五、闹钟唤醒:STOP 模式下的定时器
RTC 闹钟的典型用法是低功耗定时唤醒:设一个闹钟时间,进 STOP 模式,时间一到 RTC 闹钟中断把芯片唤醒,采一次数据再睡回去。STM32 的 STOP 模式下高速时钟全停,但 LSE 还在跑,RTC 照常工作,这是它比普通定时器更适合低功耗场景的原因。
配置注意三点:第一,闹钟分 A 和 B 两个,A 常用,B 可以做第二个唤醒点;第二,闹钟时间是“时:分:秒 + 日期/星期掩码”,掩码位可以让闹钟每天同一时刻响,不用每天重设;第三,唤醒后要清中断标志,否则一出 STOP 立刻又进中断,表现为“刚睡下就醒了”。
功耗实测参考:STOP 模式 + LSE + RTC 闹钟,整机可以做到几微安,CR2032 供电的采集节点能跑一两年。把闹钟周期从 1 分钟改成 10 分钟,待机电流不变,唤醒次数少了,平均功耗直接降一个量级——低功耗设计的收益往往不在睡眠电流,而在唤醒频率。
六、时间漂移:平滑校准怎么用
再好的晶振也有偏差。20ppm 的晶振一天漂 1.7 秒,一个月就是 50 秒。STM32 RTC 有平滑校准功能(Smooth Digital Calibration),通过 CALR 寄存器每 2 的 20 次方个时钟周期加或减几个脉冲,把平均频率拉回来。
校准值不是算出来的,是测出来的:让 RTC 跑 24 小时,对比标准时间算出偏差,再按手册公式换算成 CALP 与 CALM。注意校准只在 LSE 下有效,LSI 温漂太大,校了也没用。需要更高精度的场合,定期联网 NTP 校准比片上校准更直接,RTC 校准适合的是“长期无网、又要求时间别差太多”的设备。
七、自查清单 - VBAT 有没有接电池?断电 10 秒再上电,时间还在不在?
- LSE 起振等了多久?超时降级逻辑写了没有?
- 异步 127、同步 255,对得上你的时钟源频率吗?
- 备份寄存器魔数判断做了吗?换电池后时间会不会被错误重设?
- 读时间后有没有紧跟读日期?跨日子夜会不会错配?
- 闹钟唤醒后清中断标志了吗?STOP 模式功耗实测过吗?
- 跑 24 小时对表,漂移多少秒?要不要上平滑校准?
RTC 的配置代码不到一百行,但坑全在时钟源、供电域和初始化顺序这些外围环节上。把这张清单过一遍,掉电走时、闹钟唤醒、长期守时这三件事就能一次做对。
- 点赞
- 收藏
- 关注作者
评论(0)