Skip to content

Commit c121cf8

Browse files
committed
zephyr: Complete the UART driver and make it interrupt driven.
Signed-off-by: Daniel Campora <[email protected]>
1 parent 5cfafb7 commit c121cf8

File tree

3 files changed

+110
-18
lines changed

3 files changed

+110
-18
lines changed

ports/zephyr/machine_uart.c

Lines changed: 102 additions & 18 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
@@ -30,18 +31,30 @@
3031

3132
#include <zephyr/kernel.h>
3233
#include <zephyr/drivers/uart.h>
34+
#include <zephyr/sys/ring_buffer.h>
3335

3436
#include "py/mperrno.h"
3537
#include "zephyr_device.h"
3638

3739
// The UART class doesn't have any constants for this port.
3840
#define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS
3941

42+
#define UART_RX_RING_BUF_DEF_SIZE 128
43+
#define UART_TX_RING_BUF_DEF_SIZE 128
44+
45+
static void uart_interrupt_handler(const struct device *dev, void *user_data);
46+
4047
typedef struct _machine_uart_obj_t {
4148
mp_obj_base_t base;
4249
const struct device *dev;
4350
uint16_t timeout; // timeout waiting for first char (in ms)
4451
uint16_t timeout_char; // timeout waiting between chars (in ms)
52+
uint16_t txbuf_size;
53+
uint16_t rxbuf_size;
54+
uint8_t *rx_buffer;
55+
struct ring_buf rx_ringbuffer;
56+
uint8_t *tx_buffer;
57+
struct ring_buf tx_ringbuffer;
4558
} machine_uart_obj_t;
4659

4760
static const char *_parity_name[] = {"None", "Odd", "Even", "Mark", "Space"};
@@ -60,16 +73,30 @@ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_
6073
}
6174

6275
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 };
76+
enum { ARG_txbuf, ARG_rxbuf, ARG_timeout, ARG_timeout_char };
6477
static const mp_arg_t allowed_args[] = {
78+
{ MP_QSTR_txbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_RX_RING_BUF_DEF_SIZE} },
79+
{ MP_QSTR_rxbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_TX_RING_BUF_DEF_SIZE} },
6580
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
6681
{ MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
6782
};
83+
6884
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
6985
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
7086

87+
self->txbuf_size = args[ARG_txbuf].u_int;
88+
self->rxbuf_size = args[ARG_rxbuf].u_int;
7189
self->timeout = args[ARG_timeout].u_int;
7290
self->timeout_char = args[ARG_timeout_char].u_int;
91+
92+
self->tx_buffer = m_tracked_calloc(self->txbuf_size, sizeof(uint8_t));
93+
ring_buf_init(&self->tx_ringbuffer, self->txbuf_size, self->tx_buffer);
94+
95+
self->rx_buffer = m_tracked_calloc(self->rxbuf_size, sizeof(uint8_t));
96+
ring_buf_init(&self->rx_ringbuffer, self->rxbuf_size, self->rx_buffer);
97+
98+
uart_irq_callback_user_data_set(self->dev, uart_interrupt_handler, (void *)self);
99+
uart_irq_rx_enable(self->dev);
73100
}
74101

75102
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) {
@@ -86,65 +113,122 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg
86113
}
87114

88115
static void mp_machine_uart_deinit(machine_uart_obj_t *self) {
89-
(void)self;
116+
m_tracked_free(self->tx_buffer);
117+
m_tracked_free(self->rx_buffer);
90118
}
91119

92120
static mp_int_t mp_machine_uart_any(machine_uart_obj_t *self) {
93-
(void)self;
94-
mp_raise_NotImplementedError(NULL); // TODO
121+
return ring_buf_size_get(&self->rx_ringbuffer);
95122
}
96123

97124
static bool mp_machine_uart_txdone(machine_uart_obj_t *self) {
98-
(void)self;
99-
mp_raise_NotImplementedError(NULL); // TODO
125+
if (ring_buf_is_empty(&self->tx_ringbuffer)) {
126+
return true;
127+
}
128+
return false;
100129
}
101130

