RTC硬件原理精讲:模块架构与时钟源

RTC,说白了就是一块独立于主系统的小时钟。它能在芯片深度睡眠时继续跑,靠的是自己的电源域和时钟源。我刚开始接触L4系列时,觉得RTC就是个普通外设,后来才发现它的架构设计藏着不少门道。

RTC模块架构

STM32L4的RTC模块,内部大致分为三个部分:

  • 核心计数器:一个32位的可编程计数器,支持秒、分、时、日、月、年
  • 闹钟寄存器:两组独立的闹钟比较器,支持精确到秒的触发
  • 唤醒定时器:一个16位的递减计数器,用于周期性唤醒

这三个部分共享同一个时钟源,但各自有独立的中断和事件输出。我个人习惯把RTC看作一个“永不睡觉的秘书”——主CPU可以关机,但秘书还在看表,到点了就敲门叫醒你。

关键点:RTC的寄存器分为“影子寄存器”和“实际寄存器”。读日历时要等影子同步完成,否则可能读到跳变数据。我在项目中遇到过读RTC秒数偶尔跳变的问题,后来发现是没等RSF位就读取了。

RTC模块内部架构 时钟源 LSE (32.768kHz) LSI (~32kHz) HSE (分频) 预分频器 异步分频 (7位) 同步分频 (15位) 输出: 1Hz 核心计数器 32位可编程 日历寄存器 影子寄存器 闹钟A 精确到秒比较 闹钟B 精确到秒比较 唤醒定时器 16位递减计数器 备份域 (VBAT供电)

时钟源选择: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位递减计数器,配合可编程的预分频器,能实现从几微秒到几十秒的唤醒周期。它的工作模式:

  1. 计数器从设定值开始递减
  2. 减到0时产生唤醒事件
  3. 自动重装载(如果使能了自动重载)

唤醒周期的计算公式:

唤醒周期 = (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是低功耗系统的“心脏起搏器”,选对时钟源、配好分频器、用好备份域,你的设备才能在睡眠中精准地“醒来”。