Skip to content

Commit abcf023

Browse files
danicamporadpgeorge
authored andcommitted
zephyr/machine_uart: Complete UART driver and make it interrupt driven.
Before this commit the UART would only work in very simple use cases. Receiving large amounts of data would result in lost bytes. Plus the print function would crash due to `uart_config_get()` returning incorrect values. Additionally, receiving data with `timeout==0` would fail even if data was already available in the internal UART Rx FIFO. This commit fixes those issues. The non-implemented functions have also been made usable. Signed-off-by: Daniel Campora <[email protected]>
1 parent 431b791 commit abcf023

File tree

2 files changed

+177
-22
lines changed

2 files changed

+177
-22
lines changed

ports/zephyr/machine_uart.c

Lines changed: 172 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*
66
* Copyright (c) 2016 Damien P. George
77
* Copyright (c) 2020 Yonatan Schachter
8+
* Copyright (c) 2025 Daniel Campora on behalf of REMOTE TECH LTD
89
*
910
* Permission is hereby granted, free of charge, to any person obtaining a copy
1011
* of this software and associated documentation files (the "Software"), to deal
@@ -32,16 +33,32 @@
3233
#include <zephyr/drivers/uart.h>
3334

3435
#include "py/mperrno.h"
36+
#include "py/ringbuf.h"
3537
#include "zephyr_device.h"
3638

37-
// The UART class doesn't have any constants for this port.
38-
#define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS
39+
40+
#define MACHINE_UART_RTS 1
41+
#define MACHINE_UART_CTS 2
42+
43+
// This class needs a finalizer, so we add it here
44+
#define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS \
45+
{ MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(MACHINE_UART_RTS) }, \
46+
{ MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(MACHINE_UART_CTS) }, \
47+
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&machine_uart_deinit_obj) },
48+
49+
#define UART_RX_RING_BUF_DEF_SIZE 128
50+
#define UART_TX_RING_BUF_DEF_SIZE 128
51+
52+
static void uart_interrupt_handler(const struct device *dev, void *user_data);
3953

4054
typedef struct _machine_uart_obj_t {
4155
mp_obj_base_t base;
4256
const struct device *dev;
4357
uint16_t timeout; // timeout waiting for first char (in ms)
4458
uint16_t timeout_char; // timeout waiting between chars (in ms)
59+
ringbuf_t rx_ringbuffer;
60+
ringbuf_t tx_ringbuffer;
61+
bool tx_complete;
4562
} machine_uart_obj_t;
4663

4764
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_
6077
}
6178

6279
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) {
63-
enum { ARG_timeout, ARG_timeout_char };
80+
enum { ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_txbuf, ARG_rxbuf, ARG_timeout, ARG_timeout_char, ARG_flow };
6481
static const mp_arg_t allowed_args[] = {
82+
{ MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 115200} },
83+
{ MP_QSTR_bits, MP_ARG_INT, {.u_int = 8} },
84+
{ MP_QSTR_parity, MP_ARG_OBJ, {.u_obj = mp_const_none} },
85+
{ MP_QSTR_stop, MP_ARG_INT, {.u_int = 1} },
86+
{ MP_QSTR_txbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_RX_RING_BUF_DEF_SIZE} },
87+
{ MP_QSTR_rxbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_TX_RING_BUF_DEF_SIZE} },
6588
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
6689
{ MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
90+
{ MP_QSTR_flow, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
6791
};
92+
6893
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
6994
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
7095

7196
self->timeout = args[ARG_timeout].u_int;
7297
self->timeout_char = args[ARG_timeout_char].u_int;
98+
99+
uint8_t data_bits;
100+
if (args[ARG_bits].u_int == 5) {
101+
data_bits = UART_CFG_DATA_BITS_5;
102+
} else if (args[ARG_bits].u_int == 6) {
103+
data_bits = UART_CFG_DATA_BITS_6;
104+
} else if (args[ARG_bits].u_int == 7) {
105+
data_bits = UART_CFG_DATA_BITS_7;
106+
} else if (args[ARG_bits].u_int == 8) {
107+
data_bits = UART_CFG_DATA_BITS_8;
108+
} else if (args[ARG_bits].u_int == 9) {
109+
data_bits = UART_CFG_DATA_BITS_9;
110+
} else {
111+
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid data bits"));
112+
}
113+
114+
uint8_t parity;
115+
if (args[ARG_parity].u_obj == mp_const_none) {
116+
parity = UART_CFG_PARITY_NONE;
117+
} else if (mp_obj_get_int(args[ARG_parity].u_obj) == 0) {
118+
parity = UART_CFG_PARITY_EVEN;
119+
} else if (mp_obj_get_int(args[ARG_parity].u_obj) == 1) {
120+
parity = UART_CFG_PARITY_ODD;
121+
} else {
122+
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid parity"));
123+
}
124+
125+
uint8_t stop_bits;
126+
if (args[ARG_stop].u_int == 1) {
127+
stop_bits = UART_CFG_STOP_BITS_1;
128+
} else if (args[ARG_stop].u_int == 2) {
129+
data_bits = UART_CFG_STOP_BITS_2;
130+
} else {
131+
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid stop bits"));
132+
}
133+
134+
uint8_t flow_ctrl;
135+
if (args[ARG_flow].u_int == 0) {
136+
flow_ctrl = UART_CFG_FLOW_CTRL_NONE;
137+
} else if (args[ARG_flow].u_int == (MACHINE_UART_RTS | MACHINE_UART_CTS)) {
138+
flow_ctrl = UART_CFG_FLOW_CTRL_RTS_CTS;
139+
} else {
140+
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid flow control"));
141+
}
142+
143+
const struct uart_config cfg = {
144+
.baudrate = args[ARG_baudrate].u_int,
145+
.parity = parity,
146+
.stop_bits = args[ARG_stop].u_int,
147+
.data_bits = data_bits,
148+
.flow_ctrl = flow_ctrl
149+
};
150+
151+
int ret = uart_configure(self->dev, &cfg);
152+
if (ret < 0) {
153+
mp_raise_OSError(-ret);
154+
}
155+
156+
ringbuf_alloc(&self->tx_ringbuffer, args[ARG_txbuf].u_int);
157+
ringbuf_alloc(&self->rx_ringbuffer, args[ARG_rxbuf].u_int);
158+
159+
uart_irq_callback_user_data_set(self->dev, uart_interrupt_handler, (void *)self);
160+
uart_irq_rx_enable(self->dev);
73161
}
74162

