STM32的HAL库RTC使用CubeMX生成工程不丢日期

1、HAL库的RTC单纯的跑时间还是挺简单的,但是日期确实保存在RTC句柄下的一个结构体变量中,当掉电后日期便丢失了,重新上电初始化的话会在RTC初始化中强制赋值为0年1月1日,这就不爽了,于是通过查询资料终于解决了

2、先说硬件32内部RTC时钟可以选择LSI,但是LSI不属于备份域的范围,当掉电后,电池连接到VBAT引脚不能保证LSI工作,这样的话时钟就停止运行了,重新上电后还是掉电的时间,需要重新设置时间了,而且LSI的时钟频率30到60KHz,对应时钟来说也太不准了,而且对F103C8T6来说也不支持校准。如果不用于时间,而是短暂不精确的延时还是可以考虑的。因此还是要选LSE,这样的话就需要外部32.768晶振了,然后经典电路跨接两个电容中间接地。电容大小根据手册晶振阻抗30Ω,选择15pF就可以了。

3、说完硬件该软件了,先是CubeMX配置

 

 注意一下时钟选择LSE,数据格式跟你情况,我这里直接选择二进制了,也可以选择BCD格式,HAL自动转换也挺方便。配置好直接生成工程就行了。

4、因为main函数默认选择调用初始化函数,想自己写的话可以改一下CubeMx的配置

 

把这个√选上生成的程序就不会调用RTC初始化了 函数了,因为一旦调用RTC初始化函数就会重新配置RTC的备份域,设置时间日期,这对应掉电后重新上电会导致时间恢复为配置值,我看过一下这个博客采用了注释RTC初始化函数的方式,博主也说明了,没次重新生成工程时都需要重新注释掉初始化程序,我觉得很好解决啊

解决CUBEMX生成stm32f103xx RTC时钟掉电日期不保存的问题_C的世界的博客-CSDN博客_stm32rtc掉电不保存看

/* USER CODE BEGIN 0 */
extern RTC_TimeTypeDef Time ;
extern RTC_DateTypeDef Date;
/* USER CODE END 0 */

我先定义了两个变量用于保存时间和日期,其实可以不用定义的,因为在hrtc的句柄里都有这两个结构体。首先判断是否初始化还是利用电池供电时备份域的掉电保存功能,在初始化后写入一个非复位值这里我写成0x55,然后在初始化前读取备份域判断是否已经写入过0x55,表示已经初始化过,这个判断一定要放在 /* USER CODE BEGIN RTC_Init 1 */这个注释之后,不然CubeMx重新生成程序的话会覆盖到自己的辛苦成果。

 /* USER CODE BEGIN RTC_Init 1 */
	//第一次初始化写入0X55,下次上电通过读取该值判断是否是重新已经初始化时间否则时间进行默认设置
    hrtc.Instance = RTC;
    hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
    hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
	if(HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR1)!=0x55)
	{
  /* USER CODE END RTC_Init 1 */

当然CubeMX必须勾选这里,有点啰嗦了

 如果没有初始化则调用CubeMx生成的初始化函数,并在初始化后然后在

/* USER CODE BEGIN RTC_Init 2 */

这里写入BKP区域0x55,记录已初始化。

  /* USER CODE BEGIN RTC_Init 2 */
	//初始化结束后写入0X55 下次判断不再初始化
	HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR1,0X55);
	Date_write_BKP(&hrtc,&DateToUpdate);//将日期保存在BKP区域防止掉电后丢失
	}
	else
	{
	/*因为RTC是根据按照一天24*60*365秒进行计算时间,也就是24小时制
		hrtc.DateToUpdate的用于保存日期且初始化为0年1月1日
		当调用HAL_RTC_GetTime根据秒数,判断是否超过一天,更新日期保存并在hrtc.DateToUpdate中,
		然后计数值会减去一天的秒数,这样就不能通过读计数值算出来过了多少天
		当掉电时变量也就丢失了,也就是日期就丢失了只要定时将日期保存在后备区域,
		上电后先读取后备区域重新初始化hrtc.DateToUpdate变量,
		当调用HAL_RTC_GetTime就可以根据hrtc.DateToUpdate变量更新日期,这样就不会丢失日期了
		*/
		//已经初始化过因为不调用初始化函数但是变量该赋值的还是要赋值
		hrtc.Instance = RTC;
		hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
		hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
		
		Date_read_BKP(&hrtc);//读取BKP区域的掉电前的日期,然后根据计数值计算掉电期间过了几天
		//32位每秒计数1的话可以计数136年,不用担心溢出
	}
	HAL_RTC_MspInit(&hrtc);//因为不调用了RTC初始化避免重置日期 但是BKP的时钟中断等配置还是要有的
	HAL_RTCEx_SetSecond_IT(&hrtc);//开启1秒中断//这个函数CubeMx不会自动生成需要手动调用
	HAL_RTC_GetTime(&hrtc,&Time,RTC_FORMAT_BIN);
	HAL_RTC_GetDate(&hrtc,&Date,RTC_FORMAT_BIN);

  /* USER CODE END RTC_Init 2 */

