samd/machine_uart: Implement a Python UART IRQ handler.
Supported for all SAMD51 devices and SAMD21 with external flash. For interrupt events, IRQ_RX and IRQ_TXIDLE are provided. IRQ_RX is called for every received byte. This may not be useful for high data rates, but can be used to build a wrapper class providing an IRQ_RXIDLE event or to signal just the first byte of a message. IRQ_TXIDLE is called only when messages are longer than 5 bytes and triggers when still 5 bytes are due to be sent. The SAMD hardware does not support implementing IRQ_RXIDLE. Signed-off-by: robert-hh <robert@hammelrath.com>
This commit is contained in:
parent
01c046d2a8
commit
8e1123b25b
@ -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);
|
||||
|
||||
#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++;
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user