summaryrefslogtreecommitdiffstatshomepage
path: root/ports/zephyr/machine_uart.c
diff options
context:
space:
mode:
Diffstat (limited to 'ports/zephyr/machine_uart.c')
-rw-r--r--ports/zephyr/machine_uart.c194
1 files changed, 172 insertions, 22 deletions
diff --git a/ports/zephyr/machine_uart.c b/ports/zephyr/machine_uart.c
index 1927335ddf..60613befb1 100644
--- a/ports/zephyr/machine_uart.c
+++ b/ports/zephyr/machine_uart.c
@@ -5,6 +5,7 @@
*
* Copyright (c) 2016 Damien P. George
* Copyright (c) 2020 Yonatan Schachter
+ * Copyright (c) 2025 Daniel Campora on behalf of REMOTE TECH LTD
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -32,16 +33,32 @@
#include <zephyr/drivers/uart.h>
#include "py/mperrno.h"
+#include "py/ringbuf.h"
#include "zephyr_device.h"
-// The UART class doesn't have any constants for this port.
-#define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS
+
+#define MACHINE_UART_RTS 1
+#define MACHINE_UART_CTS 2
+
+// This class needs a finalizer, so we add it here
+#define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS \
+ { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(MACHINE_UART_RTS) }, \
+ { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(MACHINE_UART_CTS) }, \
+ { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&machine_uart_deinit_obj) },
+
+#define UART_RX_RING_BUF_DEF_SIZE 128
+#define UART_TX_RING_BUF_DEF_SIZE 128
+
+static void uart_interrupt_handler(const struct device *dev, void *user_data);
typedef struct _machine_uart_obj_t {
mp_obj_base_t base;
const struct device *dev;
uint16_t timeout; // timeout waiting for first char (in ms)
uint16_t timeout_char; // timeout waiting between chars (in ms)
+ ringbuf_t rx_ringbuffer;
+ ringbuf_t tx_ringbuffer;
+ bool tx_complete;
} machine_uart_obj_t;
static const char *_parity_name[] = {"None", "Odd", "Even", "Mark", "Space"};
@@ -60,23 +77,96 @@ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_
}
static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
- enum { ARG_timeout, ARG_timeout_char };
+ enum { ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_txbuf, ARG_rxbuf, ARG_timeout, ARG_timeout_char, ARG_flow };
static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 115200} },
+ { MP_QSTR_bits, MP_ARG_INT, {.u_int = 8} },
+ { MP_QSTR_parity, MP_ARG_OBJ, {.u_obj = mp_const_none} },
+ { MP_QSTR_stop, MP_ARG_INT, {.u_int = 1} },
+ { MP_QSTR_txbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_RX_RING_BUF_DEF_SIZE} },
+ { MP_QSTR_rxbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_TX_RING_BUF_DEF_SIZE} },
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ { MP_QSTR_flow, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
};
+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
self->timeout = args[ARG_timeout].u_int;
self->timeout_char = args[ARG_timeout_char].u_int;
+
+ uint8_t data_bits;
+ if (args[ARG_bits].u_int == 5) {
+ data_bits = UART_CFG_DATA_BITS_5;
+ } else if (args[ARG_bits].u_int == 6) {
+ data_bits = UART_CFG_DATA_BITS_6;
+ } else if (args[ARG_bits].u_int == 7) {
+ data_bits = UART_CFG_DATA_BITS_7;
+ } else if (args[ARG_bits].u_int == 8) {
+ data_bits = UART_CFG_DATA_BITS_8;
+ } else if (args[ARG_bits].u_int == 9) {
+ data_bits = UART_CFG_DATA_BITS_9;
+ } else {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid data bits"));
+ }
+
+ uint8_t parity;
+ if (args[ARG_parity].u_obj == mp_const_none) {
+ parity = UART_CFG_PARITY_NONE;
+ } else if (mp_obj_get_int(args[ARG_parity].u_obj) == 0) {
+ parity = UART_CFG_PARITY_EVEN;
+ } else if (mp_obj_get_int(args[ARG_parity].u_obj) == 1) {
+ parity = UART_CFG_PARITY_ODD;
+ } else {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid parity"));
+ }
+
+ uint8_t stop_bits;
+ if (args[ARG_stop].u_int == 1) {
+ stop_bits = UART_CFG_STOP_BITS_1;
+ } else if (args[ARG_stop].u_int == 2) {
+ data_bits = UART_CFG_STOP_BITS_2;
+ } else {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid stop bits"));
+ }
+
+ uint8_t flow_ctrl;
+ if (args[ARG_flow].u_int == 0) {
+ flow_ctrl = UART_CFG_FLOW_CTRL_NONE;
+ } else if (args[ARG_flow].u_int == (MACHINE_UART_RTS | MACHINE_UART_CTS)) {
+ flow_ctrl = UART_CFG_FLOW_CTRL_RTS_CTS;
+ } else {
+ mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid flow control"));
+ }
+
+ const struct uart_config cfg = {
+ .baudrate = args[ARG_baudrate].u_int,
+ .parity = parity,
+ .stop_bits = args[ARG_stop].u_int,
+ .data_bits = data_bits,
+ .flow_ctrl = flow_ctrl
+ };
+
+ int ret = uart_configure(self->dev, &cfg);
+ if (ret < 0) {
+ mp_raise_OSError(-ret);
+ }
+
+ ringbuf_alloc(&self->tx_ringbuffer, args[ARG_txbuf].u_int);
+ ringbuf_alloc(&self->rx_ringbuffer, args[ARG_rxbuf].u_int);
+
+ uart_irq_callback_user_data_set(self->dev, uart_interrupt_handler, (void *)self);
+ uart_irq_rx_enable(self->dev);
}
static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
- machine_uart_obj_t *self = mp_obj_malloc(machine_uart_obj_t, &machine_uart_type);
- self->dev = zephyr_device_find(args[0]);
+ const struct device *dev = zephyr_device_find(args[0]);
+ machine_uart_obj_t *self = mp_obj_malloc_with_finaliser(machine_uart_obj_t, &machine_uart_type);
+ self->dev = dev;
+ self->tx_complete = true;
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
@@ -86,37 +176,38 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg
}
static void mp_machine_uart_deinit(machine_uart_obj_t *self) {
- (void)self;
+ uart_irq_rx_disable(self->dev);
+ uart_irq_tx_disable(self->dev);
}
static mp_int_t mp_machine_uart_any(machine_uart_obj_t *self) {
- (void)self;
- mp_raise_NotImplementedError(NULL); // TODO
+ return ringbuf_avail(&self->rx_ringbuffer);
}
static bool mp_machine_uart_txdone(machine_uart_obj_t *self) {
- (void)self;
- mp_raise_NotImplementedError(NULL); // TODO
+ return self->tx_complete && !ringbuf_avail(&self->tx_ringbuffer) ? true : false;
}
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);
uint8_t *buffer = (uint8_t *)buf_in;
- uint8_t data;
mp_uint_t bytes_read = 0;
size_t elapsed_ms = 0;
size_t time_to_wait = self->timeout;
- while ((elapsed_ms < time_to_wait) && (bytes_read < size)) {
- if (!uart_poll_in(self->dev, &data)) {
- buffer[bytes_read++] = data;
+ do {
+ int _rx_len = MIN(ringbuf_avail(&self->rx_ringbuffer), size - bytes_read);
+ if (_rx_len > 0) {
+ ringbuf_get_bytes(&self->rx_ringbuffer, &buffer[bytes_read], _rx_len);
+ bytes_read += _rx_len;
elapsed_ms = 0;
time_to_wait = self->timeout_char;
} else {
k_msleep(1);
elapsed_ms++;
}
- }
+ } while ((elapsed_ms < time_to_wait) && (bytes_read < size));
+
return bytes_read;
}
@@ -124,27 +215,86 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
uint8_t *buffer = (uint8_t *)buf_in;
- for (mp_uint_t i = 0; i < size; i++) {
+ // wait for any pending transmission to complete
+ while (!mp_machine_uart_txdone(self)) {
+ MICROPY_EVENT_POLL_HOOK;
+ }
+
+ int _ex_size = 0;
+ int _free_space = ringbuf_free(&self->tx_ringbuffer);
+ if (size > _free_space) {
+ _ex_size = size - _free_space;
+ }
+
+ // do a blocking tx of what doesn't fit into the outgoing ring buffer
+ for (mp_uint_t i = 0; i < _ex_size; i++) {
uart_poll_out(self->dev, buffer[i]);
}
+ ringbuf_put_bytes(&self->tx_ringbuffer, &buffer[_ex_size], size - _ex_size);
+ self->tx_complete = false;
+ uart_irq_tx_enable(self->dev);
+
return size;
}
static mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
- mp_uint_t ret;
+ machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ mp_uint_t ret = 0;
if (request == MP_STREAM_POLL) {
- ret = 0;
- // read is always blocking
-
- if (arg & MP_STREAM_POLL_WR) {
+ uintptr_t flags = arg;
+ if ((flags & MP_STREAM_POLL_RD) && (mp_machine_uart_any(self) > 0)) {
+ ret |= MP_STREAM_POLL_RD;
+ }
+ if ((flags & MP_STREAM_POLL_WR) && mp_machine_uart_txdone(self)) {
ret |= MP_STREAM_POLL_WR;
}
- return ret;
+ } else if (request == MP_STREAM_FLUSH) {
+ while (!mp_machine_uart_txdone(self)) {
+ MICROPY_EVENT_POLL_HOOK;
+ }
} else {
*errcode = MP_EINVAL;
ret = MP_STREAM_ERROR;
}
+
return ret;
}
+
+static void uart_interrupt_handler(const struct device *dev, void *user_data) {
+ machine_uart_obj_t *self = (machine_uart_obj_t *)user_data;
+
+ while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
+ if (uart_irq_rx_ready(dev)) {
+ uint8_t _rx_buffer[32];
+ size_t _free_space = MIN(ringbuf_free(&self->rx_ringbuffer), sizeof(_rx_buffer));
+
+ // empty the uart fifo even if we can't store bytes anymore
+ // otherwise we will never exit this interrupt handler
+ int rcv_len = uart_fifo_read(dev, _rx_buffer, (_free_space > 0) ? _free_space : 1);
+ if ((rcv_len <= 0) || (_free_space == 0)) {
+ continue;
+ }
+
+ ringbuf_put_bytes(&self->rx_ringbuffer, _rx_buffer, rcv_len);
+ }
+
+ int _max_uart_tx_len = uart_irq_tx_ready(dev);
+ if (_max_uart_tx_len > 0) {
+ uint8_t _tx_buffer[32];
+ size_t _buffer_tx_len;
+
+ _max_uart_tx_len = MIN(_max_uart_tx_len, sizeof(_tx_buffer));
+ _buffer_tx_len = ringbuf_avail(&self->tx_ringbuffer);
+ if (_buffer_tx_len > 0) {
+ _buffer_tx_len = MIN(_max_uart_tx_len, _buffer_tx_len);
+ ringbuf_get_bytes(&self->tx_ringbuffer, _tx_buffer, _buffer_tx_len);
+ uart_fifo_fill(dev, _tx_buffer, _buffer_tx_len);
+ } else if (uart_irq_tx_complete(dev)) {
+ uart_irq_tx_disable(dev);
+ self->tx_complete = true;
+ }
+ }
+ }
+}