diff --git a/drivers/include/rtt_rtc.h b/drivers/include/rtt_rtc.h new file mode 100644 index 0000000000..d68c72a8af --- /dev/null +++ b/drivers/include/rtt_rtc.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2021 ML!PA Consulting GmbH + * + * 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. + */ + +/** + * @defgroup drivers_rtt_rtc RTC emulation on top of a RTT + * @ingroup drivers_periph_rtc + * + * @{ + * + * @file + * @brief Additional functions provided in addition to the normal RTC API. + * + * @author Benjamin Valentin + */ + +#ifndef RTT_RTC_H +#define RTT_RTC_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Set the time as epoch with sub-second precision + * This feature is an extension provided by the `rtt_rtc` module. + * + * @note The actual µs precision depends on the underlying hardware. + * The smallest time step will be 1 / @ref RTT_FREQUENCY. + * + * @param[in] s The new epoch timestamp + * @param[in] us Sub-Seconds + */ +void rtt_rtc_settimeofday(uint32_t s, uint32_t us); + +/** + * @brief Get the current epoch with sub-second precision + * This feature is an extension provided by the `rtt_rtc` module. + * + * @note The actual µs precision depends on the underlying hardware. + * The smallest time step will be 1 / @ref RTT_FREQUENCY. + * + * @param[out] s The current epoch timestamp + * @param[out] us Sub-Seconds + */ +void rtt_rtc_gettimeofday(uint32_t *s, uint32_t *us); + +#ifdef __cplusplus +} +#endif + +#endif /* RTT_RTC_H */ +/** @} */ diff --git a/drivers/rtt_rtc/rtt_rtc.c b/drivers/rtt_rtc/rtt_rtc.c index 2e16ac69c6..0de3fe296c 100644 --- a/drivers/rtt_rtc/rtt_rtc.c +++ b/drivers/rtt_rtc/rtt_rtc.c @@ -7,7 +7,7 @@ */ /** - * @ingroup drivers_periph_rtc + * @ingroup drivers_rtt_rtc * @{ * * @file @@ -26,9 +26,11 @@ #include #include +#include "irq.h" #include "periph/rtc.h" #include "periph/rtt.h" #include "timex.h" +#include "rtt_rtc.h" #define ENABLE_DEBUG 0 #include "debug.h" @@ -137,31 +139,31 @@ int rtc_set_time(struct tm *time) int rtc_get_time_ms(struct tm *time, uint16_t *ms) { - uint32_t prev = rtc_now; + uint32_t now, tmp; + unsigned state = irq_disable(); - /* repeat calculation if an alarm triggered in between */ - do { - uint32_t now = rtt_get_counter(); - uint32_t tmp = _rtc_now(now); + now = rtt_get_counter(); + tmp = _rtc_now(now); - rtc_localtime(tmp, time); - *ms = (SUBSECONDS(now) * MS_PER_SEC) / RTT_SECOND; - } while (prev != rtc_now); + *ms = (SUBSECONDS(now - last_alarm) * MS_PER_SEC) + / RTT_SECOND; + + irq_restore(state); + rtc_localtime(tmp, time); return 0; } int rtc_get_time(struct tm *time) { - uint32_t prev = rtc_now; + uint32_t now, tmp; + unsigned state = irq_disable(); - /* repeat calculation if an alarm triggered in between */ - do { - uint32_t now = rtt_get_counter(); - uint32_t tmp = _rtc_now(now); + now = rtt_get_counter(); + tmp = _rtc_now(now); - rtc_localtime(tmp, time); - } while (prev != rtc_now); + irq_restore(state); + rtc_localtime(tmp, time); return 0; } @@ -184,6 +186,7 @@ int rtc_set_alarm(struct tm *time, rtc_alarm_cb_t cb, void *arg) alarm_cb_arg = arg; alarm_cb = cb; + /* RTT interrupt is disabled here */ rtc_now = _rtc_now(now); _update_alarm(now); @@ -204,3 +207,27 @@ void rtc_poweroff(void) { rtt_poweroff(); } + +void rtt_rtc_settimeofday(uint32_t s, uint32_t us) +{ + /* disable alarm to prevent race condition */ + rtt_clear_alarm(); + uint32_t now = ((uint64_t)us * RTT_SECOND) / US_PER_SEC; + rtc_now = s; + rtt_set_counter(now); + /* calculate next wake-up period */ + _update_alarm(0); +} + +void rtt_rtc_gettimeofday(uint32_t *s, uint32_t *us) +{ + uint32_t now; + unsigned state = irq_disable(); + + now = rtt_get_counter(); + *s = _rtc_now(now); + *us = ((uint64_t)SUBSECONDS(now - last_alarm) * US_PER_SEC) + / RTT_SECOND; + + irq_restore(state); +} diff --git a/tests/unittests/Makefile.ci b/tests/unittests/Makefile.ci index b6c962d724..2e9b410403 100644 --- a/tests/unittests/Makefile.ci +++ b/tests/unittests/Makefile.ci @@ -14,6 +14,7 @@ BOARD_INSUFFICIENT_MEMORY := \ atmega256rfr2-xpro \ atmega328p \ atmega328p-xplained-mini \ + avsextrem \ b-l072z-lrwan1 \ blackpill \ blackpill-128kib \ diff --git a/tests/unittests/tests-rtt_rtc/tests-rtt_rtc.c b/tests/unittests/tests-rtt_rtc/tests-rtt_rtc.c index a759419536..82059fbd88 100644 --- a/tests/unittests/tests-rtt_rtc/tests-rtt_rtc.c +++ b/tests/unittests/tests-rtt_rtc/tests-rtt_rtc.c @@ -17,6 +17,8 @@ #include "embUnit.h" #include "periph/rtc.h" #include "periph/rtt.h" +#include "timex.h" +#include "rtt_rtc.h" void rtt_add_ticks(uint64_t ticks); @@ -203,6 +205,29 @@ static void test_set_alarm_set_time(void) TEST_ASSERT_EQUAL_INT(2, alarm.tm_isdst); } +static void test_rtt_rtc_settimeofday(void) +{ + uint32_t s = 10, sec; + uint32_t us = US_PER_SEC / 8, micro_sec; + + rtt_rtc_settimeofday(s, us); + rtt_rtc_gettimeofday(&sec, µ_sec); + + TEST_ASSERT_EQUAL_INT(s, sec); + TEST_ASSERT_EQUAL_INT(us, micro_sec); + + rtt_add_ticks(1LU * RTT_FREQUENCY); + s++; + rtt_rtc_gettimeofday(&sec, µ_sec); + TEST_ASSERT_EQUAL_INT(s, sec); + + rtt_add_ticks(RTT_FREQUENCY / 4); + us += US_PER_SEC / 4; + rtt_rtc_gettimeofday(&sec, µ_sec); + TEST_ASSERT_EQUAL_INT(s, sec); + TEST_ASSERT_EQUAL_INT(us, micro_sec); +} + Test *tests_rtt_rtt_tests(void) { EMB_UNIT_TESTFIXTURES(fixtures) { @@ -210,6 +235,7 @@ Test *tests_rtt_rtt_tests(void) new_TestFixture(test_set_alarm), new_TestFixture(test_set_alarm_short), new_TestFixture(test_set_alarm_set_time), + new_TestFixture(test_rtt_rtc_settimeofday), }; EMB_UNIT_TESTCALLER(rtt_rtc_tests, NULL, NULL, fixtures);