esp8266/modmachine: Handle overflow of timer to get longer periods.

Can now handle up to about 298 days maximum for millisecond periods.

Fixes issue #4664.
This commit is contained in:
Damien George 2019-05-08 23:30:57 +10:00
parent c0a1de3c21
commit 99a8fa7940
1 changed files with 59 additions and 8 deletions

View File

@ -167,12 +167,49 @@ STATIC mp_obj_t machine_deepsleep(size_t n_args, const mp_obj_t *args) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_deepsleep_obj, 0, 1, machine_deepsleep);
// These values are from the datasheet
#define ESP_TIMER_US_MIN (100)
#define ESP_TIMER_US_MAX (0xfffffff)
#define ESP_TIMER_MS_MAX (0x689d0)
typedef struct _esp_timer_obj_t {
mp_obj_base_t base;
os_timer_t timer;
uint32_t remain_ms; // if non-zero, remaining time to handle large periods
uint32_t period_ms; // if non-zero, periodic timer with a large period
mp_obj_t callback;
} esp_timer_obj_t;
STATIC void esp_timer_arm_ms(esp_timer_obj_t *self, uint32_t ms, bool repeat) {
if (ms <= ESP_TIMER_MS_MAX) {
self->remain_ms = 0;
self->period_ms = 0;
} else {
self->remain_ms = ms - ESP_TIMER_MS_MAX;
if (repeat) {
repeat = false;
self->period_ms = ms;
} else {
self->period_ms = 0;
}
ms = ESP_TIMER_MS_MAX;
}
os_timer_arm(&self->timer, ms, repeat);
}
STATIC void esp_timer_arm_us(esp_timer_obj_t *self, uint32_t us, bool repeat) {
if (us < ESP_TIMER_US_MIN) {
us = ESP_TIMER_US_MIN;
}
if (us <= ESP_TIMER_US_MAX) {
self->remain_ms = 0;
self->period_ms = 0;
os_timer_arm_us(&self->timer, us, repeat);
} else {
esp_timer_arm_ms(self, us / 1000, repeat);
}
}
const mp_obj_type_t esp_timer_type;
STATIC void esp_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
@ -189,7 +226,21 @@ STATIC mp_obj_t esp_timer_make_new(const mp_obj_type_t *type, size_t n_args, siz
STATIC void esp_timer_cb(void *arg) {
esp_timer_obj_t *self = arg;
mp_sched_schedule(self->callback, self);
if (self->remain_ms != 0) {
// Handle periods larger than the maximum system period
uint32_t next_period_ms = self->remain_ms;
if (next_period_ms > ESP_TIMER_MS_MAX) {
next_period_ms = ESP_TIMER_MS_MAX;
}
self->remain_ms -= next_period_ms;
os_timer_arm(&self->timer, next_period_ms, false);
} else {
mp_sched_schedule(self->callback, self);
if (self->period_ms != 0) {
// A periodic timer with a larger period: reschedule it
esp_timer_arm_ms(self, self->period_ms, true);
}
}
}
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) {
@ -225,30 +276,30 @@ STATIC mp_obj_t esp_timer_init_helper(esp_timer_obj_t *self, size_t n_args, cons
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);
esp_timer_arm_ms(self, (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);
esp_timer_arm_us(self, (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);
esp_timer_arm_us(self, 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);
esp_timer_arm_ms(self, period, args[ARG_mode].u_int);
} else if (hz == 1000000) {
os_timer_arm_us(&self->timer, period, args[ARG_mode].u_int);
esp_timer_arm_us(self, 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);
esp_timer_arm_us(self, (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);
esp_timer_arm_ms(self, (mp_int_t)(period_us / 1000), args[ARG_mode].u_int);
}
}
}