alif/mpuart: Enhance UART to support bits/parity/stop and more IRQs.

Signed-off-by: Damien George <damien@micropython.org>
This commit is contained in:
Damien George 2025-03-17 13:19:07 +11:00
parent 19a4689c6b
commit 293e8db9d7
3 changed files with 92 additions and 17 deletions

View file

@ -89,14 +89,20 @@ static ringbuf_t hci_rx_ringbuf = {
.iput = 0,
};
static void mp_bluetooth_hci_uart_irq_callback(unsigned int uart_id, unsigned int trigger) {
if (trigger == MP_UART_IRQ_RXIDLE) {
mp_bluetooth_hci_poll_now();
}
}
int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) {
hci_uart_id = port;
hci_uart_first_char = true;
// Initialise the UART.
mp_uart_init(hci_uart_id, baudrate, pin_BT_UART_TX, pin_BT_UART_RX, &hci_rx_ringbuf);
mp_uart_init(hci_uart_id, baudrate, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1, pin_BT_UART_TX, pin_BT_UART_RX, &hci_rx_ringbuf);
mp_uart_set_flow(hci_uart_id, pin_BT_UART_RTS, pin_BT_UART_CTS);
mp_uart_set_irq_callback(hci_uart_id, mp_bluetooth_hci_poll_now);
mp_uart_set_irq_callback(hci_uart_id, MP_UART_IRQ_RXIDLE, mp_bluetooth_hci_uart_irq_callback);
// Start the HCI polling to process any initial events/packets.
mp_bluetooth_hci_start_polling();

View file

@ -26,20 +26,28 @@
#include "py/mphal.h"
#include "py/runtime.h"
#include "shared/runtime/softtimer.h"
#include "mpuart.h"
#include "sys_ctrl_uart.h"
#include "uart.h"
#define UART_MAX (8)
#define mp_container_of(ptr, structure, member) (void *)((uintptr_t)(ptr) - offsetof(structure, member))
#define UART_LSR_TEMT_Pos (6)
#define SYST_PCLK (100000000)
#define CALCULATE_BITS_PER_CHAR(data_bits, parity, stop_bits) \
(1 + ((data_bits) + 5) + ((parity) != UART_PARITY_NONE) + ((stop_bits) + 1))
typedef struct _uart_state_t {
UART_TRANSFER_STATUS status;
uint32_t baudrate;
uint32_t bits_per_char;
ringbuf_t *rx_ringbuf;
const uint8_t *tx_src;
const uint8_t *tx_src_max;
void (*irq_callback)(void);
soft_timer_entry_t rx_idle_timer;
unsigned int irq_trigger;
void (*irq_callback)(unsigned int uart_id, unsigned int trigger);
} uart_state_t;
static const uint8_t uart_irqn[UART_MAX] = {
@ -66,7 +74,19 @@ static UART_Type *const uart_periph[UART_MAX] = {
static uart_state_t uart_state[UART_MAX];
void mp_uart_init(unsigned int uart_id, uint32_t baudrate, mp_hal_pin_obj_t tx, mp_hal_pin_obj_t rx, ringbuf_t *rx_ringbuf) {
// This is called by soft_timer and executes at IRQ_PRI_PENDSV.
static void rx_idle_timer_callback(soft_timer_entry_t *self) {
uart_state_t *state = mp_container_of(self, uart_state_t, rx_idle_timer);
if (state->irq_callback != NULL && state->irq_trigger & MP_UART_IRQ_RXIDLE) {
unsigned int uart_id = state - &uart_state[0];
state->irq_callback(uart_id, MP_UART_IRQ_RXIDLE);
}
}
void mp_uart_init(unsigned int uart_id, uint32_t baudrate,
UART_DATA_BITS data_bits, UART_PARITY parity, UART_STOP_BITS stop_bits,
mp_hal_pin_obj_t tx, mp_hal_pin_obj_t rx, ringbuf_t *rx_ringbuf) {
UART_Type *uart = uart_periph[uart_id];
uart_state_t *state = &uart_state[uart_id];
@ -82,17 +102,20 @@ void mp_uart_init(unsigned int uart_id, uint32_t baudrate, mp_hal_pin_obj_t tx,
uart_disable_tx_irq(uart);
uart_disable_rx_irq(uart);
uart_set_baudrate(uart, SYST_PCLK, baudrate);
uart_set_data_parity_stop_bits(uart, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1);
uart_set_data_parity_stop_bits(uart, data_bits, parity, stop_bits);
uart_set_flow_control(uart, UART_FLOW_CONTROL_NONE);
uart->UART_FCR |= UART_FCR_RCVR_FIFO_RESET;
uart_set_tx_trigger(uart, UART_TX_FIFO_EMPTY);
uart_set_rx_trigger(uart, UART_RX_ONE_CHAR_IN_FIFO);
uart_set_rx_trigger(uart, UART_RX_FIFO_QUARTER_FULL);
// Initialise the state.
state->status = UART_TRANSFER_STATUS_NONE;
state->baudrate = baudrate;
state->bits_per_char = CALCULATE_BITS_PER_CHAR(data_bits, parity, stop_bits);
state->rx_ringbuf = rx_ringbuf;
state->tx_src = NULL;
state->tx_src_max = NULL;
state->irq_trigger = 0;
state->irq_callback = NULL;
// Enable interrupts.
@ -100,6 +123,8 @@ void mp_uart_init(unsigned int uart_id, uint32_t baudrate, mp_hal_pin_obj_t tx,
NVIC_SetPriority(uart_irqn[uart_id], IRQ_PRI_UART_REPL);
NVIC_EnableIRQ(uart_irqn[uart_id]);
uart_enable_rx_irq(uart);
soft_timer_static_init(&state->rx_idle_timer, SOFT_TIMER_MODE_ONE_SHOT, 0, rx_idle_timer_callback);
}
void mp_uart_deinit(unsigned int uart_id) {
@ -109,9 +134,16 @@ void mp_uart_deinit(unsigned int uart_id) {
NVIC_DisableIRQ(uart_irqn[uart_id]);
}
void mp_uart_set_irq_callback(unsigned int uart_id, void (*callback)(void)) {
void mp_uart_set_irq_callback(unsigned int uart_id, unsigned int trigger, void (*callback)(unsigned int uart_id, unsigned int trigger)) {
UART_Type *uart = uart_periph[uart_id];
uart_state_t *state = &uart_state[uart_id];
state->irq_trigger = trigger;
state->irq_callback = callback;
if (trigger & MP_UART_IRQ_RX) {
uart_set_rx_trigger(uart, UART_RX_ONE_CHAR_IN_FIFO);
} else {
uart_set_rx_trigger(uart, UART_RX_FIFO_QUARTER_FULL);
}
}
void mp_uart_set_flow(unsigned int uart_id, mp_hal_pin_obj_t rts, mp_hal_pin_obj_t cts) {
@ -131,10 +163,20 @@ void mp_uart_set_flow(unsigned int uart_id, mp_hal_pin_obj_t rts, mp_hal_pin_obj
void mp_uart_set_baudrate(unsigned int uart_id, uint32_t baudrate) {
UART_Type *uart = uart_periph[uart_id];
uart_state_t *state = &uart_state[uart_id];
state->baudrate = baudrate;
uart_set_baudrate(uart, SYST_PCLK, baudrate);
}
void mp_uart_set_bits_parity_stop(unsigned int uart_id, UART_DATA_BITS data_bits, UART_PARITY parity, UART_STOP_BITS stop_bits) {
UART_Type *uart = uart_periph[uart_id];
uart_state_t *state = &uart_state[uart_id];
state->bits_per_char = CALCULATE_BITS_PER_CHAR(data_bits, parity, stop_bits);
uart_set_data_parity_stop_bits(uart, data_bits, parity, stop_bits);
}
size_t mp_uart_rx_any(unsigned int uart_id) {
uart_state_t *state = &uart_state[uart_id];
if (state->rx_ringbuf != NULL) {
@ -143,6 +185,11 @@ size_t mp_uart_rx_any(unsigned int uart_id) {
return 0;
}
size_t mp_uart_tx_any(unsigned int uart_id) {
UART_Type *uart = uart_periph[uart_id];
return uart->UART_TFL + !((uart->UART_LSR >> UART_LSR_TEMT_Pos) & 1);
}
int mp_uart_rx_char(unsigned int uart_id) {
uart_state_t *state = &uart_state[uart_id];
if (state->rx_ringbuf != NULL && ringbuf_avail(state->rx_ringbuf)) {
@ -208,9 +255,11 @@ static void mp_uart_irq_handler(unsigned int uart_id) {
}
case UART_IIR_RECEIVED_DATA_AVAILABLE:
case UART_IIR_CHARACTER_TIMEOUT:
case UART_IIR_CHARACTER_TIMEOUT: {
bool had_char = false;
while (uart->UART_USR & UART_USR_RECEIVE_FIFO_NOT_EMPTY) {
for (uint32_t rfl = uart->UART_RFL; rfl; --rfl) {
had_char = true;
int c = uart->UART_RBR;
#if MICROPY_HW_ENABLE_UART_REPL && MICROPY_KBD_EXCEPTION
if (uart_id == MICROPY_HW_UART_REPL) {
@ -226,13 +275,18 @@ static void mp_uart_irq_handler(unsigned int uart_id) {
}
}
if (iir == UART_IIR_CHARACTER_TIMEOUT) {
if (state->irq_callback != NULL) {
state->irq_callback();
if (had_char) {
if (state->irq_callback != NULL && state->irq_trigger & MP_UART_IRQ_RX) {
state->irq_callback(uart_id, MP_UART_IRQ_RX);
}
}
if (state->irq_trigger & MP_UART_IRQ_RXIDLE) {
// Wait for 2 characters worth of time before triggering the RXIDLE event.
soft_timer_reinsert(&state->rx_idle_timer, 2000 * state->bits_per_char / state->baudrate + 1);
}
break;
}
case UART_IIR_TRANSMIT_HOLDING_REG_EMPTY:
while (uart->UART_USR & UART_USR_TRANSMIT_FIFO_NOT_FULL) {
@ -241,6 +295,9 @@ static void mp_uart_irq_handler(unsigned int uart_id) {
} else {
uart_disable_tx_irq(uart);
state->status = UART_TRANSFER_STATUS_SEND_COMPLETE;
if (state->irq_callback != NULL && state->irq_trigger & MP_UART_IRQ_TXIDLE) {
state->irq_callback(uart_id, MP_UART_IRQ_TXIDLE);
}
break;
}
}
@ -273,7 +330,9 @@ DEFINE_IRQ_HANDLER(7)
#define REPL_BAUDRATE (115200)
void mp_uart_init_repl(void) {
mp_uart_init(MICROPY_HW_UART_REPL, REPL_BAUDRATE, pin_REPL_UART_TX, pin_REPL_UART_RX, &stdin_ringbuf);
mp_uart_init(MICROPY_HW_UART_REPL,
REPL_BAUDRATE, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1,
pin_REPL_UART_TX, pin_REPL_UART_RX, &stdin_ringbuf);
}
void mp_uart_write_strn_repl(const char *str, size_t len) {

View file

@ -27,15 +27,25 @@
#define MICROPY_INCLUDED_ALIF2_UART_H
#include "py/ringbuf.h"
#include "uart.h"
void mp_uart_init(unsigned int uart_id, uint32_t baudrate, mp_hal_pin_obj_t tx, mp_hal_pin_obj_t rx, ringbuf_t *rx_ringbuf);
#define UART_MAX (8)
#define MP_UART_IRQ_RX (1)
#define MP_UART_IRQ_RXIDLE (2)
#define MP_UART_IRQ_TXIDLE (4)
void mp_uart_init(unsigned int uart_id, uint32_t baudrate,
UART_DATA_BITS data_bits, UART_PARITY parity, UART_STOP_BITS stop_bits,
mp_hal_pin_obj_t tx, mp_hal_pin_obj_t rx, ringbuf_t *rx_ringbuf);
void mp_uart_deinit(unsigned int uart_id);
void mp_uart_set_irq_callback(unsigned int uart_id, void (*callback)(void));
void mp_uart_set_irq_callback(unsigned int uart_id, unsigned int trigger, void (*callback)(unsigned int uart_id, unsigned int trigger));
void mp_uart_set_flow(unsigned int uart_id, mp_hal_pin_obj_t rts, mp_hal_pin_obj_t cts);
void mp_uart_set_baudrate(unsigned int uart_id, uint32_t baudrate);
void mp_uart_set_bits_parity_stop(unsigned int uart_id, UART_DATA_BITS data_bits, UART_PARITY parity, UART_STOP_BITS stop_bits);
size_t mp_uart_rx_any(unsigned int uart_id);
size_t mp_uart_tx_any(unsigned int uart_id);
int mp_uart_rx_char(unsigned int uart_id);
void mp_uart_tx_data(unsigned int uart_id, const uint8_t *src, size_t len);