summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ports/samd/machine_uart.c89
1 files changed, 82 insertions, 7 deletions
diff --git a/ports/samd/machine_uart.c b/ports/samd/machine_uart.c
index aa781a170..b0dc4c576 100644
--- a/ports/samd/machine_uart.c
+++ b/ports/samd/machine_uart.c
@@ -32,6 +32,7 @@
#include "py/ringbuf.h"
#include "samd_soc.h"
#include "pin_af.h"
+#include "shared/runtime/softtimer.h"
#define DEFAULT_UART_BAUDRATE (115200)
#define DEFAULT_BUFFER_SIZE (256)
@@ -40,17 +41,31 @@
#define FLOW_CONTROL_RTS (1)
#define FLOW_CONTROL_CTS (2)
-#define MP_UART_ALLOWED_FLAGS (SERCOM_USART_INTFLAG_RXC | SERCOM_USART_INTFLAG_TXC)
-
#if MICROPY_PY_MACHINE_UART_IRQ
+#define UART_IRQ_RXIDLE (4096)
+#define RXIDLE_TIMER_MIN (1)
+#define MP_UART_ALLOWED_FLAGS (SERCOM_USART_INTFLAG_RXC | SERCOM_USART_INTFLAG_TXC | UART_IRQ_RXIDLE)
+
#define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS \
{ MP_ROM_QSTR(MP_QSTR_IRQ_RX), MP_ROM_INT(SERCOM_USART_INTFLAG_RXC) }, \
+ { MP_ROM_QSTR(MP_QSTR_IRQ_RXIDLE), MP_ROM_INT(UART_IRQ_RXIDLE) }, \
{ MP_ROM_QSTR(MP_QSTR_IRQ_TXIDLE), MP_ROM_INT(SERCOM_USART_INTFLAG_TXC) }, \
+enum {
+ RXIDLE_INACTIVE,
+ RXIDLE_STANDBY,
+ RXIDLE_ARMED,
+ RXIDLE_ALERT,
+};
#else
#define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS
#endif
+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;
uint8_t id;
@@ -80,6 +95,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;
#endif
} machine_uart_obj_t;
@@ -108,14 +126,24 @@ void common_uart_irq_handler(int uart_id) {
if (self != NULL) {
Sercom *uart = sercom_instance[self->id];
#if MICROPY_PY_MACHINE_UART_IRQ
- self->mp_irq_flags = 0;
+ uint16_t mp_irq_flags = 0;
#endif
if (uart->USART.INTFLAG.bit.RXC != 0) {
// Now handler the incoming data
uart_drain_rx_fifo(self, uart);
#if MICROPY_PY_MACHINE_UART_IRQ
if (ringbuf_avail(&self->read_buffer) > 0) {
- self->mp_irq_flags = SERCOM_USART_INTFLAG_RXC;
+ if (self->mp_irq_trigger & UART_IRQ_RXIDLE) {
+ if (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;
+ }
+ } else {
+ mp_irq_flags = SERCOM_USART_INTFLAG_RXC;
+ }
}
#endif
} else if (uart->USART.INTFLAG.bit.DRE != 0) {
@@ -126,7 +154,7 @@ void common_uart_irq_handler(int uart_id) {
} else {
#if MICROPY_PY_MACHINE_UART_IRQ
// Set the TXIDLE flag
- self->mp_irq_flags |= SERCOM_USART_INTFLAG_TXC;
+ mp_irq_flags |= SERCOM_USART_INTFLAG_TXC;
#endif
// Stop the DRE interrupt if there is no more data
uart->USART.INTENCLR.reg = SERCOM_USART_INTENCLR_DRE;
@@ -137,14 +165,34 @@ void common_uart_irq_handler(int uart_id) {
uart->USART.INTENCLR.reg = (uint8_t) ~(SERCOM_USART_INTENCLR_DRE | SERCOM_USART_INTENCLR_RXC);
#if MICROPY_PY_MACHINE_UART_IRQ
- // Check the flags to see if the user handler should be called
- if (self->mp_irq_trigger & self->mp_irq_flags) {
+ // Check the flags to see if the uart user handler should be called
+ // The handler for RXIDLE is called in the timer callback
+ if (self->mp_irq_trigger & mp_irq_flags) {
+ self->mp_irq_flags = mp_irq_flags;
mp_irq_handler(self->mp_irq_obj);
}
#endif
}
}
+#if MICROPY_PY_MACHINE_UART_IRQ
+static 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);
+ }
+}
+#endif
+
// Configure the Sercom device
static void machine_sercom_configure(machine_uart_obj_t *self) {
Sercom *uart = sercom_instance[self->id];
@@ -424,6 +472,7 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg
#endif
#if MICROPY_PY_MACHINE_UART_IRQ
self->mp_irq_obj = NULL;
+ self->rxidle_state = RXIDLE_INACTIVE;
#endif
self->new = true;
MP_STATE_PORT(sercom_table[uart_id]) = self;
@@ -486,8 +535,33 @@ static void mp_machine_uart_sendbreak(machine_uart_obj_t *self) {
#if MICROPY_PY_MACHINE_UART_IRQ
+// 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;
+ 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_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;
}
@@ -526,6 +600,7 @@ static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args
if (trigger != 0 && not_supported) {
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("trigger 0x%04x unsupported"), not_supported);
}
+ uart_irq_configure_timer(self, trigger);
self->mp_irq_obj->handler = handler;
self->mp_irq_obj->ishard = args[MP_IRQ_ARG_INIT_hard].u_bool;