rtc_systime_service.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  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. * \author forest-rain
  26. */
  27. #include "board.h"
  28. #ifdef MULTI_RTIMER_USING_RTC_SYSTIME_SERVICE
  29. #include "hw_rtc_stm32.h"
  30. #include "rtc_systime_service.h"
  31. #define END_OF_FEBRUARY_LEAP 60 //31+29
  32. #define END_OF_JULY_LEAP 213 //31+29+...
  33. #define END_OF_FEBRUARY_NORM 59 //31+28
  34. #define END_OF_JULY_NORM 212 //31+28+...
  35. #define UNIX_YEAR 68 //1968 is leap year
  36. //UNIX time 0 = start at 01:00:00, 01/01/1970
  37. #define UNIX_HOUR_OFFSET ( ( TM_DAYS_IN_LEAP_YEAR + TM_DAYS_IN_YEAR ) * TM_SECONDS_IN_1DAY )
  38. /*!
  39. * \brief Correction factors
  40. */
  41. #define DAYS_IN_MONTH_CORRECTION_NORM ( (uint32_t )0x99AAA0 )
  42. #define DAYS_IN_MONTH_CORRECTION_LEAP ( (uint32_t )0x445550 )
  43. /* 365.25 = (366 + 365 + 365 + 365)/4 */
  44. #define DIV_365_25( X ) ( ( ( X ) * 91867 + 22750 ) >> 25 )
  45. #define DIV_APPROX_86400( X ) ( ( ( X ) >> 18 ) + ( ( X ) >> 17 ) )
  46. #define DIV_APPROX_1000( X ) ( ( ( X ) >> 10 ) +( ( X ) >> 16 ) + ( ( X ) >> 17 ) )
  47. #define DIV_APPROX_60( X ) ( ( ( X ) * 17476 ) >> 20 )
  48. #define DIV_APPROX_61( X ) ( ( ( X ) * 68759 ) >> 22 )
  49. #define MODULO_7( X ) ( ( X ) -( ( ( ( ( X ) + 1 ) * 299593 ) >> 21 ) * 7 ) )
  50. /*!
  51. * \brief Calculates ceiling( X / N )
  52. */
  53. #define DIVC( X, N ) ( ( ( X ) + ( N ) -1 ) / ( N ) )
  54. #define DIVC_BY_4( X ) ( ( ( X ) + 3 ) >>2 )
  55. #define DIVC_BY_2( X ) ( ( ( X ) + 1 ) >> 1 )
  56. static uint32_t CalendarGetMonth( uint32_t days, uint32_t year );
  57. static void CalendarDiv86400( uint32_t in, uint32_t* out, uint32_t* remainder );
  58. static uint32_t CalendarDiv61( uint32_t in );
  59. static void CalendarDiv60( uint32_t in, uint32_t* out, uint32_t* remainder );
  60. const char *WeekDayString[]={ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  61. SysTime_t SysTimeAdd( SysTime_t a, SysTime_t b )
  62. {
  63. SysTime_t c = { .Seconds = 0, .SubSeconds = 0 };
  64. c.Seconds = a.Seconds + b.Seconds;
  65. c.SubSeconds = a.SubSeconds + b.SubSeconds;
  66. if( c.SubSeconds >= 1000 )
  67. {
  68. c.Seconds++;
  69. c.SubSeconds -= 1000;
  70. }
  71. return c;
  72. }
  73. SysTime_t SysTimeSub( SysTime_t a, SysTime_t b )
  74. {
  75. SysTime_t c = { .Seconds = 0, .SubSeconds = 0 };
  76. c.Seconds = a.Seconds - b.Seconds;
  77. c.SubSeconds = a.SubSeconds - b.SubSeconds;
  78. if( c.SubSeconds < 0 )
  79. {
  80. c.Seconds--;
  81. c.SubSeconds += 1000;
  82. }
  83. return c;
  84. }
  85. void SysTimeSet( SysTime_t sysTime )
  86. {
  87. SysTime_t DeltaTime;
  88. SysTime_t calendarTime = { .Seconds = 0, .SubSeconds = 0 };
  89. calendarTime.Seconds = rtc_get_calendar_time( ( uint32_t* )&calendarTime.SubSeconds );
  90. // sysTime is epoch
  91. DeltaTime = SysTimeSub( sysTime, calendarTime );
  92. rtc_bkup_write( DeltaTime.Seconds, ( uint32_t )DeltaTime.SubSeconds );
  93. }
  94. SysTime_t SysTimeGet( void )
  95. {
  96. SysTime_t calendarTime = { .Seconds = 0, .SubSeconds = 0 };
  97. SysTime_t sysTime = { .Seconds = 0, .SubSeconds = 0 };
  98. SysTime_t DeltaTime;
  99. calendarTime.Seconds = rtc_get_calendar_time( ( uint32_t* )&calendarTime.SubSeconds );
  100. rtc_bkup_read( &DeltaTime.Seconds, ( uint32_t* )&DeltaTime.SubSeconds );
  101. sysTime = SysTimeAdd( DeltaTime, calendarTime );
  102. return sysTime;
  103. }
  104. SysTime_t SysTimeGetMcuTime( void )
  105. {
  106. SysTime_t calendarTime = { .Seconds = 0, .SubSeconds = 0 };
  107. calendarTime.Seconds = rtc_get_calendar_time( ( uint32_t* )&calendarTime.SubSeconds );
  108. return calendarTime;
  109. }
  110. uint32_t SysTimeToMs( SysTime_t sysTime )
  111. {
  112. SysTime_t DeltaTime;
  113. rtc_bkup_read( &DeltaTime.Seconds, ( uint32_t* )&DeltaTime.SubSeconds );
  114. SysTime_t calendarTime = SysTimeSub( sysTime, DeltaTime );
  115. return calendarTime.Seconds * 1000 + calendarTime.SubSeconds;
  116. }
  117. SysTime_t SysTimeFromMs( uint32_t timeMs )
  118. {
  119. uint32_t seconds = timeMs / 1000;
  120. SysTime_t sysTime = { .Seconds = seconds, .SubSeconds = timeMs - seconds * 1000 };
  121. SysTime_t DeltaTime = { 0 };
  122. rtc_bkup_read( &DeltaTime.Seconds, ( uint32_t* )&DeltaTime.SubSeconds );
  123. return SysTimeAdd( sysTime, DeltaTime );
  124. }
  125. uint32_t SysTimeMkTime( const struct tm* localtime )
  126. {
  127. uint32_t nbdays;
  128. uint32_t nbsecs;
  129. uint32_t year = localtime->tm_year - UNIX_YEAR;
  130. uint32_t correctionMonth[4] =
  131. {
  132. DAYS_IN_MONTH_CORRECTION_LEAP,
  133. DAYS_IN_MONTH_CORRECTION_NORM,
  134. DAYS_IN_MONTH_CORRECTION_NORM,
  135. DAYS_IN_MONTH_CORRECTION_NORM
  136. };
  137. nbdays = DIVC( ( TM_DAYS_IN_YEAR * 3 + TM_DAYS_IN_LEAP_YEAR ) * year, 4 );
  138. nbdays += ( DIVC_BY_2( ( localtime->tm_mon ) * ( 30 + 31 ) ) -
  139. ( ( ( correctionMonth[year % 4] >> ( ( localtime->tm_mon ) * 2 ) ) & 0x03 ) ) );
  140. nbdays += ( localtime->tm_mday - 1 );
  141. // Convert from days to seconds
  142. nbsecs = nbdays * TM_SECONDS_IN_1DAY;
  143. nbsecs += ( ( uint32_t )localtime->tm_sec +
  144. ( ( uint32_t )localtime->tm_min * TM_SECONDS_IN_1MINUTE ) +
  145. ( ( uint32_t )localtime->tm_hour * TM_SECONDS_IN_1HOUR ) );
  146. return nbsecs - UNIX_HOUR_OFFSET;
  147. }
  148. void SysTimeLocalTime( const uint32_t timestamp, struct tm *localtime )
  149. {
  150. uint32_t correctionMonth[4] =
  151. {
  152. DAYS_IN_MONTH_CORRECTION_LEAP,
  153. DAYS_IN_MONTH_CORRECTION_NORM,
  154. DAYS_IN_MONTH_CORRECTION_NORM,
  155. DAYS_IN_MONTH_CORRECTION_NORM
  156. };
  157. uint32_t weekDays = 1; // Monday 1st January 1968
  158. uint32_t seconds;
  159. uint32_t minutes;
  160. uint32_t days;
  161. uint32_t divOut;
  162. uint32_t divReminder;
  163. CalendarDiv86400( timestamp + UNIX_HOUR_OFFSET, &days, &seconds );
  164. // Calculates seconds
  165. CalendarDiv60( seconds, &minutes, &divReminder );
  166. localtime->tm_sec = ( uint8_t )divReminder;
  167. // Calculates minutes and hours
  168. CalendarDiv60( minutes, &divOut, &divReminder);
  169. localtime->tm_min = ( uint8_t )divReminder;
  170. localtime->tm_hour = ( uint8_t )divOut;
  171. // Calculates year
  172. localtime->tm_year = DIV_365_25( days );
  173. days-= DIVC_BY_4( ( TM_DAYS_IN_YEAR * 3 + TM_DAYS_IN_LEAP_YEAR ) * localtime->tm_year );
  174. localtime->tm_yday = days;
  175. // Calculates month
  176. localtime->tm_mon = CalendarGetMonth( days, localtime->tm_year );
  177. // calculates weekdays
  178. weekDays += DIVC_BY_4( ( localtime->tm_year * 5 ) );
  179. weekDays += days;
  180. localtime->tm_wday = MODULO_7( weekDays );
  181. days -= ( DIVC_BY_2( ( localtime->tm_mon ) * ( 30 + 31 ) ) -
  182. ( ( ( correctionMonth[localtime->tm_year % 4] >> ( ( localtime->tm_mon ) * 2 ) ) & 0x03 ) ) );
  183. // Convert 0 to 1 indexed.
  184. localtime->tm_mday = days + 1;
  185. localtime->tm_year += UNIX_YEAR;
  186. localtime->tm_isdst = -1;
  187. }
  188. static uint32_t CalendarGetMonth( uint32_t days, uint32_t year )
  189. {
  190. uint32_t month;
  191. if( ( year % 4 ) == 0 )
  192. { /*leap year*/
  193. if( days < END_OF_FEBRUARY_LEAP )
  194. { // January or February
  195. // month = days * 2 / ( 30 + 31 );
  196. month = CalendarDiv61( days * 2 );
  197. }
  198. else if( days < END_OF_JULY_LEAP )
  199. {
  200. month = CalendarDiv61( ( days - END_OF_FEBRUARY_LEAP ) * 2 ) + 2;
  201. }
  202. else
  203. {
  204. month = CalendarDiv61( ( days - END_OF_JULY_LEAP ) * 2 ) + 7;
  205. }
  206. }
  207. else
  208. {
  209. if( days < END_OF_FEBRUARY_NORM )
  210. { // January or February
  211. month = CalendarDiv61( days * 2 );
  212. }
  213. else if( days < END_OF_JULY_NORM )
  214. {
  215. month = CalendarDiv61( ( days - END_OF_FEBRUARY_NORM ) * 2 ) + 2;
  216. }
  217. else
  218. {
  219. month = CalendarDiv61( ( days - END_OF_JULY_NORM ) * 2 ) + 7;
  220. }
  221. }
  222. return month;
  223. }
  224. static void CalendarDiv86400( uint32_t in, uint32_t* out, uint32_t* remainder )
  225. {
  226. #if 0
  227. *remainder = in % SECONDS_IN_1DAY;
  228. *out = in / SECONDS_IN_1DAY;
  229. #else
  230. uint32_t outTemp = 0;
  231. uint32_t divResult = DIV_APPROX_86400( in );
  232. while( divResult >=1 )
  233. {
  234. outTemp += divResult;
  235. in -= divResult * 86400;
  236. divResult= DIV_APPROX_86400( in );
  237. }
  238. if( in >= 86400 )
  239. {
  240. outTemp += 1;
  241. in -= 86400;
  242. }
  243. *remainder = in;
  244. *out = outTemp;
  245. #endif
  246. }
  247. static uint32_t CalendarDiv61( uint32_t in )
  248. {
  249. #if 0
  250. return( in / 61 );
  251. #else
  252. uint32_t outTemp = 0;
  253. uint32_t divResult = DIV_APPROX_61( in );
  254. while( divResult >=1 )
  255. {
  256. outTemp += divResult;
  257. in -= divResult * 61;
  258. divResult = DIV_APPROX_61( in );
  259. }
  260. if( in >= 61 )
  261. {
  262. outTemp += 1;
  263. in -= 61;
  264. }
  265. return outTemp;
  266. #endif
  267. }
  268. static void CalendarDiv60( uint32_t in, uint32_t* out, uint32_t* remainder )
  269. {
  270. #if 0
  271. *remainder = in % 60;
  272. *out = in / 60;
  273. #else
  274. uint32_t outTemp = 0;
  275. uint32_t divResult = DIV_APPROX_60( in );
  276. while( divResult >=1 )
  277. {
  278. outTemp += divResult;
  279. in -= divResult * 60;
  280. divResult = DIV_APPROX_60( in );
  281. }
  282. if( in >= 60 )
  283. {
  284. outTemp += 1;
  285. in -= 60;
  286. }
  287. *remainder = in;
  288. *out = outTemp;
  289. #endif
  290. }
  291. #endif /* MULTI_RTIMER_USING_RTC_SYSTIME_SERVICE */