8000 nrf: Implement time.time() and machine.RTC. · micropython/micropython@d280499 · GitHub
[go: up one dir, main page]

Skip to content

Commit d280499

Browse files
committed
nrf: Implement time.time() and machine.RTC.
Optionally adds time() and time_ns() to the time module, as well as a machine.RTC class that only implements the datetime() method (following the example of the rp2 port), whose sole purpose is to provide the ability to set the time. Also provides the basis for enabling gmtime(), localtime(), mktime() in the time module. The nRF52 does not have a dedicated real-time clock peripheral, but timekeeping can be done by the same real-time counter that already powers the time.ticks_* and related functions. For reasonable accuracy, a suitable LFCLK source is required: The internal RC oscillator (BLUETOOTH_LFCLK_RC) by itself is insufficient, but any of the following work fine: - external 32kHz crystal (default) - synthesis from HFCLK (BLUETOOTH_LFCLK_SYNTH) when HFXO (external 32MHz crystal) is enabled - BLUETOOTH_LFCLK_RC + periodical calibration from HFXO (automatically done by the SoftDevice while enabled using ble.enable()) Boards can enable this by defining both configuration options MICROPY_PY_TIME_TICKS and MICROPY_PY_TIME_TIME_TIME_NS. Additionally, they may want to enable MICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIME. This includes a generic implementation of mp_time_localtime_get() and mp_time_time_get() in terms of mp_hal_time_ns(), which could also be used by other ports. In particular by the embed port, for which I originally wrote it, noting the following: "I'm unsure where to put modtime_mphal.h, it could also be in extmod. The important thing is that for MICROPY_PY_TIME_INCLUDEFILE to work it must be at the same path in both the port build (original source tree) and the application build (micropython_embed distribution), therefore not in ports/embed/port. It is named .h, mismatching the corresponding ports/*/modtime.c, because it must not be compiled separately, which naming it .c would make harder for users of the embed port - they would need to explicitly exclude it, whereas this way they can continue to just compile all the .c files found in the micropython_embed distribution except those in lib." Signed-off-by: Christian Walther <cwalther@gmx.ch>
1 parent 98c425a commit d280499

File tree

6 files changed

+192
-0
lines changed

6 files changed

+192
-0
lines changed

