使用STM32Cube生成STM32F103 RTC时钟例程项目中,虽然有外部电池供电,使得系统断电后依然能够计时,但在实际使用中,系统掉电后,日期参数会重置,只有时间参数正常运行。这是因为STM32F103系列的RTC外设只是一个简单的计数器,并没有所谓的日历功能。
1.将日期参数保存到后备区域存储器
这种方法比较简单,但是没办法根治问题,断电时,一旦时间到达24:00,时间参数会重置从00:00重新开始计时,但是日期并不会更新。
2.将日期和时间换算为时间戳保存在计数器中
STM32F103的RTC本质上是一个32位的计数器,在断电后,由电池供电还能保持计数。所以可以将日期和时间换算为时间戳保存到计数器中,当需要读取时间时,从计数器中读取时间戳,重新换算成日期和时间即可。
相关接口函数:
- // 设置时间戳计数的基准日期
- RTC_DateTypeDef DateBase = {
- .Year = 22,
- .Month = 01,
- .Date = 01,
- .WeekDay = RTC_WEEKDAY_SATURDAY,
- };
-
- //===========================================================================
- //函数名: GetDiffDate(uint32_t StartDate, uint32_t EndDate)
- //功能 : 计算两个日期之间的天数并返回
- //参数 : RTC_DateTypeDef StartDate, RTC_DateTypeDef EndDate
- //返回值: uint32_t
- //===========================================================================
- uint32_t GetDiffDate(RTC_DateTypeDef StartDate, RTC_DateTypeDef EndDate)
- {
- uint32_t DayDiff = 0; // 两个日期的天数差
-
- while (1)
- {
- if ((StartDate.Year == EndDate.Year) && (StartDate.Month == EndDate.Month))
- {
- DayDiff += EndDate.Date - StartDate.Date;
- break;
- }
- else
- {
- DayDiff += EndDate.Date;
- EndDate.Month--;
- if (EndDate.Month == 0)
- {
- EndDate.Month = 12;
- EndDate.Year--;
- }
- EndDate.Date = GetDayOfMonth(EndDate.Year, EndDate.Month);
- }
- }
-
- return DayDiff;
- }
-
- //===========================================================================
- //函数名: CalculateDate
- //功能 : 根据起始日期和天数,计算之后的日期并返回
- //参数 : RTC_DateTypeDef FromDate, uint32_t DayDiff
- //返回值: RTC_DateTypeDef
- //===========================================================================
- RTC_DateTypeDef CalculateDate(RTC_DateTypeDef FromDate, uint32_t DayDiff)
- {
- uint16_t day = 0;
-
- while (1)
- {
- day = GetDayOfMonth(FromDate.Year, FromDate.Month);
- if (FromDate.Date + DayDiff > day)
- {
- DayDiff -= day - FromDate.Date;
- FromDate.Month++;
- FromDate.Date = 0;
-
- if (FromDate.Month == 13)
- {
- FromDate.Month = 1;
- FromDate.Year++;
- }
- }
- else
- {
- FromDate.Date += DayDiff;
- break;
- }
- }
-
- FromDate.WeekDay = DateBase.WeekDay + (DayDiff % 7);
- if (FromDate.WeekDay >= 7)
- FromDate.WeekDay -= 7;
-
- return FromDate;
- }
-
- //===========================================================================
- //函数名: GetDayOfMonth
- //功能 : 根据年月,获取当月的天数并返回
- //参数 : uint8_t year, uint8_t month
- //返回值: uint8_t
- //===========================================================================
- uint8_t GetDayOfMonth(uint8_t year, uint8_t month)
- {
- uint8_t DayOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
-
- if (month == 2)
- {
- if ((year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0)))
- return 29;
- else
- return 28;
- }
- else
- return DayOfMonth[month - 1];
- }
-
-
- //===========================================================================
- //函数名: RTC_Set_DateTimeCounter
- //功能 : 设置RTC时间(将时间转化为时间戳保存到RTC中)
- //参数 :
- //返回值:
- //===========================================================================
- void RTC_Set_DateTimeCounter(RTC_DateTypeDef *sDate, RTC_TimeTypeDef *sTime)
- {
- uint32_t timecounter = (uint32_t)sTime->Hours * 3600 + (uint32_t)sTime->Minutes * 60 + sTime->Seconds + GetDiffDate(DateBase, *sDate) * 3600 * 24;
- RTC_WriteTimeCounter(&hrtc, timecounter);
- }
-
- //===========================================================================
- //函数名: RTC_Get_DateTimeCounter
- //功能 : 获取RTC时间(获取存储在计数器中的时间戳,再将其转化为时间)
- //参数 :
- //返回值:
- //===========================================================================
- void RTC_Get_DateTimeCounter(RTC_DateTypeDef *sDate, RTC_TimeTypeDef *sTime)
- {
- uint32_t timecounter = RTC_ReadTimeCounter(&hrtc);
- uint32_t SecOfToday = timecounter % (3600 * 24);
- uint32_t DiffDay = timecounter / (3600 * 24);
-
- sTime->Hours = SecOfToday / 3600;
- sTime->Minutes = SecOfToday % 3600 / 60;
- sTime->Seconds = SecOfToday % 60;
-
- *sDate = CalculateDate(DateBase, DiffDay);
- }
改写RTC时钟初始化函数 void MX_RTC_Init(void)
- 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 */
- __HAL_RCC_BKP_CLK_ENABLE();
- __HAL_RCC_PWR_CLK_ENABLE();
- /* 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 */
- if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) != 0x0102)
- {
- /* USER CODE END Check_RTC_BKUP */
-
- /** Initialize RTC and set the Time and Date
- */
- sTime.Hours = 23;
- sTime.Minutes = 59;
- sTime.Seconds = 50;
-
- DateToUpdate.WeekDay = RTC_WEEKDAY_SATURDAY;
- DateToUpdate.Month = 01;
- DateToUpdate.Date = 01;
- DateToUpdate.Year = 22;
-
- // 将日期和时间换算为时间戳保存到计数器中
- RTC_Set_DateTimeCounter(&DateToUpdate, &sTime);
-
- HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x0102);
- /* USER CODE BEGIN RTC_Init 2 */
- }
- /* USER CODE END RTC_Init 2 */
- }
当需要获取时间时,只需要通过接口 void RTC_Get_DateTimeCounter(RTC_DateTypeDef *sDate, RTC_TimeTypeDef *sTime) 获取即可。
原文链接:https://blog.csdn.net/Yellow0102/article/details/123281814
|