## 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配置步骤 1. 使能RCC中的LSE(或LSI)。 2. 选择RTC时钟源为LSE(推荐)并激活`Calendar`。 3. 设置初始日期时间(可在代码中覆盖)。 4. 若使用闹钟:在`Alarm A`中使能中断,设置匹配条件;若使用唤醒:在`WakeUp`中设置周期,使能中断。 5. 在NVIC中使能RTC全局中断(和闹钟/唤醒子中断,注意不同系列可能共用一个IRQ)。 ## 软件实现:初始化与读写时间 HAL库提供封装好的API,初始化结构体如下: ```c 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()`使能中断: ```c 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秒唤醒一次: ```c 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寄存器值变化,确保时间正常累计。