diff --git a/ports/samd/machine_uart.c b/ports/samd/machine_uart.c index b560e0e8e692..aa781a17073a 100644 --- a/ports/samd/machine_uart.c +++ b/ports/samd/machine_uart.c @@ -40,7 +40,16 @@ #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 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_TXIDLE), MP_ROM_INT(SERCOM_USART_INTFLAG_TXC) }, \ + +#else #define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS +#endif typedef struct _machine_uart_obj_t { mp_obj_base_t base; @@ -67,6 +76,11 @@ typedef struct _machine_uart_obj_t { #if MICROPY_HW_UART_TXBUF ringbuf_t write_buffer; #endif + #if MICROPY_PY_MACHINE_UART_IRQ + 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 + #endif } machine_uart_obj_t; static const char *_parity_name[] = {"None", "", "0", "1"}; // Is defined as 0, 2, 3 @@ -93,23 +107,41 @@ void common_uart_irq_handler(int uart_id) { // Handle IRQ if (self != NULL) { Sercom *uart = sercom_instance[self->id]; + #if MICROPY_PY_MACHINE_UART_IRQ + self->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; + } + #endif } else if (uart->USART.INTFLAG.bit.DRE != 0) { #if MICROPY_HW_UART_TXBUF // handle the outgoing data if (ringbuf_avail(&self->write_buffer) > 0) { uart->USART.DATA.bit.DATA = ringbuf_get(&self->write_buffer); } else { - // Stop the interrupt if there is no more data + #if MICROPY_PY_MACHINE_UART_IRQ + // Set the TXIDLE flag + self->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; } #endif - } else { - // Disable the other interrupts, if set by error - uart->USART.INTENCLR.reg = (uint8_t) ~(SERCOM_USART_INTENCLR_DRE | SERCOM_USART_INTENCLR_RXC); } + // Disable the other interrupts, if set by error + 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) { + mp_irq_handler(self->mp_irq_obj); + } + #endif } } @@ -202,6 +234,9 @@ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_ #if MICROPY_HW_UART_RTSCTS ", rts=%q, cts=%q" #endif + #if MICROPY_PY_MACHINE_UART_IRQ + ", irq=%d" + #endif ")", self->id, self->baudrate, self->bits, _parity_name[self->parity], self->stop + 1, self->timeout, self->timeout_char, self->read_buffer.size - 1 @@ -212,6 +247,9 @@ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_ , self->rts != 0xff ? pin_find_by_id(self->rts)->name : MP_QSTR_None , self->cts != 0xff ? pin_find_by_id(self->cts)->name : MP_QSTR_None #endif + #if MICROPY_PY_MACHINE_UART_IRQ + , self->mp_irq_trigger + #endif ); } @@ -228,9 +266,7 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_rxbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, - #if MICROPY_HW_UART_TXBUF { MP_QSTR_txbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, - #endif #if MICROPY_HW_UART_RTSCTS { MP_QSTR_rts, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_cts, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, @@ -386,6 +422,9 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg self->rts = 0xff; self->cts = 0xff; #endif + #if MICROPY_PY_MACHINE_UART_IRQ + self->mp_irq_obj = NULL; + #endif self->new = true; MP_STATE_PORT(sercom_table[uart_id]) = self; @@ -445,6 +484,59 @@ static void mp_machine_uart_sendbreak(machine_uart_obj_t *self) { mp_hal_set_pin_mux(self->tx, self->tx_pad_config.alt_fct); } +#if MICROPY_PY_MACHINE_UART_IRQ + +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); + self->mp_irq_trigger = new_trigger; + return 0; +} + +static mp_uint_t uart_irq_info(mp_obj_t self_in, mp_uint_t info_type) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (info_type == MP_IRQ_INFO_FLAGS) { + return self->mp_irq_flags; + } else if (info_type == MP_IRQ_INFO_TRIGGERS) { + return self->mp_irq_trigger; + } + return 0; +} + +static const mp_irq_methods_t uart_irq_methods = { + .trigger = uart_irq_trigger, + .info = uart_irq_info, +}; + +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; + self->mp_irq_obj = mp_irq_new(&uart_irq_methods, MP_OBJ_FROM_PTR(self)); + } + + if (any_args) { + // Check the handler + mp_obj_t handler = args[MP_IRQ_ARG_INIT_handler].u_obj; + if (handler != mp_const_none && !mp_obj_is_callable(handler)) { + mp_raise_ValueError(MP_ERROR_TEXT("handler must be None or callable")); + } + + // Check the trigger + mp_uint_t trigger = args[MP_IRQ_ARG_INIT_trigger].u_int; + mp_uint_t not_supported = trigger & ~MP_UART_ALLOWED_FLAGS; + if (trigger != 0 && not_supported) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("trigger 0x%04x unsupported"), not_supported); + } + + self->mp_irq_obj->handler = handler; + self->mp_irq_obj->ishard = args[MP_IRQ_ARG_INIT_hard].u_bool; + self->mp_irq_trigger = trigger; + } + + return self->mp_irq_obj; +} + +#endif + static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); Sercom *uart = sercom_instance[self->id]; @@ -481,9 +573,17 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_ const uint8_t *src = buf_in; Sercom *uart = sercom_instance[self->id]; - #if MICROPY_HW_UART_TXBUF uint64_t t = mp_hal_ticks_ms_64() + self->timeout; + #if MICROPY_HW_UART_TXBUF + #if MICROPY_PY_MACHINE_UART_IRQ + // Prefill the FIFO to get rid of the initial IRQ_TXIDLE event + while (i < size && ringbuf_free(&(self->write_buffer)) > 0) { + ringbuf_put(&(self->write_buffer), *src++); + i++; + } + uart->USART.INTENSET.reg = SERCOM_USART_INTENSET_DRE; // kick off the IRQ + #endif while (i < size) { // Wait for the first/next character to be sent. while (ringbuf_free(&(self->write_buffer)) == 0) { @@ -506,6 +606,15 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_ while (i < size) { while (!(uart->USART.INTFLAG.bit.DRE)) { + if (mp_hal_ticks_ms_64() > t) { // timed out + if (i <= 0) { + *errcode = MP_EAGAIN; + return MP_STREAM_ERROR; + } else { + return i; + } + } + MICROPY_EVENT_POLL_HOOK } uart->USART.DATA.bit.DATA = *src++; i++; diff --git a/ports/samd/mcu/samd21/mpconfigmcu.h b/ports/samd/mcu/samd21/mpconfigmcu.h index fbb5b28e2b95..29965f50f63a 100644 --- a/ports/samd/mcu/samd21/mpconfigmcu.h +++ b/ports/samd/mcu/samd21/mpconfigmcu.h @@ -51,6 +51,7 @@ unsigned long trng_random_u32(int delay); #ifndef MICROPY_HW_UART_RTSCTS #define MICROPY_HW_UART_RTSCTS (SAMD21_EXTRA_FEATURES) #endif +#define MICROPY_PY_MACHINE_UART_IRQ (SAMD21_EXTRA_FEATURES) // selected extensions of the extra features set #define MICROPY_PY_OS_URANDOM (1) diff --git a/ports/samd/mcu/samd51/mpconfigmcu.h b/ports/samd/mcu/samd51/mpconfigmcu.h index d567f28eb423..9a7b8528f357 100644 --- a/ports/samd/mcu/samd51/mpconfigmcu.h +++ b/ports/samd/mcu/samd51/mpconfigmcu.h @@ -15,6 +15,7 @@ #define MICROPY_PY_ONEWIRE (1) #define MICROPY_PY_RANDOM_SEED_INIT_FUNC (trng_random_u32()) unsigned long trng_random_u32(void); +#define MICROPY_PY_MACHINE_UART_IRQ (1) // fatfs configuration used in ffconf.h #define MICROPY_FATFS_ENABLE_LFN (1)