1 /*!
2  * \file      systime.c
3  *
4  * \brief     System time functions implementation.
5  *
6  * \copyright Revised BSD License, see section \ref LICENSE.
7  *
8  * \code
9  *                ______                              _
10  *               / _____)             _              | |
11  *              ( (____  _____ ____ _| |_ _____  ____| |__
12  *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
13  *               _____) ) ____| | | || |_| ____( (___| | | |
14  *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
15  *              (C)2013-2018 Semtech - STMicroelectronics
16  *
17  * \endcode
18  *
19  * \author    Miguel Luis ( Semtech )
20  *
21  * \author    Gregory Cristian ( Semtech )
22  *
23  * \author    MCD Application Team ( STMicroelectronics International )
24  */
25 #include <stdio.h>
26 #include "rtc-board.h"
27 #include "systime.h"
28 
29 #define END_OF_FEBRUARY_LEAP                         60 //31+29
30 #define END_OF_JULY_LEAP                            213 //31+29+...
31 
32 #define END_OF_FEBRUARY_NORM                         59 //31+28
33 #define END_OF_JULY_NORM                            212 //31+28+...
34 
35 #define UNIX_YEAR                                    68 //1968 is leap year
36 
37 //UNIX time 0 = start at 01:00:00, 01/01/1970
38 #define UNIX_HOUR_OFFSET                            ( ( TM_DAYS_IN_LEAP_YEAR + TM_DAYS_IN_YEAR ) * TM_SECONDS_IN_1DAY )
39 
40 /*!
41  * \brief Correction factors
42  */
43 #define  DAYS_IN_MONTH_CORRECTION_NORM              ( (uint32_t )0x99AAA0 )
44 #define  DAYS_IN_MONTH_CORRECTION_LEAP              ( (uint32_t )0x445550 )
45 
46 
47 /* 365.25 = (366 + 365 + 365 + 365)/4 */
48 #define DIV_365_25( X )                             ( ( ( X ) * 91867 + 22750 ) >> 25 )
49 
50 #define DIV_APPROX_86400( X )                       ( ( ( X ) >> 18 ) + ( ( X ) >> 17 ) )
51 
52 #define DIV_APPROX_1000( X )                        ( ( ( X ) >> 10 ) +( ( X ) >> 16 ) + ( ( X ) >> 17 ) )
53 
54 #define DIV_APPROX_60( X )                          ( ( ( X ) * 17476 ) >> 20 )
55 
56 #define DIV_APPROX_61( X )                          ( ( ( X ) * 68759 ) >> 22 )
57 
58 #define MODULO_7( X )                               ( ( X ) -( ( ( ( ( X ) + 1 ) * 299593 ) >> 21 ) * 7 ) )
59 
60 /*!
61  * \brief Calculates ceiling( X / N )
62  */
63 #define DIVC( X, N )                                ( ( ( X ) + ( N ) -1 ) / ( N ) )
64 
65 #define DIVC_BY_4( X )                              ( ( ( X ) + 3 ) >>2 )
66 
67 #define DIVC_BY_2( X )                              ( ( ( X ) + 1 ) >> 1 )
68 
69 static uint32_t CalendarGetMonth( uint32_t days, uint32_t year );
70 static void CalendarDiv86400( uint32_t in, uint32_t* out, uint32_t* remainder );
71 static uint32_t CalendarDiv61( uint32_t in );
72 static void CalendarDiv60( uint32_t in, uint32_t* out, uint32_t* remainder );
73 
74 const char *WeekDayString[]={ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
75 
SysTimeAdd(SysTime_t a,SysTime_t b)76 SysTime_t SysTimeAdd( SysTime_t a, SysTime_t b )
77 {
78     SysTime_t c =  { .Seconds = 0, .SubSeconds = 0 };
79 
80     c.Seconds = a.Seconds + b.Seconds;
81     c.SubSeconds = a.SubSeconds + b.SubSeconds;
82     if( c.SubSeconds >= 1000 )
83     {
84         c.Seconds++;
85         c.SubSeconds -= 1000;
86     }
87     return c;
88 }
89 
SysTimeSub(SysTime_t a,SysTime_t b)90 SysTime_t SysTimeSub( SysTime_t a, SysTime_t b )
91 {
92     SysTime_t c = { .Seconds = 0, .SubSeconds = 0 };
93 
94     c.Seconds = a.Seconds - b.Seconds;
95     c.SubSeconds = a.SubSeconds - b.SubSeconds;
96     if( c.SubSeconds < 0 )
97     {
98         c.Seconds--;
99         c.SubSeconds += 1000;
100     }
101     return c;
102 }
103 
SysTimeSet(SysTime_t sysTime)104 void SysTimeSet( SysTime_t sysTime )
105 {
106     SysTime_t deltaTime;
107 
108     SysTime_t calendarTime = { .Seconds = 0, .SubSeconds = 0 };
109 
110     calendarTime.Seconds = RtcGetCalendarTime( ( uint16_t* )&calendarTime.SubSeconds );
111 
112     // sysTime is epoch
113     deltaTime = SysTimeSub( sysTime, calendarTime );
114 
115     RtcBkupWrite( deltaTime.Seconds, ( uint32_t )deltaTime.SubSeconds );
116 }
117 
SysTimeGet(void)118 SysTime_t SysTimeGet( void )
119 {
120     SysTime_t calendarTime = { .Seconds = 0, .SubSeconds = 0 };
121     SysTime_t sysTime = { .Seconds = 0, .SubSeconds = 0 };
122     uint32_t seconds;
123     uint32_t subSeconds;
124 
125     calendarTime.Seconds = RtcGetCalendarTime( ( uint16_t* )&calendarTime.SubSeconds );
126 
127     RtcBkupRead( &seconds, &subSeconds );
128 
129     SysTime_t deltaTime = { .Seconds = seconds, .SubSeconds = ( int16_t )subSeconds };
130 
131     sysTime = SysTimeAdd( deltaTime, calendarTime );
132 
133     return sysTime;
134 }
135 
SysTimeGetMcuTime(void)136 SysTime_t SysTimeGetMcuTime( void )
137 {
138     SysTime_t calendarTime = { .Seconds = 0, .SubSeconds = 0 };
139 
140     calendarTime.Seconds = RtcGetCalendarTime( ( uint16_t* )&calendarTime.SubSeconds );
141 
142     return calendarTime;
143 }
144 
SysTimeToMs(SysTime_t sysTime)145 TimerTime_t SysTimeToMs( SysTime_t sysTime )
146 {
147     uint32_t seconds;
148     uint32_t subSeconds;
149 
150     RtcBkupRead( &seconds, &subSeconds );
151 
152     SysTime_t deltaTime = { .Seconds = seconds, .SubSeconds = ( int16_t )subSeconds };
153 
154     SysTime_t calendarTime = SysTimeSub( sysTime, deltaTime );
155 
156     return ( TimerTime_t )( calendarTime.Seconds * 1000 + calendarTime.SubSeconds );
157 }
158 
SysTimeFromMs(TimerTime_t timeMs)159 SysTime_t SysTimeFromMs( TimerTime_t timeMs )
160 {
161     uint32_t seconds = timeMs / 1000;
162     uint32_t subSeconds = timeMs - seconds * 1000;
163     SysTime_t sysTime = { .Seconds = seconds, .SubSeconds = ( int16_t )subSeconds };
164 
165     RtcBkupRead( &seconds, &subSeconds );
166 
167     SysTime_t deltaTime = { .Seconds = seconds, .SubSeconds = ( int16_t )subSeconds };
168 
169     return SysTimeAdd( sysTime, deltaTime );
170 }
171 
SysTimeMkTime(const struct tm * localtime)172 uint32_t SysTimeMkTime( const struct tm* localtime )
173 {
174     uint32_t nbdays;
175     uint32_t nbsecs;
176     uint32_t year = localtime->tm_year - UNIX_YEAR;
177     uint32_t correctionMonth[4] =
178     {
179         DAYS_IN_MONTH_CORRECTION_LEAP,
180         DAYS_IN_MONTH_CORRECTION_NORM,
181         DAYS_IN_MONTH_CORRECTION_NORM,
182         DAYS_IN_MONTH_CORRECTION_NORM
183     };
184 
185     nbdays = DIVC( ( TM_DAYS_IN_YEAR * 3 + TM_DAYS_IN_LEAP_YEAR ) * year, 4 );
186 
187     nbdays += ( DIVC_BY_2( ( localtime->tm_mon ) * ( 30 + 31 ) ) -
188                 ( ( ( correctionMonth[year % 4] >> ( ( localtime->tm_mon ) * 2 ) ) & 0x03 ) ) );
189 
190     nbdays += ( localtime->tm_mday - 1 );
191 
192     // Convert from days to seconds
193     nbsecs = nbdays * TM_SECONDS_IN_1DAY;
194 
195     nbsecs += ( ( uint32_t )localtime->tm_sec +
196                 ( ( uint32_t )localtime->tm_min * TM_SECONDS_IN_1MINUTE ) +
197                 ( ( uint32_t )localtime->tm_hour * TM_SECONDS_IN_1HOUR ) );
198     return nbsecs - UNIX_HOUR_OFFSET;
199 }
200 
201 
202 
SysTimeLocalTime(const uint32_t timestamp,struct tm * localtime)203 void SysTimeLocalTime( const uint32_t timestamp, struct tm *localtime )
204 {
205     uint32_t correctionMonth[4] =
206     {
207         DAYS_IN_MONTH_CORRECTION_LEAP,
208         DAYS_IN_MONTH_CORRECTION_NORM,
209         DAYS_IN_MONTH_CORRECTION_NORM,
210         DAYS_IN_MONTH_CORRECTION_NORM
211     };
212     uint32_t weekDays = 1; // Monday 1st January 1968
213     uint32_t seconds;
214     uint32_t minutes;
215     uint32_t days;
216     uint32_t divOut;
217     uint32_t divReminder;
218 
219     CalendarDiv86400( timestamp + UNIX_HOUR_OFFSET, &days, &seconds );
220 
221     // Calculates seconds
222     CalendarDiv60( seconds, &minutes, &divReminder );
223     localtime->tm_sec = ( uint8_t )divReminder;
224 
225     // Calculates minutes and hours
226     CalendarDiv60( minutes, &divOut, &divReminder);
227     localtime->tm_min = ( uint8_t )divReminder;
228     localtime->tm_hour = ( uint8_t )divOut;
229 
230     // Calculates year
231     localtime->tm_year = DIV_365_25( days );
232     days-= DIVC_BY_4( ( TM_DAYS_IN_YEAR * 3 + TM_DAYS_IN_LEAP_YEAR ) * localtime->tm_year );
233 
234     localtime->tm_yday = days;
235 
236     // Calculates month
237     localtime->tm_mon = CalendarGetMonth( days, localtime->tm_year );
238 
239     // calculates weekdays
240     weekDays += DIVC_BY_4( ( localtime->tm_year * 5 ) );
241     weekDays += days;
242     localtime->tm_wday = MODULO_7( weekDays );
243 
244     days -= ( DIVC_BY_2( ( localtime->tm_mon ) * ( 30 + 31 ) ) -
245               ( ( ( correctionMonth[localtime->tm_year % 4] >> ( ( localtime->tm_mon ) * 2 ) ) & 0x03 ) ) );
246 
247     // Convert 0 to 1 indexed.
248     localtime->tm_mday = days + 1;
249 
250     localtime->tm_year += UNIX_YEAR;
251 
252     localtime->tm_isdst = -1;
253 }
254 
CalendarGetMonth(uint32_t days,uint32_t year)255 static uint32_t CalendarGetMonth( uint32_t days, uint32_t year )
256 {
257     uint32_t month;
258     if( ( year % 4 ) == 0 )
259     {   /*leap year*/
260         if( days < END_OF_FEBRUARY_LEAP )
261         {   // January or February
262             // month =  days * 2 / ( 30 + 31 );
263             month = CalendarDiv61( days * 2 );
264         }
265         else if( days < END_OF_JULY_LEAP )
266         {
267             month = CalendarDiv61( ( days - END_OF_FEBRUARY_LEAP ) * 2 ) + 2;
268         }
269         else
270         {
271             month = CalendarDiv61( ( days - END_OF_JULY_LEAP ) * 2 ) + 7;
272         }
273     }
274     else
275     {
276         if( days < END_OF_FEBRUARY_NORM )
277         {   // January or February
278             month = CalendarDiv61( days * 2 );
279         }
280         else if( days < END_OF_JULY_NORM )
281         {
282             month = CalendarDiv61( ( days - END_OF_FEBRUARY_NORM ) * 2 ) + 2;
283         }
284         else
285         {
286             month = CalendarDiv61( ( days - END_OF_JULY_NORM ) * 2 ) + 7;
287         }
288     }
289     return month;
290 }
291 
CalendarDiv86400(uint32_t in,uint32_t * out,uint32_t * remainder)292 static void CalendarDiv86400( uint32_t in, uint32_t* out, uint32_t* remainder )
293 {
294 #if 0
295     *remainder = in % SECONDS_IN_1DAY;
296     *out       = in / SECONDS_IN_1DAY;
297 #else
298     uint32_t outTemp = 0;
299     uint32_t divResult = DIV_APPROX_86400( in );
300 
301     while( divResult >=1 )
302     {
303         outTemp += divResult;
304         in -= divResult * 86400;
305         divResult= DIV_APPROX_86400( in );
306     }
307     if( in >= 86400 )
308     {
309         outTemp += 1;
310         in -= 86400;
311     }
312 
313     *remainder = in;
314     *out = outTemp;
315 #endif
316 }
317 
CalendarDiv61(uint32_t in)318 static uint32_t CalendarDiv61( uint32_t in )
319 {
320 #if 0
321     return( in / 61 );
322 #else
323     uint32_t outTemp = 0;
324     uint32_t divResult = DIV_APPROX_61( in );
325     while( divResult >=1 )
326     {
327         outTemp += divResult;
328         in -= divResult * 61;
329         divResult = DIV_APPROX_61( in );
330     }
331     if( in >= 61 )
332     {
333         outTemp += 1;
334         in -= 61;
335     }
336     return outTemp;
337 #endif
338 }
339 
CalendarDiv60(uint32_t in,uint32_t * out,uint32_t * remainder)340 static void CalendarDiv60( uint32_t in, uint32_t* out, uint32_t* remainder )
341 {
342 #if 0
343     *remainder = in % 60;
344     *out       = in / 60;
345 #else
346     uint32_t outTemp = 0;
347     uint32_t divResult = DIV_APPROX_60( in );
348 
349     while( divResult >=1 )
350     {
351         outTemp += divResult;
352         in -= divResult * 60;
353         divResult = DIV_APPROX_60( in );
354     }
355     if( in >= 60 )
356     {
357         outTemp += 1;
358         in -= 60;
359     }
360     *remainder = in;
361     *out = outTemp;
362 #endif
363 }
364