Skip to content

Commit 5a70850

Browse files
robert-hhdpgeorge
authored andcommitted
samd/machine_uart: Add full support for 9-bit data.
Prior to this commit, 9-bit UART data could be specified in the constructor and was transmitted, but the 9th bit was set to 0 when sending, and ignored when receiving. This commit completes 9-bit support in that the 9th bit is taken from the data. 9-bit data has to be provided with `uart.write()` and and read with `uart.read()` as two bytes for each transmitted item, low order byte first. The data length supplied with `uart.write()` and requested by `uart.read()` has to be even, which is checked. The size of the UART buffers will be transparently doubled to cater for 9-bit data. Signed-off-by: robert-hh <[email protected]>
1 parent e2532e0 commit 5a70850

File tree

1 file changed

+73
-11
lines changed

1 file changed

+73
-11
lines changed

ports/samd/machine_uart.c

+73-11
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,17 @@ static const char *_parity_name[] = {"None", "", "0", "1"}; // Is defined as 0,
107107

108108
// take all bytes from the fifo and store them in the buffer
109109
static void uart_drain_rx_fifo(machine_uart_obj_t *self, Sercom *uart) {
110+
uint8_t bits = self->bits;
110111
while (uart->USART.INTFLAG.bit.RXC != 0) {
111-
if (ringbuf_free(&self->read_buffer) > 0) {
112-
// get a byte from uart and put into the buffer
113-
ringbuf_put(&(self->read_buffer), uart->USART.DATA.bit.DATA);
112+
if (ringbuf_free(&self->read_buffer) >= (bits <= 8 ? 1 : 2)) {
113+
// get a word from uart and put into the buffer
114+
if (bits <= 8) {
115+
ringbuf_put(&(self->read_buffer), uart->USART.DATA.bit.DATA);
116+
} else {
117+
uint16_t data = uart->USART.DATA.bit.DATA;
118+
ringbuf_put(&(self->read_buffer), data);
119+
ringbuf_put(&(self->read_buffer), data >> 8);
120+
}
114121
} else {
115122
// if the buffer is full, disable the RX interrupt
116123
// allowing RTS to come up. It will be re-enabled by the next read
@@ -150,7 +157,12 @@ void common_uart_irq_handler(int uart_id) {
150157
#if MICROPY_HW_UART_TXBUF
151158
// handle the outgoing data
152159
if (ringbuf_avail(&self->write_buffer) > 0) {
153-
uart->USART.DATA.bit.DATA = ringbuf_get(&self->write_buffer);
160+
if (self->bits <= 8) {
161+
uart->USART.DATA.bit.DATA = ringbuf_get(&self->write_buffer);
162+
} else {
163+
uart->USART.DATA.bit.DATA =
164+
ringbuf_get(&self->write_buffer) | (ringbuf_get(&self->write_buffer) << 8);
165+
}
154166
} else {
155167
#if MICROPY_PY_MACHINE_UART_IRQ
156168
// Set the TXIDLE flag
@@ -274,6 +286,17 @@ void machine_uart_set_baudrate(mp_obj_t self_in, uint32_t baudrate) {
274286

275287
static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
276288
machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
289+
size_t rxbuf_len = self->read_buffer.size - 1;
290+
#if MICROPY_HW_UART_TXBUF
291+
size_t txbuf_len = self->write_buffer.size - 1;
292+
#endif
293+
if (self->bits > 8) {
294+
rxbuf_len /= 2;
295+
#if MICROPY_HW_UART_TXBUF
296+
txbuf_len /= 2;
297+
#endif
298+
}
299+
277300
mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, "
278301
"timeout=%u, timeout_char=%u, rxbuf=%d"
279302
#if MICROPY_HW_UART_TXBUF
@@ -287,9 +310,9 @@ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_
287310
#endif
288311
")",
289312
self->id, self->baudrate, self->bits, _parity_name[self->parity],
290-
self->stop + 1, self->timeout, self->timeout_char, self->read_buffer.size - 1
313+
self->stop + 1, self->timeout, self->timeout_char, rxbuf_len
291314
#if MICROPY_HW_UART_TXBUF
292-
, self->write_buffer.size - 1
315+
, txbuf_len
293316
#endif
294317
#if MICROPY_HW_UART_RTSCTS
295318
, self->rts != 0xff ? pin_find_by_id(self->rts)->name : MP_QSTR_None
@@ -411,6 +434,14 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args,
411434
}
412435
}
413436
#endif
437+
438+
// Double the buffer lengths for 9 bit transfer
439+
if (self->bits > 8) {
440+
rxbuf_len *= 2;
441+
#if MICROPY_HW_UART_TXBUF
442+
txbuf_len *= 2;
443+
#endif
444+
}
414445
// Initialise the UART peripheral if any arguments given, or it was not initialised previously.
415446
if (n_args > 0 || kw_args->used > 0 || self->new) {
416447
self->new = false;
@@ -619,7 +650,12 @@ static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t
619650
uint64_t timeout_char = self->timeout_char;
620651
uint8_t *dest = buf_in;
621652

622-
for (size_t i = 0; i < size; i++) {
653+
// Check that size is even for 9 bit transfers.
654+
if ((self->bits >= 9) && (size & 1)) {
655+
*errcode = MP_EIO;
656+
return MP_STREAM_ERROR;
657+
}
658+
for (size_t i = 0; i < size;) {
623659
// Wait for the first/next character
624660
while (ringbuf_avail(&self->read_buffer) == 0) {
625661
if (mp_hal_ticks_ms_64() > t) { // timed out
@@ -633,6 +669,11 @@ static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t
633669
MICROPY_EVENT_POLL_HOOK
634670
}
635671
*dest++ = ringbuf_get(&(self->read_buffer));
672+
i++;
673+
if (self->bits >= 9 && i < size) {
674+
*dest++ = ringbuf_get(&(self->read_buffer));
675+
i++;
676+
}
636677
t = mp_hal_ticks_ms_64() + timeout_char;
637678
// (Re-)Enable RXC interrupt
638679
if ((uart->USART.INTENSET.reg & SERCOM_USART_INTENSET_RXC) == 0) {
@@ -647,12 +688,19 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_
647688
size_t i = 0;
648689
const uint8_t *src = buf_in;
649690
Sercom *uart = sercom_instance[self->id];
650-
651691
uint64_t t = mp_hal_ticks_ms_64() + self->timeout;
692+
uint8_t bits = self->bits;
693+
// Check that size is even for 9 bit transfers.
694+
if ((bits >= 9) && (size & 1)) {
695+
*errcode = MP_EIO;
696+
return MP_STREAM_ERROR;
697+
}
698+
652699
#if MICROPY_HW_UART_TXBUF
653700

654701
#if MICROPY_PY_MACHINE_UART_IRQ
655702
// Prefill the FIFO to get rid of the initial IRQ_TXIDLE event
703+
// Do not care for 9 Bit transfer here since the UART is not yet started.
656704
while (i < size && ringbuf_free(&(self->write_buffer)) > 0) {
657705
ringbuf_put(&(self->write_buffer), *src++);
658706
i++;
@@ -672,8 +720,16 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_
672720
}
673721
MICROPY_EVENT_POLL_HOOK
674722
}
675-
ringbuf_put(&(self->write_buffer), *src++);
676-
i++;
723+
if (bits >= 9) {
724+
mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();
725+
ringbuf_put(&(self->write_buffer), *src++);
726+
ringbuf_put(&(self->write_buffer), *src++);
727+
i += 2;
728+
MICROPY_END_ATOMIC_SECTION(atomic_state);
729+
} else {
730+
ringbuf_put(&(self->write_buffer), *src++);
731+
i += 1;
732+
}
677733
uart->USART.INTENSET.reg = SERCOM_USART_INTENSET_DRE; // kick off the IRQ
678734
}
679735

@@ -691,7 +747,13 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_
691747
}
692748
MICROPY_EVENT_POLL_HOOK
693749
}
694-
uart->USART.DATA.bit.DATA = *src++;
750+
if (self->bits > 8 && i < (size - 1)) {
751+
uart->USART.DATA.bit.DATA = *(uint16_t *)src;
752+
i++;
753+
src += 2;
754+
} else {
755+
uart->USART.DATA.bit.DATA = *src++;
756+
}
695757
i++;
696758
}
697759
#endif

0 commit comments

Comments
 (0)