rp2: Implement time.time_ns with time_us_64 so it has us resolution.

Currently on rp2 the time.time_ns() function has only seconds resolution.
This commit makes it have microsecond resolution, by using the output of
time_us_64() instead of the RTC.

Tested that it does not drift from the RTC over long periods of time.

Signed-off-by: Damien George <damien.p.george@gmail.com>
This commit is contained in:
Damien George 2023-10-05 16:32:19 +11:00
parent c2e9a6f2a5
commit 6f76d1c7fa
4 changed files with 27 additions and 2 deletions

View File

@ -58,6 +58,7 @@ STATIC mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, s
rtc_init(); rtc_init();
datetime_t t = { .month = 1, .day = 1 }; datetime_t t = { .month = 1, .day = 1 };
rtc_set_datetime(&t); rtc_set_datetime(&t);
mp_hal_time_ns_set_from_rtc();
} }
// return constant object // return constant object
return (mp_obj_t)&machine_rtc_obj; return (mp_obj_t)&machine_rtc_obj;
@ -104,6 +105,7 @@ STATIC mp_obj_t machine_rtc_datetime(mp_uint_t n_args, const mp_obj_t *args) {
if (!rtc_set_datetime(&t)) { if (!rtc_set_datetime(&t)) {
mp_raise_OSError(MP_EINVAL); mp_raise_OSError(MP_EINVAL);
} }
mp_hal_time_ns_set_from_rtc();
} }
return mp_const_none; return mp_const_none;

View File

@ -106,6 +106,7 @@ int main(int argc, char **argv) {
}; };
rtc_init(); rtc_init();
rtc_set_datetime(&t); rtc_set_datetime(&t);
mp_hal_time_ns_set_from_rtc();
// Initialise stack extents and GC heap. // Initialise stack extents and GC heap.
mp_stack_set_top(&__StackTop); mp_stack_set_top(&__StackTop);

View File

@ -39,6 +39,10 @@
#include "lib/cyw43-driver/src/cyw43.h" #include "lib/cyw43-driver/src/cyw43.h"
#endif #endif
// This needs to be added to the result of time_us_64() to get the number of
// microseconds since the Epoch.
STATIC uint64_t time_us_64_offset_from_epoch;
#if MICROPY_HW_ENABLE_UART_REPL || MICROPY_HW_USB_CDC #if MICROPY_HW_ENABLE_UART_REPL || MICROPY_HW_USB_CDC
#ifndef MICROPY_HW_STDIN_BUFFER_LEN #ifndef MICROPY_HW_STDIN_BUFFER_LEN
@ -176,11 +180,28 @@ void mp_hal_delay_ms(mp_uint_t ms) {
} }
} }
uint64_t mp_hal_time_ns(void) { void mp_hal_time_ns_set_from_rtc(void) {
// Delay at least one RTC clock cycle so it's registers have updated with the most
// recent time settings.
sleep_us(23);
// Sample RTC and time_us_64() as close together as possible, so the offset
// calculated for the latter can be as accurate as possible.
datetime_t t; datetime_t t;
rtc_get_datetime(&t); rtc_get_datetime(&t);
uint64_t us = time_us_64();
// Calculate the difference between the RTC Epoch seconds and time_us_64().
uint64_t s = timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.min, t.sec); uint64_t s = timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.min, t.sec);
return s * 1000000000ULL; time_us_64_offset_from_epoch = (uint64_t)s * 1000000ULL - us;
}
uint64_t mp_hal_time_ns(void) {
// The RTC only has seconds resolution, so instead use time_us_64() to get a more
// precise measure of Epoch time. Both these "clocks" are clocked from the same
// source so they remain synchronised, and only differ by a fixed offset (calculated
// in mp_hal_time_ns_set_from_rtc).
return (time_us_64_offset_from_epoch + time_us_64()) * 1000ULL;
} }
// Generate a random locally administered MAC address (LAA) // Generate a random locally administered MAC address (LAA)

View File

@ -39,6 +39,7 @@ extern int mp_interrupt_char;
extern ringbuf_t stdin_ringbuf; extern ringbuf_t stdin_ringbuf;
void mp_hal_set_interrupt_char(int c); void mp_hal_set_interrupt_char(int c);
void mp_hal_time_ns_set_from_rtc(void);
static inline void mp_hal_delay_us(mp_uint_t us) { static inline void mp_hal_delay_us(mp_uint_t us) {
sleep_us(us); sleep_us(us);