summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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