Skip to content

Commit

Permalink
esp32/machine_timer: Switch from legacy driver to timer HAL.
Browse files Browse the repository at this point in the history
The legacy driver was deprecated in IDF v5, and crashes when the ISR
handler is called.  Instead of fixing the legacy code, this commit reworks
the machine.Timer class to use the low-level HAL driver.

Tested on ESP32, ESP32S2, ESP32S3 and ESP32C3.  Behaviour is the same as it
was before this commit, except the way the Timer object is printed, it now
gives more useful information (timer id, mode, period in ms).

Fixes issue micropython#11970.

Signed-off-by: Damien George <damien@micropython.org>
  • Loading branch information
dpgeorge committed Jul 14, 2023
1 parent 671b35c commit 7d66ae6
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 46 deletions.
1 change: 0 additions & 1 deletion ports/esp32/boards/sdkconfig.base
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,5 @@ CONFIG_UART_ISR_IN_IRAM=y

# IDF 5 deprecated
CONFIG_ADC_SUPPRESS_DEPRECATE_WARN=y
CONFIG_GPTIMER_SUPPRESS_DEPRECATE_WARN=y
CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y
CONFIG_I2S_SUPPRESS_DEPRECATE_WARN=y
97 changes: 52 additions & 45 deletions ports/esp32/machine_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@
#include "modmachine.h"
#include "mphalport.h"

#include "driver/timer.h"
#include "hal/timer_hal.h"
#include "hal/timer_ll.h"
#include "soc/timer_periph.h"

#define TIMER_INTR_SEL TIMER_INTR_LEVEL
#define TIMER_DIVIDER 8

// TIMER_BASE_CLK is normally 80MHz. TIMER_DIVIDER ought to divide this exactly
Expand All @@ -48,6 +48,8 @@

typedef struct _machine_timer_obj_t {
mp_obj_base_t base;

timer_hal_context_t hal_context;
mp_uint_t group;
mp_uint_t index;

Expand Down Expand Up @@ -80,15 +82,9 @@ void machine_timer_deinit_all(void) {

STATIC void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
machine_timer_obj_t *self = self_in;

timer_config_t config;
mp_printf(print, "Timer(%p; ", self);

timer_get_config(self->group, self->index, &config);

mp_printf(print, "alarm_en=%d, ", config.alarm_en);
mp_printf(print, "auto_reload=%d, ", config.auto_reload);
mp_printf(print, "counter_en=%d)", config.counter_en);
qstr mode = self->repeat ? MP_QSTR_PERIODIC : MP_QSTR_ONE_SHOT;
uint64_t period = self->period / (TIMER_SCALE / 1000); // convert to ms
mp_printf(print, "Timer(%u, mode=%q, period=%lu)", (self->group << 1) | self->index, mode, period);
}

STATIC mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
Expand Down Expand Up @@ -126,8 +122,14 @@ STATIC mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args,
}

STATIC void machine_timer_disable(machine_timer_obj_t *self) {
if (self->hal_context.dev != NULL) {
// Disable the counter and alarm.
timer_ll_enable_counter(self->hal_context.dev, self->index, false);
timer_ll_enable_alarm(self->hal_context.dev, self->index, false);
}

if (self->handle) {
timer_pause(self->group, self->index);
// Free the interrupt handler.
esp_intr_free(self->handle);
self->handle = NULL;
}
Expand All @@ -138,39 +140,47 @@ STATIC void machine_timer_disable(machine_timer_obj_t *self) {

STATIC void machine_timer_isr(void *self_in) {
machine_timer_obj_t *self = self_in;
timg_dev_t *device = self->group ? &(TIMERG1) : &(TIMERG0);

#if CONFIG_IDF_TARGET_ESP32S3
device->hw_timer[self->index].update.tn_update = 1;
#else
device->hw_timer[self->index].update.tx_update = 1;
#endif
uint32_t intr_status = timer_ll_get_intr_status(self->hal_context.dev);

timer_ll_clear_intr_status(device, self->index);
timer_ll_set_alarm_value(device, self->index, self->repeat);

mp_sched_schedule(self->callback, self);
mp_hal_wake_main_task_from_isr();
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);
}
mp_sched_schedule(self->callback, self);
mp_hal_wake_main_task_from_isr();
}
}

STATIC void machine_timer_enable(machine_timer_obj_t *self) {
timer_config_t config;
config.alarm_en = TIMER_ALARM_EN;
config.auto_reload = self->repeat;
config.counter_dir = TIMER_COUNT_UP;
config.divider = TIMER_DIVIDER;
config.intr_type = TIMER_INTR_LEVEL;
config.counter_en = TIMER_PAUSE;
#if SOC_TIMER_GROUP_SUPPORT_XTAL
config.clk_src = TIMER_SRC_CLK_APB;
#endif

check_esp_err(timer_init(self->group, self->index, &config));
check_esp_err(timer_set_counter_value(self->group, self->index, 0x00000000));
check_esp_err(timer_set_alarm_value(self->group, self->index, self->period));
check_esp_err(timer_enable_intr(self->group, self->index));
check_esp_err(timer_isr_register(self->group, self->index, machine_timer_isr, (void *)self, TIMER_FLAGS, &self->handle));
check_esp_err(timer_start(self->group, self->index));
// Initialise the timer.
timer_hal_init(&self->hal_context, self->group, self->index);
timer_ll_enable_counter(self->hal_context.dev, self->index, false);
timer_ll_set_clock_source(self->hal_context.dev, self->index, GPTIMER_CLK_SRC_APB);
timer_ll_set_clock_prescale(self->hal_context.dev, self->index, TIMER_DIVIDER);
timer_hal_set_counter_value(&self->hal_context, 0);
timer_ll_set_count_direction(self->hal_context.dev, self->index, GPTIMER_COUNT_UP);

// Allocate and enable the alarm interrupt.
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));
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)
);
timer_ll_enable_intr(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index), true);

// Enable the alarm to trigger at the given period.
timer_ll_set_alarm_value(self->hal_context.dev, self->index, self->period);
timer_ll_enable_alarm(self->hal_context.dev, self->index, true);

// Set the counter to reload at 0 if it's in repeat mode.
timer_ll_set_reload_value(self->hal_context.dev, self->index, 0);
timer_ll_enable_auto_reload(self->hal_context.dev, self->index, self->repeat);

// Enable the counter.
timer_ll_enable_counter(self->hal_context.dev, self->index, true);
}

STATIC mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
Expand Down Expand Up @@ -234,11 +244,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_timer_init_obj, 1, machine_timer_init)

STATIC mp_obj_t machine_timer_value(mp_obj_t self_in) {
machine_timer_obj_t *self = self_in;
double result;

timer_get_counter_time_sec(self->group, self->index, &result);

return MP_OBJ_NEW_SMALL_INT((mp_uint_t)(result * 1000)); // value in ms
uint64_t result = timer_ll_get_counter_value(self->hal_context.dev, self->index);
return MP_OBJ_NEW_SMALL_INT((mp_uint_t)(result / (TIMER_SCALE / 1000))); // value in ms
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_timer_value_obj, machine_timer_value);

Expand Down

0 comments on commit 7d66ae6

Please sign in to comment.