/* * Copyright (C) 2020 Kaspar Schleiser * 2020 Freie Universität Berlin * 2020 Inria * * This file is subject to the terms and conditions of the GNU Lesser General * Public License v2.1. See the file LICENSE in the top level directory for more * details. */ /** * @ingroup sys_ztimer_periph_rtc * @{ * * @file * @brief ztimer periph/rtc backend implementation * * This implementation simply converts an integer time to split RTC values and * back, which is rather inefficient. If available, use ztimer_periph_rtt. * * @author Kaspar Schleiser * * @} */ #include "periph/rtc.h" #include "ztimer/periph_rtc.h" #define ENABLE_DEBUG 0 #include "debug.h" /* this algorithm and the one from _timestamp_to_gmt_civil() have been adapted from * http://ptspts.blogspot.com/2009/11/how-to-convert-unix-timestamp-to-civil.html. * * "The algorithmic solution above is part of the programming folklore." */ static uint32_t _gmt_civil_to_timestamp(unsigned y, unsigned m, unsigned d, unsigned h, unsigned mi, unsigned s) { /* struct tm counts months from 0 to 11 */ m += 1; if (m <= 2) { y -= 1; m += 12; } return (365 * y + y / 4 - y / 100 + y / 400 + 3 * (m + 1) / 5 + 30 * m + d - 719561) * 86400 + 3600 * h + 60 * mi + s; } static void _timestamp_to_gmt_civil(struct tm *_tm, uint32_t epoch) { uint32_t s = epoch % 86400; epoch /= 86400; uint32_t h = s / 3600; uint32_t m = s / 60 % 60; s = s % 60; uint32_t x = (epoch * 4 + 102032) / 146097 + 15; uint32_t b = epoch + 2442113 + x - (x / 4); uint32_t c = (b * 20 - 2442) / 7305; uint32_t d = b - 365 * c - c / 4; uint32_t e = d * 1000 / 30601; uint32_t f = d - e * 30 - e * 601 / 1000; if (e < 14) { struct tm tmp = { .tm_year = c - 4716 - 1900, .tm_mon = e - 1, .tm_mday = f, .tm_hour = h, .tm_min = m, .tm_sec = s }; *_tm = tmp; } else { struct tm tmp = { .tm_year = c - 4715 - 1900, .tm_mon = e - 13, .tm_mday = f, .tm_hour = h, .tm_min = m, .tm_sec = s }; *_tm = tmp; } /* struct tm counts months starting from 0 */ _tm->tm_mon -= 1; } static void _ztimer_periph_rtc_callback(void *arg) { ztimer_handler((ztimer_clock_t *)arg); } static uint32_t _ztimer_periph_rtc_now(ztimer_clock_t *clock) { (void)clock; struct tm time = { .tm_year = 0 }; rtc_get_time(&time); return _gmt_civil_to_timestamp(time.tm_year + 1900, time.tm_mon, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec); } static void _ztimer_periph_rtc_set(ztimer_clock_t *clock, uint32_t val) { unsigned state = irq_disable(); uint32_t now = _ztimer_periph_rtc_now(NULL); uint32_t target; do { /* make sure there's no pending ISR */ rtc_clear_alarm(); target = now + val; struct tm _tm = { .tm_year = 0 }; _timestamp_to_gmt_civil(&_tm, target); /* TODO: ensure this doesn't underflow */ rtc_set_alarm(&_tm, _ztimer_periph_rtc_callback, clock); if (val > 1) { /* If val <= 1, it is possible that the RTC second flips somewhere * between getting the current value and adding 1, resulting in * setting the current time as target, which in turn would make the * RTC never trigger. In that case, check the target that as been * set is still in the future at the end of the loop body. * * Skip that if val was more than a second away. */ break; } } while (target <= (now = _ztimer_periph_rtc_now(NULL))); irq_restore(state); } static void _ztimer_periph_rtc_cancel(ztimer_clock_t *clock) { (void)clock; rtc_clear_alarm(); } #if MODULE_ZTIMER_ONDEMAND_RTC static void _ztimer_periph_rtc_start(ztimer_clock_t *clock) { (void)clock; rtc_poweron(); } static void _ztimer_periph_rtc_stop(ztimer_clock_t *clock) { (void)clock; rtc_poweroff(); } #endif /* MODULE_ZTIMER_ONDEMAND_RTC */ static const ztimer_ops_t _ztimer_periph_rtc_ops = { .set = _ztimer_periph_rtc_set, .now = _ztimer_periph_rtc_now, .cancel = _ztimer_periph_rtc_cancel, #if MODULE_ZTIMER_ONDEMAND_RTC .start = _ztimer_periph_rtc_start, .stop = _ztimer_periph_rtc_stop, #endif }; void ztimer_periph_rtc_init(ztimer_periph_rtc_t *clock) { clock->ops = &_ztimer_periph_rtc_ops; clock->max_value = UINT32_MAX; rtc_init(); #if MODULE_ZTIMER_ONDEMAND_RTC rtc_poweroff(); #else rtc_poweron(); #endif }