ports/nrf/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ endif
266266
DRIVERS_SRC_C += $(addprefix modules/,\
267267
machine/spi.c \
268268
machine/i2c.c \
269+
machine/machine_rtc.c \
269270
machine/pin.c \
270271
machine/timer.c \
271272
machine/rtcounter.c \
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2021 "Krzysztof Adamski" <k@japko.eu>
7+
* Copyright (c) 2024 Christian Walther
8+
*
9+
* Permission is hereby granted, free of charge, to any person obtaining a copy
10+
* of this software and associated documentation files (the "Software"), to deal
11+
* in the Software without restriction, including without limitation the rights
12+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
* copies of the Software, and to permit persons to whom the Software is
14+
* furnished to do so, subject to the following conditions:
15+
*
16+
* The above copyright notice and this permission notice shall be included in
17+
* all copies or substantial portions of the Software.
18+
*
19+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
* THE SOFTWARE.
26+
*/
27+
28+
#include "py/runtime.h"
29+
30+
// Timekeeping is handled by the ticks machinery, so only enable the
31+
// machine.RTC type (whose sole purpose is to provide the ability to set the
32+
// time) if that is enabled.
33+
#if MICROPY_PY_TIME_TIME_TIME_NS && MICROPY_PY_TIME_TICKS
34+
35+
#include "extmod/modmachine.h"
36+
#include "shared/timeutils/timeutils.h"
37+
38+
typedef struct _machine_rtc_obj_t {
39+
mp_obj_base_t base;
40+
} machine_rtc_obj_t;
41+
42+
// singleton RTC object
43+
STATIC const machine_rtc_obj_t machine_rtc_obj = {{&machine_rtc_type}};
44+
45+
STATIC mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
46+
// check arguments
47+
mp_arg_check_num(n_args, n_kw, 0, 0, false);
48+
// return constant object
49+
return (mp_obj_t)&machine_rtc_obj;
50+
}
51+
52+
STATIC mp_obj_t machine_rtc_datetime(mp_uint_t n_args, const mp_obj_t *args) {
53+
if (n_args == 1) {
54+
uint64_t nanoseconds = mp_hal_time_ns();
55+
uint64_t seconds = nanoseconds / 1000000000;
56+
nanoseconds -= seconds * 1000000000;
57+
timeutils_struct_time_t tm;
58+
timeutils_seconds_since_epoch_to_struct_time(seconds, &tm);
59+
mp_obj_t tuple[8] = {
60+
mp_obj_new_int(tm.tm_year),
61+
mp_obj_new_int(tm.tm_mon),
62+
mp_obj_new_int(tm.tm_mday),
63+
mp_obj_new_int(tm.tm_wday),
64+
mp_obj_new_int(tm.tm_hour),
65+
mp_obj_new_int(tm.tm_min),
66+
mp_obj_new_int(tm.tm_sec),
67+
mp_obj_new_int(nanoseconds)
68+
};
69+
return mp_obj_new_tuple(8, tuple);
70+
} else {
71+
mp_obj_t *items;
72+
mp_obj_get_array_fixed_n(args[1], 8, &items);
73+
uint64_t t = 1000000000ULL * timeutils_seconds_since_epoch(
74+
mp_obj_get_int(items[0]), // year
75+
mp_obj_get_int(items[1]), // month
76+
mp_obj_get_int(items[2]), // date
77+
mp_obj_get_int(items[4]), // hour
78+
mp_obj_get_int(items[5]), // minute
79+
mp_obj_get_int(items[6]) // second
80+
) + mp_obj_get_int(items[7]); // nanoseconds
81+
mp_hal_set_time_ns(t);
82+
}
83+
return mp_const_none;
84+
}
85+
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_rtc_datetime_obj, 1, 2, machine_rtc_datetime);
86+
87+
STATIC const mp_rom_map_elem_t machine_rtc_locals_dict_table[] = {
88+
{ MP_ROM_QSTR(MP_QSTR_datetime), MP_ROM_PTR(&machine_rtc_datetime_obj) },
89+
};
90+
STATIC MP_DEFINE_CONST_DICT(machine_rtc_locals_dict, machine_rtc_locals_dict_table);
91+
92+
MP_DEFINE_CONST_OBJ_TYPE(
93+
machine_rtc_type,
94+
MP_QSTR_RTC,
95+
MP_TYPE_FLAG_NONE,
96+
make_new, machine_rtc_make_new,
97+
locals_dict, &machine_rtc_locals_dict
98+
);
99+
100+
#endif

ports/nrf/modules/machine/modmachine.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@
6565
#define MICROPY_PY_MACHINE_RTCOUNTER_ENTRY
6666
#endif
6767

68+
#if MICROPY_PY_TIME_TIME_TIME_NS && MICROPY_PY_TIME_TICKS
69+
#define MICROPY_PY_MACHINE_RTC_ENTRY { MP_ROM_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&machine_rtc_type) },
70+
#else
71+
#define MICROPY_PY_MACHINE_RTC_ENTRY
72+
#endif
73+
6874
#if MICROPY_PY_MACHINE_TIMER_NRF
6975
#define MICROPY_PY_MACHINE_TIMER_ENTRY { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) },
7076
#else
@@ -91,6 +97,7 @@
9197
{ MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&pin_type) }, \
9298
\
9399
MICROPY_PY_MACHINE_RTCOUNTER_ENTRY \
100+
MICROPY_PY_MACHINE_RTC_ENTRY \
94101
MICROPY_PY_MACHINE_TIMER_ENTRY \
95102
MICROPY_PY_MACHINE_TEMP_ENTRY \
96103
{ MP_ROM_QSTR(MP_QSTR_HARD_RESET), MP_ROM_INT(PYB_RESET_HARD) }, \

