/** ****************************************************************************** * File Name : RTC.c * Description : This file provides code for the configuration * of the RTC instances. ****************************************************************************** * @attention * *

© Copyright (c) 2021 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 "rtc.h" #include #include "board_common.h" // MCU Wake Up Time #define MIN_ALARM_DELAY 3 // in ticks // sub-second number of bits #define N_PREDIV_S 10 // Synchronous prediv #define PREDIV_S ( ( 1 << N_PREDIV_S ) - 1 ) // Asynchronous prediv #define PREDIV_A ( 1 << ( 15 - N_PREDIV_S ) ) - 1 // Sub-second mask definition #define ALARM_SUBSECOND_MASK ( N_PREDIV_S << RTC_ALRMASSR_MASKSS_Pos ) // RTC Time base in us #define USEC_NUMBER 1000000 #define MSEC_NUMBER ( USEC_NUMBER / 1000 ) #define COMMON_FACTOR 3 #define CONV_NUMER ( MSEC_NUMBER >> COMMON_FACTOR ) #define CONV_DENOM ( 1 << ( N_PREDIV_S - COMMON_FACTOR ) ) /*! * \brief Days, Hours, Minutes and seconds */ #define DAYS_IN_LEAP_YEAR ( ( uint32_t ) 366U ) #define DAYS_IN_YEAR ( ( uint32_t ) 365U ) #define SECONDS_IN_1DAY ( ( uint32_t )86400U ) #define SECONDS_IN_1HOUR ( ( uint32_t ) 3600U ) #define SECONDS_IN_1MINUTE ( ( uint32_t ) 60U ) #define MINUTES_IN_1HOUR ( ( uint32_t ) 60U ) #define HOURS_IN_1DAY ( ( uint32_t ) 24U ) /*! * \brief Correction factors */ #define DAYS_IN_MONTH_CORRECTION_NORM ( ( uint32_t )0x99AAA0 ) #define DAYS_IN_MONTH_CORRECTION_LEAP ( ( uint32_t )0x445550 ) /*! * \brief Calculates ceiling( X / N ) */ #define DIVC( X, N ) ( ( ( X ) + ( N ) -1 ) / ( N ) ) /*! * RTC timer context */ typedef struct { uint32_t Time; // Reference time RTC_TimeTypeDef CalendarTime; // Reference time in calendar format RTC_DateTypeDef CalendarDate; // Reference date in calendar format }RtcTimerContext_t; /*! * \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 }; /*! * Keep the value of the RTC timer when the RTC alarm is set * Set with the \ref RtcSetTimerContext function * Value is kept as a Reference to calculate alarm */ static RtcTimerContext_t RtcTimerContext; static RTC_AlarmTypeDef RtcAlarm; RTC_HandleTypeDef RtcHandle; static bool RtcInitialized = false; static uint64_t RtcGetCalendarValue( RTC_DateTypeDef* date, RTC_TimeTypeDef* time ); /* RTC init function */ void Board_RTCInit(void) { RTC_TimeTypeDef sTime = {0}; RTC_DateTypeDef DateToUpdate = {0}; if( RtcInitialized == false ) { /** Initialize RTC Only */ RtcHandle.Instance = RTC; RtcHandle.Lock = HAL_UNLOCKED; RtcHandle.Init.AsynchPrediv = RTC_AUTO_1_SECOND; RtcHandle.Init.OutPut = RTC_OUTPUTSOURCE_ALARM; if (HAL_RTC_Init(&RtcHandle) != HAL_OK) { Error_Handler(); } /** Initialize RTC and set the Time and Date */ sTime.Hours = 0x0; sTime.Minutes = 0x0; sTime.Seconds = 0x0; if (HAL_RTC_SetTime(&RtcHandle, &sTime, RTC_FORMAT_BIN) != HAL_OK) { Error_Handler(); } DateToUpdate.WeekDay = RTC_WEEKDAY_MONDAY; DateToUpdate.Month = RTC_MONTH_JANUARY; DateToUpdate.Date = 0x1; DateToUpdate.Year = 0x0; if (HAL_RTC_SetDate(&RtcHandle, &DateToUpdate, RTC_FORMAT_BIN) != HAL_OK) { Error_Handler(); } RtcSetTimerContext( ); //HAL_RTCEx_SetSecond_IT(&RtcHandle); RtcInitialized = true; } } 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, 1, 0); HAL_NVIC_EnableIRQ(RTC_IRQn); //HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0x0, 0); //HAL_NVIC_EnableIRQ(RTC_Alarm_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 END RTC_MspDeInit 1 */ } } /*! * \brief RTC IRQ Handler of the RTC Alarm */ void HW_RTC_IrqHandler( void ) { RTC_HandleTypeDef* hrtc = &RtcHandle; // Enable low power at irq // LPM_SetStopMode(LPM_RTC_Id , LPM_Enable ); // Clear the EXTI's line Flag for RTC Alarm __HAL_RTC_ALARM_EXTI_CLEAR_FLAG( ); // Gets the AlarmA interrupt source enable status if( __HAL_RTC_ALARM_GET_IT_SOURCE( hrtc, RTC_IT_ALRA ) != RESET ) { // Gets the pending status of the AlarmA interrupt if( __HAL_RTC_ALARM_GET_FLAG( hrtc, RTC_FLAG_ALRAF ) != RESET ) { // Clear the AlarmA interrupt pending bit __HAL_RTC_ALARM_CLEAR_FLAG( hrtc, RTC_FLAG_ALRAF ); // AlarmA callback //HAL_RTC_AlarmAEventCallback( hrtc ); TimerIrqHandler( ); } } } /*! * \brief Sets the RTC timer reference, sets also the RTC_DateStruct and RTC_TimeStruct * * \param none * \retval timerValue In ticks */ uint32_t RtcSetTimerContext( void ) { RtcTimerContext.Time = ( uint32_t )RtcGetCalendarValue( &RtcTimerContext.CalendarDate, &RtcTimerContext.CalendarTime ); return ( uint32_t )RtcTimerContext.Time; } /*! * \brief Gets the RTC timer reference * * \param none * \retval timerValue In ticks */ uint32_t RtcGetTimerContext( void ) { return RtcTimerContext.Time; } /*! * \brief returns the wake up time in ticks * * \retval wake up time in ticks */ uint32_t RtcGetMinimumTimeout( void ) { return( MIN_ALARM_DELAY ); } uint32_t RtcGetTimerValue( void ) { RTC_TimeTypeDef time; RTC_DateTypeDef date; uint32_t calendarValue = ( uint32_t )RtcGetCalendarValue( &date, &time ); return( calendarValue ); } /*! * \brief converts time in ms to time in ticks * * \param[IN] milliseconds Time in milliseconds * \retval returns time in timer ticks */ uint32_t RtcMs2Tick( uint32_t milliseconds ) { return ( uint32_t )( ( ( ( uint64_t )milliseconds ) * CONV_DENOM ) / CONV_NUMER ); //return milliseconds; } /*! * \brief converts time in ticks to time in ms * * \param[IN] time in timer ticks * \retval returns time in milliseconds */ uint32_t RtcTick2Ms( uint32_t tick ) { uint32_t seconds = tick >> N_PREDIV_S; tick = tick & PREDIV_S; return ( ( seconds * 1000 ) + ( ( tick * 1000 ) >> N_PREDIV_S ) ); } /*! * \brief a delay of delay ms by polling RTC * * \param[IN] delay in ms */ void RtcDelayMs( uint32_t delay ) { uint64_t delayTicks = 0; uint64_t refTicks = RtcGetTimerValue( ); delayTicks = RtcMs2Tick( delay ); // Wait delay ms while( ( ( RtcGetTimerValue( ) - refTicks ) ) < delayTicks ) { __NOP( ); } } /*! * \brief Sets the alarm * * \note The alarm is set at now (read in this function) + timeout * * \param timeout Duration of the Timer ticks */ void RtcSetAlarm( uint32_t timeout ) { #if 0 // We don't go in Low Power mode for timeout below MIN_ALARM_DELAY if( ( int64_t )( MIN_ALARM_DELAY + McuWakeUpTimeCal ) < ( int64_t )( timeout - RtcGetTimerElapsedTime( ) ) ) { LPM_SetStopMode(LPM_RTC_Id , LPM_Enable ); } else { LPM_SetStopMode(LPM_RTC_Id , LPM_Disable ); } // In case stop mode is required if( LPM_GetMode() == LPM_StopMode ) { timeout = timeout - McuWakeUpTimeCal; } #endif //timeout = timeout - McuWakeUpTimeCal; //PRINTF("---timeout:%d\r\n", timeout); RtcStartAlarm( timeout ); } void RtcStopAlarm( void ) { // Disable the Alarm A interrupt HAL_RTC_DeactivateAlarm( &RtcHandle, RTC_ALARM_A ); // Clear RTC Alarm Flag __HAL_RTC_ALARM_CLEAR_FLAG( &RtcHandle, RTC_FLAG_ALRAF ); // Clear the EXTI's line Flag for RTC Alarm __HAL_RTC_ALARM_EXTI_CLEAR_FLAG( ); } #if 1 void RtcStartAlarm( uint32_t timeoutValue ) { #if 1 //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.CalendarTime; RTC_DateTypeDef RTC_DateStruct = RtcTimerContext.CalendarDate; RtcStopAlarm( ); //DBG_GPIO_SET(GPIOB, GPIO_PIN_13); //PRINTF("timeoutValue:%d\r\n", timeoutValue); /*reverse counter */ //rtcAlarmSubSeconds = PREDIV_S - RTC_TimeStruct.SubSeconds; //rtcAlarmSubSeconds += ( timeoutValue & PREDIV_S); //PRINTF("rtcAlarmSeconds:%d, PREDIV_S:%d\r\n", rtcAlarmSeconds, PREDIV_S); /* convert timeout to seconds */ timeoutValue >>= N_PREDIV_S; /* convert timeout in seconds */ //PRINTF("timeoutValue:%d\r\n", timeoutValue); if (timeoutValue != 0) timeoutValue -= 1; /*convert microsecs to RTC format and add to 'Now' */ rtcAlarmDays = RTC_DateStruct.Date; while (timeoutValue >= SECONDS_IN_1DAY) { timeoutValue -= SECONDS_IN_1DAY; rtcAlarmDays++; } //PRINTF("timeoutValue:%d\r\n", timeoutValue); /* calc hours */ rtcAlarmHours = RTC_TimeStruct.Hours; while (timeoutValue >= SECONDS_IN_1HOUR) { timeoutValue -= SECONDS_IN_1HOUR; rtcAlarmHours++; } //PRINTF("timeoutValue:%d\r\n", timeoutValue); /* calc minutes */ rtcAlarmMinutes = RTC_TimeStruct.Minutes; while (timeoutValue >= SECONDS_IN_1MINUTE) { timeoutValue -= SECONDS_IN_1MINUTE; rtcAlarmMinutes++; } //PRINTF("timeoutValue:%d\r\n", timeoutValue); /* calc seconds */ rtcAlarmSeconds = RTC_TimeStruct.Seconds + timeoutValue; //PRINTF("rtcAlarmSeconds:%d, RTC_TimeStruct.Seconds:%d\r\n", rtcAlarmSeconds, RTC_TimeStruct.Seconds); #if 0 /***** correct for modulo********/ while (rtcAlarmSubSeconds >= (PREDIV_S+1)) { rtcAlarmSubSeconds -= (PREDIV_S+1); rtcAlarmSeconds++; } #endif 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 ]; } } #endif #if 1 /* Set RTC_AlarmStructure with calculated values*/ RtcAlarm.AlarmTime.Seconds = rtcAlarmSeconds; RtcAlarm.AlarmTime.Minutes = rtcAlarmMinutes; RtcAlarm.AlarmTime.Hours = rtcAlarmHours; RtcAlarm.Alarm = RTC_ALARM_A; //PRINTF("--wqw--rtcAlarmSeconds:%d, rtcAlarmMinutes:%d\r\n", rtcAlarmSeconds, rtcAlarmMinutes); #else RtcAlarm.Alarm = RTC_ALARM_A; RtcAlarm.AlarmTime.Seconds += 1; if (RtcAlarm.AlarmTime.Seconds==60) { RtcAlarm.AlarmTime.Minutes += 1; RtcAlarm.AlarmTime.Seconds = 0; PRINTF("Minutes:%d\r\n", RtcAlarm.AlarmTime.Minutes); } #endif /* Set RTC_Alarm */ HAL_RTC_SetAlarm_IT( &RtcHandle, &RtcAlarm, RTC_FORMAT_BIN); #if 0 /* Debug Printf*/ DBG( HW_RTC_GetCalendarValue( &RTC_DateStruct, &RTC_TimeStruct ); ); DBG_PRINTF("it's %d:%d:%d:%d ", RTC_TimeStruct.Hours, RTC_TimeStruct.Minutes, RTC_TimeStruct.Seconds, ((PREDIV_S - RTC_TimeStruct.SubSeconds)*1000)>>N_PREDIV_S); DBG_PRINTF("WU@ %d:%d:%d:%d\n\r", rtcAlarmHours, rtcAlarmMinutes, rtcAlarmSeconds, (rtcAlarmSubSeconds*1000)>>N_PREDIV_S ); DBG_GPIO_RST(GPIOB, GPIO_PIN_13); #endif } #else void RtcStartAlarm(uint32_t AlarmCounter) { RTC_AlarmTypeDef sAlarm; uint32_t counter_alarm = 0; uint32_t counter_time = 0; RTC_TimeTypeDef stime = {0}; RtcStopAlarm( ); // assert_param(AlarmCounter<= 86400); HAL_RTC_GetTime(&RtcHandle, &stime, RTC_FORMAT_BCD); //sprintf((char*)ShowTime1," GetTime %02d:%02d:%02d",stime.Hours, stime.Minutes, stime.Seconds); /* Convert time in seconds */ counter_time = (uint32_t)(((uint32_t)stime.Hours * 3600) + \ ((uint32_t)stime.Minutes * 60) + \ ((uint32_t)stime.Seconds)); counter_alarm = counter_time+AlarmCounter; sAlarm.Alarm = RTC_ALARM_A; sAlarm.AlarmTime.Hours = (uint32_t)((counter_alarm / 3600) % 24); sAlarm.AlarmTime.Minutes = (uint32_t)((counter_alarm % 3600) / 60); sAlarm.AlarmTime.Seconds = (uint32_t)((counter_alarm % 3600) % 60); //sprintf((char*)ShowTime2,"SetAlarm %02d:%02d:%02d", sAlarm.AlarmTime.Hours, sAlarm.AlarmTime.Minutes, sAlarm.AlarmTime.Seconds); if(HAL_RTC_SetAlarm_IT(&RtcHandle,&sAlarm,RTC_FORMAT_BCD) != HAL_OK) { /* Initialization Error */ Error_Handler(); } } #endif static uint64_t RtcGetCalendarValue( RTC_DateTypeDef* date, RTC_TimeTypeDef* time ) { uint64_t calendarValue = 0; //uint32_t firstRead; uint32_t correction; uint32_t seconds; // Make sure it is correct due to asynchronus nature of RTC //do //{ // firstRead = RTC->SSR; HAL_RTC_GetDate( &RtcHandle, date, RTC_FORMAT_BIN ); HAL_RTC_GetTime( &RtcHandle, time, RTC_FORMAT_BIN ); //}while( firstRead != RTC->SSR ); // Calculte amount of elapsed days since 01/01/2000 seconds = DIVC( ( DAYS_IN_YEAR * 3 + DAYS_IN_LEAP_YEAR ) * date->Year , 4 ); correction = ( ( date->Year % 4 ) == 0 ) ? DAYS_IN_MONTH_CORRECTION_LEAP : DAYS_IN_MONTH_CORRECTION_NORM; seconds += ( DIVC( ( date->Month-1 ) * ( 30 + 31 ), 2 ) - ( ( ( correction >> ( ( date->Month - 1 ) * 2 ) ) & 0x03 ) ) ); seconds += ( date->Date -1 ); // Convert from days to seconds seconds *= SECONDS_IN_1DAY; seconds += ( ( uint32_t )time->Seconds + ( ( uint32_t )time->Minutes * SECONDS_IN_1MINUTE ) + ( ( uint32_t )time->Hours * SECONDS_IN_1HOUR ) ) ; calendarValue = ( ( ( uint64_t )seconds ) << N_PREDIV_S ); return( calendarValue ); } uint32_t RtcGetCalendarTime( uint16_t *milliseconds ) { RTC_TimeTypeDef time ; RTC_DateTypeDef date; uint32_t ticks; uint64_t calendarValue = RtcGetCalendarValue( &date, &time ); uint32_t seconds = ( uint32_t )( calendarValue >> N_PREDIV_S ); ticks = ( uint32_t )calendarValue & PREDIV_S; *milliseconds = RtcTick2Ms( ticks ); return seconds; } uint32_t RtcGetTimerElapsedTime( void ) { RTC_TimeTypeDef time; RTC_DateTypeDef date; uint32_t calendarValue = ( uint32_t )RtcGetCalendarValue( &date, &time ); return( ( uint32_t )( calendarValue - RtcTimerContext.Time ) ); } void RtcBkupWrite( uint32_t data0, uint32_t data1 ) { HAL_RTCEx_BKUPWrite( &RtcHandle, RTC_BKP_DR1, data0 ); HAL_RTCEx_BKUPWrite( &RtcHandle, RTC_BKP_DR2, data1 ); } void RtcBkupRead( uint32_t *data0, uint32_t *data1 ) { *data0 = HAL_RTCEx_BKUPRead( &RtcHandle, RTC_BKP_DR1 ); *data1 = HAL_RTCEx_BKUPRead( &RtcHandle, RTC_BKP_DR2 ); } void RtcProcess( void ) { // Not used on this platform. } TimerTime_t RtcTempCompensation( TimerTime_t period, float temperature ) { float k = RTC_TEMP_COEFFICIENT; float kDev = RTC_TEMP_DEV_COEFFICIENT; float t = RTC_TEMP_TURNOVER; float tDev = RTC_TEMP_DEV_TURNOVER; float interim = 0.0; float ppm = 0.0; if( k < 0.0 ) { ppm = ( k - kDev ); } else { ppm = ( k + kDev ); } interim = ( temperature - ( t - tDev ) ); ppm *= interim * interim; // Calculate the drift in time interim = ( ( float ) period * ppm ) / 1e6; // Calculate the resulting time period interim += period; interim = floor( interim ); if( interim < 0.0 ) { interim = ( float )period; } // Calculate the resulting period return ( TimerTime_t ) interim; } /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/