summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrobert-hh <robert@hammelrath.com>2022-06-09 13:46:54 +0200
committerDamien George <damien@micropython.org>2022-10-06 22:57:37 +1100
commit9a567b04e7c43024a2a4dce2618b24f822dfe5bc (patch)
tree1d231a3e4309c3b5cd504c2844d3510bcc14f650
parent37449df821a47698779b48e6c101b084cfbdb6c7 (diff)
samd/machine_uart: Support buffered TX for UART.
It can be enabled/disabled by a configuration switch. The code size increase is 308 bytes, but it requires RAM space for buffers, the larger UART object and root pointers.
-rw-r--r--ports/samd/machine_uart.c78
-rw-r--r--ports/samd/mcu/samd21/mpconfigmcu.h2
-rw-r--r--ports/samd/mcu/samd51/mpconfigmcu.h2
3 files changed, 66 insertions, 16 deletions
diff --git a/ports/samd/machine_uart.c b/ports/samd/machine_uart.c
index 0ed7fb95d..92e63ee51 100644
--- a/ports/samd/machine_uart.c
+++ b/ports/samd/machine_uart.c
@@ -37,7 +37,6 @@
#define DEFAULT_BUFFER_SIZE (256)
#define MIN_BUFFER_SIZE (32)
#define MAX_BUFFER_SIZE (32766)
-#define USART_BUFFER_TX (0)
typedef struct _machine_uart_obj_t {
mp_obj_base_t base;
@@ -54,7 +53,7 @@ typedef struct _machine_uart_obj_t {
uint16_t timeout_char; // timeout waiting between chars (in ms)
bool new;
ringbuf_t read_buffer;
- #if USART_BUFFER_TX
+ #if MICROPY_HW_UART_TXBUF
ringbuf_t write_buffer;
#endif
} machine_uart_obj_t;
@@ -91,7 +90,15 @@ void common_uart_irq_handler(int uart_id) {
// Now handler the incoming data
uart_drain_rx_fifo(self, uart);
} 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
+ uart->USART.INTENCLR.bit.DRE = 1;
+ }
+ #endif
} else {
// Disable the other interrupts, if set by error
uart->USART.INTENCLR.reg = (uint8_t) ~(SERCOM_USART_INTENCLR_DRE | SERCOM_USART_INTENCLR_RXC);
@@ -127,7 +134,7 @@ STATIC mp_obj_t 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 USART_BUFFER_TX
+ #if MICROPY_HW_UART_TXBUF
{ MP_QSTR_txbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
#endif
};
@@ -191,7 +198,7 @@ STATIC mp_obj_t machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args
}
}
- #if USART_BUFFER_TX
+ #if MICROPY_HW_UART_TXBUF
// Set the TX buffer size if configured.
size_t txbuf_len = DEFAULT_BUFFER_SIZE;
if (args[ARG_txbuf].u_int > 0) {
@@ -224,7 +231,7 @@ STATIC mp_obj_t machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args
ringbuf_alloc(&(self->read_buffer), rxbuf_len + 1);
MP_STATE_PORT(samd_uart_rx_buffer[self->id]) = self->read_buffer.buf;
- #if USART_BUFFER_TX
+ #if MICROPY_HW_UART_TXBUF
ringbuf_alloc(&(self->write_buffer), txbuf_len + 1);
MP_STATE_PORT(samd_uart_tx_buffer[self->id]) = self->write_buffer.buf;
#endif
@@ -274,6 +281,8 @@ STATIC mp_obj_t machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args
uint32_t baud = 65536 - ((uint64_t)(65536 * 16) * self->baudrate + get_apb_freq() / 2) / get_apb_freq();
uart->USART.BAUD.bit.BAUD = baud; // Set Baud
+ sercom_register_irq(self->id, &common_uart_irq_handler);
+
// Enable RXC interrupt
uart->USART.INTENSET.bit.RXC = 1;
#if defined(MCU_SAMD21)
@@ -281,7 +290,13 @@ STATIC mp_obj_t machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args
#elif defined(MCU_SAMD51)
NVIC_EnableIRQ(SERCOM0_0_IRQn + 4 * self->id + 2);
#endif
- sercom_register_irq(self->id, &common_uart_irq_handler);
+ #if MICROPY_HW_UART_TXBUF
+ // Enable DRE interrupt
+ // SAMD21 has just 1 IRQ for all USART events, so no need for an additional NVIC enable
+ #if defined(MCU_SAMD51)
+ NVIC_EnableIRQ(SERCOM0_0_IRQn + 4 * self->id + 0);
+ #endif
+ #endif
sercom_enable(uart, 1);
}
@@ -330,7 +345,7 @@ STATIC mp_obj_t machine_uart_deinit(mp_obj_t self_in) {
// Disable interrupts
uart->USART.INTENCLR.reg = 0xff;
MP_STATE_PORT(samd_uart_rx_buffer[self->id]) = NULL;
- #if USART_BUFFER_TX
+ #if MICROPY_HW_UART_TXBUF
MP_STATE_PORT(samd_uart_tx_buffer[self->id]) = NULL;
#endif
return mp_const_none;
@@ -339,7 +354,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_deinit_obj, machine_uart_deinit);
STATIC mp_obj_t machine_uart_any(mp_obj_t self_in) {
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
- // get all bytes from the fifo first
+ // get all bytes from the fifo first. May be obsolete.
uart_drain_rx_fifo(self, sercom_instance[self->id]);
return MP_OBJ_NEW_SMALL_INT(ringbuf_avail(&self->read_buffer));
}
@@ -349,9 +364,14 @@ STATIC mp_obj_t machine_uart_sendbreak(mp_obj_t self_in) {
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
uint32_t break_time_us = 13 * 1000000 / self->baudrate;
+ // Wait for the tx buffer to drain.
+ #if MICROPY_HW_UART_TXBUF
+ while (ringbuf_avail(&self->write_buffer) > 0) {
+ MICROPY_EVENT_POLL_HOOK
+ }
+ #endif
// Wait for the TX queue & register to clear
// Since the flags are not safe, just wait sufficiently long.
- // Once tx buffering is implemented, wait as well for the buffer to clear.
mp_hal_delay_us(2 * break_time_us);
// Disable MUX
PORT->Group[self->tx / 32].PINCFG[self->tx % 32].bit.PMUXEN = 0;
@@ -389,7 +409,7 @@ STATIC MP_DEFINE_CONST_DICT(machine_uart_locals_dict, machine_uart_locals_dict_t
STATIC mp_uint_t 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);
- uint64_t t = mp_hal_ticks_ms() + self->timeout;
+ uint64_t t = mp_hal_ticks_ms_64() + self->timeout;
uint64_t timeout_char = self->timeout_char;
uint8_t *dest = buf_in;
Sercom *uart = sercom_instance[self->id];
@@ -403,7 +423,7 @@ STATIC mp_uint_t machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t siz
uart_drain_rx_fifo(self, uart);
break;
}
- if (mp_hal_ticks_ms() > t) { // timed out
+ if (mp_hal_ticks_ms_64() > t) { // timed out
if (i <= 0) {
*errcode = MP_EAGAIN;
return MP_STREAM_ERROR;
@@ -421,17 +441,40 @@ STATIC mp_uint_t machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t siz
STATIC mp_uint_t machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) {
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
- size_t remaining = size;
+ size_t i = 0;
const uint8_t *src = buf_in;
Sercom *uart = sercom_instance[self->id];
- while (remaining--) {
- while (!(uart->USART.INTFLAG.bit.DRE)) {
+ #if MICROPY_HW_UART_TXBUF
+ uint64_t t = mp_hal_ticks_ms_64() + self->timeout;
+
+ while (i < size) {
+ // Wait for the first/next character to be sent.
+ while (ringbuf_free(&(self->write_buffer)) == 0) {
+ 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;
- src += 1;
+ ringbuf_put(&(self->write_buffer), *src++);
+ i++;
+ uart->USART.INTENSET.bit.DRE = 1; // kick off the IRQ
}
+ #else
+
+ while (i < size) {
+ while (!(uart->USART.INTFLAG.bit.DRE)) {
+ }
+ uart->USART.DATA.bit.DATA = *src++;
+ i++;
+ }
+ #endif
return size;
}
@@ -473,3 +516,6 @@ MP_DEFINE_CONST_OBJ_TYPE(
);
MP_REGISTER_ROOT_POINTER(void *samd_uart_rx_buffer[SERCOM_INST_NUM]);
+#if MICROPY_HW_UART_TXBUF
+MP_REGISTER_ROOT_POINTER(void *samd_uart_tx_buffer[SERCOM_INST_NUM]);
+#endif
diff --git a/ports/samd/mcu/samd21/mpconfigmcu.h b/ports/samd/mcu/samd21/mpconfigmcu.h
index 587f3f4b3..7b91be143 100644
--- a/ports/samd/mcu/samd21/mpconfigmcu.h
+++ b/ports/samd/mcu/samd21/mpconfigmcu.h
@@ -5,6 +5,8 @@
#define MICROPY_HW_FLASH_STORAGE_BYTES (0xFFFF)
#define VFS_BLOCK_SIZE_BYTES (1536) // 24x 64B flash pages;
+#define MICROPY_HW_UART_TXBUF (1)
+
#define CPU_FREQ (48000000)
#define APB_FREQ (48000000)
diff --git a/ports/samd/mcu/samd51/mpconfigmcu.h b/ports/samd/mcu/samd51/mpconfigmcu.h
index 95fc635e5..089ca48de 100644
--- a/ports/samd/mcu/samd51/mpconfigmcu.h
+++ b/ports/samd/mcu/samd51/mpconfigmcu.h
@@ -19,6 +19,8 @@ unsigned long trng_random_u32(void);
#define MICROPY_HW_FLASH_STORAGE_BYTES (0x1FFFF)
#define VFS_BLOCK_SIZE_BYTES (1536) //
+#define MICROPY_HW_UART_TXBUF (1)
+
#define CPU_FREQ (120000000)
#define APB_FREQ (48000000)
#define DPLLx_REF_FREQ (32768)