RTC硬件原理精讲:模块架构与时钟源
RTC,说白了就是一块独立于主系统的小时钟。它能在芯片深度睡眠时继续跑,靠的是自己的电源域和时钟源。我刚开始接触L4系列时,觉得RTC就是个普通外设,后来才发现它的架构设计藏着不少门道。
RTC模块架构
STM32L4的RTC模块,内部大致分为三个部分:
- 核心计数器:一个32位的可编程计数器,支持秒、分、时、日、月、年
- 闹钟寄存器:两组独立的闹钟比较器,支持精确到秒的触发
- 唤醒定时器:一个16位的递减计数器,用于周期性唤醒
这三个部分共享同一个时钟源,但各自有独立的中断和事件输出。我个人习惯把RTC看作一个“永不睡觉的秘书”——主CPU可以关机,但秘书还在看表,到点了就敲门叫醒你。
关键点:RTC的寄存器分为“影子寄存器”和“实际寄存器”。读日历时要等影子同步完成,否则可能读到跳变数据。我在项目中遇到过读RTC秒数偶尔跳变的问题,后来发现是没等RSF位就读取了。
时钟源选择:LSE vs LSI vs HSE
RTC的时钟源有三种选择,每种都有自己的脾气。我列个表对比一下:
| 时钟源 | 频率 | 精度 | 功耗 | 典型场景 |
|---|---|---|---|---|
| LSE | 32.768 kHz | ±20 ppm(晶振) | ~0.5 µA | 高精度日历、闹钟 |
| LSI | ~32 kHz | ±5%(RC振荡器) | ~0.2 µA | 唤醒定时器、低精度计时 |
| HSE | 1~16 MHz(分频后) | ±50 ppm | ~5 µA(含振荡器) | 极少使用,功耗高 |
我的建议:做低功耗产品,优先选LSE。虽然需要外接晶振,但精度和功耗的平衡最好。LSI适合对时间精度要求不高的唤醒场景,比如每10秒唤醒一次检查传感器。
为什么会这样?LSE是外部晶振,频率稳定,温度漂移小。LSI是内部RC振荡器,省了晶振和两个电容,但频率随温度和电压变化。我记得有一次用LSI做RTC唤醒,结果在-20°C环境下,实际唤醒周期比设定值慢了将近15%。
备份域寄存器
备份域是RTC的“安全屋”。它由VBAT引脚独立供电,主电源掉电后数据不丢失。备份域包含:
- RTC寄存器:日历、闹钟、唤醒配置
- 20个备份寄存器:每个32位,共80字节用户数据
- RCC相关配置:时钟源选择、分频系数
备份域有个坑:写备份寄存器前,必须先使能PWR时钟,然后调用HAL_PWR_EnableBkUpAccess()。我曾经在调试时忘了这步,写进去的数据读出来全是0xFFFFFFFF,排查了半天才发现是写保护没解除。
注意:备份域复位条件比较特殊。只有以下情况会复位备份域:
- VBAT和VDD同时掉电
- 软件设置RCC_BDCR中的BDRST位
- 检测到侵入事件(TAMPER)
普通的系统复位或看门狗复位不会影响备份域数据。
RTC日历与闹钟功能
日历功能支持完整的年月日时分秒,还自动处理闰年。配置时要注意:
// 设置日历的典型流程
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
sTime.Hours = 14;
sTime.Minutes = 30;
sTime.Seconds = 0;
sTime.TimeFormat = RTC_HOURFORMAT_24;
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;
sDate.WeekDay = RTC_WEEKDAY_MONDAY;
sDate.Month = RTC_MONTH_JANUARY;
sDate.Date = 15;
sDate.Year = 24; // 2024年
HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
闹钟功能支持两组独立闹钟(Alarm A和Alarm B)。每组可以配置:
- 精确到秒的触发时间
- 掩码模式(忽略年/月/日/时/分/秒中的某些字段)
- 中断输出或事件输出
我个人习惯用闹钟做“定时任务”,比如每天上午8点唤醒系统上报数据。掩码模式特别灵活——你可以设置“每天14:30触发”,而不需要指定具体日期。
RTC唤醒定时器
唤醒定时器是一个16位递减计数器,配合可编程的预分频器,能实现从几微秒到几十秒的唤醒周期。它的工作模式:
- 计数器从设定值开始递减
- 减到0时产生唤醒事件
- 自动重装载(如果使能了自动重载)
唤醒周期的计算公式:
唤醒周期 = (WUCKSEL分频系数) × (WUT计数器值 + 1) / RTC时钟频率
举例:LSE = 32768Hz,WUCKSEL = 16分频,WUT = 2047
唤醒周期 = 16 × 2048 / 32768 = 1秒
避坑指南:我曾经把WUT计数器值设成0,结果唤醒定时器每1个时钟周期就触发一次,系统根本没法进入低功耗模式。记住:WUT的最小有效值是1,除非你想做“连续唤醒”测试。
唤醒定时器最大的优势是功耗极低——它不需要外部晶振,用LSI就能工作。在Stop 2模式下,整个系统功耗可以降到1 µA以下,但RTC唤醒定时器依然在跑。这对电池供电的设备来说,简直是福音。
嗯,RTC的硬件原理就讲到这里。记住一句话:RTC是低功耗系统的“心脏起搏器”,选对时钟源、配好分频器、用好备份域,你的设备才能在睡眠中精准地“醒来”。