From 83de7f4200211cc93f1228679758999f68379bb7 Mon Sep 17 00:00:00 2001 From: pstruebi Date: Wed, 9 Apr 2025 10:46:02 +0200 Subject: [PATCH] Initial commit for echo_bot --- .gitignore | 6 +++ CMakeLists.txt | 8 ++++ README.rst | 41 +++++++++++++++++++ prj.conf | 2 + sample.yaml | 13 ++++++ src/main.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 177 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README.rst create mode 100644 prj.conf create mode 100644 sample.yaml create mode 100644 src/main.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..635a99b --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# editors +*.swp +*~ + +# build +/build*/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7069dfa --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(echo_bot) + +target_sources(app PRIVATE src/main.c) diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..c38c19d --- /dev/null +++ b/README.rst @@ -0,0 +1,41 @@ +.. zephyr:code-sample:: uart + :name: UART echo + :relevant-api: uart_interface + + Read data from the console and echo it back. + +Overview +******** + +This sample demonstrates how to use the UART serial driver with a simple +echo bot. It reads data from the console and echoes the characters back after +an end of line (return key) is received. + +The polling API is used for sending data and the interrupt-driven API +for receiving, so that in theory the thread could do something else +while waiting for incoming data. + +By default, the UART peripheral that is normally used for the Zephyr shell +is used, so that almost every board should be supported. + +Building and Running +******************** + +Build and flash the sample as follows, changing ``nrf52840dk/nrf52840`` for +your board: + +.. zephyr-app-commands:: + :zephyr-app: samples/drivers/uart/echo_bot + :board: nrf52840dk/nrf52840 + :goals: build flash + :compact: + +Sample Output +============= + +.. code-block:: console + + Hello! I\'m your echo bot. + Tell me something and press enter: + # Type e.g. "Hi there!" and hit enter! + Echo: Hi there! diff --git a/prj.conf b/prj.conf new file mode 100644 index 0000000..8698ce3 --- /dev/null +++ b/prj.conf @@ -0,0 +1,2 @@ +CONFIG_SERIAL=y +CONFIG_UART_INTERRUPT_DRIVEN=y diff --git a/sample.yaml b/sample.yaml new file mode 100644 index 0000000..759bcdc --- /dev/null +++ b/sample.yaml @@ -0,0 +1,13 @@ +sample: + name: UART driver sample +tests: + sample.drivers.uart: + integration_platforms: + - qemu_x86 + tags: + - serial + - uart + filter: CONFIG_SERIAL and + CONFIG_UART_INTERRUPT_DRIVEN and + dt_chosen_enabled("zephyr,shell-uart") + harness: keyboard diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..549c746 --- /dev/null +++ b/src/main.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2022 Libre Solar Technologies GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include + +/* change this to any other UART peripheral if desired */ +#define UART_DEVICE_NODE DT_CHOSEN(zephyr_shell_uart) + +#define MSG_SIZE 32 + +/* queue to store up to 10 messages (aligned to 4-byte boundary) */ +K_MSGQ_DEFINE(uart_msgq, MSG_SIZE, 10, 4); + +static const struct device *const uart_dev = DEVICE_DT_GET(UART_DEVICE_NODE); + +/* receive buffer used in UART ISR callback */ +static char rx_buf[MSG_SIZE]; +static int rx_buf_pos; + +/* + * Read characters from UART until line end is detected. Afterwards push the + * data to the message queue. + */ +void serial_cb(const struct device *dev, void *user_data) +{ + uint8_t c; + + if (!uart_irq_update(uart_dev)) { + return; + } + + if (!uart_irq_rx_ready(uart_dev)) { + return; + } + + /* read until FIFO empty */ + while (uart_fifo_read(uart_dev, &c, 1) == 1) { + if ((c == '\n' || c == '\r') && rx_buf_pos > 0) { + /* terminate string */ + rx_buf[rx_buf_pos] = '\0'; + + /* if queue is full, message is silently dropped */ + k_msgq_put(&uart_msgq, &rx_buf, K_NO_WAIT); + + /* reset the buffer (it was copied to the msgq) */ + rx_buf_pos = 0; + } else if (rx_buf_pos < (sizeof(rx_buf) - 1)) { + rx_buf[rx_buf_pos++] = c; + } + /* else: characters beyond buffer size are dropped */ + } +} + +/* + * Print a null-terminated string character by character to the UART interface + */ +void print_uart(char *buf) +{ + int msg_len = strlen(buf); + + for (int i = 0; i < msg_len; i++) { + uart_poll_out(uart_dev, buf[i]); + } +} + +int main(void) +{ + char tx_buf[MSG_SIZE]; + + if (!device_is_ready(uart_dev)) { + printk("UART device not found!"); + return 0; + } + + /* configure interrupt and callback to receive data */ + int ret = uart_irq_callback_user_data_set(uart_dev, serial_cb, NULL); + + if (ret < 0) { + if (ret == -ENOTSUP) { + printk("Interrupt-driven UART API support not enabled\n"); + } else if (ret == -ENOSYS) { + printk("UART device does not support interrupt-driven API\n"); + } else { + printk("Error setting UART callback: %d\n", ret); + } + return 0; + } + uart_irq_rx_enable(uart_dev); + + print_uart("Hello! I'm your echo bot.\r\n"); + print_uart("Tell me something and press enter:\r\n"); + + /* indefinitely wait for input from the user */ + while (k_msgq_get(&uart_msgq, &tx_buf, K_FOREVER) == 0) { + print_uart("Echo: "); + print_uart(tx_buf); + print_uart("\r\n"); + } + return 0; +}