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