1 /*!
2  * \file      rtc-board.c
3  *
4  * \brief     Target board RTC timer and low power modes management
5  *
6  * \copyright Revised BSD License, see section \ref LICENSE.
7  *
8  * \code
9  *                ______                              _
10  *               / _____)             _              | |
11  *              ( (____  _____ ____ _| |_ _____  ____| |__
12  *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
13  *               _____) ) ____| | | || |_| ____( (___| | | |
14  *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
15  *              (C)2013-2017 Semtech - STMicroelectronics
16  *
17  * \endcode
18  *
19  * \author    Miguel Luis ( Semtech )
20  *
21  * \author    Gregory Cristian ( Semtech )
22  *
23  * \author    MCD Application Team (C)( STMicroelectronics International )
24  */
25 #include <math.h>
26 #include <time.h>
27 #include "stm32l4xx.h"
28 #include "utilities.h"
29 #include "delay.h"
30 #include "board.h"
31 #include "timer.h"
32 #include "systime.h"
33 #include "gpio.h"
34 #include "sysIrqHandlers.h"
35 #include "lpm-board.h"
36 #include "rtc-board.h"
37 
38 // MCU Wake Up Time
39 #define MIN_ALARM_DELAY                             3 // in ticks
40 
41 // sub-second number of bits
42 #define N_PREDIV_S                                  10
43 
44 // Synchronous prediv
45 #define PREDIV_S                                    ( ( 1 << N_PREDIV_S ) - 1 )
46 
47 // Asynchronous prediv
48 #define PREDIV_A                                    ( 1 << ( 15 - N_PREDIV_S ) ) - 1
49 
50 // Sub-second mask definition
51 #define ALARM_SUBSECOND_MASK                        ( N_PREDIV_S << RTC_ALRMASSR_MASKSS_Pos )
52 
53 // RTC Time base in us
54 #define USEC_NUMBER                                 1000000
55 #define MSEC_NUMBER                                 ( USEC_NUMBER / 1000 )
56 
57 #define COMMON_FACTOR                               3
58 #define CONV_NUMER                                  ( MSEC_NUMBER >> COMMON_FACTOR )
59 #define CONV_DENOM                                  ( 1 << ( N_PREDIV_S - COMMON_FACTOR ) )
60 
61 /*!
62  * \brief Days, Hours, Minutes and seconds
63  */
64 #define DAYS_IN_LEAP_YEAR                           ( ( uint32_t )  366U )
65 #define DAYS_IN_YEAR                                ( ( uint32_t )  365U )
66 #define SECONDS_IN_1DAY                             ( ( uint32_t )86400U )
67 #define SECONDS_IN_1HOUR                            ( ( uint32_t ) 3600U )
68 #define SECONDS_IN_1MINUTE                          ( ( uint32_t )   60U )
69 #define MINUTES_IN_1HOUR                            ( ( uint32_t )   60U )
70 #define HOURS_IN_1DAY                               ( ( uint32_t )   24U )
71 
72 /*!
73  * \brief Correction factors
74  */
75 #define  DAYS_IN_MONTH_CORRECTION_NORM              ( ( uint32_t )0x99AAA0 )
76 #define  DAYS_IN_MONTH_CORRECTION_LEAP              ( ( uint32_t )0x445550 )
77 
78 /*!
79  * \brief Calculates ceiling( X / N )
80  */
81 #define DIVC( X, N )                                ( ( ( X ) + ( N ) -1 ) / ( N ) )
82 
83 /*!
84  * RTC timer context
85  */
86 typedef struct
87 {
88     uint32_t        Time;         // Reference time
89     RTC_TimeTypeDef CalendarTime; // Reference time in calendar format
90     RTC_DateTypeDef CalendarDate; // Reference date in calendar format
91 }RtcTimerContext_t;
92 
93 /*!
94  * \brief Indicates if the RTC is already Initialized or not
95  */
96 static bool RtcInitialized = false;
97 
98 /*!
99  * Number of days in each month on a normal year
100  */
101 static const uint8_t DaysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
102 
103 /*!
104  * Number of days in each month on a leap year
105  */
106 static const uint8_t DaysInMonthLeapYear[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
107 
108 /*!
109  * \brief RTC Handle
110  */
111 static RTC_HandleTypeDef RtcHandle =
112 {
113     .Instance = NULL,
114     .Init =
115     {
116         .HourFormat = 0,
117         .AsynchPrediv = 0,
118         .SynchPrediv = 0,
119         .OutPut = 0,
120         .OutPutPolarity = 0,
121         .OutPutType = 0
122     },
123     .Lock = HAL_UNLOCKED,
124     .State = HAL_RTC_STATE_RESET
125 };
126 
127 /*!
128  * \brief RTC Alarm
129  */
130 static RTC_AlarmTypeDef RtcAlarm;
131 
132 /*!
133  * Keep the value of the RTC timer when the RTC alarm is set
134  * Set with the \ref RtcSetTimerContext function
135  * Value is kept as a Reference to calculate alarm
136  */
137 static RtcTimerContext_t RtcTimerContext;
138 
139 /*!
140  * \brief Get the current time from calendar in ticks
141  *
142  * \param [IN] date           Pointer to RTC_DateStruct
143  * \param [IN] time           Pointer to RTC_TimeStruct
144  * \retval calendarValue Time in ticks
145  */
146 static uint64_t RtcGetCalendarValue( RTC_DateTypeDef* date, RTC_TimeTypeDef* time );
147 
RtcInit(void)148 void RtcInit( void )
149 {
150     RTC_DateTypeDef date;
151     RTC_TimeTypeDef time;
152 
153     if( RtcInitialized == false )
154     {
155         __HAL_RCC_RTC_ENABLE( );
156 
157         RtcHandle.Instance            = RTC;
158         RtcHandle.Init.HourFormat     = RTC_HOURFORMAT_24;
159         RtcHandle.Init.AsynchPrediv   = PREDIV_A;  // RTC_ASYNCH_PREDIV;
160         RtcHandle.Init.SynchPrediv    = PREDIV_S;  // RTC_SYNCH_PREDIV;
161         RtcHandle.Init.OutPut         = RTC_OUTPUT_DISABLE;
162         RtcHandle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
163         RtcHandle.Init.OutPutType     = RTC_OUTPUT_TYPE_OPENDRAIN;
164         HAL_RTC_Init( &RtcHandle );
165 
166         date.Year                     = 0;
167         date.Month                    = RTC_MONTH_JANUARY;
168         date.Date                     = 1;
169         date.WeekDay                  = RTC_WEEKDAY_MONDAY;
170         HAL_RTC_SetDate( &RtcHandle, &date, RTC_FORMAT_BIN );
171 
172         /*at 0:0:0*/
173         time.Hours                    = 0;
174         time.Minutes                  = 0;
175         time.Seconds                  = 0;
176         time.SubSeconds               = 0;
177         time.TimeFormat               = 0;
178         time.StoreOperation           = RTC_STOREOPERATION_RESET;
179         time.DayLightSaving           = RTC_DAYLIGHTSAVING_NONE;
180         HAL_RTC_SetTime( &RtcHandle, &time, RTC_FORMAT_BIN );
181 
182         // Enable Direct Read of the calendar registers (not through Shadow registers)
183         HAL_RTCEx_EnableBypassShadow( &RtcHandle );
184 
185         HAL_NVIC_SetPriority( RTC_Alarm_IRQn, 1, 0 );
186         HAL_NVIC_EnableIRQ( RTC_Alarm_IRQn );
187 
188         // Init alarm.
189         HAL_RTC_DeactivateAlarm( &RtcHandle, RTC_ALARM_A );
190 
191         RtcSetTimerContext( );
192         RtcInitialized = true;
193     }
194 }
195 
196 /*!
197  * \brief Sets the RTC timer reference, sets also the RTC_DateStruct and RTC_TimeStruct
198  *
199  * \param none
200  * \retval timerValue In ticks
201  */
RtcSetTimerContext(void)202 uint32_t RtcSetTimerContext( void )
203 {
204     RtcTimerContext.Time = ( uint32_t )RtcGetCalendarValue( &RtcTimerContext.CalendarDate, &RtcTimerContext.CalendarTime );
205     return ( uint32_t )RtcTimerContext.Time;
206 }
207 
208 /*!
209  * \brief Gets the RTC timer reference
210  *
211  * \param none
212  * \retval timerValue In ticks
213  */
RtcGetTimerContext(void)214 uint32_t RtcGetTimerContext( void )
215 {
216     return RtcTimerContext.Time;
217 }
218 
219 /*!
220  * \brief returns the wake up time in ticks
221  *
222  * \retval wake up time in ticks
223  */
RtcGetMinimumTimeout(void)224 uint32_t RtcGetMinimumTimeout( void )
225 {
226     return( MIN_ALARM_DELAY );
227 }
228 
229 /*!
230  * \brief converts time in ms to time in ticks
231  *
232  * \param[IN] milliseconds Time in milliseconds
233  * \retval returns time in timer ticks
234  */
RtcMs2Tick(uint32_t milliseconds)235 uint32_t RtcMs2Tick( uint32_t milliseconds )
236 {
237     return ( uint32_t )( ( ( ( uint64_t )milliseconds ) * CONV_DENOM ) / CONV_NUMER );
238 }
239 
240 /*!
241  * \brief converts time in ticks to time in ms
242  *
243  * \param[IN] time in timer ticks
244  * \retval returns time in milliseconds
245  */
RtcTick2Ms(uint32_t tick)246 uint32_t RtcTick2Ms( uint32_t tick )
247 {
248     uint32_t seconds = tick >> N_PREDIV_S;
249 
250     tick = tick & PREDIV_S;
251     return ( ( seconds * 1000 ) + ( ( tick * 1000 ) >> N_PREDIV_S ) );
252 }
253 
254 /*!
255  * \brief a delay of delay ms by polling RTC
256  *
257  * \param[IN] delay in ms
258  */
RtcDelayMs(uint32_t delay)259 void RtcDelayMs( uint32_t delay )
260 {
261     uint64_t delayTicks = 0;
262     uint64_t refTicks = RtcGetTimerValue( );
263 
264     delayTicks = RtcMs2Tick( delay );
265 
266     // Wait delay ms
267     while( ( ( RtcGetTimerValue( ) - refTicks ) ) < delayTicks )
268     {
269         __NOP( );
270     }
271 }
272 
273 /*!
274  * \brief Sets the alarm
275  *
276  * \note The alarm is set at now (read in this function) + timeout
277  *
278  * \param timeout Duration of the Timer ticks
279  */
RtcSetAlarm(uint32_t timeout)280 void RtcSetAlarm( uint32_t timeout )
281 {
282     // We don't go in Low Power mode for timeout below MIN_ALARM_DELAY
283     if( ( int64_t )MIN_ALARM_DELAY < ( int64_t )( timeout - RtcGetTimerElapsedTime( ) ) )
284     {
285         LpmSetStopMode( LPM_RTC_ID, LPM_ENABLE );
286     }
287     else
288     {
289         LpmSetStopMode( LPM_RTC_ID, LPM_DISABLE );
290     }
291 
292     RtcStartAlarm( timeout );
293 }
294 
RtcStopAlarm(void)295 void RtcStopAlarm( void )
296 {
297     // Disable the Alarm A interrupt
298     HAL_RTC_DeactivateAlarm( &RtcHandle, RTC_ALARM_A );
299 
300     // Clear RTC Alarm Flag
301     __HAL_RTC_ALARM_CLEAR_FLAG( &RtcHandle, RTC_FLAG_ALRAF );
302 
303     // Clear the EXTI's line Flag for RTC Alarm
304     __HAL_RTC_ALARM_EXTI_CLEAR_FLAG( );
305 }
306 
RtcStartAlarm(uint32_t timeout)307 void RtcStartAlarm( uint32_t timeout )
308 {
309     uint16_t rtcAlarmSubSeconds = 0;
310     uint16_t rtcAlarmSeconds = 0;
311     uint16_t rtcAlarmMinutes = 0;
312     uint16_t rtcAlarmHours = 0;
313     uint16_t rtcAlarmDays = 0;
314     RTC_TimeTypeDef time = RtcTimerContext.CalendarTime;
315     RTC_DateTypeDef date = RtcTimerContext.CalendarDate;
316 
317     RtcStopAlarm( );
318 
319     /*reverse counter */
320     rtcAlarmSubSeconds =  PREDIV_S - time.SubSeconds;
321     rtcAlarmSubSeconds += ( timeout & PREDIV_S );
322     // convert timeout  to seconds
323     timeout >>= N_PREDIV_S;
324 
325     // Convert microsecs to RTC format and add to 'Now'
326     rtcAlarmDays =  date.Date;
327     while( timeout >= TM_SECONDS_IN_1DAY )
328     {
329         timeout -= TM_SECONDS_IN_1DAY;
330         rtcAlarmDays++;
331     }
332 
333     // Calc hours
334     rtcAlarmHours = time.Hours;
335     while( timeout >= TM_SECONDS_IN_1HOUR )
336     {
337         timeout -= TM_SECONDS_IN_1HOUR;
338         rtcAlarmHours++;
339     }
340 
341     // Calc minutes
342     rtcAlarmMinutes = time.Minutes;
343     while( timeout >= TM_SECONDS_IN_1MINUTE )
344     {
345         timeout -= TM_SECONDS_IN_1MINUTE;
346         rtcAlarmMinutes++;
347     }
348 
349     // Calc seconds
350     rtcAlarmSeconds =  time.Seconds + timeout;
351 
352     //***** Correct for modulo********
353     while( rtcAlarmSubSeconds >= ( PREDIV_S + 1 ) )
354     {
355         rtcAlarmSubSeconds -= ( PREDIV_S + 1 );
356         rtcAlarmSeconds++;
357     }
358 
359     while( rtcAlarmSeconds >= TM_SECONDS_IN_1MINUTE )
360     {
361         rtcAlarmSeconds -= TM_SECONDS_IN_1MINUTE;
362         rtcAlarmMinutes++;
363     }
364 
365     while( rtcAlarmMinutes >= TM_MINUTES_IN_1HOUR )
366     {
367         rtcAlarmMinutes -= TM_MINUTES_IN_1HOUR;
368         rtcAlarmHours++;
369     }
370 
371     while( rtcAlarmHours >= TM_HOURS_IN_1DAY )
372     {
373         rtcAlarmHours -= TM_HOURS_IN_1DAY;
374         rtcAlarmDays++;
375     }
376 
377     if( date.Year % 4 == 0 )
378     {
379         if( rtcAlarmDays > DaysInMonthLeapYear[date.Month - 1] )
380         {
381             rtcAlarmDays = rtcAlarmDays % DaysInMonthLeapYear[date.Month - 1];
382         }
383     }
384     else
385     {
386         if( rtcAlarmDays > DaysInMonth[date.Month - 1] )
387         {
388             rtcAlarmDays = rtcAlarmDays % DaysInMonth[date.Month - 1];
389         }
390     }
391 
392     /* Set RTC_AlarmStructure with calculated values*/
393     RtcAlarm.AlarmTime.SubSeconds     = PREDIV_S - rtcAlarmSubSeconds;
394     RtcAlarm.AlarmSubSecondMask       = ALARM_SUBSECOND_MASK;
395     RtcAlarm.AlarmTime.Seconds        = rtcAlarmSeconds;
396     RtcAlarm.AlarmTime.Minutes        = rtcAlarmMinutes;
397     RtcAlarm.AlarmTime.Hours          = rtcAlarmHours;
398     RtcAlarm.AlarmDateWeekDay         = ( uint8_t )rtcAlarmDays;
399     RtcAlarm.AlarmTime.TimeFormat     = time.TimeFormat;
400     RtcAlarm.AlarmDateWeekDaySel      = RTC_ALARMDATEWEEKDAYSEL_DATE;
401     RtcAlarm.AlarmMask                = RTC_ALARMMASK_NONE;
402     RtcAlarm.Alarm                    = RTC_ALARM_A;
403     RtcAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
404     RtcAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET;
405 
406     // Set RTC_Alarm
407     HAL_RTC_SetAlarm_IT( &RtcHandle, &RtcAlarm, RTC_FORMAT_BIN );
408 }
409 
RtcGetTimerValue(void)410 uint32_t RtcGetTimerValue( void )
411 {
412     RTC_TimeTypeDef time;
413     RTC_DateTypeDef date;
414 
415     uint32_t calendarValue = ( uint32_t )RtcGetCalendarValue( &date, &time );
416 
417     return( calendarValue );
418 }
419 
RtcGetTimerElapsedTime(void)420 uint32_t RtcGetTimerElapsedTime( void )
421 {
422   RTC_TimeTypeDef time;
423   RTC_DateTypeDef date;
424 
425   uint32_t calendarValue = ( uint32_t )RtcGetCalendarValue( &date, &time );
426 
427   return( ( uint32_t )( calendarValue - RtcTimerContext.Time ) );
428 }
429 
RtcGetCalendarValue(RTC_DateTypeDef * date,RTC_TimeTypeDef * time)430 static uint64_t RtcGetCalendarValue( RTC_DateTypeDef* date, RTC_TimeTypeDef* time )
431 {
432     uint64_t calendarValue = 0;
433     uint32_t firstRead;
434     uint32_t correction;
435     uint32_t seconds;
436 
437     // Make sure it is correct due to asynchronus nature of RTC
438     do
439     {
440         firstRead = RTC->SSR;
441         HAL_RTC_GetDate( &RtcHandle, date, RTC_FORMAT_BIN );
442         HAL_RTC_GetTime( &RtcHandle, time, RTC_FORMAT_BIN );
443     }while( firstRead != RTC->SSR );
444 
445     // Calculte amount of elapsed days since 01/01/2000
446     seconds = DIVC( ( DAYS_IN_YEAR * 3 + DAYS_IN_LEAP_YEAR ) * date->Year , 4 );
447 
448     correction = ( ( date->Year % 4 ) == 0 ) ? DAYS_IN_MONTH_CORRECTION_LEAP : DAYS_IN_MONTH_CORRECTION_NORM;
449 
450     seconds += ( DIVC( ( date->Month-1 ) * ( 30 + 31 ), 2 ) - ( ( ( correction >> ( ( date->Month - 1 ) * 2 ) ) & 0x03 ) ) );
451 
452     seconds += ( date->Date -1 );
453 
454     // Convert from days to seconds
455     seconds *= SECONDS_IN_1DAY;
456 
457     seconds += ( ( uint32_t )time->Seconds +
458                  ( ( uint32_t )time->Minutes * SECONDS_IN_1MINUTE ) +
459                  ( ( uint32_t )time->Hours * SECONDS_IN_1HOUR ) ) ;
460 
461     calendarValue = ( ( ( uint64_t )seconds ) << N_PREDIV_S ) + ( PREDIV_S - time->SubSeconds );
462 
463     return( calendarValue );
464 }
465 
RtcGetCalendarTime(uint16_t * milliseconds)466 uint32_t RtcGetCalendarTime( uint16_t *milliseconds )
467 {
468     RTC_TimeTypeDef time ;
469     RTC_DateTypeDef date;
470     uint32_t ticks;
471 
472     uint64_t calendarValue = RtcGetCalendarValue( &date, &time );
473 
474     uint32_t seconds = ( uint32_t )( calendarValue >> N_PREDIV_S );
475 
476     ticks =  ( uint32_t )calendarValue & PREDIV_S;
477 
478     *milliseconds = RtcTick2Ms( ticks );
479 
480     return seconds;
481 }
482 
483 /*!
484  * \brief RTC IRQ Handler of the RTC Alarm
485  */
RTC_Alarm_IRQHandler(void)486 void RTC_Alarm_IRQHandler( void )
487 {
488     RTC_HandleTypeDef* hrtc = &RtcHandle;
489 
490     // Enable low power at irq
491     LpmSetStopMode( LPM_RTC_ID, LPM_ENABLE );
492 
493     // Clear the EXTI's line Flag for RTC Alarm
494     __HAL_RTC_ALARM_EXTI_CLEAR_FLAG( );
495 
496     // Gets the AlarmA interrupt source enable status
497     if( __HAL_RTC_ALARM_GET_IT_SOURCE( hrtc, RTC_IT_ALRA ) != RESET )
498     {
499         // Gets the pending status of the AlarmA interrupt
500         if( __HAL_RTC_ALARM_GET_FLAG( hrtc, RTC_FLAG_ALRAF ) != RESET )
501         {
502             // Clear the AlarmA interrupt pending bit
503             __HAL_RTC_ALARM_CLEAR_FLAG( hrtc, RTC_FLAG_ALRAF );
504             // AlarmA callback
505             HAL_RTC_AlarmAEventCallback( hrtc );
506         }
507     }
508 }
509 
510 /*!
511  * \brief  Alarm A callback.
512  *
513  * \param [IN] hrtc RTC handle
514  */
HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef * hrtc)515 void HAL_RTC_AlarmAEventCallback( RTC_HandleTypeDef *hrtc )
516 {
517     TimerIrqHandler( );
518 }
519 
RtcBkupWrite(uint32_t data0,uint32_t data1)520 void RtcBkupWrite( uint32_t data0, uint32_t data1 )
521 {
522     HAL_RTCEx_BKUPWrite( &RtcHandle, RTC_BKP_DR0, data0 );
523     HAL_RTCEx_BKUPWrite( &RtcHandle, RTC_BKP_DR1, data1 );
524 }
525 
RtcBkupRead(uint32_t * data0,uint32_t * data1)526 void RtcBkupRead( uint32_t *data0, uint32_t *data1 )
527 {
528   *data0 = HAL_RTCEx_BKUPRead( &RtcHandle, RTC_BKP_DR0 );
529   *data1 = HAL_RTCEx_BKUPRead( &RtcHandle, RTC_BKP_DR1 );
530 }
531 
RtcProcess(void)532 void RtcProcess( void )
533 {
534     // Not used on this platform.
535 }
536 
RtcTempCompensation(TimerTime_t period,float temperature)537 TimerTime_t RtcTempCompensation( TimerTime_t period, float temperature )
538 {
539     float k = RTC_TEMP_COEFFICIENT;
540     float kDev = RTC_TEMP_DEV_COEFFICIENT;
541     float t = RTC_TEMP_TURNOVER;
542     float tDev = RTC_TEMP_DEV_TURNOVER;
543     float interim = 0.0f;
544     float ppm = 0.0f;
545 
546     if( k < 0.0f )
547     {
548         ppm = ( k - kDev );
549     }
550     else
551     {
552         ppm = ( k + kDev );
553     }
554     interim = ( temperature - ( t - tDev ) );
555     ppm *=  interim * interim;
556 
557     // Calculate the drift in time
558     interim = ( ( float ) period * ppm ) / 1000000.0f;
559     // Calculate the resulting time period
560     interim += period;
561     interim = floor( interim );
562 
563     if( interim < 0.0f )
564     {
565         interim = ( float )period;
566     }
567 
568     // Calculate the resulting period
569     return ( TimerTime_t ) interim;
570 }
571