102131
static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) {
103132
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
104133
uint8_t *buffer = (uint8_t *)buf_in;
105-
uint8_t data;
106134
mp_uint_t bytes_read = 0;
107135
size_t elapsed_ms = 0;
108136
size_t time_to_wait = self->timeout;
109137

110-
while ((elapsed_ms < time_to_wait) && (bytes_read < size)) {
111-
if (!uart_poll_in(self->dev, &data)) {
112-
buffer[bytes_read++] = data;
138+
do {
139+
int _rx_len = ring_buf_get(&self->rx_ringbuffer, &buffer[bytes_read], size - bytes_read);
140+
if (_rx_len > 0) {
141+
bytes_read += _rx_len;
113142
elapsed_ms = 0;
114143
time_to_wait = self->timeout_char;
115144
} else {
116145
k_msleep(1);
117146
elapsed_ms++;
118147
}
119-
}
148+
} while ((elapsed_ms < time_to_wait) && (bytes_read < size));
149+
120150
return bytes_read;
121151
}
122152

123153
static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) {
124154
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
125155
uint8_t *buffer = (uint8_t *)buf_in;
126156

127-
for (mp_uint_t i = 0; i < size; i++) {
157+
// wait for any pending transmission to complete
158+
while (!mp_machine_uart_txdone(self)) {
159+
MICROPY_EVENT_POLL_HOOK;
160+
}
161+
162+
int _ex_size = 0;
163+
int _free_space = ring_buf_space_get(&self->tx_ringbuffer);
164+
if (size > _free_space) {
165+
_ex_size = size - _free_space;
166+
}
167+
168+
// do a blocking tx of what doesn't fit into the outgoing ring buffer
169+
for (mp_uint_t i = 0; i < _ex_size; i++) {
128170
uart_poll_out(self->dev, buffer[i]);
129171
}
130172

173+
ring_buf_put(&self->tx_ringbuffer, &buffer[_ex_size], size - _ex_size);
174+
uart_irq_tx_enable(self->dev);
175+
131176
return size;
132177
}
133178

134179
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;
180+
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
181+
mp_uint_t ret = 0;
136182

137183
if (request == MP_STREAM_POLL) {
138-
ret = 0;
139-
// read is always blocking
140-
141-
if (arg & MP_STREAM_POLL_WR) {
184+
uintptr_t flags = arg;
185+
if ((flags & MP_STREAM_POLL_RD) && (mp_machine_uart_any(self) > 0)) {
186+
ret |= MP_STREAM_POLL_RD;
187+
}
188+
if ((flags & MP_STREAM_POLL_WR) && mp_machine_uart_txdone(self)) {
142189
ret |= MP_STREAM_POLL_WR;
143190
}
144-
return ret;
191+
} else if (request == MP_STREAM_FLUSH) {
192+
while (!mp_machine_uart_txdone(self)) {
193+
MICROPY_EVENT_POLL_HOOK;
194+
}
145195
} else {
146196
*errcode = MP_EINVAL;
147197
ret = MP_STREAM_ERROR;
148198
}
199+
149200
return ret;
150201
}
202+
203+
static void uart_interrupt_handler(const struct device *dev, void *user_data) {
204+
machine_uart_obj_t *self = (machine_uart_obj_t *)user_data;
205+
206+
while (uart_irq_update(dev) && uart_irq_is_pending(dev)) {
207+
if (uart_irq_rx_ready(dev)) {
208+
uint8_t _rx_buffer[32];
209+
size_t _free_space = MIN(ring_buf_space_get(&self->rx_ringbuffer), sizeof(_rx_buffer));
210+
211+
// empty the uart fifo even if we can't store bytes anymore
212+
// otherwise we will never exit this interrupt handler
213+
int rcv_len = uart_fifo_read(dev, _rx_buffer, (_free_space > 0) ? _free_space : 1);
214+
if ((rcv_len <= 0) || (_free_space == 0)) {
215+
continue;
216+
}
217+
218+
ring_buf_put(&self->rx_ringbuffer, _rx_buffer, rcv_len);
219+
}
220+
221+
int _max_tx_len = uart_irq_tx_ready(dev);
222+
if (_max_tx_len > 0) {
223+
uint8_t _tx_buffer[32];
224+
size_t _tx_len = MIN(_max_tx_len, sizeof(_tx_buffer));
225+
226+
_tx_len = ring_buf_get(&self->tx_ringbuffer, _tx_buffer, _tx_len);
227+
if (_tx_len > 0) {
228+
uart_fifo_fill(dev, _tx_buffer, _tx_len);
229+
} else {
230+
uart_irq_tx_disable(dev);
231+
}
232+
}
233+
}
234+
}

ports/zephyr/mpconfigport.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@
110110
#define MICROPY_FATFS_RPATH (2)
111111
#define MICROPY_FATFS_NORTC (1)
112112

113+
#define MICROPY_TRACKED_ALLOC (1)
114+
113115
// Saving extra crumbs to make sure binary fits in 128K
114116
#define MICROPY_COMP_CONST_FOLDING (0)
115117
#define MICROPY_COMP_CONST (0)

ports/zephyr/prj.conf

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

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

0 commit comments

Comments
 (0)