由于生成的初始化代码在if判断中,如果已经初始化过,就不会调用生成的代码,避免时间被重置为初始值,但同事也产生了一个问题,就是RTC句柄和备份域或者中断什么的都不会初始化,这就要自己来进行赋值了。RTC的时间是保存在RTC句柄hrtc中的DateToUpdate中 包括星期 月 日 年,重新上电后要从备份域中读取出来掉电前的日期恢复到DateToUpdate中

void Date_read_BKP(RTC_HandleTypeDef *  hrtc)
{//将日期从备份域还原到hrtc->DateToUpdate用于HAL_RTC_GetDate更新日期
Date.WeekDay=HAL_RTCEx_BKUPRead(hrtc,RTC_BKP_DR2);
Date.Year=HAL_RTCEx_BKUPRead(hrtc,RTC_BKP_DR3);
Date.Month=HAL_RTCEx_BKUPRead(hrtc,RTC_BKP_DR4);
Date.Date=HAL_RTCEx_BKUPRead(hrtc,RTC_BKP_DR5);
HAL_RTC_SetDate(hrtc, &Date, RTC_FORMAT_BIN);
//设置到hrtc.DateToUpdate中 因为HAL_RTC_GetTime只更新DateToUpdate 其实我是多定义了一个Date可以直接使用hrtc的DateToUpdate
}

这样再获取时间时,日期就能正常连接上了

	HAL_RTC_GetTime(&hrtc,&Time,RTC_FORMAT_BIN);
	HAL_RTC_GetDate(&hrtc,&Date,RTC_FORMAT_BIN);

5、但是正常运行时要将最新的日期保存在备份域中,我这里时开启了秒中断,在中断里判断是否有日期更新,如果更新了就保存最新的到备份域

void HAL_RTCEx_RTCEventCallback  ( RTC_HandleTypeDef *  hrtc ) 
{//秒中断更新时间
	static uint8_t Time_Date=0xFF;
	HAL_RTC_GetTime(hrtc,&Time,RTC_FORMAT_BIN);
	HAL_RTC_GetDate(hrtc,&Date,RTC_FORMAT_BIN);
	//如果日期有变化就保存当前日期到备份域BKP为下次掉电上电后计算日期提供初始化数据
	if(Time_Date!=Date.Date)
	{
	Time_Date=Date.Date;
	Date_write_BKP(hrtc,&Date);
	}
}
/* USER CODE END 1 */
void Date_write_BKP(RTC_HandleTypeDef *  hrtc,RTC_DateTypeDef * Date)
{//将日期保存在备份域
HAL_RTCEx_BKUPWrite(hrtc,RTC_BKP_DR2,Date->WeekDay);
HAL_RTCEx_BKUPWrite(hrtc,RTC_BKP_DR3,Date->Year);
HAL_RTCEx_BKUPWrite(hrtc,RTC_BKP_DR4,Date->Month);
HAL_RTCEx_BKUPWrite(hrtc,RTC_BKP_DR5,Date->Date);
}

