diff --git a/ports/renesas-ra/machine_uart.c b/ports/renesas-ra/machine_uart.c index 4a659d107af39..b70978ad7a5fe 100644 --- a/ports/renesas-ra/machine_uart.c +++ b/ports/renesas-ra/machine_uart.c @@ -41,7 +41,8 @@ #define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS \ { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(UART_HWCONTROL_RTS) }, \ { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(UART_HWCONTROL_CTS) }, \ - { MP_ROM_QSTR(MP_QSTR_IRQ_RX), MP_ROM_INT(0x10) }, \ + { 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) }, \ static const char *_parity_name[] = {"None", "ODD", "EVEN"}; @@ -297,6 +298,8 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg // reference existing UART object self = MP_STATE_PORT(machine_uart_obj_all)[uart_id]; } + self->mp_irq_obj = NULL; + self->rxidle_state = RXIDLE_INACTIVE; // start the peripheral mp_map_t kw_args; @@ -351,6 +354,29 @@ static mp_int_t mp_machine_uart_readchar(machine_uart_obj_t *self) { } } +// Configure the timer used for IRQ_RXIDLE +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; + mp_int_t ms = 13000 / self->baudrate + 1; + if (ms < RXIDLE_TIMER_MIN) { + ms = RXIDLE_TIMER_MIN; + } + self->rxidle_ms = ms; + self->rxidle_timer.context = self; + soft_timer_static_init( + &self->rxidle_timer.base, + SOFT_TIMER_MODE_PERIODIC, + ms, + uart_soft_timer_callback + ); + self->rxidle_state = RXIDLE_STANDBY; + } +} + static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args, mp_arg_val_t *args) { if (self->mp_irq_obj == NULL) { self->mp_irq_trigger = 0; @@ -376,6 +402,7 @@ static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args self->mp_irq_obj->handler = handler; self->mp_irq_obj->ishard = args[MP_IRQ_ARG_INIT_hard].u_bool; self->mp_irq_trigger = trigger; + uart_irq_configure_timer(self, trigger); uart_irq_config(self, true); } diff --git a/ports/renesas-ra/uart.c b/ports/renesas-ra/uart.c index 83b3394f7b453..d17a1fc913fe0 100644 --- a/ports/renesas-ra/uart.c +++ b/ports/renesas-ra/uart.c @@ -85,12 +85,36 @@ static void uart_rx_cb(uint32_t ch, int d) { } #endif + if ((self->mp_irq_trigger & UART_IRQ_RXIDLE) && (self->rxidle_state != RXIDLE_INACTIVE)) { + if (self->rxidle_state == RXIDLE_STANDBY) { + self->rxidle_timer.base.mode = SOFT_TIMER_MODE_PERIODIC; + soft_timer_insert(&self->rxidle_timer.base, self->rxidle_ms); + } + self->rxidle_state = RXIDLE_ALERT; + } // Check the flags to see if the user handler should be called - if (self->mp_irq_trigger) { + if (self->mp_irq_trigger & UART_IRQ_RX) { + self->mp_irq_flags = UART_IRQ_RX; mp_irq_handler(self->mp_irq_obj); } } +void uart_soft_timer_callback(soft_timer_entry_t *self) { + machine_uart_obj_t *uart = ((soft_timer_entry_extended_t *)self)->context; + 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 + // by setting the mode to SOFT_TIMER_MODE_ONE_SHOT. + // Calling soft_timer_remove() would fail here. + self->mode = SOFT_TIMER_MODE_ONE_SHOT; + uart->rxidle_state = RXIDLE_STANDBY; + uart->mp_irq_flags = UART_IRQ_RXIDLE; + mp_irq_handler(uart->mp_irq_obj); + } +} + void uart_init0(void) { } @@ -513,6 +537,7 @@ void uart_tx_strn(machine_uart_obj_t *uart_obj, const char *str, uint len) { 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_config(self, false); + uart_irq_configure_timer(self, new_trigger); self->mp_irq_trigger = new_trigger; uart_irq_config(self, true); return 0; diff --git a/ports/renesas-ra/uart.h b/ports/renesas-ra/uart.h index ee8eb321d552c..1f74df412cb43 100644 --- a/ports/renesas-ra/uart.h +++ b/ports/renesas-ra/uart.h @@ -29,6 +29,7 @@ #define MICROPY_INCLUDED_RA_UART_H #include "shared/runtime/mpirq.h" +#include "shared/runtime/softtimer.h" #include "pin.h" typedef enum { @@ -57,12 +58,28 @@ typedef enum { #define UART_HWCONTROL_CTS (1) #define UART_HWCONTROL_RTS (2) +#define UART_IRQ_RX (0x10) +#define UART_IRQ_RXIDLE (0x1000) +#define RXIDLE_TIMER_MIN (1) + // OR-ed IRQ flags which are allowed to be used by the user -#define MP_UART_ALLOWED_FLAGS ((uint32_t)0x00000010) +#define MP_UART_ALLOWED_FLAGS ((uint32_t)(UART_IRQ_RX | UART_IRQ_RXIDLE)) // OR-ed IRQ flags which should not be touched by the user #define MP_UART_RESERVED_FLAGS ((uint16_t)0x0020) +enum { + RXIDLE_INACTIVE, + RXIDLE_STANDBY, + RXIDLE_ARMED, + RXIDLE_ALERT, +}; + +typedef struct _soft_timer_entry_extended_t { + soft_timer_entry_t base; + void *context; +} soft_timer_entry_extended_t; + typedef struct _machine_uart_obj_t { mp_obj_base_t base; machine_uart_t uart_id : 8; @@ -87,6 +104,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 + soft_timer_entry_extended_t rxidle_timer; + uint8_t rxidle_state; + uint16_t rxidle_ms; } machine_uart_obj_t; extern const mp_irq_methods_t uart_irq_methods; @@ -100,6 +120,8 @@ void uart_irq_config(machine_uart_obj_t *self, bool enable); void uart_set_rxbuf(machine_uart_obj_t *self, size_t len, void *buf); void uart_deinit(machine_uart_obj_t *uart_obj); // void uart_irq_handler(mp_uint_t uart_id); +void uart_irq_configure_timer(machine_uart_obj_t *self, mp_uint_t trigger); +void uart_soft_timer_callback(soft_timer_entry_t *self); void uart_attach_to_repl(machine_uart_obj_t *self, bool attached); uint32_t uart_get_baudrate(machine_uart_obj_t *self);