summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrobert-hh <robert@hammelrath.com>2021-03-11 08:36:16 +0100
committerDamien George <damien@micropython.org>2021-04-12 21:31:08 +1000
commit1be74b94b6f3263b8e360a0a012ae87301539f91 (patch)
tree5f918dbea30ad4bfb4ecb83d4114762c6bbd2c06
parent22554cf8e21ab97c60137539536eab05d6a6cdcb (diff)
rp2/machine_uart: Add buffered transfer of data with rxbuf/txbuf kwargs.
Instantiation and init now support the rxbuf and txbuf keywords for setting the buffer size. The default size is 256 bytes. The minimum and maximum sizes are 32 and 32766 respectively. uart.write() still includes checks for timeout, even if it is very unlikely to happen due to a) lack of flow control support and b) the minimal timeout values being longer than the time it needs to send a byte.
-rw-r--r--ports/rp2/machine_uart.c159
-rw-r--r--ports/rp2/mpconfigport.h2
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