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
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+
4047typedef 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
4760static 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
6275static 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
75102static 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
88115static 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
92120static 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
97124static 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
102131static 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
123153static 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
134179static 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+ }
0 commit comments