mirror of
https://github.com/micropython/micropython.git
synced 2026-01-28 14:50:21 +01:00
mimxrt: Increase resolution of RTC to 1/32768 seconds.
Currently the mimxrt port has a resolution of only 1 second for `machine.RTC().datetime()` and `time.time_ns()`. This means (among other things) that it fails the `tests/extmod/time_time_ns.py` test, which requires requires at least 5ms of resolution. The underlying RTC hardware is just a 64-bit counter, and the HAL functions `SNVS_LP_SRTC_GetDatetime()` and `SNVS_LP_SRTC_SetDatetime()` do conversions between y/m/d/h/m/s and this 64-bit value, which counts at a rate of 32kHz. This commit changes the RTC code to access the 64-bit counter directly and therefore improve resolution of all RTC functions to 1/32768 seconds. That makes things much simpler because it a lot of places the code wants to know the number of seconds since the Epoch. Currently it uses a combination of `SNVS_LP_SRTC_GetDatetime()` and `timeutils_seconds_since_epoch()` which converts the 64-bit counter to date-time and then back to seconds. Those operations are computationally expensive. With this commit, getting the number of seconds since the Epoch is as simple as reading the 64-bit counter and dividing by 32768. We can leverage a lot of the timeutils functions to simplify everything, and make it similar to other ports like rp2. Benefits of this change: - simpler, more efficient code to get/set RTC - `machine.RTC().datetime()` now has a non-zero value in the last slot, being the number of microseconds, and has a resolution of 1/32768 seconds - `time.time_ns()` now has a resolution of 1/32768 seconds - the `test/extmod/time_time_ns.py` test now passes Signed-off-by: Damien George <damien@micropython.org>
This commit is contained in:
@@ -26,14 +26,12 @@
|
||||
*/
|
||||
|
||||
#include "lib/oofatfs/ff.h"
|
||||
#include "fsl_snvs_lp.h"
|
||||
|
||||
#include "shared/timeutils/timeutils.h"
|
||||
#include "modmachine.h"
|
||||
|
||||
MP_WEAK DWORD get_fattime(void) {
|
||||
snvs_lp_srtc_datetime_t srtcDate;
|
||||
|
||||
SNVS_LP_SRTC_GetDatetime(SNVS, &srtcDate);
|
||||
|
||||
return ((srtcDate.year - 1980) << 25) | (srtcDate.month << 21) | (srtcDate.day << 16) |
|
||||
(srtcDate.hour << 11) | ((srtcDate.minute << 5) | (srtcDate.second / 2));
|
||||
uint64_t seconds = machine_rtc_get_seconds();
|
||||
timeutils_struct_time_t tm;
|
||||
timeutils_seconds_since_epoch_to_struct_time(seconds, &tm);
|
||||
return ((tm.tm_year - 1980) << 25) | ((tm.tm_mon) << 21) | ((tm.tm_mday) << 16) | ((tm.tm_hour) << 11) | ((tm.tm_min) << 5) | (tm.tm_sec / 2);
|
||||
}
|
||||
|
||||
@@ -73,18 +73,33 @@ void machine_rtc_alarm_on() {
|
||||
machine_rtc_alarm_set_en();
|
||||
}
|
||||
|
||||
uint32_t machine_rtc_get_seconds() {
|
||||
uint32_t seconds = 0;
|
||||
uint32_t tmp = 0;
|
||||
// Returned ticks are in units of 1/32768 seconds.
|
||||
uint64_t machine_rtc_get_ticks(void) {
|
||||
uint64_t ticks = 0;
|
||||
uint64_t tmp = 0;
|
||||
|
||||
// Do consecutive reads until value is correct
|
||||
uint32_t state = disable_irq();
|
||||
do {
|
||||
seconds = tmp;
|
||||
tmp = (SNVS->LPSRTCMR << 17U);
|
||||
tmp |= (SNVS->LPSRTCLR >> 15U);
|
||||
} while (tmp != seconds);
|
||||
ticks = tmp;
|
||||
tmp = (uint64_t)SNVS->LPSRTCMR << 32U;
|
||||
tmp |= SNVS->LPSRTCLR;
|
||||
} while (tmp != ticks);
|
||||
enable_irq(state);
|
||||
|
||||
return seconds;
|
||||
return ticks;
|
||||
}
|
||||
|
||||
uint64_t machine_rtc_get_seconds(void) {
|
||||
return machine_rtc_get_ticks() / 32768U;
|
||||
}
|
||||
|
||||
// Input ticks are in units of 1/32768 seconds.
|
||||
static void machine_rtc_set_ticks(uint64_t ticks) {
|
||||
SNVS_LP_SRTC_StopTimer(SNVS);
|
||||
SNVS->LPSRTCMR = (uint32_t)(ticks >> 32U);
|
||||
SNVS->LPSRTCLR = (uint32_t)ticks;
|
||||
SNVS_LP_SRTC_StartTimer(SNVS);
|
||||
}
|
||||
|
||||
void machine_rtc_alarm_helper(int seconds, bool repeat) {
|
||||
@@ -164,18 +179,9 @@ void machine_rtc_start(void) {
|
||||
SNVS_LP_SRTC_StartTimer(SNVS);
|
||||
// If the date is not set, set it to a more recent start date,
|
||||
// MicroPython's first commit.
|
||||
snvs_lp_srtc_datetime_t srtc_date;
|
||||
SNVS_LP_SRTC_GetDatetime(SNVS, &srtc_date);
|
||||
if (srtc_date.year <= 1970) {
|
||||
srtc_date = (snvs_lp_srtc_datetime_t) {
|
||||
.year = 2013,
|
||||
.month = 10,
|
||||
.day = 14,
|
||||
.hour = 19,
|
||||
.minute = 53,
|
||||
.second = 11,
|
||||
};
|
||||
SNVS_LP_SRTC_SetDatetime(SNVS, &srtc_date);
|
||||
if (machine_rtc_get_ticks() < 10) {
|
||||
mp_timestamp_t seconds = timeutils_seconds_since_epoch(2013, 10, 14, 19, 53, 11);
|
||||
machine_rtc_set_ticks((uint64_t)seconds * 32768ULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,39 +196,34 @@ static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, s
|
||||
static mp_obj_t machine_rtc_datetime_helper(size_t n_args, const mp_obj_t *args, int hour_index) {
|
||||
if (n_args == 1) {
|
||||
// Get date and time.
|
||||
snvs_lp_srtc_datetime_t srtc_date;
|
||||
SNVS_LP_SRTC_GetDatetime(SNVS, &srtc_date);
|
||||
|
||||
uint64_t ticks = machine_rtc_get_ticks();
|
||||
timeutils_struct_time_t tm;
|
||||
timeutils_seconds_since_epoch_to_struct_time(ticks / 32768U, &tm);
|
||||
mp_obj_t tuple[8] = {
|
||||
mp_obj_new_int(srtc_date.year),
|
||||
mp_obj_new_int(srtc_date.month),
|
||||
mp_obj_new_int(srtc_date.day),
|
||||
mp_obj_new_int(timeutils_calc_weekday(srtc_date.year, srtc_date.month, srtc_date.day)),
|
||||
mp_obj_new_int(srtc_date.hour),
|
||||
mp_obj_new_int(srtc_date.minute),
|
||||
mp_obj_new_int(srtc_date.second),
|
||||
mp_obj_new_int(0),
|
||||
mp_obj_new_int(tm.tm_year),
|
||||
mp_obj_new_int(tm.tm_mon),
|
||||
mp_obj_new_int(tm.tm_mday),
|
||||
mp_obj_new_int(tm.tm_wday),
|
||||
mp_obj_new_int(tm.tm_hour),
|
||||
mp_obj_new_int(tm.tm_min),
|
||||
mp_obj_new_int(tm.tm_sec),
|
||||
mp_obj_new_int((ticks % 32768) * 15625U / 512U),
|
||||
};
|
||||
return mp_obj_new_tuple(8, tuple);
|
||||
} else {
|
||||
// Set date and time.
|
||||
mp_obj_t *items;
|
||||
mp_int_t year;
|
||||
mp_obj_get_array_fixed_n(args[1], 8, &items);
|
||||
|
||||
snvs_lp_srtc_datetime_t srtc_date;
|
||||
year = mp_obj_get_int(items[0]);
|
||||
srtc_date.year = year >= 100 ? year : year + 2000; // allow 21 for 2021
|
||||
srtc_date.month = mp_obj_get_int(items[1]);
|
||||
srtc_date.day = mp_obj_get_int(items[2]);
|
||||
// Ignore weekday at items[3]
|
||||
srtc_date.hour = mp_obj_get_int(items[hour_index]);
|
||||
srtc_date.minute = mp_obj_get_int(items[hour_index + 1]);
|
||||
srtc_date.second = mp_obj_get_int(items[hour_index + 2]);
|
||||
if (SNVS_LP_SRTC_SetDatetime(SNVS, &srtc_date) != kStatus_Success) {
|
||||
mp_raise_ValueError(NULL);
|
||||
}
|
||||
|
||||
timeutils_struct_time_t tm = {
|
||||
.tm_year = mp_obj_get_int(items[0]),
|
||||
.tm_mon = mp_obj_get_int(items[1]),
|
||||
.tm_mday = mp_obj_get_int(items[2]),
|
||||
.tm_hour = mp_obj_get_int(items[4]),
|
||||
.tm_min = mp_obj_get_int(items[5]),
|
||||
.tm_sec = mp_obj_get_int(items[6]),
|
||||
};
|
||||
uint32_t seconds = timeutils_seconds_since_epoch(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||||
machine_rtc_set_ticks((uint64_t)seconds * 32768ULL);
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
|
||||
#include "mbedtls_config_port.h"
|
||||
#if defined(MBEDTLS_HAVE_TIME) || defined(MBEDTLS_HAVE_TIME_DATE)
|
||||
#include "fsl_snvs_lp.h"
|
||||
#include "shared/timeutils/timeutils.h"
|
||||
#include "modmachine.h"
|
||||
#include "mbedtls/platform_time.h"
|
||||
#endif
|
||||
|
||||
@@ -49,9 +49,7 @@ int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, size_t
|
||||
#if defined(MBEDTLS_HAVE_TIME)
|
||||
time_t mimxrt_rtctime_seconds(time_t *timer) {
|
||||
// Get date and date in CPython order.
|
||||
snvs_lp_srtc_datetime_t date;
|
||||
SNVS_LP_SRTC_GetDatetime(SNVS, &date);
|
||||
return timeutils_seconds_since_epoch(date.year, date.month, date.day, date.hour, date.minute, date.second);
|
||||
return machine_rtc_get_seconds();
|
||||
}
|
||||
|
||||
mbedtls_ms_time_t mbedtls_ms_time(void) {
|
||||
|
||||
@@ -42,6 +42,8 @@ void mimxrt_sdram_init(void);
|
||||
void machine_i2s_init0();
|
||||
void machine_i2s_deinit_all(void);
|
||||
void machine_rtc_start(void);
|
||||
uint64_t machine_rtc_get_ticks(void);
|
||||
uint64_t machine_rtc_get_seconds(void);
|
||||
void machine_rtc_alarm_helper(int seconds, bool repeat);
|
||||
void machine_uart_set_baudrate(mp_obj_t uart, uint32_t baudrate);
|
||||
|
||||
|
||||
@@ -25,30 +25,17 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/obj.h"
|
||||
#include "shared/timeutils/timeutils.h"
|
||||
#include "fsl_snvs_lp.h"
|
||||
#include "modmachine.h"
|
||||
|
||||
// Get the localtime.
|
||||
static void mp_time_localtime_get(timeutils_struct_time_t *tm) {
|
||||
// Get current date and time.
|
||||
snvs_lp_srtc_datetime_t t;
|
||||
SNVS_LP_SRTC_GetDatetime(SNVS, &t);
|
||||
tm->tm_year = t.year;
|
||||
tm->tm_mon = t.month;
|
||||
tm->tm_mday = t.day;
|
||||
tm->tm_hour = t.hour;
|
||||
tm->tm_min = t.minute;
|
||||
tm->tm_sec = t.second;
|
||||
tm->tm_wday = timeutils_calc_weekday(t.year, t.month, t.day);
|
||||
tm->tm_yday = timeutils_year_day(t.year, t.month, t.day);
|
||||
uint64_t seconds = machine_rtc_get_seconds();
|
||||
timeutils_seconds_since_epoch_to_struct_time(seconds, tm);
|
||||
}
|
||||
|
||||
// Return the number of seconds since the Epoch.
|
||||
static mp_obj_t mp_time_time_get(void) {
|
||||
snvs_lp_srtc_datetime_t t;
|
||||
SNVS_LP_SRTC_GetDatetime(SNVS, &t);
|
||||
return timeutils_obj_from_timestamp(
|
||||
timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.minute, t.second)
|
||||
);
|
||||
return timeutils_obj_from_timestamp(machine_rtc_get_seconds());
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include "extmod/misc.h"
|
||||
#include "ticks.h"
|
||||
#include "tusb.h"
|
||||
#include "fsl_snvs_lp.h"
|
||||
#include "modmachine.h"
|
||||
|
||||
#ifndef MICROPY_HW_STDIN_BUFFER_LEN
|
||||
#define MICROPY_HW_STDIN_BUFFER_LEN 512
|
||||
@@ -96,10 +96,12 @@ mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
|
||||
}
|
||||
|
||||
uint64_t mp_hal_time_ns(void) {
|
||||
snvs_lp_srtc_datetime_t t;
|
||||
SNVS_LP_SRTC_GetDatetime(SNVS, &t);
|
||||
uint64_t s = timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.minute, t.second);
|
||||
return s * 1000000000ULL;
|
||||
uint64_t ticks = machine_rtc_get_ticks();
|
||||
// Need to compute:
|
||||
// nanoseconds = ticks * 1_000_000_000 / 32768
|
||||
// = ticks * 5**9 / 64
|
||||
// but split it into upper and lower 32-bit values so the multiplication doesn't overflow.
|
||||
return (((ticks >> 32U) * 1953125ULL) << 26U) + (((ticks & 0xffffffff) * 1953125ULL) >> 6U);
|
||||
}
|
||||
|
||||
/*******************************************************************************/
|
||||
|
||||
Reference in New Issue
Block a user