diff options
-rw-r--r-- | ports/rp2/machine_uart.c | 159 | ||||
-rw-r--r-- | ports/rp2/mpconfigport.h | 2 |
2 files changed, 147 insertions, 14 deletions
diff --git a/ports/rp2/machine_uart.c b/ports/rp2/machine_uart.c index 936b9a93e..2da3ab41f 100644 --- a/ports/rp2/machine_uart.c +++ b/ports/rp2/machine_uart.c @@ -28,9 +28,12 @@ #include "py/stream.h" #include "py/mphal.h" #include "py/mperrno.h" +#include "py/ringbuf.h" #include "modmachine.h" +#include "hardware/irq.h" #include "hardware/uart.h" +#include "hardware/regs/uart.h" #define DEFAULT_UART_BAUDRATE (115200) #define DEFAULT_UART_BITS (8) @@ -39,6 +42,9 @@ #define DEFAULT_UART0_RX (1) #define DEFAULT_UART1_TX (4) #define DEFAULT_UART1_RX (5) +#define DEFAULT_BUFFER_SIZE (256) +#define MIN_BUFFER_SIZE (32) +#define MAX_BUFFER_SIZE (32766) #define IS_VALID_PERIPH(uart, pin) (((((pin) + 4) & 8) >> 3) == (uart)) #define IS_VALID_TX(uart, pin) (((pin) & 3) == 0 && IS_VALID_PERIPH(uart, pin)) @@ -61,28 +67,81 @@ typedef struct _machine_uart_obj_t { uint16_t timeout; // timeout waiting for first char (in ms) uint16_t timeout_char; // timeout waiting between chars (in ms) uint8_t invert; + ringbuf_t read_buffer; + bool read_lock; + ringbuf_t write_buffer; + bool write_lock; } machine_uart_obj_t; STATIC machine_uart_obj_t machine_uart_obj[] = { - {{&machine_uart_type}, uart0, 0, 0, DEFAULT_UART_BITS, UART_PARITY_NONE, DEFAULT_UART_STOP, DEFAULT_UART0_TX, DEFAULT_UART0_RX, 0, 0, 0}, - {{&machine_uart_type}, uart1, 1, 0, DEFAULT_UART_BITS, UART_PARITY_NONE, DEFAULT_UART_STOP, DEFAULT_UART1_TX, DEFAULT_UART1_RX, 0, 0, 0}, + {{&machine_uart_type}, uart0, 0, 0, DEFAULT_UART_BITS, UART_PARITY_NONE, DEFAULT_UART_STOP, + DEFAULT_UART0_TX, DEFAULT_UART0_RX, 0, 0, 0, {NULL, 1, 0, 0}, 0, {NULL, 1, 0, 0}, 0}, + {{&machine_uart_type}, uart1, 1, 0, DEFAULT_UART_BITS, UART_PARITY_NONE, DEFAULT_UART_STOP, + DEFAULT_UART1_TX, DEFAULT_UART1_RX, 0, 0, 0, {NULL, 1, 0, 0}, 0, {NULL, 1, 0, 0}, 0,}, }; STATIC const char *_parity_name[] = {"None", "0", "1"}; STATIC const char *_invert_name[] = {"None", "INV_TX", "INV_RX", "INV_TX|INV_RX"}; /******************************************************************************/ +// IRQ and buffer handling + +// take all bytes from the fifo and store them, if possible, in the buffer +STATIC void uart_drain_rx_fifo(machine_uart_obj_t *self) { + while (uart_is_readable(self->uart)) { + // try to write the data, ignore the fail + ringbuf_put(&(self->read_buffer), uart_get_hw(self->uart)->dr); + } +} + +// take bytes from the buffer and put them into the UART FIFO +STATIC void uart_fill_tx_fifo(machine_uart_obj_t *self) { + while (uart_is_writable(self->uart) && ringbuf_avail(&self->write_buffer) > 0) { + // get a byte from the buffer and put it into the uart + uart_get_hw(self->uart)->dr = ringbuf_get(&(self->write_buffer)); + } +} + +STATIC inline void uart_service_interrupt(machine_uart_obj_t *self) { + if (uart_get_hw(self->uart)->mis & UART_UARTMIS_RXMIS_BITS) { // rx interrupt? + // clear all interrupt bits but tx + uart_get_hw(self->uart)->icr = UART_UARTICR_BITS & (~UART_UARTICR_TXIC_BITS); + if (!self->read_lock) { + uart_drain_rx_fifo(self); + } + } + if (uart_get_hw(self->uart)->mis & UART_UARTMIS_TXMIS_BITS) { // tx interrupt? + // clear all interrupt bits but rx + uart_get_hw(self->uart)->icr = UART_UARTICR_BITS & (~UART_UARTICR_RXIC_BITS); + if (!self->write_lock) { + uart_fill_tx_fifo(self); + } + } +} + +STATIC void uart0_irq_handler(void) { + uart_service_interrupt(&machine_uart_obj[0]); +} + +STATIC void uart1_irq_handler(void) { + uart_service_interrupt(&machine_uart_obj[1]); +} + +/******************************************************************************/ // MicroPython bindings for UART STATIC void machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, tx=%d, rx=%d, timeout=%u, timeout_char=%u, invert=%s)", + mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, tx=%d, rx=%d, " + "txbuf=%d, rxbuf=%d, timeout=%u, timeout_char=%u, invert=%s)", self->uart_id, self->baudrate, self->bits, _parity_name[self->parity], - self->stop, self->tx, self->rx, self->timeout, self->timeout_char, _invert_name[self->invert]); + self->stop, self->tx, self->rx, self->write_buffer.size - 1, self->read_buffer.size - 1, + self->timeout, self->timeout_char, _invert_name[self->invert]); } STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - enum { ARG_id, ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_tx, ARG_rx, ARG_timeout, ARG_timeout_char, ARG_invert }; + enum { ARG_id, ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_tx, ARG_rx, + ARG_timeout, ARG_timeout_char, ARG_invert, ARG_txbuf, ARG_rxbuf}; static const mp_arg_t allowed_args[] = { { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} }, @@ -94,6 +153,8 @@ STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, 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_invert, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_txbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_rxbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, }; // Parse args. @@ -169,6 +230,30 @@ STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, self->invert = args[ARG_invert].u_int; } + self->read_lock = false; + + // Set the RX buffer size if configured. + size_t rxbuf_len = DEFAULT_BUFFER_SIZE; + if (args[ARG_rxbuf].u_int > 0) { + rxbuf_len = args[ARG_rxbuf].u_int; + if (rxbuf_len < MIN_BUFFER_SIZE) { + rxbuf_len = MIN_BUFFER_SIZE; + } else if (rxbuf_len > MAX_BUFFER_SIZE) { + mp_raise_ValueError(MP_ERROR_TEXT("rxbuf too large")); + } + } + + // Set the TX buffer size if configured. + size_t txbuf_len = DEFAULT_BUFFER_SIZE; + if (args[ARG_txbuf].u_int > 0) { + txbuf_len = args[ARG_txbuf].u_int; + if (txbuf_len < MIN_BUFFER_SIZE) { + txbuf_len = MIN_BUFFER_SIZE; + } else if (txbuf_len > MAX_BUFFER_SIZE) { + mp_raise_ValueError(MP_ERROR_TEXT("txbuf too large")); + } + } + // Initialise the UART peripheral if any arguments given, or it was not initialised previously. if (n_args > 1 || n_kw > 0 || self->baudrate == 0) { if (self->baudrate == 0) { @@ -192,6 +277,25 @@ STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, if (self->invert & UART_INVERT_TX) { gpio_set_outover(self->tx, GPIO_OVERRIDE_INVERT); } + + // Allocate the RX/TX buffers. + ringbuf_alloc(&(self->read_buffer), rxbuf_len + 1); + MP_STATE_PORT(rp2_uart_rx_buffer[uart_id]) = self->read_buffer.buf; + + ringbuf_alloc(&(self->write_buffer), txbuf_len + 1); + MP_STATE_PORT(rp2_uart_tx_buffer[uart_id]) = self->write_buffer.buf; + + // Set the irq handler. + if (self->uart_id == 0) { + irq_set_exclusive_handler(UART0_IRQ, uart0_irq_handler); + irq_set_enabled(UART0_IRQ, true); + } else { + irq_set_exclusive_handler(UART1_IRQ, uart1_irq_handler); + irq_set_enabled(UART1_IRQ, true); + } + + // Enable the uart irq; this macro sets the rx irq level to 4. + uart_set_irq_enables(self->uart, true, true); } return MP_OBJ_FROM_PTR(self); @@ -199,7 +303,11 @@ STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, STATIC mp_obj_t machine_uart_any(mp_obj_t self_in) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); - return MP_OBJ_NEW_SMALL_INT(uart_is_readable(self->uart)); + // get all bytes from the fifo first + self->read_lock = true; + uart_drain_rx_fifo(self); + self->read_lock = false; + return MP_OBJ_NEW_SMALL_INT(ringbuf_avail(&self->read_buffer)); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_any_obj, machine_uart_any); @@ -236,7 +344,7 @@ STATIC mp_uint_t machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t siz for (size_t i = 0; i < size; i++) { // Wait for the first/next character - while (!uart_is_readable(self->uart)) { + while (ringbuf_avail(&self->read_buffer) == 0) { if (time_us_64() > t) { // timed out if (i <= 0) { *errcode = MP_EAGAIN; @@ -246,8 +354,12 @@ STATIC mp_uint_t machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t siz } } MICROPY_EVENT_POLL_HOOK + // Force a few incoming bytes to the buffer + self->read_lock = true; + uart_drain_rx_fifo(self); + self->read_lock = false; } - *dest++ = uart_get_hw(self->uart)->dr; + *dest++ = ringbuf_get(&(self->read_buffer)); t = time_us_64() + timeout_char_us; } return size; @@ -258,10 +370,23 @@ STATIC mp_uint_t machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_uin uint64_t t = time_us_64() + (uint64_t)self->timeout * 1000; uint64_t timeout_char_us = (uint64_t)self->timeout_char * 1000; const uint8_t *src = buf_in; + size_t i = 0; - for (size_t i = 0; i < size; i++) { - // wait for the first/next character - while (!uart_is_writable(self->uart)) { + // Put as many bytes as possible into the transmit buffer. + while (i < size && ringbuf_free(&(self->write_buffer)) > 0) { + ringbuf_put(&(self->write_buffer), *src++); + ++i; + } + + // Kickstart the UART transmit. + self->write_lock = true; + uart_fill_tx_fifo(self); + self->write_lock = false; + + // Send the next characters while busy waiting. + while (i < size) { + // Wait for the first/next character to be sent. + while (ringbuf_free(&(self->write_buffer)) == 0) { if (time_us_64() > t) { // timed out if (i <= 0) { *errcode = MP_EAGAIN; @@ -272,9 +397,15 @@ STATIC mp_uint_t machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_uin } MICROPY_EVENT_POLL_HOOK } - uart_get_hw(self->uart)->dr = *src++; + ringbuf_put(&(self->write_buffer), *src++); + ++i; t = time_us_64() + timeout_char_us; + self->write_lock = true; + uart_fill_tx_fifo(self); + self->write_lock = false; } + + // Just in case the fifo was drained during refill of the ringbuf. return size; } @@ -284,10 +415,10 @@ STATIC mp_uint_t machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint if (request == MP_STREAM_POLL) { uintptr_t flags = arg; ret = 0; - if ((flags & MP_STREAM_POLL_RD) && uart_is_readable(self->uart)) { + if ((flags & MP_STREAM_POLL_RD) && ringbuf_avail(&self->read_buffer) > 0) { ret |= MP_STREAM_POLL_RD; } - if ((flags & MP_STREAM_POLL_WR) && uart_is_writable(self->uart)) { + if ((flags & MP_STREAM_POLL_WR) && ringbuf_free(&self->write_buffer) > 0) { ret |= MP_STREAM_POLL_WR; } } else { diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 13db589ec..6c82d1862 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -174,6 +174,8 @@ extern const struct _mp_obj_module_t mp_module_utime; void *machine_pin_irq_obj[30]; \ void *rp2_pio_irq_obj[2]; \ void *rp2_state_machine_irq_obj[8]; \ + void *rp2_uart_rx_buffer[2]; \ + void *rp2_uart_tx_buffer[2]; \ #define MP_STATE_PORT MP_STATE_VM |