1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
| /*
* Copyright (c) 2022 Libre Solar Technologies GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/sys/ring_buffer.h>
#include <string.h>
/* change this to any other UART peripheral if desired */
#define UART_DEVICE_NODE DT_NODELABEL(xiao_serial)
#define RX_BUF_SIZE 32
#define RX_TIMEOUT_US 1000
#define RING_BUF_BYTES 256
#define TX_CHUNK_BYTES 64
static const struct device *const uart_dev = DEVICE_DT_GET(UART_DEVICE_NODE);
/* double buffer used by the async RX engine */
static uint8_t rx_buf_a[RX_BUF_SIZE];
static uint8_t rx_buf_b[RX_BUF_SIZE];
static uint8_t *next_rx_buf = rx_buf_b;
/* incoming bytes are pushed here from the callback and drained by main */
RING_BUF_DECLARE(rx_ring, RING_BUF_BYTES);
static K_SEM_DEFINE(rx_data_sem, 0, 1);
/* signalled by the async callback when a TX completes (or aborts) */
static K_SEM_DEFINE(tx_done_sem, 0, 1);
/* DMA-capable RAM buffer for TX (EasyDMA cannot read from flash) */
static uint8_t tx_ram_buf[TX_CHUNK_BYTES];
static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
{
ARG_UNUSED(user_data);
switch (evt->type) {
case UART_TX_DONE:
case UART_TX_ABORTED:
k_sem_give(&tx_done_sem);
printk("TX %s\n", evt->type == UART_TX_DONE ? "done" : "aborted");
break;
case UART_RX_RDY:
(void)ring_buf_put(&rx_ring,
evt->data.rx.buf + evt->data.rx.offset,
evt->data.rx.len);
k_sem_give(&rx_data_sem);
printk("RX ready: offset=%zu len=%zu\n", evt->data.rx.offset, evt->data.rx.len);
break;
case UART_RX_BUF_REQUEST:
(void)uart_rx_buf_rsp(dev, next_rx_buf, RX_BUF_SIZE);
printk("RX buffer requested\n");
break;
case UART_RX_BUF_RELEASED:
next_rx_buf = evt->data.rx_buf.buf;
printk("RX buffer released\n");
break;
case UART_RX_STOPPED:
/* reason is a bitmask: 1=OVERRUN 2=PARITY 4=FRAMING 8=BREAK 16=COLLISION 32=NOISE */
printk("RX stopped, reason=0x%x\n", evt->data.rx_stop.reason);
break;
case UART_RX_DISABLED: {
int r = uart_rx_enable(dev, rx_buf_a, RX_BUF_SIZE, RX_TIMEOUT_US);
next_rx_buf = rx_buf_b;
printk("RX disabled, re-arm=%d\n", r);
break;
}
default:
printk("UART event: %d\n", evt->type);
break;
}
}
/*
* Send a fixed-length buffer over UART using the async API and wait for the
* transfer to complete.
*/
static void send_uart(const uint8_t *data, size_t len)
{
if (len == 0) {
return;
}
if (len > sizeof(tx_ram_buf)) {
len = sizeof(tx_ram_buf);
}
memcpy(tx_ram_buf, data, len);
int ret = uart_tx(uart_dev, tx_ram_buf, len, SYS_FOREVER_US);
if (ret < 0) {
printk("uart_tx failed: %d\n", ret);
return;
}
k_sem_take(&tx_done_sem, K_FOREVER);
}
static void print_uart(const char *s)
{
send_uart((const uint8_t *)s, strlen(s));
}
int main(void)
{
NRF_POWER->TASKS_CONSTLAT = 1;
uint8_t chunk[TX_CHUNK_BYTES];
if (!device_is_ready(uart_dev)) {
printk("UART device not found!\n");
return 0;
}
int ret = uart_callback_set(uart_dev, uart_cb, NULL);
if (ret < 0) {
if (ret == -ENOTSUP) {
printk("Async UART API support not enabled\n");
} else if (ret == -ENOSYS) {
printk("UART device does not support async API\n");
} else {
printk("Error setting UART callback: %d\n", ret);
}
return 0;
}
ret = uart_rx_enable(uart_dev, rx_buf_a, RX_BUF_SIZE, SYS_FOREVER_US);
if (ret < 0) {
printk("uart_rx_enable failed: %d\n", ret);
return 0;
}
print_uart("Hello! I'm your echo bot.\r\n");
print_uart("Type anything; it will be echoed back immediately.\r\n");
/* echo any received bytes back to the sender as soon as they arrive */
while (1) {
k_sem_take(&rx_data_sem, K_FOREVER);
uint32_t n;
while ((n = ring_buf_get(&rx_ring, chunk, sizeof(chunk))) > 0) {
send_uart(chunk, n);
}
}
NRF_POWER->TASKS_LOWPWR = 1;
return 0;
}
|