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 "stm32l0xx.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         .OutPutRemap = 0,
121         .OutPutPolarity = 0,
122         .OutPutType = 0
123     },
124     .Lock = HAL_UNLOCKED,
125     .State = HAL_RTC_STATE_RESET
126 };
127 
128 /*!
129  * \brief RTC Alarm
130  */
131 static RTC_AlarmTypeDef RtcAlarm;
132 
133 /*!
134  * Keep the value of the RTC timer when the RTC alarm is set
135  * Set with the \ref RtcSetTimerContext function
136  * Value is kept as a Reference to calculate alarm
137  */
138 static RtcTimerContext_t RtcTimerContext;
139 
140 /*!
141  * \brief Get the current time from calendar in ticks
142  *
143  * \param [IN] date           Pointer to RTC_DateStruct
144  * \param [IN] time           Pointer to RTC_TimeStruct
145  * \retval calendarValue Time in ticks
146  */
147 static uint64_t RtcGetCalendarValue( RTC_DateTypeDef* date, RTC_TimeTypeDef* time );
148 
RtcInit(void)149 void RtcInit( void )
150 {
151     RTC_DateTypeDef date;
152     RTC_TimeTypeDef time;
153 
154     if( RtcInitialized == false )
155     {
156         __HAL_RCC_RTC_ENABLE( );
157 
158         RtcHandle.Instance            = RTC;
159         RtcHandle.Init.HourFormat     = RTC_HOURFORMAT_24;
160         RtcHandle.Init.AsynchPrediv   = PREDIV_A;  // RTC_ASYNCH_PREDIV;
161         RtcHandle.Init.SynchPrediv    = PREDIV_S;  // RTC_SYNCH_PREDIV;
162         RtcHandle.Init.OutPut         = RTC_OUTPUT_DISABLE;
163         RtcHandle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
164         RtcHandle.Init.OutPutType     = RTC_OUTPUT_TYPE_OPENDRAIN;
165         HAL_RTC_Init( &RtcHandle );
166 
167         date.Year                     = 0;
168         date.Month                    = RTC_MONTH_JANUARY;
169         date.Date                     = 1;
170         date.WeekDay                  = RTC_WEEKDAY_MONDAY;
171         HAL_RTC_SetDate( &RtcHandle, &date, RTC_FORMAT_BIN );
172 
173         /*at 0:0:0*/
174         time.Hours                    = 0;
175         time.Minutes                  = 0;
176         time.Seconds                  = 0;
177         time.SubSeconds               = 0;
178         time.TimeFormat               = 0;
179         time.StoreOperation           = RTC_STOREOPERATION_RESET;
180         time.DayLightSaving           = RTC_DAYLIGHTSAVING_NONE;
181         HAL_RTC_SetTime( &RtcHandle, &time, RTC_FORMAT_BIN );
182 
183         // Enable Direct Read of the calendar registers (not through Shadow registers)
184         HAL_RTCEx_EnableBypassShadow( &RtcHandle );
185 
186         HAL_NVIC_SetPriority( RTC_IRQn, 1, 0 );
187         HAL_NVIC_EnableIRQ( RTC_IRQn );
188 
189         // Init alarm.
190         HAL_RTC_DeactivateAlarm( &RtcHandle, RTC_ALARM_A );
191 
192         RtcSetTimerContext( );
193         RtcInitialized = true;
194     }
195 }
196 
197 /*!
198  * \brief Sets the RTC timer reference, sets also the RTC_DateStruct and RTC_TimeStruct
199  *
200  * \param none
201  * \retval timerValue In ticks
202  */
RtcSetTimerContext(void)203 uint32_t RtcSetTimerContext( void )
204 {
205     RtcTimerContext.Time = ( uint32_t )RtcGetCalendarValue( &RtcTimerContext.CalendarDate, &RtcTimerContext.CalendarTime );
206     return ( uint32_t )RtcTimerContext.Time;
207 }
208 
209 /*!
210  * \brief Gets the RTC timer reference
211  *
212  * \param none
213  * \retval timerValue In ticks
214  */
RtcGetTimerContext(void)215 uint32_t RtcGetTimerContext( void )
216 {
217     return RtcTimerContext.Time;
218 }
219 
220 /*!
221  * \brief returns the wake up time in ticks
222  *
223  * \retval wake up time in ticks
224  */
RtcGetMinimumTimeout(void)225 uint32_t RtcGetMinimumTimeout( void )
226 {
227     return( MIN_ALARM_DELAY );
228 }
229 
230 /*!
231  * \brief converts time in ms to time in ticks
232  *
233  * \param[IN] milliseconds Time in milliseconds
234  * \retval returns time in timer ticks
235  */
RtcMs2Tick(uint32_t milliseconds)236 uint32_t RtcMs2Tick( uint32_t milliseconds )
237 {
238     return ( uint32_t )( ( ( ( uint64_t )milliseconds ) * CONV_DENOM ) / CONV_NUMER );
239 }
240 
241 /*!
242  * \brief converts time in ticks to time in ms
243  *
244  * \param[IN] time in timer ticks
245  * \retval returns time in milliseconds
246  */
RtcTick2Ms(uint32_t tick)247 uint32_t RtcTick2Ms( uint32_t tick )
248 {
249     uint32_t seconds = tick >> N_PREDIV_S;
250 
251     tick = tick & PREDIV_S;
252     return ( ( seconds * 1000 ) + ( ( tick * 1000 ) >> N_PREDIV_S ) );
253 }
254 
255 /*!
256  * \brief a delay of delay ms by polling RTC
257  *
258  * \param[IN] delay in ms
259  */
RtcDelayMs(uint32_t delay)260 void RtcDelayMs( uint32_t delay )
261 {
262     uint64_t delayTicks = 0;
263     uint64_t refTicks = RtcGetTimerValue( );
264 
265     delayTicks = RtcMs2Tick( delay );
266 
267     // Wait delay ms
268     while( ( ( RtcGetTimerValue( ) - refTicks ) ) < delayTicks )
269     {
270         __NOP( );
271     }
272 }
273 
274 /*!
275  * \brief Sets the alarm
276  *
277  * \note The alarm is set at now (read in this function) + timeout
278  *
279  * \param timeout Duration of the Timer ticks
280  */
RtcSetAlarm(uint32_t timeout)281 void RtcSetAlarm( uint32_t timeout )
282 {
283     // We don't go in Low Power mode for timeout below MIN_ALARM_DELAY
284     if( ( int64_t )MIN_ALARM_DELAY < ( int64_t )( timeout - RtcGetTimerElapsedTime( ) ) )
285     {
286         LpmSetStopMode( LPM_RTC_ID, LPM_ENABLE );
287     }
288     else
289     {
290         LpmSetStopMode( LPM_RTC_ID, LPM_DISABLE );
291     }
292 
293     RtcStartAlarm( timeout );
294 }
295 
RtcStopAlarm(void)296 void RtcStopAlarm( void )
297 {
298     // Disable the Alarm A interrupt
299     HAL_RTC_DeactivateAlarm( &RtcHandle, RTC_ALARM_A );
300 
301     // Clear RTC Alarm Flag
302     __HAL_RTC_ALARM_CLEAR_FLAG( &RtcHandle, RTC_FLAG_ALRAF );
303 
304     // Clear the EXTI's line Flag for RTC Alarm
305     __HAL_RTC_ALARM_EXTI_CLEAR_FLAG( );
306 }
307 
RtcStartAlarm(uint32_t timeout)308 void RtcStartAlarm( uint32_t timeout )
309 {
310     uint16_t rtcAlarmSubSeconds = 0;
311     uint16_t rtcAlarmSeconds = 0;
312     uint16_t rtcAlarmMinutes = 0;
313     uint16_t rtcAlarmHours = 0;
314     uint16_t rtcAlarmDays = 0;
315     RTC_TimeTypeDef time = RtcTimerContext.CalendarTime;
316     RTC_DateTypeDef date = RtcTimerContext.CalendarDate;
317 
318     RtcStopAlarm( );
319 
320     /*reverse counter */
321     rtcAlarmSubSeconds =  PREDIV_S - time.SubSeconds;
322     rtcAlarmSubSeconds += ( timeout & PREDIV_S );
323     // convert timeout  to seconds
324     timeout >>= N_PREDIV_S;
325 
326     // Convert microsecs to RTC format and add to 'Now'
327     rtcAlarmDays =  date.Date;
328     while( timeout >= TM_SECONDS_IN_1DAY )
329     {
330         timeout -= TM_SECONDS_IN_1DAY;
331         rtcAlarmDays++;
332     }
333 
334     // Calc hours
335     rtcAlarmHours = time.Hours;
336     while( timeout >= TM_SECONDS_IN_1HOUR )
337     {
338         timeout -= TM_SECONDS_IN_1HOUR;
339         rtcAlarmHours++;
340     }
341 
342     // Calc minutes
343     rtcAlarmMinutes = time.Minutes;
344     while( timeout >= TM_SECONDS_IN_1MINUTE )
345     {
346         timeout -= TM_SECONDS_IN_1MINUTE;
347         rtcAlarmMinutes++;
348     }
349 
350     // Calc seconds
351     rtcAlarmSeconds =  time.Seconds + timeout;
352 
353     //***** Correct for modulo********
354     while( rtcAlarmSubSeconds >= ( PREDIV_S + 1 ) )
355     {
356         rtcAlarmSubSeconds -= ( PREDIV_S + 1 );
357         rtcAlarmSeconds++;
358     }
359 
360     while( rtcAlarmSeconds >= TM_SECONDS_IN_1MINUTE )
361     {
362         rtcAlarmSeconds -= TM_SECONDS_IN_1MINUTE;
363         rtcAlarmMinutes++;
364     }
365 
366     while( rtcAlarmMinutes >= TM_MINUTES_IN_1HOUR )
367     {
368         rtcAlarmMinutes -= TM_MINUTES_IN_1HOUR;
369         rtcAlarmHours++;
370     }
371 
372     while( rtcAlarmHours >= TM_HOURS_IN_1DAY )
373     {
374         rtcAlarmHours -= TM_HOURS_IN_1DAY;
375         rtcAlarmDays++;
376     }
377 
378     if( date.Year % 4 == 0 )
379     {
380         if( rtcAlarmDays > DaysInMonthLeapYear[date.Month - 1] )
381         {
382             rtcAlarmDays = rtcAlarmDays % DaysInMonthLeapYear[date.Month - 1];
383         }
384     }
385     else
386     {
387         if( rtcAlarmDays > DaysInMonth[date.Month - 1] )
388         {
389             rtcAlarmDays = rtcAlarmDays % DaysInMonth[date.Month - 1];
390         }
391     }
392 
393     /* Set RTC_AlarmStructure with calculated values*/
394     RtcAlarm.AlarmTime.SubSeconds     = PREDIV_S - rtcAlarmSubSeconds;
395     RtcAlarm.AlarmSubSecondMask       = ALARM_SUBSECOND_MASK;
396     RtcAlarm.AlarmTime.Seconds        = rtcAlarmSeconds;
397     RtcAlarm.AlarmTime.Minutes        = rtcAlarmMinutes;
398     RtcAlarm.AlarmTime.Hours          = rtcAlarmHours;
399     RtcAlarm.AlarmDateWeekDay         = ( uint8_t )rtcAlarmDays;
400     RtcAlarm.AlarmTime.TimeFormat     = time.TimeFormat;
401     RtcAlarm.AlarmDateWeekDaySel      = RTC_ALARMDATEWEEKDAYSEL_DATE;
402     RtcAlarm.AlarmMask                = RTC_ALARMMASK_NONE;
403     RtcAlarm.Alarm                    = RTC_ALARM_A;
404     RtcAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
405     RtcAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET;
406 
407     // Set RTC_Alarm
408     HAL_RTC_SetAlarm_IT( &RtcHandle, &RtcAlarm, RTC_FORMAT_BIN );
409 }
410 
RtcGetTimerValue(void)411 uint32_t RtcGetTimerValue( void )
412 {
413     RTC_TimeTypeDef time;
414     RTC_DateTypeDef date;
415 
416     uint32_t calendarValue = ( uint32_t )RtcGetCalendarValue( &date, &time );
417 
418     return( calendarValue );
419 }
420 
RtcGetTimerElapsedTime(void)421 uint32_t RtcGetTimerElapsedTime( void )
422 {
423   RTC_TimeTypeDef time;
424   RTC_DateTypeDef date;
425 
426   uint32_t calendarValue = ( uint32_t )RtcGetCalendarValue( &date, &time );
427 
428   return( ( uint32_t )( calendarValue - RtcTimerContext.Time ) );
429 }
430 
RtcGetCalendarValue(RTC_DateTypeDef * date,RTC_TimeTypeDef * time)431 static uint64_t RtcGetCalendarValue( RTC_DateTypeDef* date, RTC_TimeTypeDef* time )
432 {
433     uint64_t calendarValue = 0;
434     uint32_t firstRead;
435     uint32_t correction;
436     uint32_t seconds;
437 
438     // Make sure it is correct due to asynchronus nature of RTC
439     do
440     {
441         firstRead = RTC->SSR;
442         HAL_RTC_GetDate( &RtcHandle, date, RTC_FORMAT_BIN );
443         HAL_RTC_GetTime( &RtcHandle, time, RTC_FORMAT_BIN );
444     }while( firstRead != RTC->SSR );
445 
446     // Calculte amount of elapsed days since 01/01/2000
447     seconds = DIVC( ( DAYS_IN_YEAR * 3 + DAYS_IN_LEAP_YEAR ) * date->Year , 4 );
448 
449     correction = ( ( date->Year % 4 ) == 0 ) ? DAYS_IN_MONTH_CORRECTION_LEAP : DAYS_IN_MONTH_CORRECTION_NORM;
450 
451     seconds += ( DIVC( ( date->Month-1 ) * ( 30 + 31 ), 2 ) - ( ( ( correction >> ( ( date->Month - 1 ) * 2 ) ) & 0x03 ) ) );
452 
453     seconds += ( date->Date -1 );
454 
455     // Convert from days to seconds
456     seconds *= SECONDS_IN_1DAY;
457 
458     seconds += ( ( uint32_t )time->Seconds +
459                  ( ( uint32_t )time->Minutes * SECONDS_IN_1MINUTE ) +
460                  ( ( uint32_t )time->Hours * SECONDS_IN_1HOUR ) ) ;
461 
462     calendarValue = ( ( ( uint64_t )seconds ) << N_PREDIV_S ) + ( PREDIV_S - time->SubSeconds );
463 
464     return( calendarValue );
465 }
466 
RtcGetCalendarTime(uint16_t * milliseconds)467 uint32_t RtcGetCalendarTime( uint16_t *milliseconds )
468 {
469     RTC_TimeTypeDef time ;
470     RTC_DateTypeDef date;
471     uint32_t ticks;
472 
473     uint64_t calendarValue = RtcGetCalendarValue( &date, &time );
474 
475     uint32_t seconds = ( uint32_t )( calendarValue >> N_PREDIV_S );
476 
477     ticks =  ( uint32_t )calendarValue & PREDIV_S;
478 
479     *milliseconds = RtcTick2Ms( ticks );
480 
481     return seconds;
482 }
483 
484 /*!
485  * \brief RTC IRQ Handler of the RTC Alarm
486  */
RTC_IRQHandler(void)487 void RTC_IRQHandler( void )
488 {
489     RTC_HandleTypeDef* hrtc = &RtcHandle;
490 
491     // Enable low power at irq
492     LpmSetStopMode( LPM_RTC_ID, LPM_ENABLE );
493 
494     // Clear the EXTI's line Flag for RTC Alarm
495     __HAL_RTC_ALARM_EXTI_CLEAR_FLAG( );
496 
497     // Gets the AlarmA interrupt source enable status
498     if( __HAL_RTC_ALARM_GET_IT_SOURCE( hrtc, RTC_IT_ALRA ) != RESET )
499     {
500         // Gets the pending status of the AlarmA interrupt
501         if( __HAL_RTC_ALARM_GET_FLAG( hrtc, RTC_FLAG_ALRAF ) != RESET )
502         {
503             // Clear the AlarmA interrupt pending bit
504             __HAL_RTC_ALARM_CLEAR_FLAG( hrtc, RTC_FLAG_ALRAF );
505             // AlarmA callback
506             HAL_RTC_AlarmAEventCallback( hrtc );
507         }
508     }
509 }
510 
511 /*!
512  * \brief  Alarm A callback.
513  *
514  * \param [IN] hrtc RTC handle
515  */
HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef * hrtc)516 void HAL_RTC_AlarmAEventCallback( RTC_HandleTypeDef *hrtc )
517 {
518     TimerIrqHandler( );
519 }
520 
RtcBkupWrite(uint32_t data0,uint32_t data1)521 void RtcBkupWrite( uint32_t data0, uint32_t data1 )
522 {
523     HAL_RTCEx_BKUPWrite( &RtcHandle, RTC_BKP_DR0, data0 );
524     HAL_RTCEx_BKUPWrite( &RtcHandle, RTC_BKP_DR1, data1 );
525 }
526 
RtcBkupRead(uint32_t * data0,uint32_t * data1)527 void RtcBkupRead( uint32_t *data0, uint32_t *data1 )
528 {
529   *data0 = HAL_RTCEx_BKUPRead( &RtcHandle, RTC_BKP_DR0 );
530   *data1 = HAL_RTCEx_BKUPRead( &RtcHandle, RTC_BKP_DR1 );
531 }
532 
RtcProcess(void)533 void RtcProcess( void )
534 {
535     // Not used on this platform.
536 }
537 
RtcTempCompensation(TimerTime_t period,float temperature)538 TimerTime_t RtcTempCompensation( TimerTime_t period, float temperature )
539 {
540     float k = RTC_TEMP_COEFFICIENT;
541     float kDev = RTC_TEMP_DEV_COEFFICIENT;
542     float t = RTC_TEMP_TURNOVER;
543     float tDev = RTC_TEMP_DEV_TURNOVER;
544     float interim = 0.0f;
545     float ppm = 0.0f;
546 
547     if( k < 0.0f )
548     {
549         ppm = ( k - kDev );
550     }
551     else
552     {
553         ppm = ( k + kDev );
554     }
555     interim = ( temperature - ( t - tDev ) );
556     ppm *=  interim * interim;
557 
558     // Calculate the drift in time
559     interim = ( ( float ) period * ppm ) / 1000000.0f;
560     // Calculate the resulting time period
561     interim += period;
562     interim = floor( interim );
563 
564     if( interim < 0.0f )
565     {
566         interim = ( float )period;
567     }
568 
569     // Calculate the resulting period
570     return ( TimerTime_t ) interim;
571 }
572