/******************************************************************************* * @file rtc_if.c * @author MCD Application Team * @brief Handles the RTC ****************************************************************************** * @attention * *

© Copyright (c) 2019 STMicroelectronics. * All rights reserved.

* * This software component is licensed by ST under Ultimate Liberty license * SLA0044, the "License"; You may not use this file except in compliance with * the License. You may obtain a copy of the License at: * www.st.com/SLA0044 * ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include #include "platform.h" #include "rtc_if.h" #include "stm32_lpm.h" #include "app_system.h" #include "stm32wlxx_ll_rtc.h" /* External variables ---------------------------------------------------------*/ extern RTC_HandleTypeDef hrtc; #define RtcHandle hrtc /* Private typedef -----------------------------------------------------------*/ typedef struct { uint32_t Rtc_Time; /* Reference time */ RTC_TimeTypeDef RTC_Calndr_Time; /* Reference time in calendar format */ RTC_DateTypeDef RTC_Calndr_Date; /* Reference date in calendar format */ } RtcTimerContext_t; /* Private define ------------------------------------------------------------*/ /* MCU Wake Up Time */ #define MIN_ALARM_DELAY 3 /* in ticks */ /* subsecond number of bits */ /* Now defined in main.h via MX GUI */ //#define RTC_N_PREDIV_S 10 //#define RTC_PREDIV_S ((1<>RTC_N_PREDIV_S) #define COMMON_FACTOR 3 #define CONV_NUMER (MSEC_NUMBER>>COMMON_FACTOR) #define CONV_DENOM (1<<(RTC_N_PREDIV_S-COMMON_FACTOR)) #define DAYS_IN_LEAP_YEAR (uint32_t) 366 #define DAYS_IN_YEAR (uint32_t) 365 #define SECONDS_IN_1DAY (uint32_t) 86400 #define SECONDS_IN_1HOUR (uint32_t) 3600 #define SECONDS_IN_1MINUTE (uint32_t) 60 #define MINUTES_IN_1HOUR (uint32_t) 60 #define HOURS_IN_1DAY (uint32_t) 24 #define DAYS_IN_MONTH_CORRECTION_NORM ((uint32_t) 0x99AAA0 ) #define DAYS_IN_MONTH_CORRECTION_LEAP ((uint32_t) 0x445550 ) /* Calculates ceiling(X/N) */ #define DIVC(X,N) ( ( (X) + (N) -1 ) / (N) ) /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ /*! * \brief Indicates if the RTC is already Initalized or not */ static bool RTC_Initalized = false; /*! * \brief compensates MCU wakeup time */ static bool McuWakeUpTimeInitialized = false; /*! * \brief compensates MCU wakeup time */ static int16_t McuWakeUpTimeCal = 0; /*! * Number of days in each month on a normal year */ static const uint8_t DaysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; /*! * Number of days in each month on a leap year */ static const uint8_t DaysInMonthLeapYear[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static RTC_AlarmTypeDef RTC_AlarmStructure; /*! * Keep the value of the RTC timer when the RTC alarm is set * Set with the RTC_SetTimerContext function * Value is kept as a Reference to calculate alarm */ static RtcTimerContext_t RtcTimerContext; /* Private function prototypes -----------------------------------------------*/ /*! * @brief start wake up alarm * @note alarm in RtcTimerContext.Rtc_Time + timeoutValue * @param timeoutValue in ticks * @retval none */ static void RTC_StartWakeUpAlarm(uint32_t timeoutValue); /*! * @brief get current time from calendar in ticks * @param pointer to RTC_DateStruct * @param pointer to RTC_TimeStruct * @retval time in ticks */ static uint32_t RTC_GetCalendarValue(RTC_DateTypeDef *RTC_DateStruct, RTC_TimeTypeDef *RTC_TimeStruct); /* Exported variables ---------------------------------------------------------*/ /* Exported functions ---------------------------------------------------------*/ /** * @brief This function configures the source of the time base. * @brief don't enable systick * @param TickPriority: Tick interrupt priority. * @retval HAL status */ HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority) { /*Initialize the RTC services */ RTC_Init(); return HAL_OK; } /** * @brief Provide a tick value in millisecond measured using RTC * @note This function overwrites the __weak one from HAL * @retval tick value */ uint32_t HAL_GetTick(void) { if (RTC_Initalized == false) { return 0; /* HAL_GetTick() based on RTC cannot be used until RTC is initialised */ } else { return RTC_GetTimerValue(); } } /** * @brief This function provides delay (in ms) * @param Delay: specifies the delay time length, in milliseconds. * @retval None */ void HAL_Delay(__IO uint32_t Delay) { RTC_DelayMs(Delay); /* based on RTC */ } /** * @brief Alarm A callback. * @param RtcHandle: RTC handle * @retval None */ void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *RtcHandle) { //UTIL_TIMER_IRQ_Handler( ); } void RTC_Init(void) { RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; if (RTC_Initalized == false) { /*##-1- Configue the RTC clock soucre ######################################*/ /* Select LSE as RTC clock source */ PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC; PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { /* Initialization Error */ while (1); } MX_RTC_Init(); #if defined (LPM_STOP_MODE_DISABLE) && (LPM_STOP_MODE_DISABLE == 0) /* LOW_POWER enabled */ /* enable RTC ALARM EXTI (AIEC) line */ LL_EXTI_EnableIT_0_31(LL_EXTI_LINE_17); LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_17); #elif !defined (LPM_STOP_MODE_DISABLE) #error LPM_STOP_MODE_DISABLE not defined #endif /*Enable Direct Read of the calendar registers (not through Shadow) */ HAL_RTCEx_EnableBypassShadow(&RtcHandle); /** Configure the Alarm A */ HAL_RTC_DeactivateAlarm(&RtcHandle, RTC_ALARM_A); RTC_SetTimerContext(); RTC_Initalized = true; } } void RTC_setMcuWakeUpTime(void) { RTC_TimeTypeDef RTC_TimeStruct; RTC_DateTypeDef RTC_DateStruct; uint32_t now, hit; int16_t McuWakeUpTime; if ((McuWakeUpTimeInitialized == false) && (HAL_NVIC_GetPendingIRQ(RTC_Alarm_IRQn) == 1)) { /* warning: works ok if now is below 30 days it is ok since it's done once at first alarm wake-up*/ McuWakeUpTimeInitialized = true; now = RTC_GetCalendarValue(&RTC_DateStruct, &RTC_TimeStruct); HAL_RTC_GetAlarm(&RtcHandle, &RTC_AlarmStructure, RTC_ALARM_A, RTC_FORMAT_BIN); hit = RTC_AlarmStructure.AlarmTime.Seconds + 60 * (RTC_AlarmStructure.AlarmTime.Minutes + 60 * (RTC_AlarmStructure.AlarmTime.Hours + 24 * (RTC_AlarmStructure.AlarmDateWeekDay))); hit = (hit << RTC_N_PREDIV_S) + (RTC_PREDIV_S - RTC_AlarmStructure.AlarmTime.SubSeconds); McuWakeUpTime = (int16_t)((now - hit)); McuWakeUpTimeCal += McuWakeUpTime; APP_LOG("Cal=%d, %d\n\r", McuWakeUpTimeCal, McuWakeUpTime); } } int16_t RTC_getMcuWakeUpTime(void) { return McuWakeUpTimeCal; } uint32_t RTC_GetMinimumTimeout(void) { return (MIN_ALARM_DELAY); } uint32_t RTC_ms2Tick(uint32_t timeMicroSec) { /*return( ( timeMicroSec / RTC_ALARM_TIME_BASE ) ); */ return (uint32_t)((((uint64_t)timeMicroSec) * CONV_DENOM) / CONV_NUMER); } uint32_t RTC_Tick2ms(uint32_t tick) { /*return( ( timeMicroSec * RTC_ALARM_TIME_BASE ) ); */ return (((uint64_t)(tick) * CONV_NUMER) / CONV_DENOM); } uint32_t RTC_GetTimerElapsedTime(void) { RTC_TimeTypeDef RTC_TimeStruct; RTC_DateTypeDef RTC_DateStruct; uint32_t CalendarValue = RTC_GetCalendarValue(&RTC_DateStruct, &RTC_TimeStruct); return ((uint32_t)(CalendarValue - RtcTimerContext.Rtc_Time)); } uint32_t RTC_GetTimerValue(void) { RTC_TimeTypeDef RTC_TimeStruct; RTC_DateTypeDef RTC_DateStruct; uint32_t CalendarValue = (uint32_t) RTC_GetCalendarValue(&RTC_DateStruct, &RTC_TimeStruct); return (CalendarValue); } void RTC_DelayMs(uint32_t delay) { uint32_t delayValue = 0; uint32_t timeout = 0; delayValue = RTC_ms2Tick(delay); /* Wait delay ms */ timeout = RTC_GetTimerValue(); while (((RTC_GetTimerValue() - timeout)) < delayValue) { __NOP(); } } uint32_t RTC_SetTimerContext(void) { RtcTimerContext.Rtc_Time = RTC_GetCalendarValue(&RtcTimerContext.RTC_Calndr_Date, &RtcTimerContext.RTC_Calndr_Time); return (uint32_t) RtcTimerContext.Rtc_Time; } uint32_t RTC_GetTimerContext(void) { return (uint32_t) RtcTimerContext.Rtc_Time; } /* Private functions ---------------------------------------------------------*/ /*! * @brief start wake up alarm * @note alarm in RtcTimerContext.Rtc_Time + timeoutValue * @param timeoutValue in ticks * @retval none */ static void RTC_StartWakeUpAlarm(uint32_t timeoutValue) { uint16_t rtcAlarmSubSeconds = 0; uint16_t rtcAlarmSeconds = 0; uint16_t rtcAlarmMinutes = 0; uint16_t rtcAlarmHours = 0; uint16_t rtcAlarmDays = 0; RTC_TimeTypeDef RTC_TimeStruct = RtcTimerContext.RTC_Calndr_Time; RTC_DateTypeDef RTC_DateStruct = RtcTimerContext.RTC_Calndr_Date; //RTC_StopAlarm(); /*reverse counter */ rtcAlarmSubSeconds = RTC_PREDIV_S - RTC_TimeStruct.SubSeconds; rtcAlarmSubSeconds += (timeoutValue & RTC_PREDIV_S); /* convert timeout to seconds */ timeoutValue >>= RTC_N_PREDIV_S; /* convert timeout in seconds */ /*convert microsecs to RTC format and add to 'Now' */ rtcAlarmDays = RTC_DateStruct.Date; while (timeoutValue >= SECONDS_IN_1DAY) { timeoutValue -= SECONDS_IN_1DAY; rtcAlarmDays++; } /* calc hours */ rtcAlarmHours = RTC_TimeStruct.Hours; while (timeoutValue >= SECONDS_IN_1HOUR) { timeoutValue -= SECONDS_IN_1HOUR; rtcAlarmHours++; } /* calc minutes */ rtcAlarmMinutes = RTC_TimeStruct.Minutes; while (timeoutValue >= SECONDS_IN_1MINUTE) { timeoutValue -= SECONDS_IN_1MINUTE; rtcAlarmMinutes++; } /* calc seconds */ rtcAlarmSeconds = RTC_TimeStruct.Seconds + timeoutValue; /***** correct for modulo********/ while (rtcAlarmSubSeconds >= (RTC_PREDIV_S + 1)) { rtcAlarmSubSeconds -= (RTC_PREDIV_S + 1); rtcAlarmSeconds++; } while (rtcAlarmSeconds >= SECONDS_IN_1MINUTE) { rtcAlarmSeconds -= SECONDS_IN_1MINUTE; rtcAlarmMinutes++; } while (rtcAlarmMinutes >= MINUTES_IN_1HOUR) { rtcAlarmMinutes -= MINUTES_IN_1HOUR; rtcAlarmHours++; } while (rtcAlarmHours >= HOURS_IN_1DAY) { rtcAlarmHours -= HOURS_IN_1DAY; rtcAlarmDays++; } if (RTC_DateStruct.Year % 4 == 0) { if (rtcAlarmDays > DaysInMonthLeapYear[ RTC_DateStruct.Month - 1 ]) { rtcAlarmDays = rtcAlarmDays % DaysInMonthLeapYear[ RTC_DateStruct.Month - 1 ]; } } else { if (rtcAlarmDays > DaysInMonth[ RTC_DateStruct.Month - 1 ]) { rtcAlarmDays = rtcAlarmDays % DaysInMonth[ RTC_DateStruct.Month - 1 ]; } } /* Set RTC_AlarmStructure with calculated values*/ RTC_AlarmStructure.AlarmTime.SubSeconds = RTC_PREDIV_S - rtcAlarmSubSeconds; RTC_AlarmStructure.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK; RTC_AlarmStructure.AlarmTime.Seconds = rtcAlarmSeconds; RTC_AlarmStructure.AlarmTime.Minutes = rtcAlarmMinutes; RTC_AlarmStructure.AlarmTime.Hours = rtcAlarmHours; RTC_AlarmStructure.AlarmDateWeekDay = (uint8_t)rtcAlarmDays; RTC_AlarmStructure.AlarmTime.TimeFormat = RTC_TimeStruct.TimeFormat; RTC_AlarmStructure.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE; RTC_AlarmStructure.AlarmMask = RTC_ALARMMASK_NONE; RTC_AlarmStructure.Alarm = RTC_ALARM_A; RTC_AlarmStructure.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; RTC_AlarmStructure.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET; /* Set RTC_Alarm */ HAL_RTC_SetAlarm_IT(&RtcHandle, &RTC_AlarmStructure, RTC_FORMAT_BIN); } /*! * @brief get current time from calendar in ticks * @param pointer to RTC_DateStruct * @param pointer to RTC_TimeStruct * @retval time in ticks */ static uint32_t RTC_GetCalendarValue(RTC_DateTypeDef *RTC_DateStruct, RTC_TimeTypeDef *RTC_TimeStruct) { uint32_t calendarValue = 0; uint32_t first_read; uint32_t correction; /* Get Time and Date*/ HAL_RTC_GetTime(&RtcHandle, RTC_TimeStruct, RTC_FORMAT_BIN); /* make sure it is correct due to asynchronus nature of RTC*/ do { first_read = LL_RTC_TIME_GetSubSecond(RTC); HAL_RTC_GetDate(&RtcHandle, RTC_DateStruct, RTC_FORMAT_BIN); HAL_RTC_GetTime(&RtcHandle, RTC_TimeStruct, RTC_FORMAT_BIN); } while (first_read != LL_RTC_TIME_GetSubSecond(RTC)); /* calculte amount of elapsed days since 01/01/2000 */ calendarValue = DIVC((DAYS_IN_YEAR * 3 + DAYS_IN_LEAP_YEAR) * RTC_DateStruct->Year, 4); correction = ((RTC_DateStruct->Year % 4) == 0) ? DAYS_IN_MONTH_CORRECTION_LEAP : DAYS_IN_MONTH_CORRECTION_NORM ; calendarValue += (DIVC((RTC_DateStruct->Month - 1) * (30 + 31), 2) - (((correction >> ((RTC_DateStruct->Month - 1) * 2)) & 0x3))); calendarValue += (RTC_DateStruct->Date - 1); /* convert from days to seconds */ calendarValue *= SECONDS_IN_1DAY; calendarValue += ((uint32_t)RTC_TimeStruct->Seconds + ((uint32_t)RTC_TimeStruct->Minutes * SECONDS_IN_1MINUTE) + ((uint32_t)RTC_TimeStruct->Hours * SECONDS_IN_1HOUR)) ; calendarValue = (calendarValue << RTC_N_PREDIV_S) + (RTC_PREDIV_S - RTC_TimeStruct->SubSeconds); return (calendarValue); } /*! * \brief Get system time * \param [IN] pointer to ms * * \return uint32_t seconds */ uint32_t HW_RTC_GetCalendarTime(uint16_t *mSeconds) { RTC_TimeTypeDef RTC_TimeStruct ; RTC_DateTypeDef RTC_DateStruct; uint32_t ticks; uint64_t calendarValue = RTC_GetCalendarValue(&RTC_DateStruct, &RTC_TimeStruct); uint32_t seconds = (uint32_t)(calendarValue >> RTC_N_PREDIV_S); ticks = (uint32_t) calendarValue & RTC_PREDIV_S; *mSeconds = RTC_Tick2ms(ticks); return seconds; } void HW_RTC_BKUPWrite(uint32_t Data0, uint32_t Data1) { HAL_RTCEx_BKUPWrite(&RtcHandle, RTC_BKP_DR0, Data0); HAL_RTCEx_BKUPWrite(&RtcHandle, RTC_BKP_DR1, Data1); } void HW_RTC_BKUPRead(uint32_t *Data0, uint32_t *Data1) { *Data0 = HAL_RTCEx_BKUPRead(&RtcHandle, RTC_BKP_DR0); *Data1 = HAL_RTCEx_BKUPRead(&RtcHandle, RTC_BKP_DR1); } /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/