3. STM32CubeMX RTC配置实战:时钟树、参数与中断

好,咱们直接进入正题。上一章我们把RTC的理论基础过了一遍,这一章我带你亲手在CubeMX里把RTC的配置跑通。说白了,就是让工具帮我们把那些繁琐的寄存器初始化代码生成出来,我们只负责“告诉”它我们要什么。

我个人习惯,做低功耗项目时,第一步永远是先搞定时钟树。时钟不对,后面全是白搭。尤其是RTC,它对时钟源的要求非常苛刻。

3.1 时钟树配置:LSE是重中之重

RTC要跑得准,时钟源必须稳。在STM32L4上,RTC的时钟源有三个选项:LSE(低速外部晶振)、LSI(低速内部RC振荡器)和HSE(高速外部晶振分频)。

做低功耗产品,我几乎只用LSE。 为什么?

  • LSE(32.768 kHz): 精度最高,温漂最小。适合需要长期走时、带日历功能的产品。比如我做过的一个数据记录仪,野外工作半年,靠LSE走时,误差不超过30秒。
  • LSI(约32 kHz): 精度差,随温度和电压变化大。适合对时间精度要求不高的简单唤醒定时器。
  • HSE分频: 精度高,但功耗大。只有在系统主时钟运行时才用,低功耗模式下基本不考虑。
核心结论: 如果你的产品需要“看时间”,请无条件选择LSE。如果只是“定时唤醒”,LSI可以凑合,但要做好时间漂移的心理准备。

在CubeMX的时钟树配置界面(Pinout & Configuration -> Clock Configuration),你需要做这几步:

  1. 确认LSE已使能: 在左侧RCC(Reset and Clock Control)配置中,将LSE的选项从Disabled改为Crystal/Ceramic Resonator。注意,不是Bypass,除非你外部输入的是方波。
  2. 选择RTC时钟源: 在时钟树图中,找到RTC的时钟源选择器(MUX)。将输入源选为LSE。
  3. 检查分频器: 默认情况下,LSE经过RTC的异步预分频器(Asynchronous Prescaler)和同步预分频器(Synchronous Prescaler)后,输出1 Hz的时钟给日历。这个分频系数CubeMX会自动算好,你只需要确认一下。

我曾经遇到过一个坑:有次我为了省一个晶振,用了LSI做RTC时钟。结果产品在高温环境下测试,一天能差好几分钟。后来老老实实换回LSE,问题解决。所以,该花的钱不能省,一个32.768kHz的晶振才几毛钱,别因小失大。

3.2 RTC参数设置:日期、时间与格式

时钟树搞定后,我们切换到Pinout & Configuration -> Timers -> RTC界面。这里才是配置RTC核心参数的地方。

你会看到一堆选项,别慌,我们一个一个来。我个人习惯先配置格式,再配置时间。

3.2.1 时间格式:24小时 vs 12小时

在RTC Format下拉菜单中,选择Hour Format。默认是24小时制。除非你的产品面向北美市场,否则用24小时制就行,省得处理AM/PM的麻烦。

3.2.2 日期与时间初始值

在Date and Time区域,你可以设置初始的日期和时间。这里要注意:

  • Year、Month、Date: 直接填。注意年份是两位数(比如24代表2024),但CubeMX生成的代码里会自动处理BCD码转换。
  • Week Day: 这个要小心。很多新手会忽略它。Week Day的值范围是1-7,具体对应周几,要看芯片手册。STM32L4里,1代表周一,7代表周日。我习惯在代码注释里写清楚,防止以后自己忘了。
  • Hour、Minute、Second: 按实际时间填。如果你需要同步到UTC时间,这里就填UTC。
小技巧: 在量产时,我不会在CubeMX里写死初始时间。我会在代码里留一个接口,让产线测试时通过串口或按键来校准时间。CubeMX这里填的,更多是给开发板调试用的。

3.2.3 关键参数速查表

参数项 推荐值 说明
RTC Format 24 Hour 避免AM/PM处理
Date & Time 开发调试用 量产时建议代码动态设置
Week Day 1-7 1=周一,7=周日
Data Format BCD 默认即可,与寄存器一致

3.3 中断与唤醒配置:让RTC叫醒你