我使用的是备份域的2到5,1用于记录是否已经初始化。这样就可以保证掉电后时间正常走,上电后日期也不丢失了。

6、最后将rtc.h代码整体附上

/**
  ******************************************************************************
  * @file    rtc.c
  * @brief   This file provides code for the configuration
  *          of the RTC instances.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "rtc.h"

/* USER CODE BEGIN 0 */
extern RTC_TimeTypeDef Time ;
extern RTC_DateTypeDef Date;
/* 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 */
	//第一次初始化写入0X55,下次上电通过读取该值判断是否是重新已经初始化时间否则时间进行默认设置
    hrtc.Instance = RTC;
    hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
    hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
	if(HAL_RTCEx_BKUPRead(&hrtc,RTC_BKP_DR1)!=0x55)
	{
  /* USER CODE END RTC_Init 1 */
  /** Initialize RTC Only
  */
  hrtc.Instance = RTC;
  hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
  hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
  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 = 23;
  sTime.Minutes = 59;
  sTime.Seconds = 55;

  if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
  {
    Error_Handler();
  }
  DateToUpdate.WeekDay = RTC_WEEKDAY_SUNDAY;
  DateToUpdate.Month = RTC_MONTH_APRIL;
  DateToUpdate.Date = 3;
  DateToUpdate.Year = 22;
  if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN RTC_Init 2 */
	//初始化结束后写入0X55 下次判断不再初始化
	HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR1,0X55);
	Date_write_BKP(&hrtc,&DateToUpdate);//将日期保存在BKP区域防止掉电后丢失
	}
	else
	{
	/*因为RTC是根据按照一天24*60*365秒进行计算时间,也就是24小时制
		hrtc.DateToUpdate的用于保存日期且初始化为0年1月1日
		当调用HAL_RTC_GetTime根据秒数,判断是否超过一天,更新日期保存并在hrtc.DateToUpdate中,
		然后计数值会减去一天的秒数,这样就不能通过读计数值算出来过了多少天
		当掉电时变量也就丢失了,也就是日期就丢失了只要定时将日期保存在后备区域,
		上电后先读取后备区域重新初始化hrtc.DateToUpdate变量,
		当调用HAL_RTC_GetTime就可以根据hrtc.DateToUpdate变量更新日期,这样就不会丢失日期了
		*/
		//已经初始化过因为不调用初始化函数但是变量该赋值的还是要赋值
		hrtc.Instance = RTC;
		hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
		hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
		
		Date_read_BKP(&hrtc);//读取BKP区域的掉电前的日期,然后根据计数值计算掉电期间过了几天
		//32位每秒计数1的话可以计数136年,不用担心溢出
	}
	HAL_RTC_MspInit(&hrtc);//因为不调用了RTC初始化避免重置日期 但是BKP的时钟中断等配置还是要有的
	HAL_RTCEx_SetSecond_IT(&hrtc);//开启1秒中断//这个函数CubeMx不会自动生成需要手动调用
	HAL_RTC_GetTime(&hrtc,&Time,RTC_FORMAT_BIN);
	HAL_RTC_GetDate(&hrtc,&Date,RTC_FORMAT_BIN);

  /* 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();

    /* RTC interrupt Init */
    HAL_NVIC_SetPriority(RTC_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(RTC_IRQn);
  /* 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();

    /* RTC interrupt Deinit */
    HAL_NVIC_DisableIRQ(RTC_IRQn);
  /* USER CODE BEGIN RTC_MspDeInit 1 */

  /* USER CODE END RTC_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */
void Date_write_BKP(RTC_HandleTypeDef *  hrtc,RTC_DateTypeDef * Date)
{//将日期保存在备份域
HAL_RTCEx_BKUPWrite(hrtc,RTC_BKP_DR2,Date->WeekDay);
HAL_RTCEx_BKUPWrite(hrtc,RTC_BKP_DR3,Date->Year);
HAL_RTCEx_BKUPWrite(hrtc,RTC_BKP_DR4,Date->Month);
HAL_RTCEx_BKUPWrite(hrtc,RTC_BKP_DR5,Date->Date);
}
void Date_read_BKP(RTC_HandleTypeDef *  hrtc)
{//将日期从备份域还原到hrtc->DateToUpdate用于HAL_RTC_GetDate更新日期
Date.WeekDay=HAL_RTCEx_BKUPRead(hrtc,RTC_BKP_DR2);
Date.Year=HAL_RTCEx_BKUPRead(hrtc,RTC_BKP_DR3);
Date.Month=HAL_RTCEx_BKUPRead(hrtc,RTC_BKP_DR4);
Date.Date=HAL_RTCEx_BKUPRead(hrtc,RTC_BKP_DR5);
HAL_RTC_SetDate(hrtc, &Date, RTC_FORMAT_BIN);
//设置到hrtc.DateToUpdate中 因为HAL_RTC_GetTime只更新DateToUpdate 其实我是多定义了一个Date可以直接使用hrtc的DateToUpdate
}
void HAL_RTCEx_RTCEventCallback  ( RTC_HandleTypeDef *  hrtc ) 
{//秒中断更新时间
	static uint8_t Time_Date=0xFF;
	HAL_RTC_GetTime(hrtc,&Time,RTC_FORMAT_BIN);
	HAL_RTC_GetDate(hrtc,&Date,RTC_FORMAT_BIN);
	//如果日期有变化就保存当前日期到备份域BKP为下次掉电上电后计算日期提供初始化数据
	if(Time_Date!=Date.Date)
	{
	Time_Date=Date.Date;
	Date_write_BKP(hrtc,&Date);
	}
}
/* USER CODE END 1 */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

`HAL_RTC_GetTime()` 函数是 STM32Cube HAL (Hardware Abstraction Layer) 提供的一个用于获取RTC(Real-Time Clock)模块当前时间的函数。`hrtc` 参数是一个 `RTC_HandleTypeDef` 类型的指针,它代表了连接到STM32微控制器的特定RTC硬件实例。 在使用这个函数之前,你需要首先对 `RTC_HandleTypeDef` 进行初始化和配置。以下是基本步骤: 1. **创建并初始化结构体**: 首先,在头文件中包含相关的头文件,如 `<stm32f1xx_hal_rtc.h>`。然后,定义一个 `RTC_HandleTypeDef` 结构体变量,并分配内存: ```c RTC_HandleTypeDef hrtc; MX_RTC_Init(&hrtc); // 假设有一个全局函数MX_RTC_Init() 负责初始化 ``` 2. **配置RTC模块**: - 完成系统时钟的配置,确保RTC时钟源可用(例如,通过 PLL或其他时钟源驱动RTC)。 - 根据需要设置RTC的工作模式、闹钟、日历等功能。这通常包括配置定时器、事件寄存器等。 ```c RTC_TimeTypeDef rtcTime; // 初始化一个时间类型结构体,用于存储获取的时间 // 设置日期和时间(如果需要) RTC_Set时间和日期(&hrtc, &rtcTime); ``` 3. **启用中断或者定期刷新时间**: 如果你想从RTC获取实时时间,可能需要配置中断服务程序来更新时间信息,或者周期性地调用 `HAL_RTCEx_DeInit()` 和 `HAL_RTCEx.setTime()` 来同步时间。 4. **调用函数获取时间**: 确保RTC处于运行状态并且配置完成后,你可以安全地调用 `HAL_RTC_GetTime()` 获取当前时间: ```c HAL_RTC_GetTime(&hrtc, &rtcTime, FORMAT_BIN); // 格式可以是二进制(BIN)、十进制(DW)等,具体看你的应用需求 ``` 记得在每次操作前检查 `hrtc.Instance->State` 是否为 `HAL_RTC_STATE_READY` 或者已经初始化完成,以保证操作有效。如果你遇到具体的错误,可以查阅官方文档或查看错误码。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_43175613

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值