5 #define _TWR_RTC_LEAP_YEAR(year) ((((year) % 4 == 0) && ((year) % 100 != 0)) || ((year) % 400 == 0))
6 #define _TWR_RTC_DAYS_IN_YEAR(x) _TWR_RTC_LEAP_YEAR(x) ? 366 : 365
7 #define _TWR_RTC_CALENDAR_CENTURY 2000
8 #define _TWR_RTC_OFFSET_YEAR 1970
9 #define _TWR_RTC_SECONDS_PER_DAY 86400
10 #define _TWR_RTC_SECONDS_PER_HOUR 3600
11 #define _TWR_RTC_SECONDS_PER_MINUTE 60
26 unsigned int res1 : 1;
29 unsigned int res2 : 1;
88 static const uint8_t is_leap[] = {
89 1, 0, 0, 0, 1, 0, 0, 0, 1, 0,
90 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
91 1, 0, 0, 0, 1, 0, 0, 0, 1, 0,
92 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
93 1, 0, 0, 0, 1, 0, 0, 0, 1, 0,
94 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
95 1, 0, 0, 0, 1, 0, 0, 0, 1, 0,
96 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
97 1, 0, 0, 0, 1, 0, 0, 0, 1, 0,
98 0, 0, 1, 0, 0, 0, 1, 0, 0, 0
114 static const uint16_t days_since_epoch[] = {
115 0x2acd, 0x2c3b, 0x2da8, 0x2f15, 0x3082, 0x31f0, 0x335d, 0x34ca, 0x3637, 0x37a5,
116 0x3912, 0x3a7f, 0x3bec, 0x3d5a, 0x3ec7, 0x4034, 0x41a1, 0x430f, 0x447c, 0x45e9,
117 0x4756, 0x48c4, 0x4a31, 0x4b9e, 0x4d0b, 0x4e79, 0x4fe6, 0x5153, 0x52c0, 0x542e,
118 0x559b, 0x5708, 0x5875, 0x59e3, 0x5b50, 0x5cbd, 0x5e2a, 0x5f98, 0x6105, 0x6272,
119 0x63df, 0x654d, 0x66ba, 0x6827, 0x6994, 0x6b02, 0x6c6f, 0x6ddc, 0x6f49, 0x70b7,
120 0x7224, 0x7391, 0x74fe, 0x766c, 0x77d9, 0x7946, 0x7ab3, 0x7c21, 0x7d8e, 0x7efb,
121 0x8068, 0x81d6, 0x8343, 0x84b0, 0x861d, 0x878b, 0x88f8, 0x8a65, 0x8bd2, 0x8d40,
122 0x8ead, 0x901a, 0x9187, 0x92f5, 0x9462, 0x95cf, 0x973c, 0x98aa, 0x9a17, 0x9b84,
123 0x9cf1, 0x9e5f, 0x9fcc, 0xa139, 0xa2a6, 0xa414, 0xa581, 0xa6ee, 0xa85b, 0xa9c9,
124 0xab36, 0xaca3, 0xae10, 0xaf7e, 0xb0eb, 0xb258, 0xb3c5, 0xb533, 0xb6a0, 0xb80d
135 static const uint16_t days_since_new_year[2][12] = {
136 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
137 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
140 void twr_rtc_init(
void)
146 uint32_t days = 0, seconds = 0;
148 int year = tm->tm_year + 1900;
151 if (year < _TWR_RTC_OFFSET_YEAR)
156 for (i = _TWR_RTC_OFFSET_YEAR; i < year; i++)
158 days += _TWR_RTC_DAYS_IN_YEAR(i);
161 days += days_since_new_year[_TWR_RTC_LEAP_YEAR(year)][tm->tm_mon];
164 days += tm->tm_mday - 1;
165 seconds = days * _TWR_RTC_SECONDS_PER_DAY;
166 seconds += tm->tm_hour * _TWR_RTC_SECONDS_PER_HOUR;
167 seconds += tm->tm_min * _TWR_RTC_SECONDS_PER_MINUTE;
168 seconds += tm->tm_sec;
185 } mem = { .dr.i = 0xFFFFFFFF, .tr.i = 0xFFFFFFFF, .tm.tm_isdst = -1 };
190 RTC_TR tr = { .i = RTC->TR };
191 RTC_DR dr = { .i = RTC->DR };
193 if (tr.i != mem.tr.i) {
194 mem.tm.tm_sec = 10 * tr.ST + tr.SU;
195 mem.tm.tm_min = 10 * tr.MNT + tr.MNU;
196 mem.tm.tm_hour = 10 * tr.HT + tr.HU;
206 if (dr.i != mem.dr.i) {
207 int year = 10 * dr.YT + dr.YU;
208 mem.tm.tm_mday = 10 * dr.DT + dr.DU;
209 mem.tm.tm_mon = 10 * dr.MT + dr.MU - 1;
210 mem.tm.tm_year = year + _TWR_RTC_CALENDAR_CENTURY - 1900;
211 mem.tm.tm_wday = dr.WDU == 7 ? 0 : dr.WDU;
212 mem.tm.tm_yday = days_since_new_year[is_leap[year]][mem.tm.tm_mon] + mem.tm.tm_mday - 1;
228 } mem = { .dr.i = 0xFFFFFFFF, .tr.i = 0xFFFFFFFF };
233 RTC_SSR ssr = { .i = RTC->SSR };
238 RTC_TR tr = { .i = RTC->TR };
239 RTC_DR dr = { .i = RTC->DR };
243 tv->tv_nsec = 1000000 * (TWR_RTC_PREDIV_S - 1 - ssr.SS) / TWR_RTC_PREDIV_S;
245 if (tr.i != mem.tr.i) {
246 mem.s1 = (10 * tr.HT + tr.HU) * _TWR_RTC_SECONDS_PER_HOUR
247 + (10 * tr.MNT + tr.MNU) * _TWR_RTC_SECONDS_PER_MINUTE
248 + 10 * tr.ST + tr.SU;
259 if (dr.i != mem.dr.i) {
260 int year = 10 * dr.YT + dr.YU;
261 mem.s2 = (days_since_epoch[year]
262 + days_since_new_year[is_leap[year]][10 * dr.MT + dr.MU - 1]
263 + 10 * dr.DT + dr.DU - 1) * _TWR_RTC_SECONDS_PER_DAY;
267 tv->tv_sec = mem.s1 + mem.s2;
272 int year = tm->tm_year + 1900 - _TWR_RTC_CALENDAR_CENTURY;
273 if (year < 0 || year > 99)
return -1;
275 int month = tm->tm_mon + 1;
277 if (ms < 0 || ms > 999)
return -2;
279 .SS = TWR_RTC_PREDIV_S - ms * (TWR_RTC_PREDIV_S + 1) / 1000
286 RTC_TR old_tr = { .i = RTC->TR };
289 .SU = tm->tm_sec % 10,
290 .ST = tm->tm_sec / 10,
292 .MNU = tm->tm_min % 10,
293 .MNT = tm->tm_min / 10,
295 .HU = tm->tm_hour % 10,
296 .HT = tm->tm_hour / 10,
306 RTC_DR old_dr = { .i = RTC->DR };
308 .DU = tm->tm_mday % 10,
309 .DT = tm->tm_mday / 10,
313 .WDU = tm->tm_wday == 0 ? 7 : tm->tm_wday,
318 twr_rtc_enable_write();
324 twr_rtc_disable_write();
332 RTC->ISR |= RTC_ISR_INIT;
335 while ((RTC->ISR & RTC_ISR_INITF) == 0)
341 RTC->ISR &= ~RTC_ISR_INIT;
uint32_t twr_rtc_datetime_to_timestamp(struct tm *tm)
Convert date and time to UNIX timestamp.
int _twr_rtc_writable_semaphore
Initialize real-time clock.
void twr_rtc_get_timestamp(struct timespec *tv)
void twr_rtc_get_datetime(struct tm *tm)
int twr_rtc_set_datetime(struct tm *tm, int ms)
void twr_rtc_set_init(bool state)
Enable or disable RTC initialization mode.