diff --git a/ports/esp32/machine_timer.c b/ports/esp32/machine_timer.c index 011f87ba9e..278deb1064 100644 --- a/ports/esp32/machine_timer.c +++ b/ports/esp32/machine_timer.c @@ -38,6 +38,7 @@ #include "hal/timer_hal.h" #include "hal/timer_ll.h" #include "soc/timer_periph.h" +#include "machine_timer.h" #define TIMER_DIVIDER 8 @@ -46,27 +47,8 @@ #define TIMER_FLAGS 0 -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; - - mp_uint_t repeat; - // ESP32 timers are 64 or 54-bit - uint64_t period; - - mp_obj_t callback; - - intr_handle_t handle; - - struct _machine_timer_obj_t *next; -} machine_timer_obj_t; - const mp_obj_type_t machine_timer_type; -static void machine_timer_disable(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); void machine_timer_deinit_all(void) { @@ -91,17 +73,16 @@ static void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_pr #endif } -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) { - mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); - #if CONFIG_IDF_TARGET_ESP32C3 - mp_uint_t group = mp_obj_get_int(args[0]) & 1; - mp_uint_t index = 0; - #else - mp_uint_t group = (mp_obj_get_int(args[0]) >> 1) & 1; - mp_uint_t index = mp_obj_get_int(args[0]) & 1; - #endif +machine_timer_obj_t *machine_timer_create(mp_uint_t timer) { machine_timer_obj_t *self = NULL; + #if CONFIG_IDF_TARGET_ESP32C3 + mp_uint_t group = timer & 1; + mp_uint_t index = 0; + #else + mp_uint_t group = (timer >> 1) & 1; + mp_uint_t index = timer & 1; + #endif // Check whether the timer is already initialized, if so use it for (machine_timer_obj_t *t = MP_STATE_PORT(machine_timer_obj_head); t; t = t->next) { @@ -120,6 +101,14 @@ static mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args, self->next = MP_STATE_PORT(machine_timer_obj_head); MP_STATE_PORT(machine_timer_obj_head) = self; } + return self; +} + +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) { + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + // Create the new timer. + machine_timer_obj_t *self = machine_timer_create(mp_obj_get_int(args[0])); if (n_args > 1 || n_kw > 0) { mp_map_t kw_args; @@ -130,7 +119,7 @@ static mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args, return self; } -static void machine_timer_disable(machine_timer_obj_t *self) { +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); @@ -162,7 +151,7 @@ static void machine_timer_isr(void *self_in) { } } -static void machine_timer_enable(machine_timer_obj_t *self) { +void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)) { // Initialise the timer. timer_hal_init(&self->hal_context, self->group, self->index); timer_ll_enable_counter(self->hal_context.dev, self->index, false); @@ -176,7 +165,7 @@ static void machine_timer_enable(machine_timer_obj_t *self) { 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_FLAGS, timer_isr, self, &self->handle) ); timer_ll_enable_intr(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index), true); @@ -234,7 +223,7 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n self->callback = args[ARG_callback].u_obj; self->handle = NULL; - machine_timer_enable(self); + machine_timer_enable(self, machine_timer_isr); return mp_const_none; } diff --git a/ports/esp32/machine_timer.h b/ports/esp32/machine_timer.h new file mode 100644 index 0000000000..914bedd86b --- /dev/null +++ b/ports/esp32/machine_timer.h @@ -0,0 +1,66 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2015 Damien P. George + * Copyright (c) 2016 Paul Sokolovsky + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_ESP32_MACHINE_TIMER_H +#define MICROPY_INCLUDED_ESP32_MACHINE_TIMER_H + +#include "hal/timer_hal.h" +#include "hal/timer_ll.h" +#include "soc/timer_periph.h" + +#define TIMER_DIVIDER 8 + +// TIMER_BASE_CLK is normally 80MHz. TIMER_DIVIDER ought to divide this exactly +#define TIMER_SCALE (APB_CLK_FREQ / TIMER_DIVIDER) + +#define TIMER_FLAGS 0 + +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; + + mp_uint_t repeat; + // ESP32 timers are 64-bit + uint64_t period; + + mp_obj_t callback; + + intr_handle_t handle; + + 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_disable(machine_timer_obj_t *self); + +#endif // MICROPY_INCLUDED_ESP32_MACHINE_TIMER_H diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c index 1a02594132..d260c45c48 100644 --- a/ports/esp32/machine_uart.c +++ b/ports/esp32/machine_uart.c @@ -39,6 +39,7 @@ #include "py/mperrno.h" #include "py/mphal.h" #include "uart.h" +#include "machine_timer.h" #if SOC_UART_SUPPORT_XTAL_CLK // Works independently of APB frequency, on ESP32C3, ESP32S3. @@ -54,8 +55,17 @@ #define UART_INV_MASK (UART_INV_TX | UART_INV_RX | UART_INV_RTS | UART_INV_CTS) #define UART_IRQ_RX (1 << UART_DATA) +#define UART_IRQ_RXIDLE (0x1000) #define UART_IRQ_BREAK (1 << UART_BREAK) -#define MP_UART_ALLOWED_FLAGS (UART_IRQ_RX | UART_IRQ_BREAK) +#define MP_UART_ALLOWED_FLAGS (UART_IRQ_RX | UART_IRQ_RXIDLE | UART_IRQ_BREAK) +#define RXIDLE_TIMER_MIN (5000) // 500 us + +enum { + RXIDLE_INACTIVE, + RXIDLE_STANDBY, + RXIDLE_ARMED, + RXIDLE_ALERT, +}; typedef struct _machine_uart_obj_t { mp_obj_base_t base; @@ -78,6 +88,9 @@ typedef struct _machine_uart_obj_t { uint16_t mp_irq_trigger; // user IRQ trigger mask uint16_t mp_irq_flags; // user IRQ active IRQ flags mp_irq_obj_t *mp_irq_obj; // user IRQ object + machine_timer_obj_t *rxidle_timer; + uint8_t rxidle_state; + uint16_t rxidle_period; } machine_uart_obj_t; static const char *_parity_name[] = {"None", "1", "0"}; @@ -93,28 +106,67 @@ static const char *_parity_name[] = {"None", "1", "0"}; { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(UART_HW_FLOWCTRL_RTS) }, \ { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(UART_HW_FLOWCTRL_CTS) }, \ { MP_ROM_QSTR(MP_QSTR_IRQ_RX), MP_ROM_INT(UART_IRQ_RX) }, \ + { 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); + } + } + + // 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) { + // At the first call, just switch the state + uart->rxidle_state = RXIDLE_ARMED; + } else if (uart->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); + mp_hal_wake_main_task_from_isr(); + machine_timer_disable(uart->rxidle_timer); + } +} + static void uart_event_task(void *self_in) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); uart_event_t event; for (;;) { // Waiting for an UART event. if (xQueueReceive(self->uart_queue, (void *)&event, (TickType_t)portMAX_DELAY)) { - self->mp_irq_flags = 0; + uint16_t mp_irq_flags = 0; switch (event.type) { // Event of UART receiving data case UART_DATA: - self->mp_irq_flags |= UART_IRQ_RX; + 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); + } + } + self->rxidle_state = RXIDLE_ALERT; + } + mp_irq_flags |= UART_IRQ_RX; break; case UART_BREAK: - self->mp_irq_flags |= UART_IRQ_BREAK; + mp_irq_flags |= UART_IRQ_BREAK; break; default: break; } // Check the flags to see if the user handler should be called - if (self->mp_irq_trigger & self->mp_irq_flags) { + if (self->mp_irq_trigger & mp_irq_flags) { + self->mp_irq_flags = mp_irq_flags; mp_irq_handler(self->mp_irq_obj); mp_hal_wake_main_task_from_isr(); } @@ -390,6 +442,7 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg self->invert = 0; self->flowcontrol = 0; self->uart_event_task = 0; + self->rxidle_state = RXIDLE_INACTIVE; switch (uart_num) { case UART_NUM_0: @@ -464,8 +517,35 @@ static void mp_machine_uart_sendbreak(machine_uart_obj_t *self) { check_esp_err(uart_set_baudrate(self->uart_num, baudrate)); } +// Configure the timer used for IRQ_RXIDLE +static void uart_irq_configure_timer(machine_uart_obj_t *self, mp_uint_t trigger) { + + self->rxidle_state = RXIDLE_INACTIVE; + + if (trigger & UART_IRQ_RXIDLE) { + // The RXIDLE event is always a soft IRQ. + self->mp_irq_obj->ishard = false; + uint32_t baudrate; + uart_get_baudrate(self->uart_num, &baudrate); + mp_int_t period = TIMER_SCALE * 20 / baudrate + 1; + if (period < RXIDLE_TIMER_MIN) { + period = RXIDLE_TIMER_MIN; + } + self->rxidle_period = period; + self->rxidle_timer->period = period; + // 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; + } +} + static mp_uint_t uart_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + + uart_irq_configure_timer(self, new_trigger); self->mp_irq_trigger = new_trigger; return 0; } @@ -511,6 +591,9 @@ static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args } self->mp_irq_obj->ishard = false; self->mp_irq_trigger = trigger; + self->rxidle_timer = machine_timer_create(0); + uart_irq_configure_timer(self, trigger); + // Start a task for handling events if (handler != mp_const_none && self->uart_event_task == NULL) { xTaskCreatePinnedToCore(uart_event_task, "uart_event_task", 2048, self,