RTC最大的价值之一,就是能在低功耗模式下唤醒MCU。配置中断是核心。

在RTC配置界面的NVIC Settings选项卡里,你需要使能RTC global interrupt。但光使能这个还不够,你还需要在RTC Configuration里指定触发中断的事件。

常用的唤醒事件有:

  • Alarm A / Alarm B: 闹钟中断。可以设置到具体的日期、小时、分钟、秒。比如“每天上午8点整唤醒”。
  • WakeUp Timer: 唤醒定时器中断。这是一个可编程的计数器,从1 Hz到几十kHz的时钟源计数。比如“每10秒唤醒一次”。
  • Tamper: 入侵检测中断。检测到外部引脚电平变化时唤醒,常用于防拆机。
  • TimeStamp: 时间戳中断。外部事件发生时,记录当前时间。

我个人最常用的是WakeUp Timer。因为它灵活,不需要设置具体的日历时间,只需要一个计数值。比如在电池供电的传感器节点里,我让它每5分钟醒来一次,采集数据,然后继续睡。

注意: 配置WakeUp Timer时,要算好重装载值。公式是:WakeUp Counter = (目标唤醒时间(秒) * 时钟源频率) - 1。如果时钟源是LSE(32768 Hz),你想每1秒唤醒一次,那么重装载值就是 1 * 32768 - 1 = 32767。别算错了,否则唤醒周期会不对。

3.4 生成初始化代码:一键生成,但别无脑用

所有参数配置好后,点击Project Manager -> Code Generator,勾选Generate peripheral initialization as a pair of .c/.h files per peripheral。这样CubeMX会为RTC单独生成rtc.c和rtc.h文件,方便管理。

然后点击GENERATE CODE。几秒钟后,代码就生成了。

打开生成的rtc.c,你会看到MX_RTC_Init()函数。里面包含了我们刚才配置的所有寄存器设置。但我要提醒你:不要完全信任自动生成的代码。

比如,我遇到过CubeMX生成的代码里,RTC_InitStruct.OutPut默认使能了RTC_OUTPUT引脚,而这个引脚可能和你的其他外设冲突。所以,每次生成代码后,我建议你快速扫一眼初始化函数,确认没有多余的配置。

下面是一段典型的初始化代码结构,我加了注释,方便你理解:

void MX_RTC_Init(void)
{
  RTC_TimeTypeDef sTime = {0};
  RTC_DateTypeDef sDate = {0};

  // 1. 初始化RTC句柄
  hrtc.Instance = RTC;
  hrtc.Init.HourFormat = RTC_HOURFORMAT_24;          // 24小时制
  hrtc.Init.AsynchPrediv = 127;                       // 异步分频,自动计算
  hrtc.Init.SynchPrediv = 255;                        // 同步分频,自动计算
  hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;              // 关闭RTC输出,省电
  hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
  hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
  if (HAL_RTC_Init(&hrtc) != HAL_OK)
  {
    Error_Handler();
  }

  // 2. 设置初始时间
  sTime.Hours = 10;
  sTime.Minutes = 30;
  sTime.Seconds = 0;
  sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  sTime.StoreOperation = RTC_STOREOPERATION_RESET;
  if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
  {
    Error_Handler();
  }

  // 3. 设置初始日期
  sDate.WeekDay = RTC_WEEKDAY_MONDAY;  // 周一
  sDate.Month = RTC_MONTH_JANUARY;
  sDate.Date = 15;
  sDate.Year = 24;                     // 2024年
  if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
  {
    Error_Handler();
  }

  // 4. 使能唤醒定时器中断(如果配置了)
  HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 32767, RTC_WAKEUPCLOCK_CK_SPRE_16BITS);
}

嗯,到这里,RTC的CubeMX配置就完成了。你生成的代码里,应该包含了时钟初始化、RTC初始化、以及中断使能。下一节,我们会把这些代码移植到实际项目中,并验证它能不能在低功耗模式下正常工作。

RTC配置核心流程 RTC配置核心流程 1. 时钟树配置 使能LSE晶振 RTC时钟源选LSE 确认预分频系数 2. RTC参数设置 设置时间格式 设置初始日期时间 设置星期值 3. 中断与唤醒配置 4. 生成初始化代码 💡 生成后建议检查 RTC_OUTPUT是否关闭