diff --git a/cpu/atmega_common/Kconfig b/cpu/atmega_common/Kconfig index 7130759aee..a35a7f3258 100644 --- a/cpu/atmega_common/Kconfig +++ b/cpu/atmega_common/Kconfig @@ -20,6 +20,7 @@ config CPU_COMMON_ATMEGA select HAS_PERIPH_GPIO select HAS_PERIPH_GPIO_IRQ select HAS_PERIPH_PM + select HAS_PERIPH_RTC_MS select HAS_PERIPH_RTT_SET_COUNTER select HAS_PERIPH_TIMER_PERIODIC select HAS_PERIPH_RTT_OVERFLOW diff --git a/cpu/atmega_common/Makefile.features b/cpu/atmega_common/Makefile.features index 5303f12db8..1987a53914 100644 --- a/cpu/atmega_common/Makefile.features +++ b/cpu/atmega_common/Makefile.features @@ -10,6 +10,7 @@ FEATURES_PROVIDED += periph_cpuid FEATURES_PROVIDED += periph_eeprom FEATURES_PROVIDED += periph_gpio periph_gpio_irq FEATURES_PROVIDED += periph_pm +FEATURES_PROVIDED += periph_rtc_ms FEATURES_PROVIDED += periph_rtt_set_counter FEATURES_PROVIDED += periph_timer_periodic FEATURES_PROVIDED += periph_rtt_overflow diff --git a/cpu/atmega_common/periph/rtc.c b/cpu/atmega_common/periph/rtc.c index 086438be51..dfea732364 100644 --- a/cpu/atmega_common/periph/rtc.c +++ b/cpu/atmega_common/periph/rtc.c @@ -103,6 +103,28 @@ int rtc_get_time(struct tm *time) return 0; } +int rtc_get_time_ms(struct tm *time, uint16_t *ms) +{ + uint8_t cnt_before, cnt_after; + + /* loop in case of overflow */ + do { + cnt_before = TCNT2; + + /* prevent compiler from reordering memory access to tm_now, + * including moving it out of the loop + */ + __asm__ volatile ("" : : : "memory"); + *time = tm_now; + + cnt_after = TCNT2; + } while (cnt_before > cnt_after); + + *ms = (cnt_after * 1000UL) >> 8; + + return 0; +} + int rtc_get_alarm(struct tm *time) { *time = tm_alarm; diff --git a/cpu/lpc23xx/Kconfig b/cpu/lpc23xx/Kconfig index 49d96963bf..e44ce63356 100644 --- a/cpu/lpc23xx/Kconfig +++ b/cpu/lpc23xx/Kconfig @@ -15,6 +15,7 @@ config CPU_FAM_LPC23XX select HAS_PERIPH_GPIO select HAS_PERIPH_GPIO_IRQ select HAS_PERIPH_TIMER_PERIODIC + select HAS_PERIPH_RTC_MS ## CPU Models config CPU_MODEL_LPC2387 diff --git a/cpu/lpc23xx/Makefile.features b/cpu/lpc23xx/Makefile.features index 0d9de8eb1c..850654fb2d 100644 --- a/cpu/lpc23xx/Makefile.features +++ b/cpu/lpc23xx/Makefile.features @@ -4,5 +4,6 @@ FEATURES_PROVIDED += periph_dac FEATURES_PROVIDED += periph_flashpage FEATURES_PROVIDED += periph_gpio periph_gpio_irq FEATURES_PROVIDED += periph_timer_periodic +FEATURES_PROVIDED += periph_rtc_ms include $(RIOTCPU)/arm7_common/Makefile.features diff --git a/cpu/lpc23xx/periph/rtc.c b/cpu/lpc23xx/periph/rtc.c index 2680d1806a..475aba0aa7 100644 --- a/cpu/lpc23xx/periph/rtc.c +++ b/cpu/lpc23xx/periph/rtc.c @@ -28,6 +28,7 @@ #include "periph/rtc.h" #include "VIC.h" #include "lpc23xx.h" +#include "timex.h" #define ENABLE_DEBUG 0 #include "debug.h" @@ -110,6 +111,22 @@ int rtc_get_time(struct tm *localt) return 0; } +int rtc_get_time_ms(struct tm *time, uint16_t *ms) +{ + uint16_t ccr_before, ccr_after; + + /* loop in case of overflow */ + do { + ccr_before = RTC_CCR >> 1; + rtc_get_time(time); + ccr_after = RTC_CCR >> 1; + } while (ccr_before > ccr_after); + + /* CCR is 15-bit counter, increments second with each overflow */ + *ms = (ccr_after * MS_PER_SEC) >> 15; + + return 0; +} int rtc_set_alarm(struct tm *localt, rtc_alarm_cb_t cb, void *arg) { diff --git a/drivers/include/periph/rtc.h b/drivers/include/periph/rtc.h index 9a424fecc7..0071711999 100644 --- a/drivers/include/periph/rtc.h +++ b/drivers/include/periph/rtc.h @@ -90,6 +90,19 @@ int rtc_set_time(struct tm *time); */ int rtc_get_time(struct tm *time); +/** + * @brief Get current RTC time with sub-second component. + * Requires the `periph_rtc_ms` feature. + * + * @param[out] time Pointer to the struct to write the time to. + * @param[out] ms Pointer to a variable to hold the microsecond + * component of the current RTC time. + * + * @return 0 for success + * @return -1 an error occurred + */ +int rtc_get_time_ms(struct tm *time, uint16_t *ms); + /** * @brief Set an alarm for RTC to the specified value. * diff --git a/drivers/rtt_rtc/Makefile.features b/drivers/rtt_rtc/Makefile.features new file mode 100644 index 0000000000..1ed82b05fe --- /dev/null +++ b/drivers/rtt_rtc/Makefile.features @@ -0,0 +1,2 @@ +FEATURES_PROVIDED += periph_rtc +FEATURES_PROVIDED += periph_rtc_ms diff --git a/drivers/rtt_rtc/rtt_rtc.c b/drivers/rtt_rtc/rtt_rtc.c index 878bc6ea94..2e16ac69c6 100644 --- a/drivers/rtt_rtc/rtt_rtc.c +++ b/drivers/rtt_rtc/rtt_rtc.c @@ -28,6 +28,7 @@ #include "periph/rtc.h" #include "periph/rtt.h" +#include "timex.h" #define ENABLE_DEBUG 0 #include "debug.h" @@ -40,6 +41,7 @@ #define TICKS(x) ( (x) * RTT_SECOND) #define SECONDS(x) (_RTT(x) / RTT_SECOND) +#define SUBSECONDS(x) (_RTT(x) % RTT_SECOND) /* Place counter in .noinit section if no backup RAM is available. This means the date is undefined at cold boot, but will likely still @@ -133,6 +135,22 @@ int rtc_set_time(struct tm *time) return 0; } +int rtc_get_time_ms(struct tm *time, uint16_t *ms) +{ + uint32_t prev = rtc_now; + + /* repeat calculation if an alarm triggered in between */ + do { + uint32_t now = rtt_get_counter(); + uint32_t tmp = _rtc_now(now); + + rtc_localtime(tmp, time); + *ms = (SUBSECONDS(now) * MS_PER_SEC) / RTT_SECOND; + } while (prev != rtc_now); + + return 0; +} + int rtc_get_time(struct tm *time) { uint32_t prev = rtc_now; diff --git a/kconfigs/Kconfig.features b/kconfigs/Kconfig.features index f7acc8e560..43b800f18a 100644 --- a/kconfigs/Kconfig.features +++ b/kconfigs/Kconfig.features @@ -252,6 +252,11 @@ config HAS_PERIPH_RTC help Indicates that an RTC peripheral is present. +config HAS_PERIPH_RTC_MS + bool + help + Indicates that the RTC peripheral can provide sub-second timestamps. + config HAS_PERIPH_RTT bool help diff --git a/tests/periph_rtc/Makefile b/tests/periph_rtc/Makefile index e549720ff4..3d33287f19 100644 --- a/tests/periph_rtc/Makefile +++ b/tests/periph_rtc/Makefile @@ -1,6 +1,7 @@ include ../Makefile.tests_common -FEATURES_REQUIRED = periph_rtc +FEATURES_REQUIRED += periph_rtc +FEATURES_OPTIONAL += periph_rtc_ms DISABLE_MODULE += periph_init_rtc diff --git a/tests/periph_rtc/main.c b/tests/periph_rtc/main.c index 09c4c41999..db798a6fd0 100644 --- a/tests/periph_rtc/main.c +++ b/tests/periph_rtc/main.c @@ -47,6 +47,18 @@ static void print_time(const char *label, const struct tm *time) time->tm_sec); } +static void print_time_ms(const char *label, const struct tm *time, uint16_t ms) +{ + printf("%s %04d-%02d-%02d %02d:%02d:%02d.%03d\n", label, + time->tm_year + TM_YEAR_OFFSET, + time->tm_mon + 1, + time->tm_mday, + time->tm_hour, + time->tm_min, + time->tm_sec, + ms); +} + static void inc_secs(struct tm *time, unsigned val) { time->tm_sec += val; @@ -81,8 +93,14 @@ int main(void) rtc_set_time(&time); /* read RTC to confirm value */ - rtc_get_time(&time); - print_time("Clock value is now ", &time); + if (IS_USED(MODULE_PERIPH_RTC_MS)) { + uint16_t ms; + rtc_get_time_ms(&time, &ms); + print_time_ms("Clock value is now ", &time, ms); + } else { + rtc_get_time(&time); + print_time("Clock value is now ", &time); + } /* set initial alarm */ inc_secs(&time, PERIOD); @@ -95,17 +113,32 @@ int main(void) /* clear alarm */ rtc_clear_alarm(); - rtc_get_time(&time); - print_time(" Alarm cleared at ", &time); + if (IS_USED(MODULE_PERIPH_RTC_MS)) { + uint16_t ms; + rtc_get_time_ms(&time, &ms); + print_time_ms(" Alarm cleared at ", &time, ms); + } else { + rtc_get_time(&time); + print_time(" Alarm cleared at ", &time); + } /* verify alarm has been cleared */ xtimer_sleep(PERIOD); - rtc_get_time(&time); + + const char *message; if (mutex_trylock(&rtc_mtx)) { - print_time(" Error: Alarm at ", &time); + message = " Error: Alarm at "; + } else { + message = " No alarm at "; } - else { - print_time(" No alarm at ", &time); + + if (IS_USED(MODULE_PERIPH_RTC_MS)) { + uint16_t ms; + rtc_get_time_ms(&time, &ms); + print_time_ms(message, &time, ms); + } else { + rtc_get_time(&time); + print_time(message, &time); } /* set alarm */