Skip to content

Commit bb0f192

Browse files
JordanYateskartben
authored andcommitted
samples: uart: async_api: added
Add a sample that utilises the ASYNC API to queue packets in bursts. Signed-off-by: Jordan Yates <[email protected]>
1 parent 9bbc6d1 commit bb0f192

File tree

5 files changed

+220
-0
lines changed

5 files changed

+220
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
5+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
project(uart_async_api)
7+
8+
target_sources(app PRIVATE src/main.c)
+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
.. zephyr:code-sample:: uart_async
2+
:name: UART ASYNC API
3+
:relevant-api: uart_interface
4+
5+
Demonstrate the use of the asynchronous API
6+
7+
Overview
8+
********
9+
10+
This sample demonstrates how to use the UART serial driver asynchronous
11+
API through a simple packet transmitter.
12+
13+
Every 5 seconds, 1 to 4 data payloads are generated and queued for
14+
transmission. Every other 5 second period, receiving is enabled through
15+
the asynchronous API.
16+
17+
By default, the UART peripheral that is normally used for the Zephyr shell
18+
is used, so that almost every board should be supported.
19+
20+
Building and Running
21+
********************
22+
23+
Build and flash the sample as follows, changing ``nrf52840dk/nrf52840`` for
24+
your board:
25+
26+
.. zephyr-app-commands::
27+
:zephyr-app: samples/drivers/uart/async_api
28+
:board: nrf52840dk/nrf52840
29+
:goals: build flash
30+
:compact:
31+
32+
Sample Output
33+
=============
34+
35+
.. code-block:: console
36+
37+
Loop 0: Packet: 0
38+
Loop 0: Packet: 1
39+
Loop 0: Packet: 2
40+
[00:00:05.001,919] <inf> sample: Loop 0: Sending 3 packets
41+
[00:00:05.002,008] <inf> sample: RX is now enabled
42+
Loop 1: Packet: 0
43+
[00:00:10.002,086] <inf> sample: Loop 1: Sending 1 packets
44+
[00:00:10.002,138] <inf> sample: RX is now disabled
45+
Loop 2: Packet: 0
46+
Loop 2: Packet: 1
47+
[00:00:15.002,215] <inf> sample: Loop 2: Sending 2 packets
48+
[00:00:15.002,293] <inf> sample: RX is now enabled
49+
[00:00:15.009,010] <inf> sample: RX_RDY
50+
68 65 6c 6c 6f 0a |hello.
51+
Loop 3: Packet: 0
52+
Loop 3: Packet: 1
53+
Loop 3: Packet: 2
54+
[00:00:20.002,343] <inf> sample: Loop 3: Sending 3 packets
55+
[00:00:20.002,424] <inf> sample: RX is now disabled
56+
57+
Note that because the UART transmissions are triggered directly, they will
58+
appear in the serial logs **before** the ``LOG_INF`` message at the top of
59+
the loop, since log writes are typically deferred by several seconds
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
CONFIG_LOG=y
2+
CONFIG_NET_BUF=y
3+
CONFIG_SERIAL=y
4+
CONFIG_UART_ASYNC_API=y
5+
CONFIG_TEST_RANDOM_GENERATOR=y
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
sample:
2+
name: UART ASYNC API driver sample
3+
tests:
4+
sample.drivers.uart.async_api:
5+
integration_platforms:
6+
- nrf52840dk/nrf52840
7+
tags:
8+
- serial
9+
- uart
10+
filter: CONFIG_SERIAL and
11+
CONFIG_UART_ASYNC_API and
12+
dt_chosen_enabled("zephyr,shell-uart")
13+
harness: keyboard
+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/*
2+
* Copyright (c) 2025 Embeint Inc
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/kernel.h>
8+
#include <zephyr/device.h>
9+
#include <zephyr/net_buf.h>
10+
#include <zephyr/logging/log.h>
11+
#include <zephyr/drivers/uart.h>
12+
#include <zephyr/random/random.h>
13+
14+
/* change this to any other UART peripheral if desired */
15+
#define UART_DEVICE_NODE DT_CHOSEN(zephyr_shell_uart)
16+
17+
/* Maximum number of packets to generate per iteration */
18+
#define LOOP_ITER_MAX_TX 4
19+
/* Maximum size of our TX packets */
20+
#define MAX_TX_LEN 32
21+
#define RX_CHUNK_LEN 32
22+
23+
/* Buffer pool for our TX payloads */
24+
NET_BUF_POOL_DEFINE(tx_pool, LOOP_ITER_MAX_TX, MAX_TX_LEN, 0, NULL);
25+
26+
struct k_fifo tx_queue;
27+
struct net_buf *tx_pending_buffer;
28+
uint8_t async_rx_buffer[2][RX_CHUNK_LEN];
29+
volatile uint8_t async_rx_buffer_idx;
30+
31+
static const struct device *const uart_dev = DEVICE_DT_GET(UART_DEVICE_NODE);
32+
33+
LOG_MODULE_REGISTER(sample, LOG_LEVEL_INF);
34+
35+
static void uart_callback(const struct device *dev, struct uart_event *evt, void *user_data)
36+
{
37+
struct net_buf *buf;
38+
int rc;
39+
40+
LOG_DBG("EVENT: %d", evt->type);
41+
42+
switch (evt->type) {
43+
case UART_TX_DONE:
44+
LOG_DBG("TX complete %p", tx_pending_buffer);
45+
46+
/* Free TX buffer */
47+
net_buf_unref(tx_pending_buffer);
48+
tx_pending_buffer = NULL;
49+
50+
/* Handle any queued buffers */
51+
buf = k_fifo_get(&tx_queue, K_NO_WAIT);
52+
if (buf != NULL) {
53+
rc = uart_tx(dev, buf->data, buf->len, 0);
54+
if (rc != 0) {
55+
LOG_ERR("TX from ISR failed (%d)", rc);
56+
net_buf_unref(buf);
57+
} else {
58+
tx_pending_buffer = buf;
59+
}
60+
}
61+
break;
62+
case UART_RX_BUF_REQUEST:
63+
/* Return the next buffer index */
64+
LOG_DBG("Providing buffer index %d", async_rx_buffer_idx);
65+
rc = uart_rx_buf_rsp(dev, async_rx_buffer[async_rx_buffer_idx],
66+
sizeof(async_rx_buffer[0]));
67+
__ASSERT_NO_MSG(rc == 0);
68+
async_rx_buffer_idx = async_rx_buffer_idx ? 0 : 1;
69+
break;
70+
case UART_RX_BUF_RELEASED:
71+
case UART_RX_DISABLED:
72+
break;
73+
case UART_RX_RDY:
74+
LOG_HEXDUMP_INF(evt->data.rx.buf + evt->data.rx.offset,
75+
evt->data.rx.len, "RX_RDY");
76+
break;
77+
default:
78+
LOG_WRN("Unhandled event %d", evt->type);
79+
}
80+
}
81+
82+
int main(void)
83+
{
84+
bool rx_enabled = false;
85+
struct net_buf *tx_buf;
86+
int loop_counter = 0;
87+
uint8_t num_tx;
88+
int tx_len;
89+
int rc;
90+
91+
/* Register the async interrupt handler */
92+
uart_callback_set(uart_dev, uart_callback, (void *)uart_dev);
93+
94+
while (1) {
95+
/* Wait a while until the next burst transmission */
96+
k_sleep(K_SECONDS(5));
97+
98+
/* Each loop, try to send a random number of packets */
99+
num_tx = (sys_rand32_get() % LOOP_ITER_MAX_TX) + 1;
100+
LOG_INF("Loop %d: Sending %d packets", loop_counter, num_tx);
101+
for (int i = 0; i < num_tx; i++) {
102+
/* Allocate the data packet */
103+
tx_buf = net_buf_alloc(&tx_pool, K_FOREVER);
104+
/* Populate it with data */
105+
tx_len = snprintk(tx_buf->data, net_buf_tailroom(tx_buf),
106+
"Loop %d: Packet: %d\r\n", loop_counter, i);
107+
net_buf_add(tx_buf, tx_len);
108+
109+
/* Queue packet for transmission */
110+
rc = uart_tx(uart_dev, tx_buf->data, tx_buf->len, SYS_FOREVER_US);
111+
if (rc == 0) {
112+
/* Store the pending buffer */
113+
tx_pending_buffer = tx_buf;
114+
} else if (rc == -EBUSY) {
115+
/* Transmission is already in progress */
116+
LOG_DBG("Queuing buffer %p", tx_buf);
117+
k_fifo_put(&tx_queue, tx_buf);
118+
} else {
119+
LOG_ERR("Unknown error (%d)", rc);
120+
}
121+
}
122+
123+
/* Toggle the RX state */
124+
if (rx_enabled) {
125+
uart_rx_disable(uart_dev);
126+
} else {
127+
async_rx_buffer_idx = 1;
128+
uart_rx_enable(uart_dev, async_rx_buffer[0], RX_CHUNK_LEN, 100);
129+
}
130+
rx_enabled = !rx_enabled;
131+
LOG_INF("RX is now %s", rx_enabled ? "enabled" : "disabled");
132+
133+
loop_counter += 1;
134+
}
135+
}

0 commit comments

Comments
 (0)