diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h index 67dcf2760056e4ac2e6715b522865b403947d654..3ed02dabb5e1aad6f8c64867e740ab5afd3b370f 100644 --- a/epicardium/epicardium.h +++ b/epicardium/epicardium.h @@ -81,6 +81,8 @@ typedef _Bool bool; #define API_RTC_SCHEDULE_ALARM 0x51 #define API_RTC_SET_MILLISECONDS 0x52 #define API_RTC_GET_MILLISECONDS 0x53 +#define API_RTC_GET_MONOTONIC_SECONDS 0x54 +#define API_RTC_GET_MONOTONIC_MILLISECONDS 0x55 #define API_LEDS_SET 0x60 #define API_LEDS_SET_HSV 0x61 @@ -1733,6 +1735,24 @@ API(API_FILE_MKDIR, int epic_file_mkdir(const char *dirname)); * === */ +/** + * Get the monotonic time in seconds. + * + * :return: monotonic time in seconds + */ +API(API_RTC_GET_MONOTONIC_SECONDS, + uint32_t epic_rtc_get_monotonic_seconds(void) +); + +/** + * Get the monotonic time in ms. + * + * :return: monotonic time in milliseconds + */ +API(API_RTC_GET_MONOTONIC_MILLISECONDS, + uint64_t epic_rtc_get_monotonic_milliseconds(void) +); + /** * Read the current RTC value. * diff --git a/epicardium/modules/rtc.c b/epicardium/modules/rtc.c index de1ef70d26653a8b1f2441b8bf6a62a3d0124ef7..b563462dbc242194e7f931721d82fcd21d0b344e 100644 --- a/epicardium/modules/rtc.c +++ b/epicardium/modules/rtc.c @@ -9,6 +9,18 @@ #include <stdint.h> +uint64_t monotonic_offset = 0; + +uint32_t epic_rtc_get_monotonic_seconds(void) +{ + return epic_rtc_get_seconds() + monotonic_offset / 1000ULL; +} + +uint64_t epic_rtc_get_monotonic_milliseconds(void) +{ + return epic_rtc_get_milliseconds() + monotonic_offset; +} + uint32_t epic_rtc_get_seconds(void) { uint32_t sec, subsec; @@ -32,12 +44,31 @@ uint64_t epic_rtc_get_milliseconds(void) while (RTC_GetTime(&sec, &subsec) == E_BUSY) { vTaskDelay(pdMS_TO_TICKS(4)); } - return subsec * 1000ULL / 4096 + sec * 1000ULL; + + // Without the bias of 999 (0.24 milliseconds), this decoding function is + // numerically unstable: + // + // Encoding 5 milliseconds into 20 subsecs (using the encoding function in + // epic_rtc_set_milliseconds) and decoding it without the bias of 999 yields + // 4 milliseconds. + // + // The following invariants should hold when encoding / decoding from and to + // milliseconds / subseconds: + // + // - 0 <= encode(ms) < 4096 for 0 <= ms < 1000 + // - decode(encode(ms)) == ms for 0 <= ms < 1000 + // - 0 <= decode(subsec) < 1000 for 0 <= subsec < 4096 + // + // These invariants were proven experimentally. + return (subsec * 1000ULL + 999ULL) / 4096 + sec * 1000ULL; } void epic_rtc_set_milliseconds(uint64_t milliseconds) { uint32_t sec, subsec; + uint64_t old_milliseconds, diff; + + old_milliseconds = epic_rtc_get_milliseconds(); sec = milliseconds / 1000; subsec = (milliseconds % 1000); @@ -48,6 +79,9 @@ void epic_rtc_set_milliseconds(uint64_t milliseconds) ; while (RTC_EnableRTCE(MXC_RTC) == E_BUSY) ; + + diff = old_milliseconds - milliseconds; + monotonic_offset += diff; } void RTC_IRQHandler(void) diff --git a/pycardium/modules/qstrdefs.h b/pycardium/modules/qstrdefs.h index 2e24b0504929c48546845cc9d7942f5f0a6e7536..0430d4d7c2df04658765d8cfa99b475ff84c6d87 100644 --- a/pycardium/modules/qstrdefs.h +++ b/pycardium/modules/qstrdefs.h @@ -48,6 +48,8 @@ Q(ticks_add) Q(ticks_diff) Q(localtime) Q(mktime) +Q(monotonic) +Q(monotonic_ms) Q(time) Q(time_ms) Q(set_time) diff --git a/pycardium/modules/utime.c b/pycardium/modules/utime.c index 4b06a9cd5419ffb77f433d3a4fed2a3a464df63b..9f71eef51e0dd22bec48f9c2091851c8c509cb27 100644 --- a/pycardium/modules/utime.c +++ b/pycardium/modules/utime.c @@ -51,6 +51,22 @@ static mp_obj_t time_time_ms(void) } MP_DEFINE_CONST_FUN_OBJ_0(time_time_ms_obj, time_time_ms); +static mp_obj_t time_monotonic(void) +{ + mp_int_t seconds; + seconds = epic_rtc_get_monotonic_seconds(); + return mp_obj_new_int(seconds); +} +MP_DEFINE_CONST_FUN_OBJ_0(time_monotonic_obj, time_monotonic); + +static mp_obj_t time_monotonic_ms(void) +{ + uint64_t milliseconds; + milliseconds = epic_rtc_get_monotonic_milliseconds(); + return mp_obj_new_int_from_ull(milliseconds); +} +MP_DEFINE_CONST_FUN_OBJ_0(time_monotonic_ms_obj, time_monotonic_ms); + static mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) { mp_int_t seconds; @@ -130,6 +146,9 @@ static const mp_rom_map_elem_t time_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utime) }, { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&time_time_obj) }, { MP_ROM_QSTR(MP_QSTR_time_ms), MP_ROM_PTR(&time_time_ms_obj) }, + { MP_ROM_QSTR(MP_QSTR_monotonic), MP_ROM_PTR(&time_monotonic_obj) }, + { MP_ROM_QSTR(MP_QSTR_monotonic_ms), + MP_ROM_PTR(&time_monotonic_ms_obj) }, { MP_ROM_QSTR(MP_QSTR_set_time), MP_ROM_PTR(&time_set_time_obj) }, { MP_ROM_QSTR(MP_QSTR_set_unix_time), MP_ROM_PTR(&time_set_unix_time_obj) },