esp32: Re-use allocated timer interrupts and simplify UART timer code.

If the interrupt is not freed but merely disabled, instead of reallocating
it every time the timer is enabled again we can instead just re-enable it.
That means we're no longer setting the handler every time, and we need to
ensure it does not change. Doing so by adding an additional wrapper
function does not only solve that problem, it also allows us to remove
some code duplication and simplify how machine_uart uses the timer.

Signed-off-by: Daniël van de Giessen <daniel@dvdgiessen.nl>
This commit is contained in:
Daniël van de Giessen
2025-04-22 17:42:29 +02:00
committed by Damien George
parent bf909303ff
commit 2c2f0b292a
3 changed files with 31 additions and 36 deletions

View File

@@ -151,12 +151,16 @@ static void machine_timer_isr(void *self_in) {
if (self->repeat) {
timer_ll_enable_alarm(self->hal_context.dev, self->index, true);
}
mp_sched_schedule(self->callback, self);
mp_hal_wake_main_task_from_isr();
self->handler(self);
}
}
void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)) {
static void machine_timer_isr_handler(machine_timer_obj_t *self) {
mp_sched_schedule(self->callback, self);
mp_hal_wake_main_task_from_isr();
}
void machine_timer_enable(machine_timer_obj_t *self) {
// Initialise the timer.
timer_hal_init(&self->hal_context, self->group, self->index);
timer_ll_enable_counter(self->hal_context.dev, self->index, false);
@@ -169,13 +173,16 @@ void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)) {
timer_ll_enable_intr(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index), false);
timer_ll_clear_intr_status(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index));
if (self->handle) {
ESP_ERROR_CHECK(esp_intr_free(self->handle));
self->handle = NULL;
ESP_ERROR_CHECK(esp_intr_enable(self->handle));
} else {
ESP_ERROR_CHECK(esp_intr_alloc(
timer_group_periph_signals.groups[self->group].timer_irq_id[self->index],
TIMER_FLAGS,
machine_timer_isr,
self,
&self->handle
));
}
ESP_ERROR_CHECK(
esp_intr_alloc(timer_group_periph_signals.groups[self->group].timer_irq_id[self->index],
TIMER_FLAGS, timer_isr, self, &self->handle)
);
timer_ll_enable_intr(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index), true);
// Enable the alarm to trigger at the given period.
@@ -229,10 +236,10 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n
}
self->repeat = args[ARG_mode].u_int;
self->handler = machine_timer_isr_handler;
self->callback = args[ARG_callback].u_obj;
self->handle = NULL;
machine_timer_enable(self, machine_timer_isr);
machine_timer_enable(self);
return mp_const_none;
}

View File

@@ -55,12 +55,13 @@ typedef struct _machine_timer_obj_t {
mp_obj_t callback;
intr_handle_t handle;
void (*handler)(struct _machine_timer_obj_t *timer);
struct _machine_timer_obj_t *next;
} machine_timer_obj_t;
machine_timer_obj_t *machine_timer_create(mp_uint_t timer);
void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr));
void machine_timer_enable(machine_timer_obj_t *self);
void machine_timer_disable(machine_timer_obj_t *self);
#endif // MICROPY_INCLUDED_ESP32_MACHINE_TIMER_H

View File

@@ -110,30 +110,19 @@ static const char *_parity_name[] = {"None", "1", "0"};
{ MP_ROM_QSTR(MP_QSTR_IRQ_RXIDLE), MP_ROM_INT(UART_IRQ_RXIDLE) }, \
{ MP_ROM_QSTR(MP_QSTR_IRQ_BREAK), MP_ROM_INT(UART_IRQ_BREAK) }, \
static void uart_timer_callback(void *self_in) {
machine_timer_obj_t *self = self_in;
uint32_t intr_status = timer_ll_get_intr_status(self->hal_context.dev);
if (intr_status & TIMER_LL_EVENT_ALARM(self->index)) {
timer_ll_clear_intr_status(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index));
if (self->repeat) {
timer_ll_enable_alarm(self->hal_context.dev, self->index, true);
}
}
static void uart_timer_callback(machine_timer_obj_t *timer) {
// The UART object is referred here by the callback field.
machine_uart_obj_t *uart = (machine_uart_obj_t *)self->callback;
if (uart->rxidle_state == RXIDLE_ALERT) {
machine_uart_obj_t *self = (machine_uart_obj_t *)timer->callback;
if (self->rxidle_state == RXIDLE_ALERT) {
// At the first call, just switch the state
uart->rxidle_state = RXIDLE_ARMED;
} else if (uart->rxidle_state == RXIDLE_ARMED) {
self->rxidle_state = RXIDLE_ARMED;
} else if (self->rxidle_state == RXIDLE_ARMED) {
// At the second call, run the irq callback and stop the timer
uart->rxidle_state = RXIDLE_STANDBY;
uart->mp_irq_flags = UART_IRQ_RXIDLE;
mp_irq_handler(uart->mp_irq_obj);
self->rxidle_state = RXIDLE_STANDBY;
self->mp_irq_flags = UART_IRQ_RXIDLE;
mp_irq_handler(self->mp_irq_obj);
mp_hal_wake_main_task_from_isr();
machine_timer_disable(uart->rxidle_timer);
machine_timer_disable(self->rxidle_timer);
}
}
@@ -150,9 +139,7 @@ static void uart_event_task(void *self_in) {
if (self->mp_irq_trigger & UART_IRQ_RXIDLE) {
if (self->rxidle_state != RXIDLE_INACTIVE) {
if (self->rxidle_state == RXIDLE_STANDBY) {
self->rxidle_timer->repeat = true;
self->rxidle_timer->handle = NULL;
machine_timer_enable(self->rxidle_timer, uart_timer_callback);
machine_timer_enable(self->rxidle_timer);
}
}
self->rxidle_state = RXIDLE_ALERT;
@@ -553,11 +540,11 @@ static void uart_irq_configure_timer(machine_uart_obj_t *self, mp_uint_t trigger
}
self->rxidle_period = period;
self->rxidle_timer->period = period;
self->rxidle_timer->handler = uart_timer_callback;
// The Python callback is not used. So use this
// data field to hold a reference to the UART object.
self->rxidle_timer->callback = self;
self->rxidle_timer->repeat = true;
self->rxidle_timer->handle = NULL;
self->rxidle_state = RXIDLE_STANDBY;
}
}