STM32设置RTC的代码如下:
RTC_HandleTypeDef hrtc;
/* RTC init function */
void MX_RTC_Init(void)
{
/* USER CODE BEGIN RTC_Init 0 */
/* USER CODE END RTC_Init 0 */
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
/* USER CODE BEGIN RTC_Init 1 */
/* USER CODE END RTC_Init 1 */
/** Initialize RTC Only
*/
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 127;
hrtc.Init.SynchPrediv = 255;
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
hrtc.Init.OutPutPullUp = RTC_OUTPUT_PULLUP_NONE;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN Check_RTC_BKUP */
uint32_t iSetFlag = 0x5050;
if(iSetFlag != HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR0))
{
/* USER CODE END Check_RTC_BKUP */
/** Initialize RTC and set the Time and Date
*/
sTime.Hours = 12;
sTime.Minutes = 30;
sTime.Seconds = 30;
sTime.SubSeconds = 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_NOVEMBER;
sDate.Date = 17;
sDate.Year = 21;
if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN RTC_Init 2 */
//写入标志位,RTC有外部电源供电,该标志位不会改变,上电不会再次初始化时间
HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR0,iSetFlag);
}
/* USER CODE END RTC_Init 2 */
}
现象
当吧时间设置到极限时间2099-12-31 23:59:59 关机后再次开机,设置的时间为初始值,而且每次开机都是从初始值开始运行。
原因分析
查看代码逻辑
uint32_t iSetFlag = 0x5050;
if(iSetFlag != HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR0))
{
/*
...
RTC初始化
*/
HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR0,iSetFlag);//写入标志位
}
上述代码中向寄存器RTC_BKP_DR0中写入0x5050,当使用了RTC的备用电源,单片机掉电时RTC_BKP_DR0寄存器保存,当再次上电时就不再执行初始化的代码,这就保证了RTC的正确性。
从现象上来说,RTC_BKP_DR0的值再次上电后并不是0x5050。
从官方手册中可知,在使用了Vbat供电时,TAMP 备份 x 寄存器 (TAMP_BKPxR)不会复位,导致备份寄存器复位的原因可能为两个
- 至少有一个内部或者外部入侵标志置1;
- 禁止读取保护(RDP)。
程序中并未进行禁止读取保护的操作,所以重点查找哪里产生了入侵。
两个外部入侵事件和4个内部入侵事件,代码中并没有配置外部入侵引脚,排除外部。
RTC日历溢出会产生一个tamp_itamp5的内部入侵事件源。
TAMP control register 1的复位值为0xFFFF 0000,也就是说位20上电默认是1,使能内部入侵5,所以当设置时间99 年 12 月 31 日 23:59:59产生入侵事件,导致了TAMP 备份 x 寄存器 (TAMP_BKPxR)复位,再次上电,RTC会初始化。
备份寄存器 (TAMP_BKPxR) 在 RTC 域中实现,可在 VDD 电源关闭时通过 VBAT 保持上电状态。
所以当产生了入侵事件之后,入侵事件在VBAT保持下是记录的,要想去除入侵事件,一个是写寄存器,另一个办法就是扣掉VBAT电池。
实验
设置RTC产生日历溢出事件,再次开机RTC初始化,重新设置时间,再次开机RTC仍初始化;
扣掉VBAT电池,再次开机RTC初始化,重新设置时间,再次开机RTC不再初始化,时间与上次设置一致。