RTC原理简介
STM32的RTC(Real-Time Clock)是一组独立的定时器,封装在备份域内,由Vbat引脚供电时可保证系统主电源掉电后继续走时。核心功能包括:
- 日历/时间:秒、分、时、星期、日期、月、年,以BCD格式存储。
- 闹钟A/B:比较匹配日历字段触发中断或事件。
- 唤醒定时器:一个可编程的倒数计数器,用于周期性唤醒低功耗模式。
- 校准与数字指纹:支持RC振荡器频率校准和有损/无损校准。
访问RTC寄存器前必须使能PWR时钟并打开备份域访问(HAL_PWR_EnableBkupAccess()),否则任何写操作都会被忽略。
硬件设计要点
- 使用外部32.768kHz晶振时,需匹配负载电容(通常6~12pF),走线尽量短,远离高频信号。
- 若成本敏感或精度要求不高,可选用内部LSI(约32kHz),但温度漂移较大。
- VBAT引脚应连接纽扣电池或大容量电容,保证掉电后RTC和备份寄存器继续工作。
STM32CubeMX配置步骤
- 使能RCC中的LSE(或LSI)。
- 选择RTC时钟源为LSE(推荐)并激活
Calendar。 - 设置初始日期时间(可在代码中覆盖)。
- 若使用闹钟:在
Alarm A中使能中断,设置匹配条件;若使用唤醒:在WakeUp中设置周期,使能中断。 - 在NVIC中使能RTC全局中断(和闹钟/唤醒子中断,注意不同系列可能共用一个IRQ)。
软件实现:初始化与读写时间
HAL库提供封装好的API,初始化结构体如下:
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
void MX_RTC_Init(void)
{
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 127; // 异步分频
hrtc.Init.SynchPrediv = 255; // 同步分频,配合LSE得到1Hz
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
if (HAL_RTC_Init(&hrtc) != HAL_OK) Error_Handler();
// 设置时间:2025年3月18日 10:30:00 星期三
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();
sDate.WeekDay = RTC_WEEKDAY_WEDNESDAY;
sDate.Month = RTC_MONTH_MARCH;
sDate.Date = 18;
sDate.Year = 25; // 25代表2025年
if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK) Error_Handler();
}
读取时间时注意先读日期再读时间,避免跨越进位时数据不一致。HAL库在读取时会自动处理影子寄存器。
闹钟中断示例
设置闹钟A在每天10:30:00触发,使用HAL_RTC_SetAlarm_IT()使能中断:
void RTC_Alarm_Config(void)
{
RTC_AlarmTypeDef sAlarm = {0};
sAlarm.AlarmTime.Hours = 10;
sAlarm.AlarmTime.Minutes = 30;
sAlarm.AlarmTime.Seconds = 0;
sAlarm.AlarmTime.SubSeconds = 0;
sAlarm.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY; // 不在乎日期,只匹配时分秒
sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
sAlarm.Alarm = RTC_ALARM_A;
if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN) != HAL_OK) Error_Handler();
}
// 在void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)中处理
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
// 关闭闹钟标志(HAL库中断处理中已自动清除),此处放用户逻辑
// 例如点亮LED或打印日志
}
如果希望闹钟只触发一次,可在回调中调用HAL_RTC_DeactivateAlarm(&hrtc, RTC_ALARM_A)。
低功耗唤醒示例
利用RTC唤醒定时器,配合__WFI进入STOP模式。唤醒定时器时钟为LSE的2kHz(默认分频),可通过RTC_WakeUpClock_CK_SPRE_16bits等选项调整。以下示例配置为每10秒唤醒一次:
void RTC_WakeUp_Config(void)
{
// 使能唤醒定时器,周期 = 20000 / 2000 = 10s
// 使用1Hz时钟(RTC_WAKEUPCLOCK_CK_SPRE_16BITS),直接写周期秒数
if (HAL_RTC_SetWakeUpTimer_IT(&hrtc, 10, RTC_WAKEUPCLOCK_CK_SPRE_16BITS) != HAL_OK)
Error_Handler();
}
void Enter_StopMode(void)
{
HAL_SuspendTick(); // 暂停SysTick,避免中断唤醒
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
// 唤醒后恢复
HAL_ResumeTick();
SystemClock_Config(); // 重新配置系统时钟(HSE/PLL可能被关闭)
}
void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
// 唤醒后的处理,如采集传感器、上报数据等
}
注意:使用HAL库时,HAL_PWR_EnterSTOPMode内部会执行WFI,用户需要在整个主循环中调度低功耗逻辑,并确保唤醒后重新配置外设时钟。
注意事项
- 备份域复位会清空RTC配置和备份寄存器,调试时如需复位不影响RTC,可设置
DBGMCU_APB1_FZ中的DBG_RTC_Stop调试选项。 - 所有写入RTC的时间/日期数据在读取时返回BCD格式(
RTC_FORMAT_BCD),建议统一使用RTC_FORMAT_BIN并手动转换。 - 闹钟匹配掩码可精确到秒甚至亚秒,但必须注意掩码与闹钟时间字段的配合,例如
RTC_ALARMMASK_DATEWEEKDAY表示忽略日期和星期,只匹配时分秒。 - LSI温漂较大,若产品需长期计时,优先使用LSE并定期校准。HAL库提供
HAL_RTCEx_SetSmoothCalib()进行数字校准。 - 进入STOP模式前,务必关闭不需要的外设中断,尤其要注意待机模式(Standby)会清空RAM,而RTC仍可照常工作。
- 唤醒定时器在待机模式下也能唤醒,但待机模式不执行中断回调,只能通过复位唤醒,需根据需求选择停止模式或待机模式。
综上,STM32 RTC是低功耗和实时任务不可或缺的模块。通过CubeMX与HAL库可以快速集成,但底层原理和备份域特性是排查问题的关键。建议在实际项目中先验证晶振起振情况,再利用调试器观察RTC寄存器值变化,确保时间正常累计。