/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file rtc.c * @brief This file provides code for the configuration * of the RTC instances. ****************************************************************************** * @attention * * Copyright (c) 2025 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 */ #define RTC_BKP_CONFIGURED_MARKER 0x32A5U #define RTC_BKP_MARKER_REG RTC_BKP_DR1 #define RTC_BKP_YEAR_MONTH_REG RTC_BKP_DR2 #define RTC_BKP_DATE_WEEKDAY_REG RTC_BKP_DR3 static uint8_t RTC_NormalizeYear(uint16_t year, uint8_t *normalized_year) { if (year <= 99U) { *normalized_year = (uint8_t)year; return 1U; } if ((year >= 2000U) && (year <= 2099U)) { *normalized_year = (uint8_t)(year - 2000U); return 1U; } return 0U; } static uint8_t RTC_IsLeapYearFull(uint16_t year) { return (uint8_t)(((year % 4U) == 0U) && (((year % 100U) != 0U) || ((year % 400U) == 0U))); } static uint8_t RTC_DaysInMonth(uint16_t year, uint8_t month) { static const uint8_t days_in_month[] = { 31U, 28U, 31U, 30U, 31U, 30U, 31U, 31U, 30U, 31U, 30U, 31U }; if ((month == 0U) || (month > 12U)) { return 0U; } if ((month == 2U) && RTC_IsLeapYearFull(year)) { return 29U; } return days_in_month[month - 1U]; } static uint8_t RTC_IsDateValid(uint16_t year, uint8_t month, uint8_t date) { uint8_t normalized_year = 0U; uint16_t full_year = year; uint8_t max_day = 0U; if (RTC_NormalizeYear(year, &normalized_year) == 0U) { return 0U; } if (year <= 99U) { full_year = 2000U + year; } max_day = RTC_DaysInMonth(full_year, month); return (uint8_t)((date >= 1U) && (date <= max_day)); } static void RTC_SaveDateToBackup(const RTC_DateTypeDef *date) { uint32_t year_month = (((uint32_t)date->Year) << 8U) | date->Month; uint32_t date_weekday = (((uint32_t)date->Date) << 8U) | date->WeekDay; if ((HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_YEAR_MONTH_REG) != year_month) || (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DATE_WEEKDAY_REG) != date_weekday)) { HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_YEAR_MONTH_REG, year_month); HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DATE_WEEKDAY_REG, date_weekday); } if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_MARKER_REG) != RTC_BKP_CONFIGURED_MARKER) { HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_MARKER_REG, RTC_BKP_CONFIGURED_MARKER); } } static uint8_t RTC_RestoreDateFromBackup(void) { RTC_DateTypeDef restored_date = {0}; uint32_t year_month = 0U; uint32_t date_weekday = 0U; if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_MARKER_REG) != RTC_BKP_CONFIGURED_MARKER) { return 0U; } year_month = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_YEAR_MONTH_REG); date_weekday = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DATE_WEEKDAY_REG); restored_date.Year = (uint8_t)((year_month >> 8U) & 0xFFU); restored_date.Month = (uint8_t)(year_month & 0xFFU); restored_date.Date = (uint8_t)((date_weekday >> 8U) & 0xFFU); restored_date.WeekDay = (uint8_t)(date_weekday & 0xFFU); if (RTC_IsDateValid(restored_date.Year, restored_date.Month, restored_date.Date) == 0U) { return 0U; } if (restored_date.WeekDay > RTC_WEEKDAY_SATURDAY) { restored_date.WeekDay = RTC_WEEKDAY_MONDAY; } hrtc.DateToUpdate = restored_date; return 1U; } /* 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 */ uint8_t rtc_backup_ready = 0U; /* USER CODE END RTC_Init 1 */ /** Initialize RTC Only */ hrtc.Instance = RTC; hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND; hrtc.Init.OutPut = RTC_OUTPUTSOURCE_SECOND; if (HAL_RTC_Init(&hrtc) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN Check_RTC_BKUP */ rtc_backup_ready = RTC_RestoreDateFromBackup(); /* USER CODE END Check_RTC_BKUP */ /** Initialize RTC and set the Time and Date */ if (rtc_backup_ready == 0U) { sTime.Hours = 0U; sTime.Minutes = 0U; sTime.Seconds = 0U; if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK) { Error_Handler(); } DateToUpdate.WeekDay = RTC_WEEKDAY_MONDAY; DateToUpdate.Month = RTC_MONTH_JANUARY; DateToUpdate.Date = 1U; DateToUpdate.Year = 0U; if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN) != HAL_OK) { Error_Handler(); } RTC_SaveDateToBackup(&DateToUpdate); } /* 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(); /* 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 */ uint8_t RTC_CalendarIsValid(const RTC_CalendarTypeDef *calendar) { if (calendar == NULL) { return 0U; } return (uint8_t)((calendar->hours <= 23U) && (calendar->minutes <= 59U) && (calendar->seconds <= 59U) && RTC_IsDateValid(calendar->year, calendar->month, calendar->date)); } HAL_StatusTypeDef RTC_CalendarGet(RTC_CalendarTypeDef *calendar) { RTC_TimeTypeDef time = {0}; RTC_DateTypeDef date = {0}; if (calendar == NULL) { return HAL_ERROR; } if (HAL_RTC_GetDate(&hrtc, &date, RTC_FORMAT_BIN) != HAL_OK) { return HAL_ERROR; } if (HAL_RTC_GetTime(&hrtc, &time, RTC_FORMAT_BIN) != HAL_OK) { return HAL_ERROR; } RTC_SaveDateToBackup(&date); calendar->hours = time.Hours; calendar->minutes = time.Minutes; calendar->seconds = time.Seconds; calendar->date = date.Date; calendar->month = date.Month; calendar->year = 2000U + date.Year; calendar->weekday = date.WeekDay; return HAL_OK; } HAL_StatusTypeDef RTC_CalendarSet(const RTC_CalendarTypeDef *calendar) { RTC_TimeTypeDef time = {0}; RTC_DateTypeDef date = {0}; uint8_t normalized_year = 0U; if ((calendar == NULL) || (RTC_CalendarIsValid(calendar) == 0U)) { return HAL_ERROR; } if (RTC_NormalizeYear(calendar->year, &normalized_year) == 0U) { return HAL_ERROR; } time.Hours = (uint8_t)calendar->hours; time.Minutes = (uint8_t)calendar->minutes; time.Seconds = (uint8_t)calendar->seconds; date.Year = normalized_year; date.Month = (uint8_t)calendar->month; date.Date = (uint8_t)calendar->date; date.WeekDay = RTC_WEEKDAY_MONDAY; if (HAL_RTC_SetTime(&hrtc, &time, RTC_FORMAT_BIN) != HAL_OK) { return HAL_ERROR; } if (HAL_RTC_SetDate(&hrtc, &date, RTC_FORMAT_BIN) != HAL_OK) { return HAL_ERROR; } RTC_SaveDateToBackup(&date); return HAL_OK; } /* USER CODE END 1 */