diff --git a/ports/esp32/machine_timer.c b/ports/esp32/machine_timer.c index eee77e482..7dca9e014 100644 --- a/ports/esp32/machine_timer.c +++ b/ports/esp32/machine_timer.c @@ -37,7 +37,9 @@ #include "mphalport.h" #define TIMER_INTR_SEL TIMER_INTR_LEVEL -#define TIMER_DIVIDER 40000 +#define TIMER_DIVIDER 8 + +// TIMER_BASE_CLK is normally 80MHz. TIMER_DIVIDER ought to divide this exactly #define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) #define TIMER_FLAGS 0 @@ -48,7 +50,8 @@ typedef struct _machine_timer_obj_t { mp_uint_t index; mp_uint_t repeat; - mp_uint_t period; + // ESP32 timers are 64-bit + uint64_t period; mp_obj_t callback; @@ -131,10 +134,23 @@ STATIC void machine_timer_enable(machine_timer_obj_t *self) { } 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) { + enum { + ARG_mode, + ARG_callback, + ARG_period, + ARG_tick_hz, + ARG_freq, + }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, { MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, { MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_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} }, +#if MICROPY_PY_BUILTINS_FLOAT + { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, +#else + { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, +#endif }; machine_timer_disable(self); @@ -142,10 +158,21 @@ STATIC mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - // Timer uses an 80MHz base clock, which is divided by the divider/scalar, we then convert to ms. - self->period = (args[0].u_int * TIMER_BASE_CLK) / (1000 * TIMER_DIVIDER); - self->repeat = args[1].u_int; - self->callback = args[2].u_obj; +#if MICROPY_PY_BUILTINS_FLOAT + if (args[ARG_freq].u_obj != mp_const_none) { + self->period = (uint64_t)(TIMER_SCALE / mp_obj_get_float(args[ARG_freq].u_obj)); + } +#else + if (args[ARG_freq].u_int != 0xffffffff) { + self->period = TIMER_SCALE / ((uint64_t)args[ARG_freq].u_int); + } +#endif + else { + self->period = (((uint64_t)args[ARG_period].u_int) * TIMER_SCALE) / args[ARG_tick_hz].u_int; + } + + self->repeat = args[ARG_mode].u_int; + self->callback = args[ARG_callback].u_obj; self->handle = NULL; machine_timer_enable(self); diff --git a/ports/esp8266/main.c b/ports/esp8266/main.c index 7e5034b04..55fd0e3a0 100644 --- a/ports/esp8266/main.c +++ b/ports/esp8266/main.c @@ -33,6 +33,10 @@ #include "py/mperrno.h" #include "py/mphal.h" #include "py/gc.h" + +// This needs to be defined before any ESP SDK headers are included +#define USE_US_TIMER 1 + #include "extmod/misc.h" #include "lib/mp-readline/readline.h" #include "lib/utils/pyexec.h" @@ -126,6 +130,7 @@ soft_reset: } void user_init(void) { + system_timer_reinit(); system_init_done_cb(init_done); } diff --git a/ports/esp8266/modmachine.c b/ports/esp8266/modmachine.c index 7e5f6714b..1c1d902e5 100644 --- a/ports/esp8266/modmachine.c +++ b/ports/esp8266/modmachine.c @@ -30,6 +30,10 @@ #include "py/obj.h" #include "py/runtime.h" + +// This needs to be set before we include the RTOS headers +#define USE_US_TIMER 1 + #include "extmod/machine_mem.h" #include "extmod/machine_signal.h" #include "extmod/machine_pulse.h" @@ -160,22 +164,65 @@ STATIC void esp_timer_cb(void *arg) { } STATIC mp_obj_t esp_timer_init_helper(esp_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, + }; static const mp_arg_t allowed_args[] = { -// { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, - { MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, { MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, { MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_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} }, +#if MICROPY_PY_BUILTINS_FLOAT + { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, +#else + { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, +#endif }; // parse args mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - self->callback = args[2].u_obj; + self->callback = args[ARG_callback].u_obj; // Be sure to disarm timer before making any changes os_timer_disarm(&self->timer); os_timer_setfn(&self->timer, esp_timer_cb, self); - os_timer_arm(&self->timer, args[0].u_int, args[1].u_int); + +#if MICROPY_PY_BUILTINS_FLOAT + if (args[ARG_freq].u_obj != mp_const_none) { + mp_float_t freq = mp_obj_get_float(args[ARG_freq].u_obj); + if (freq < 0.001) { + os_timer_arm(&self->timer, (mp_int_t)(1000 / freq), args[ARG_mode].u_int); + } else { + os_timer_arm_us(&self->timer, (mp_int_t)(1000000 / freq), args[ARG_mode].u_int); + } + } +#else + if (args[ARG_freq].u_int != 0xffffffff) { + os_timer_arm_us(&self->timer, 1000000 / args[ARG_freq].u_int, args[ARG_mode].u_int); + } +#endif + else { + mp_int_t period = args[ARG_period].u_int; + mp_int_t hz = args[ARG_tick_hz].u_int; + if (hz == 1000) { + os_timer_arm(&self->timer, period, args[ARG_mode].u_int); + } else if (hz == 1000000) { + os_timer_arm_us(&self->timer, period, args[ARG_mode].u_int); + } else { + // Use a long long to ensure that we don't either overflow or loose accuracy + uint64_t period_us = (((uint64_t)period) * 1000000) / hz; + if (period_us < 0x80000000ull) { + os_timer_arm_us(&self->timer, (mp_int_t)period_us, args[ARG_mode].u_int); + } else { + os_timer_arm(&self->timer, (mp_int_t)(period_us / 1000), args[ARG_mode].u_int); + } + } + } return mp_const_none; }