STM32F103R8T6 RTC断电后不更新时间问题求助
STM32F103R8T6 RTC断电后停止计时问题排查
我使用STM32F103R8T6芯片,通过RTC进行时间测量。通电时RTC计时正常,时间能正确存入备份寄存器,重启后也能成功读取备份寄存器中的最后值,但断电后时间停止更新,希望实现断电后持续计时。已确认VBAT电池连接良好,断电后电压仍保持正常3V,请问我的代码存在什么问题?
提供的代码
rtc.c
/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file rtc.c * @brief This file provides code for the configuration * of the RTC instances. ****************************************************************************** * @attention * * Copyright (c) 2024 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "rtc.h" /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ 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 DateToUpdate = {0}; /* USER CODE BEGIN RTC_Init 1 */ /* USER CODE END RTC_Init 1 */ /** Initialize RTC Only */ hrtc.Instance = RTC; hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND; hrtc.Init.OutPut = RTC_OUTPUTSOURCE_ALARM; if (HAL_RTC_Init(&hrtc) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN Check_RTC_BKUP */ /* USER CODE END Check_RTC_BKUP */ /** Initialize RTC and set the Time and Date */ sTime.Hours = 0x0; sTime.Minutes = 0x0; sTime.Seconds = 0x0; if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK) { Error_Handler(); } DateToUpdate.WeekDay = RTC_WEEKDAY_MONDAY; DateToUpdate.Month = RTC_MONTH_JANUARY; DateToUpdate.Date = 0x1; DateToUpdate.Year = 0x0; if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BCD) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN RTC_Init 2 */ /* USER CODE END RTC_Init 2 */ } void HAL_RTC_MspInit(RTC_HandleTypeDef* rtcHandle) { if(rtcHandle->Instance==RTC) { /* USER CODE BEGIN RTC_MspInit 0 */ /* USER CODE END RTC_MspInit 0 */ HAL_PWR_EnableBkUpAccess(); /* Enable BKP CLK enable for backup registers */ __HAL_RCC_BKP_CLK_ENABLE(); /* RTC clock enable */ __HAL_RCC_RTC_ENABLE(); /* USER CODE BEGIN RTC_MspInit 1 */ /* USER CODE END RTC_MspInit 1 */ } } void HAL_RTC_MspDeInit(RTC_HandleTypeDef* rtcHandle) { if(rtcHandle->Instance==RTC) { /* USER CODE BEGIN RTC_MspDeInit 0 */ /* USER CODE END RTC_MspDeInit 0 */ /* Peripheral clock disable */ __HAL_RCC_RTC_DISABLE(); /* USER CODE BEGIN RTC_MspDeInit 1 */ /* USER CODE END RTC_MspDeInit 1 */ } } /* USER CODE BEGIN 1 */ /* USER CODE END 1 */
main.c
/* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART1_UART_Init(); MX_USART3_UART_Init(); MX_I2C1_Init(); MX_TIM4_Init(); MX_ADC2_Init(); MX_TIM1_Init(); MX_TIM2_Init(); MX_RTC_Init(); MX_TIM3_Init(); /* USER CODE BEGIN 2 */ RTCTimerInit(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ GetCurrentBCDTime(); } /* USER CODE END 3 */ /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSE; RCC_OscInitStruct.LSEState = RCC_LSE_ON; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_ADC; PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV2; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } }
my rtc.c
RTC_TimeTypeDef rtcTime; RTC_DateTypeDef rtcDate; void RTCTimerInit() { rtcTime.Hours = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1); rtcTime.Minutes = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2); rtcTime.Seconds = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR3); rtcDate.Year = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR4); rtcDate.Date = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR5); rtcDate.Month = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR6); rtcDate.WeekDay = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR7); HAL_RTC_SetTime(&hrtc, &rtcTime, RTC_FORMAT_BCD); HAL_RTC_SetDate(&hrtc, &rtcDate, RTC_FORMAT_BCD); } void GetCurrentBCDTime() { static u8 oldSec = 255; HAL_RTC_GetTime(&hrtc, &rtcTime, RTC_FORMAT_BCD); HAL_RTC_GetDate(&hrtc, &rtcDate, RTC_FORMAT_BCD); if (oldSec != rtcTime.Seconds) { HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, rtcTime.Hours); HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR2, rtcTime.Minutes); HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR3, rtcTime.Seconds); HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR4, rtcDate.Year); HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR5, rtcDate.Date); HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR6, rtcDate.Month); HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR7, rtcDate.WeekDay); } oldSec = rtcTime.Seconds; }
核心问题分析
- MX_RTC_Init()每次上电强制重置RTC时间:当前函数无首次上电判断逻辑,每次上电都会执行
HAL_RTC_SetTime()和HAL_RTC_SetDate(),把时间重置为初始值,直接覆盖了VBAT供电下RTC持续计时的结果。 - RTCTimerInit()逻辑冗余且干扰计时:RTC在VBAT供电期间会自主持续计时,无需从备份寄存器读取时间再写回RTC,该操作反而可能破坏RTC的当前计数状态。
- 备份寄存器写入逻辑完全多余:
GetCurrentBCDTime()每秒将RTC时间写入备份寄存器毫无必要,RTC时间寄存器本身在VBAT供电下会保持更新,备份寄存器仅需存储初始化标记即可。
修改方案
1. 修改MX_RTC_Init(),添加首次上电判断
在MX_RTC_Init()的/* USER CODE BEGIN Check_RTC_BKUP */区域添加初始化标记检查,同时删除原函数中默认的时间设置代码:
/* USER CODE BEGIN Check_RTC_BKUP */ // 检查备份寄存器的初始化标记,判断是否为首次上电 if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0) != 0x5A5A) { // 首次上电,设置初始时间 RTC_TimeTypeDef sTime = {0}; RTC_DateTypeDef DateToUpdate = {0}; sTime.Hours = 0x0; sTime.Minutes = 0x0; sTime.Seconds = 0x0; if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK) { Error_Handler(); } DateToUpdate.WeekDay = RTC_WEEKDAY_MONDAY; DateToUpdate.Month = RTC_MONTH_JANUARY; DateToUpdate.Date = 0x1; DateToUpdate.Year = 0x0; if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BCD) != HAL_OK) { Error_Handler(); } // 写入初始化标记,避免下次上电重置时间 HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, 0x5A5A); } /* USER CODE END Check_RTC_BKUP */
2. 移除冗余的RTCTimerInit()函数
删除RTCTimerInit()函数,同时在main.c中去掉RTCTimerInit();的调用。上电后直接读取RTC时间即可,无需从备份寄存器恢复。
3. 删除多余的备份寄存器写入逻辑
修改GetCurrentBCDTime()函数,仅保留时间读取逻辑:
void GetCurrentBCDTime() { static u8 oldSec = 255; HAL_RTC_GetTime(&hrtc, &rtcTime, RTC_FORMAT_BCD); HAL_RTC_GetDate(&hrtc, &rtcDate, RTC_FORMAT_BCD); if (oldSec != rtcTime.Seconds) { // 此处可添加时间打印或其他业务逻辑,无需写入备份寄存器 oldSec = rtcTime.Seconds; } }
原理说明
STM32F1的RTC模块在VBAT引脚有供电时,会持续运行,RTC的时间寄存器、预分频器等状态会被保持。仅当首次上电(VBAT也无电)时,才需要初始化时间;后续断电再上电,只要VBAT有电,RTC已在持续计时,直接读取即可。
内容的提问来源于stack exchange,提问作者JeHeeYu
相关产品推荐
相关产品推荐

