You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 02:26:07