From 62baffe99225d1efaaad650b499d3c9d825f9c7c Mon Sep 17 00:00:00 2001 From: Timo Schneider Date: Fri, 13 Mar 2026 12:45:14 +0100 Subject: [PATCH] working tx --- .gitignore | 1 + boards/xiao_nrf54l15_nrf54l15_cpuapp.overlay | 2 +- .../serial/uart-interrupt-poll-bridge/Kconfig | 1 + .../uart-interrupt-poll-bridge.c | 277 ++++++++++++++++-- .../dts/bindings/interrupt-poll-bridge.yaml | 2 +- prj.conf | 8 +- src/main.cpp | 84 +++++- 7 files changed, 341 insertions(+), 34 deletions(-) diff --git a/.gitignore b/.gitignore index d163863..474e379 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ +.vscode build/ \ No newline at end of file diff --git a/boards/xiao_nrf54l15_nrf54l15_cpuapp.overlay b/boards/xiao_nrf54l15_nrf54l15_cpuapp.overlay index dc99e27..826eeaa 100644 --- a/boards/xiao_nrf54l15_nrf54l15_cpuapp.overlay +++ b/boards/xiao_nrf54l15_nrf54l15_cpuapp.overlay @@ -6,7 +6,7 @@ test_uart: test_uart { compatible = "uart-interrupt-poll-bridge"; uart = <&uart20>; - timer = <&timer00>; + counter = <&timer00>; interrupts = <1 2>; interrupt-parent = <&nvic>; status = "okay"; diff --git a/modules/uart-interrupt-poll-bridge/drivers/serial/uart-interrupt-poll-bridge/Kconfig b/modules/uart-interrupt-poll-bridge/drivers/serial/uart-interrupt-poll-bridge/Kconfig index 97a0720..af4e3c0 100644 --- a/modules/uart-interrupt-poll-bridge/drivers/serial/uart-interrupt-poll-bridge/Kconfig +++ b/modules/uart-interrupt-poll-bridge/drivers/serial/uart-interrupt-poll-bridge/Kconfig @@ -1,6 +1,7 @@ config UART_INTERRUPT_POLL_BRIDGE bool "UART interrupt/poll bridge driver" default y + select UART_USE_RUNTIME_CONFIGURE config UART_INTERRUPT_POLL_BRIDGE_QUEUE_SIZE int "UART interrupt/poll bridge driver rx / tx buffer size" diff --git a/modules/uart-interrupt-poll-bridge/drivers/serial/uart-interrupt-poll-bridge/uart-interrupt-poll-bridge.c b/modules/uart-interrupt-poll-bridge/drivers/serial/uart-interrupt-poll-bridge/uart-interrupt-poll-bridge.c index a2d7108..2658319 100644 --- a/modules/uart-interrupt-poll-bridge/drivers/serial/uart-interrupt-poll-bridge/uart-interrupt-poll-bridge.c +++ b/modules/uart-interrupt-poll-bridge/drivers/serial/uart-interrupt-poll-bridge/uart-interrupt-poll-bridge.c @@ -1,31 +1,49 @@ #define DT_DRV_COMPAT uart_interrupt_poll_bridge #include -#ifdef CONFIG_UART_INTERRUPT_DRIVEN +#include +#if IS_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN) +#include #include #endif +#define REDUCE_TO_BIT(obj) (!!obj) + struct uart_pti_bridge_config { const struct device *uart; -#ifdef CONFIG_UART_INTERRUPT_DRIVEN - const struct device *timer; +#if IS_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN) + const struct device *counter; const uint8_t interrupt_line; #endif }; struct uart_pti_bridge_data { -#ifdef CONFIG_UART_INTERRUPT_DRIVEN + struct k_spinlock lock; +#if IS_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN) struct ring_buf *rx_buf; struct ring_buf *tx_buf; + uart_irq_callback_user_data_t cb; + void *user_data; + union { + struct { + const uint8_t irq_triggered : 4; + const uint8_t irq_enabled : 4; + }; + struct { + bool irq_tx_ready_triggered : 1; + bool irq_tx_complete_triggered: 1; + bool irq_rx_ready_triggered : 1; + bool irq_err_triggered : 1; + bool irq_tx_enabled : 1; + bool : 1; + bool irq_rx_enabled : 1; + bool irq_err_enabled : 1; + + }; + }; #endif }; -int uart_pti_bridge_init(const struct device *dev) { - ARG_UNUSED(dev); - return 0; -} - - static int uart_pti_bridge_poll_in(const struct device *dev, unsigned char *c) { const struct uart_pti_bridge_config *config = dev->config; @@ -48,7 +66,7 @@ static int uart_pti_bridge_err_check(const struct device *dev) { } -#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE +#if IS_ENABLED(CONFIG_UART_USE_RUNTIME_CONFIGURE) static int uart_pti_bridge_configure(const struct device *dev, const struct uart_config *cfg) { ARG_UNUSED(dev); @@ -64,6 +82,213 @@ static int uart_pti_bridge_config_get(const struct device *dev, } #endif + +#if IS_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN) +int uart_pti_bridge_fifo_fill(const struct device *dev, const uint8_t *msg, int len) { + struct uart_pti_bridge_data *data = dev->data; + + k_spinlock_key_t key = k_spin_lock(&data->lock); + uint32_t bytes_written = ring_buf_put(data->tx_buf, msg, len); + k_spin_unlock(&data->lock, key); + + return bytes_written; +} + + +int uart_pti_bridge_fifo_read(const struct device *dev, uint8_t *msg, const int size) { + struct uart_pti_bridge_data *data = dev->data; + + k_spinlock_key_t key = k_spin_lock(&data->lock); + uint32_t bytes_read = ring_buf_get(data->rx_buf, msg, size); + k_spin_unlock(&data->lock, key); + + return bytes_read; +} + + +void uart_pti_bridge_irq_tx_enable(const struct device *dev) { + struct uart_pti_bridge_data *data = dev->data; + + data->irq_tx_enabled = true; +} + + +void uart_pti_bridge_irq_tx_disable(const struct device *dev) { + struct uart_pti_bridge_data *data = dev->data; + + data->irq_tx_enabled = false; +} + + +int uart_pti_bridge_irq_tx_ready(const struct device *dev) { + struct uart_pti_bridge_data *data = dev->data; + + return data->irq_tx_ready_triggered; +} + + +int uart_pti_bridge_irq_tx_complete(const struct device *dev) { + struct uart_pti_bridge_data *data = dev->data; + + return data->irq_tx_complete_triggered; +} + + +void uart_pti_bridge_irq_rx_enable(const struct device *dev) { + struct uart_pti_bridge_data *data = dev->data; + + data->irq_rx_enabled = true; +} + + +void uart_pti_bridge_irq_rx_disable(const struct device *dev) { + struct uart_pti_bridge_data *data = dev->data; + + data->irq_rx_enabled = false; +} + + +int uart_pti_bridge_irq_rx_ready(const struct device *dev) { + struct uart_pti_bridge_data *data = dev->data; + + return data->irq_rx_ready_triggered; +} + + +void uart_pti_bridge_irq_err_enable(const struct device *dev) { + struct uart_pti_bridge_data *data = dev->data; + + data->irq_err_enabled = true; +} + + +void uart_pti_bridge_irq_err_disable(const struct device *dev) { + struct uart_pti_bridge_data *data = dev->data; + + data->irq_err_enabled = false; +} + + +int uart_pti_bridge_irq_is_pending(const struct device *dev) { + struct uart_pti_bridge_data *data = dev->data; + + return REDUCE_TO_BIT(data->irq_triggered); +} + + +int uart_pti_bridge_irq_update(const struct device *dev) { + ARG_UNUSED(dev); + return 1; // success +} + + +void uart_pti_bridge_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb, void *user_data) { + struct uart_pti_bridge_data *data = dev->data; + + k_spinlock_key_t key = k_spin_lock(&data->lock); + data->cb = cb; + data->user_data = user_data; + k_spin_unlock(&data->lock, key); +} + + +void uart_pti_bridge_timer_irq(const struct device *timer_dev, void *user_data) { + ARG_UNUSED(timer_dev); + + const struct device *dev = user_data; + struct uart_pti_bridge_data *data = dev->data; + + k_spinlock_key_t key = k_spin_lock(&data->lock); + + data->irq_tx_ready_triggered = REDUCE_TO_BIT(ring_buf_space_get(data->tx_buf)); + data->irq_tx_complete_triggered = REDUCE_TO_BIT(ring_buf_is_empty(data->tx_buf)); + data->irq_rx_ready_triggered = REDUCE_TO_BIT(!ring_buf_is_empty(data->rx_buf)); + data->irq_err_triggered = REDUCE_TO_BIT(uart_pti_bridge_err_check(dev)); + + if (data->irq_tx_ready_triggered && !data->irq_tx_complete_triggered) { + uint8_t chunk; + if (ring_buf_get(data->tx_buf, &chunk, 1)) { + uart_poll_out(dev, chunk); + } + } + + if (data->irq_rx_enabled) { + uint32_t rx_buf_free_space = ring_buf_space_get(data->rx_buf); + uint8_t chunk; + + for (uint32_t bytes_read = 0; bytes_read <= rx_buf_free_space; bytes_read++) { + if (uart_poll_in(dev, &chunk) < 0) { + break; + } + ring_buf_put(data->rx_buf, &chunk, 1); + } + + } + + k_spin_unlock(&data->lock, key); + + if (data->cb && data->irq_triggered) { + data->cb(dev, data->user_data); + } +} + +static uint32_t uart_pti_bridge_us_per_byte(const struct uart_config *cfg) +{ + uint8_t bits = 1; // start bit + + // data bits + switch (cfg->data_bits) { + case UART_CFG_DATA_BITS_5: bits += 5; break; + case UART_CFG_DATA_BITS_6: bits += 6; break; + case UART_CFG_DATA_BITS_7: bits += 7; break; + case UART_CFG_DATA_BITS_8: bits += 8; break; + case UART_CFG_DATA_BITS_9: bits += 9; break; + } + + // parity bit + if (cfg->parity != UART_CFG_PARITY_NONE) { + bits += 1; + } + + // stop bits + switch (cfg->stop_bits) { + case UART_CFG_STOP_BITS_0_5: break; // rare, ignore + case UART_CFG_STOP_BITS_1: bits += 1; break; + case UART_CFG_STOP_BITS_1_5: bits += 1; break; // round down + case UART_CFG_STOP_BITS_2: bits += 2; break; + } + + // round up to ensure buffer is always large enough + return (bits * 1000000 + cfg->baudrate - 1) / cfg->baudrate; +} +#endif + + +int uart_pti_bridge_init(const struct device *dev) { +#if IS_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN) + const struct uart_pti_bridge_config *config = dev->config; + + struct uart_config cfg; + uart_config_get(config->uart, &cfg); + + const uint32_t us_per_byte = uart_pti_bridge_us_per_byte(&cfg); + + struct counter_top_cfg top_cfg = { + .callback = uart_pti_bridge_timer_irq, + .ticks = counter_us_to_ticks(config->counter, us_per_byte), + .user_data = (struct device *)dev, + .flags = 0, + }; + + counter_set_top_value(config->counter, &top_cfg); + counter_start(config->counter); +#else + ARG_UNUSED(dev); +#endif + return 0; +} + + static DEVICE_API(uart, uart_interrupt_poll_bridge_api) = { .poll_in = uart_pti_bridge_poll_in, .poll_out = uart_pti_bridge_poll_out, @@ -73,20 +298,20 @@ static DEVICE_API(uart, uart_interrupt_poll_bridge_api) = { .config_get = uart_pti_bridge_config_get, #endif #ifdef CONFIG_UART_INTERRUPT_DRIVEN - .fifo_fill = NULL, - .fifo_read = NULL, - .irq_tx_enable = NULL, - .irq_tx_disable = NULL, - .irq_tx_ready = NULL, - .irq_tx_complete = NULL, - .irq_rx_enable = NULL, - .irq_rx_disable = NULL, - .irq_rx_ready = NULL, - .irq_err_enable = NULL, - .irq_err_disable = NULL, - .irq_is_pending = NULL, - .irq_update = NULL, - .irq_callback_set = NULL, + .fifo_fill = uart_pti_bridge_fifo_fill, + .fifo_read = uart_pti_bridge_fifo_read, + .irq_tx_enable = uart_pti_bridge_irq_tx_enable, + .irq_tx_disable = uart_pti_bridge_irq_tx_disable, + .irq_tx_ready = uart_pti_bridge_irq_tx_ready, + .irq_tx_complete = uart_pti_bridge_irq_tx_complete, + .irq_rx_enable = uart_pti_bridge_irq_rx_enable, + .irq_rx_disable = uart_pti_bridge_irq_rx_disable, + .irq_rx_ready = uart_pti_bridge_irq_rx_ready, + .irq_err_enable = uart_pti_bridge_irq_err_enable, + .irq_err_disable = uart_pti_bridge_irq_err_disable, + .irq_is_pending = uart_pti_bridge_irq_is_pending, + .irq_update = uart_pti_bridge_irq_update, + .irq_callback_set = uart_pti_bridge_callback_set, #endif }; @@ -107,7 +332,7 @@ static DEVICE_API(uart, uart_interrupt_poll_bridge_api) = { static const struct uart_pti_bridge_config uart_pti_bridge_config_##inst = { \ .uart = DEVICE_DT_GET(DT_INST_PHANDLE(inst, uart)), \ COND_CODE_1(CONFIG_UART_INTERRUPT_DRIVEN, ( \ - .timer = DEVICE_DT_GET(DT_INST_PHANDLE(inst, timer)), \ + .counter = DEVICE_DT_GET(DT_INST_PHANDLE(inst, counter)), \ .interrupt_line = DT_INST_IRQN(inst), \ ), ()) \ }; \ diff --git a/modules/uart-interrupt-poll-bridge/dts/bindings/interrupt-poll-bridge.yaml b/modules/uart-interrupt-poll-bridge/dts/bindings/interrupt-poll-bridge.yaml index d940686..a68edcd 100644 --- a/modules/uart-interrupt-poll-bridge/dts/bindings/interrupt-poll-bridge.yaml +++ b/modules/uart-interrupt-poll-bridge/dts/bindings/interrupt-poll-bridge.yaml @@ -9,7 +9,7 @@ properties: interrupts: type: array required: true - timer: + counter: type: phandle required: true uart: diff --git a/prj.conf b/prj.conf index 41d45fb..c766abe 100644 --- a/prj.conf +++ b/prj.conf @@ -1,2 +1,8 @@ CONFIG_UART_INTERRUPT_DRIVEN=y -CONFIG_COUNTER=y \ No newline at end of file +CONFIG_COUNTER=y + +# Disable compiler optimization +CONFIG_DEBUG=y +CONFIG_NO_OPTIMIZATIONS=y +CONFIG_THREAD_RUNTIME_STATS=y +CONFIG_THREAD_RUNTIME_STATS_USE_TIMING_FUNCTIONS=y \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index ab87bd9..56ef50b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,17 +5,91 @@ const struct device *test_uart = DEVICE_DT_GET(DT_ALIAS(testuart)); +// TEST GENERATED CODE START +#include +#include + +#define RX_BUF_SIZE 256 +#define TX_BUF_SIZE 256 + +RING_BUF_DECLARE(rx_buf, RX_BUF_SIZE); +RING_BUF_DECLARE(tx_buf, TX_BUF_SIZE); + +static struct k_spinlock uart_lock; + +static void uart_isr(const struct device *dev, void *user_data) +{ + uart_irq_update(dev); + + if (uart_irq_rx_ready(dev)) { + uint8_t byte; + while (uart_fifo_read(dev, &byte, 1) == 1) { + k_spinlock_key_t key = k_spin_lock(&uart_lock); + ring_buf_put(&rx_buf, &byte, 1); + k_spin_unlock(&uart_lock, key); + } + } + + if (uart_irq_tx_ready(dev)) { + uint8_t byte; + k_spinlock_key_t key = k_spin_lock(&uart_lock); + if (ring_buf_get(&tx_buf, &byte, 1) == 1) { + k_spin_unlock(&uart_lock, key); + uart_fifo_fill(dev, &byte, 1); + } else { + k_spin_unlock(&uart_lock, key); + uart_irq_tx_disable(dev); // nothing left, stop firing + } + } +} + +/* Call once at init */ +void uart_init(const struct device *dev) +{ + uart_irq_callback_set(dev, uart_isr); + uart_irq_rx_enable(dev); + /* TX IRQ is enabled on demand when we have data to send */ +} + +/* Write bytes to TX — call from thread context */ +int uart_write(const struct device *dev, const uint8_t *data, size_t len) +{ + k_spinlock_key_t key = k_spin_lock(&uart_lock); + uint32_t written = ring_buf_put(&tx_buf, data, len); + k_spin_unlock(&uart_lock, key); + + uart_irq_tx_enable(dev); // kick off TX if not already running + return written; +} + +/* Read bytes from RX — call from thread context */ +int uart_read(const struct device *dev, uint8_t *data, size_t len) +{ + k_spinlock_key_t key = k_spin_lock(&uart_lock); + uint32_t read = ring_buf_get(&rx_buf, data, len); + k_spin_unlock(&uart_lock, key); + return read; +} +// TEST GENERATED CODE END + int main(void) { - const char msg [] = "Hello World"; + const uint8_t msg [] = "Hello World\r\n"; + + uart_irq_callback_set(test_uart, uart_isr); while (1) { - for (char c : msg) { - uart_poll_out(test_uart, c); - } + uart_write(test_uart, msg, sizeof(msg)); - k_sleep(K_MSEC(100)); + + k_thread_runtime_stats_t stats; + + k_thread_runtime_stats_all_get(&stats); + uint8_t buff[32]; + sprintf((char*)&buff, "total cycles: %15llu\r\n", stats.execution_cycles); + uart_write(test_uart, buff, sizeof(buff)); + k_sleep(K_MSEC(1000)); } return 0; } \ No newline at end of file