2828#include "py/mphal.h"
2929#include "py/mperrno.h"
3030#include "extmod/modmachine.h"
31+ #include "shared/timeutils/timeutils.h"
3132#include "rtc.h"
3233#include "sys_ctrl_rtc.h"
3334
35+ // The LPRTC (low-power real-time counter) is a 32-bit counter with a 16-bit prescaler,
36+ // and usually clocked by a 32768Hz clock source. To get a large date range of around
37+ // 136 years, the prescaler is set to 32768 and so the counter clocks at 1Hz. Then the
38+ // counter counts the number of seconds since the 1970 Epoch. The prescaler is used to
39+ // get the subseconds which are then converted to microseconds.
40+ //
41+ // The combined counter+prescaler counts starting at 0 from the year 1970 up to the year
42+ // 2106, with a resolution of 30.52 microseconds.
43+ #define LPRTC_PRESCALER_SETTING (32768)
44+
3445typedef struct _machine_rtc_obj_t {
3546 mp_obj_base_t base ;
3647 LPRTC_Type * rtc ;
@@ -44,24 +55,97 @@ void LPRTC_IRQHandler(void) {
4455 lprtc_interrupt_disable (machine_rtc .rtc );
4556}
4657
58+ // Returns the number of seconds and microseconds since the Epoch.
59+ uint32_t mp_hal_time_get (uint32_t * microseconds ) {
60+ uint32_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION ();
61+ uint32_t count = lprtc_get_count (machine_rtc .rtc );
62+ if (microseconds == NULL ) {
63+ MICROPY_END_ATOMIC_SECTION (atomic_state );
64+ return count ;
65+ }
66+ uint32_t prescaler = machine_rtc .rtc -> LPRTC_CPCVR ;
67+ uint32_t count2 = lprtc_get_count (machine_rtc .rtc );
68+ if (count != count2 ) {
69+ // The counter incremented during sampling of the prescaler, so resample the prescaler.
70+ prescaler = machine_rtc .rtc -> LPRTC_CPCVR ;
71+ }
72+ MICROPY_END_ATOMIC_SECTION (atomic_state );
73+
74+ // Compute the microseconds from the up-counting prescaler value.
75+ MP_STATIC_ASSERT (LPRTC_PRESCALER_SETTING == 32768 );
76+ * microseconds = 15625UL * prescaler / 512UL ;
77+
78+ return count2 ;
79+ }
80+
4781static 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 ) {
4882 const machine_rtc_obj_t * self = & machine_rtc ;
4983
5084 // Check arguments.
5185 mp_arg_check_num (n_args , n_kw , 0 , 0 , false);
5286
53- enable_lprtc_clk ();
54- lprtc_prescaler_disable (self -> rtc );
55- lprtc_counter_wrap_disable (self -> rtc );
5687 lprtc_interrupt_disable (self -> rtc );
5788 lprtc_interrupt_unmask (self -> rtc );
5889
90+ // Initialise the LPRTC if it's not already enabled.
91+ if (!((VBAT -> RTC_CLK_EN & RTC_CLK_ENABLE )
92+ && (self -> rtc -> LPRTC_CCR & CCR_LPRTC_EN )
93+ && (self -> rtc -> LPRTC_CPSR == LPRTC_PRESCALER_SETTING ))) {
94+ enable_lprtc_clk ();
95+ self -> rtc -> LPRTC_CCR = 0 ;
96+ lprtc_load_prescaler (self -> rtc , LPRTC_PRESCALER_SETTING );
97+ lprtc_load_count (self -> rtc , 0 );
98+ self -> rtc -> LPRTC_CCR = CCR_LPRTC_PSCLR_EN | CCR_LPRTC_EN ;
99+ }
100+
59101 NVIC_SetPriority (LPRTC_IRQ_IRQn , IRQ_PRI_RTC );
60102 NVIC_ClearPendingIRQ (LPRTC_IRQ_IRQn );
61103 NVIC_EnableIRQ (LPRTC_IRQ_IRQn );
104+
62105 return MP_OBJ_FROM_PTR (self );
63106}
64107
108+ static mp_obj_t machine_rtc_datetime (mp_uint_t n_args , const mp_obj_t * args ) {
109+ if (n_args == 1 ) {
110+ // Get datetime.
111+ uint32_t microseconds ;
112+ mp_timestamp_t s = mp_hal_time_get (& microseconds );
113+ timeutils_struct_time_t tm ;
114+ timeutils_seconds_since_epoch_to_struct_time (s , & tm );
115+ mp_obj_t tuple [8 ] = {
116+ mp_obj_new_int (tm .tm_year ),
117+ mp_obj_new_int (tm .tm_mon ),
118+ mp_obj_new_int (tm .tm_mday ),
119+ mp_obj_new_int (tm .tm_wday ),
120+ mp_obj_new_int (tm .tm_hour ),
121+ mp_obj_new_int (tm .tm_min ),
122+ mp_obj_new_int (tm .tm_sec ),
123+ mp_obj_new_int (microseconds ),
124+ };
125+ return mp_obj_new_tuple (8 , tuple );
126+ } else {
127+ // Set datetime.
128+ mp_obj_t * items ;
129+ mp_obj_get_array_fixed_n (args [1 ], 8 , & items );
130+ timeutils_struct_time_t tm = {
131+ .tm_year = mp_obj_get_int (items [0 ]),
132+ .tm_mon = mp_obj_get_int (items [1 ]),
133+ .tm_mday = mp_obj_get_int (items [2 ]),
134+ .tm_hour = mp_obj_get_int (items [4 ]),
135+ .tm_min = mp_obj_get_int (items [5 ]),
136+ .tm_sec = mp_obj_get_int (items [6 ]),
137+ };
138+ mp_timestamp_t s = timeutils_seconds_since_epoch (tm .tm_year , tm .tm_mon , tm .tm_mday , tm .tm_hour , tm .tm_min , tm .tm_sec );
139+
140+ // Disable then re-enable the LPRTC so that the prescaler counter resets to 0.
141+ machine_rtc .rtc -> LPRTC_CCR = 0 ;
142+ lprtc_load_count (machine_rtc .rtc , s );
143+ machine_rtc .rtc -> LPRTC_CCR = CCR_LPRTC_PSCLR_EN | CCR_LPRTC_EN ;
144+ }
145+ return mp_const_none ;
146+ }
147+ static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (machine_rtc_datetime_obj , 1 , 2 , machine_rtc_datetime ) ;
148+
65149static mp_obj_t machine_rtc_alarm (size_t n_args , const mp_obj_t * pos_args , mp_map_t * kw_args ) {
66150 enum { ARG_id , ARG_time , ARG_repeat };
67151
@@ -80,13 +164,18 @@ static mp_obj_t machine_rtc_alarm(size_t n_args, const mp_obj_t *pos_args, mp_ma
80164 if (mp_obj_is_int (args [ARG_time ].u_obj )) {
81165 uint32_t seconds = mp_obj_get_int (args [1 ].u_obj ) / 1000 ;
82166
83- lprtc_counter_disable (self -> rtc );
84- lprtc_load_count (self -> rtc , 1 );
85- lprtc_load_counter_match_register (self -> rtc , seconds * 32768 );
167+ // Make sure we are guaranteed an interrupt:
168+ // - if seconds = 0 it won't fire
169+ // - if seconds = 1 it may miss if the counter rolls over just after it's read
170+ // - if seconds >= 2 then it will always fire (when read/written close enough)
171+ seconds = MAX (2 , seconds );
86172
173+ // Configure the counter match as atomically as possible.
174+ uint32_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION ();
87175 lprtc_interrupt_ack (self -> rtc );
176+ lprtc_load_counter_match_register (self -> rtc , lprtc_get_count (self -> rtc ) + seconds );
88177 lprtc_interrupt_enable (self -> rtc );
89- lprtc_counter_enable ( self -> rtc );
178+ MICROPY_END_ATOMIC_SECTION ( atomic_state );
90179 } else {
91180 mp_raise_ValueError (MP_ERROR_TEXT ("invalid argument(s)" ));
92181 }
@@ -96,6 +185,7 @@ static mp_obj_t machine_rtc_alarm(size_t n_args, const mp_obj_t *pos_args, mp_ma
96185static MP_DEFINE_CONST_FUN_OBJ_KW (machine_rtc_alarm_obj , 1 , machine_rtc_alarm ) ;
97186
98187static const mp_rom_map_elem_t machine_rtc_locals_dict_table [] = {
188+ { MP_ROM_QSTR (MP_QSTR_datetime ), MP_ROM_PTR (& machine_rtc_datetime_obj ) },
99189 { MP_ROM_QSTR (MP_QSTR_alarm ), MP_ROM_PTR (& machine_rtc_alarm_obj ) },
100190};
101191static MP_DEFINE_CONST_DICT (machine_rtc_locals_dict , machine_rtc_locals_dict_table ) ;
0 commit comments