esp8266,esp32: Implement high-res timers using new tick_hz argument.
machine.Timer now takes a new argument in its constructor (or init method): tick_hz which specified the units for the period argument. The period of the timer in seconds is: period/tick_hz. For backwards compatibility tick_hz defaults to 1000. If the user wants to specify the period (numerator) in microseconds then tick_hz can be set to 1000000. The user can also specify a period of an arbitrary number of cycles of an arbitrary frequency using these two arguments. An additional freq argument has been added to allow frequencies to be specified directly in Hertz. This supports floating point values when available.
This commit is contained in:
parent
a3ba5f127e
commit
c3c914f4dd
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user