summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Webb <chris@arachsys.com>2025-06-16 12:35:51 +0100
committerChris Webb <chris@arachsys.com>2025-06-16 12:35:51 +0100
commit7816b1f5134b24d5c352af679d398a491a427edd (patch)
tree704dfac117e3f5100f1df23cbf6c18952a905a66
parentcfcc53da72d330d2eb9728d5f75c3ef5b459f1cf (diff)
rp2/machine_timer: Support hard IRQ timer callbacks.
Unlike some boards like stm32, timer callbacks on the rp2 port are unconditionally dispatched via mp_sched_schedule(), behaving like soft IRQs with consequent GC jitter and delays. Add a 'hard' keyword argument to the rp2 Timer constructor and init. This defaults to False but if it is set True, the timer callback will be dispatched in hard IRQ context rather than queued. Signed-off-by: Chris Webb <chris@arachsys.com>
-rw-r--r--ports/rp2/machine_timer.c30
1 files changed, 28 insertions, 2 deletions
diff --git a/ports/rp2/machine_timer.c b/ports/rp2/machine_timer.c
index 6f8b04f10..ffb4c7024 100644
--- a/ports/rp2/machine_timer.c
+++ b/ports/rp2/machine_timer.c
@@ -27,6 +27,7 @@
#include "py/runtime.h"
#include "py/mperrno.h"
#include "py/mphal.h"
+#include "py/gc.h"
#include "pico/time.h"
#define ALARM_ID_INVALID (-1)
@@ -40,13 +41,36 @@ typedef struct _machine_timer_obj_t {
uint32_t mode;
uint64_t delta_us; // for periodic mode
mp_obj_t callback;
+ bool ishard;
} machine_timer_obj_t;
const mp_obj_type_t machine_timer_type;
static int64_t alarm_callback(alarm_id_t id, void *user_data) {
machine_timer_obj_t *self = user_data;
- mp_sched_schedule(self->callback, MP_OBJ_FROM_PTR(self));
+
+ if (self->ishard) {
+ // When executing code within a handler we must lock the scheduler to
+ // prevent any scheduled callbacks from running, and lock the GC to
+ // prevent any memory allocations.
+ mp_sched_lock();
+ gc_lock();
+ nlr_buf_t nlr;
+ if (nlr_push(&nlr) == 0) {
+ mp_call_function_1(self->callback, MP_OBJ_FROM_PTR(self));
+ nlr_pop();
+ } else {
+ // Uncaught exception; disable the callback so it doesn't run again.
+ self->mode = TIMER_MODE_ONE_SHOT;
+ mp_printf(MICROPY_ERROR_PRINTER, "uncaught exception in timer callback\n");
+ mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(nlr.ret_val));
+ }
+ gc_unlock();
+ mp_sched_unlock();
+ } else {
+ mp_sched_schedule(self->callback, MP_OBJ_FROM_PTR(self));
+ }
+
if (self->mode == TIMER_MODE_ONE_SHOT) {
return 0;
} else {
@@ -66,13 +90,14 @@ static void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_pr
}
static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
- enum { ARG_mode, ARG_callback, ARG_period, ARG_tick_hz, ARG_freq, };
+ enum { ARG_mode, ARG_callback, ARG_period, ARG_tick_hz, ARG_freq, ARG_hard, };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = TIMER_MODE_PERIODIC} },
{ MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} },
{ MP_QSTR_tick_hz, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1000} },
{ MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
+ { MP_QSTR_hard, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
};
// Parse args
@@ -96,6 +121,7 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, size_t n_ar
}
self->callback = args[ARG_callback].u_obj;
+ self->ishard = args[ARG_hard].u_bool;
self->alarm_id = alarm_pool_add_alarm_in_us(self->pool, self->delta_us, alarm_callback, self, true);
if (self->alarm_id == -1) {
mp_raise_OSError(MP_ENOMEM);