75163
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) {
76164
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
77165

78-
machine_uart_obj_t *self = mp_obj_malloc(machine_uart_obj_t, &machine_uart_type);
79-
self->dev = zephyr_device_find(args[0]);
166+
const struct device *dev = zephyr_device_find(args[0]);
167+
machine_uart_obj_t *self = mp_obj_malloc_with_finaliser(machine_uart_obj_t, &machine_uart_type);
168+
self->dev = dev;
169+
self->tx_complete = true;
80170

81171
mp_map_t kw_args;
82172
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
@@ -86,65 +176,125 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg
86176
}
87177

88178
static void mp_machine_uart_deinit(machine_uart_obj_t *self) {
89-
(void)self;
179+
uart_irq_rx_disable(self->dev);
180+
uart_irq_tx_disable(self->dev);
90181
}
91182

92183
static mp_int_t mp_machine_uart_any(machine_uart_obj_t *self) {
93-
(void)self;
94-
mp_raise_NotImplementedError(NULL); // TODO
184+
return ringbuf_avail(&self->rx_ringbuffer);
95185
}
96186

97187
static bool mp_machine_uart_txdone(machine_uart_obj_t *self) {
98-
(void)self;
99-
mp_raise_NotImplementedError(NULL); // TODO
188+
return self->tx_complete && !ringbuf_avail(&self->tx_ringbuffer) ? true : false;
100189
}
101190

