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