ports/nrf/mphalport.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,35 @@ mp_uint_t mp_hal_ticks_ms(void) {
192192

193193
#endif
194194

195+
#if MICROPY_PY_TIME_TIME_TIME_NS && MICROPY_PY_TIME_TICKS
196+
197+
// nanoseconds between 2000-01-01 and tick counter zero, to adjust clock
198+
STATIC uint64_t epoch_offset = 0;
199+
200+
uint64_t mp_hal_time_ns(void) {
201+
// Range of `overflows` is sufficient: nanoseconds overflow 64 bits before `overflows` overflows
202+
// 32 bits.
203+
// Same logic as in mp_hal_ticks_ms, no need to worry about intermediate result overflows if we
204+
// do everything in 64-bit (this probably needn't be fast).
205+
uint32_t overflows;
206+
uint32_t counter;
207+
// guard against overflow irq
208+
RTC1_GET_TICKS_ATOMIC(rtc1, overflows, counter)
209+
return (((uint64_t)overflows << 18) * 1953125) + (((uint64_t)counter * 1953125) >> 6) + epoch_offset;
210+
}
211+
212+
void mp_hal_set_time_ns(uint64_t ns_since_epoch) {
213+
epoch_offset += ns_since_epoch - mp_hal_time_ns();
214+
}
215+
216+
#else
217+
195218
uint64_t mp_hal_time_ns(void) {
196219
return 0;
197220
}
198221

222+
#endif
223+
199224
// this table converts from HAL_StatusTypeDef to POSIX errno
200225
const byte mp_hal_status_to_errno_table[4] = {
201226
[HAL_OK] = 0,

ports/nrf/mphalport.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ mp_uint_t mp_hal_ticks_ms(void);
7575
#define mp_hal_ticks_us() (0)
7676
#endif
7777

78+
#if MICROPY_PY_TIME_TIME_TIME_NS && MICROPY_PY_TIME_TICKS
79+
void mp_hal_set_time_ns(uint64_t ns_since_epoch);
80+
#endif
81+
7882
// TODO: empty implementation for now. Used by machine_spi.c:69
7983
#define mp_hal_delay_us_fast(p)
8084
#define mp_hal_ticks_cpu() (0)

shared/timeutils/modtime_mphal.h

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2013-2023 Damien P. George
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
// This file is never compiled standalone, it's included directly from
28+
// extmod/modtime.c via MICROPY_PY_TIME_INCLUDEFILE.
29+
30+
#include "py/obj.h"
31+
#include "py/mphal.h"
32+
#include "shared/timeutils/timeutils.h"
33+
34+
// Return the localtime as an 8-tuple.
35+
STATIC mp_obj_t mp_time_localtime_get(void) {
36+
mp_int_t seconds = mp_hal_time_ns() / 1000000000;
37+
timeutils_struct_time_t tm;
38+
timeutils_seconds_since_epoch_to_struct_time(seconds, &tm);
39+
mp_obj_t tuple[8] = {
40+
tuple[0] = mp_obj_new_int(tm.tm_year),
41+
tuple[1] = mp_obj_new_int(tm.tm_mon),
42+
tuple[2] = mp_obj_new_int(tm.tm_mday),
43+
tuple[3] = mp_obj_new_int(tm.tm_hour),
44+
tuple[4] = mp_obj_new_int(tm.tm_min),
45+
tuple[5] = mp_obj_new_int(tm.tm_sec),
46+
tuple[6] = mp_obj_new_int(tm.tm_wday),
47+
tuple[7] = mp_obj_new_int(tm.tm_yday),
48+
};
49+
return mp_obj_new_tuple(8, tuple);
50+
}
51+
52+
// Returns the number of seconds, as an integer, since the Epoch.
53+
STATIC mp_obj_t mp_time_time_get(void) {
54+
return mp_obj_new_int(mp_hal_time_ns() / 1000000000);
55+
}

0 commit comments

Comments
 (0)
0