102191
static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) {
103192
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
104193
uint8_t *buffer = (uint8_t *)buf_in;
105-
uint8_t data;
106194
mp_uint_t bytes_read = 0;
107195
size_t elapsed_ms = 0;
108196
size_t time_to_wait = self->timeout;
109197

110-
while ((elapsed_ms < time_to_wait) && (bytes_read < size)) {
111-
if (!uart_poll_in(self->dev, &data)) {
112-
buffer[bytes_read++] = data;
198+
do {
199+
int _rx_len = MIN(ringbuf_avail(&self->rx_ringbuffer), size - bytes_read);
200+
if (_rx_len > 0) {
201+
ringbuf_get_bytes(&self->rx_ringbuffer, &buffer[bytes_read], _rx_len);
202+
bytes_read += _rx_len;
113203
elapsed_ms = 0;
114204
time_to_wait = self->timeout_char;
115205
} else {
116206
k_msleep(1);
117207
elapsed_ms++;
118208
}
119-
}
209+
} while ((elapsed_ms < time_to_wait) && (bytes_read < size));
210+
120211
return bytes_read;
121212
}
122213

123214
static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) {
124215
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
125216
uint8_t *buffer = (uint8_t *)buf_in;
126217

127-
for (mp_uint_t i = 0; i < size; i++) {
218+
// wait for any pending transmission to complete
219+
while (!mp_machine_uart_txdone(self)) {
220+
MICROPY_EVENT_POLL_HOOK;
221+
}
222+
223+
int _ex_size = 0;
224+
int _free_space = ringbuf_free(&self->tx_ringbuffer);
225+
if (size > _free_space) {
226+
_ex_size = size - _free_space;
227+
}
228+
229+
// do a blocking tx of what doesn't fit into the outgoing ring buffer
230+
for (mp_uint_t i = 0; i < _ex_size; i++) {
128231
uart_poll_out(self->dev, buffer[i]);
129232
}
130233

234+
ringbuf_put_bytes(&self->tx_ringbuffer, &buffer[_ex_size], size - _ex_size);
235+
self->tx_complete = false;
236+
uart_irq_tx_enable(self->dev);
237+
131238
return size;
132239
}
133240

134241
static mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
135-
mp_uint_t ret;
242+
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
243+
mp_uint_t ret = 0;
136244

137245
if (request == MP_STREAM_POLL) {
138-
ret = 0;
139-
// read is always blocking
140-
141-
if (arg & MP_STREAM_POLL_WR) {
246+
uintptr_t flags = arg;
247+
if ((flags & MP_STREAM_POLL_RD) && (mp_machine_uart_any(self) > 0)) {
248+
ret |= MP_STREAM_POLL_RD;
249+
}
250+
if ((flags & MP_STREAM_POLL_WR) && mp_machine_uart_txdone(self)) {
142251
ret |= MP_STREAM_POLL_WR;
143252
}
144-
return ret;
253+
} else if (request == MP_STREAM_FLUSH) {
254+
while (!mp_machine_uart_txdone(self)) {
255+
MICROPY_EVENT_POLL_HOOK;
256+
}
145257
} else {
146258
*errcode = MP_EINVAL;
147259
ret = MP_STREAM_ERROR;
148260
}
261+
149262
return ret;
150263
}
264+
265+
static void uart_interrupt_handler(const struct device *dev, void *user_data) {
266+
machine_uart_obj_t *self = (machine_uart_obj_t *)user_data;
267+
268+
while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
269+
if (uart_irq_rx_ready(dev)) {
270+
uint8_t _rx_buffer[32];
271+
size_t _free_space = MIN(ringbuf_free(&self->rx_ringbuffer), sizeof(_rx_buffer));
272+
273+
// empty the uart fifo even if we can't store bytes anymore
274+
// otherwise we will never exit this interrupt handler
275+
int rcv_len = uart_fifo_read(dev, _rx_buffer, (_free_space > 0) ? _free_space : 1);
276+
if ((rcv_len <= 0) || (_free_space == 0)) {
277+
continue;
278+
}
279+
280+
ringbuf_put_bytes(&self->rx_ringbuffer, _rx_buffer, rcv_len);
281+
}
282+
283+
int _max_uart_tx_len = uart_irq_tx_ready(dev);
284+
if (_max_uart_tx_len > 0) {
285+
uint8_t _tx_buffer[32];
286+
size_t _buffer_tx_len;
287+
288+
_max_uart_tx_len = MIN(_max_uart_tx_len, sizeof(_tx_buffer));
289+
_buffer_tx_len = ringbuf_avail(&self->tx_ringbuffer);
290+
if (_buffer_tx_len > 0) {
291+
_buffer_tx_len = MIN(_max_uart_tx_len, _buffer_tx_len);
292+
ringbuf_get_bytes(&self->tx_ringbuffer, _tx_buffer, _buffer_tx_len);
293+
uart_fifo_fill(dev, _tx_buffer, _buffer_tx_len);
294+
} else if (uart_irq_tx_complete(dev)) {
295+
uart_irq_tx_disable(dev);
296+
self->tx_complete = true;
297+
}
298+
}
299+
}
300+
}

ports/zephyr/prj.conf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,8 @@ CONFIG_MICROPY_VFS_LFS2=y
8181

8282
CONFIG_WATCHDOG=y
8383
CONFIG_WDT_DISABLE_AT_BOOT=y
84+
85+
CONFIG_SERIAL=y
86+
CONFIG_UART_INTERRUPT_DRIVEN=y
87+
CONFIG_UART_LINE_CTRL=y
88+
CONFIG_UART_USE_RUNTIME_CONFIGURE=y

0 commit comments

Comments
 (0)