Skip to content

Commit 9ba2c96

Browse files
committed
Add an example of using using DMA with PIO
Fixes #467
1 parent d03763e commit 9ba2c96

File tree

4 files changed

+282
-0
lines changed

4 files changed

+282
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ App|Description
305305
[uart_tx](pio/uart_tx) | Implement the transmit component of a UART serial port, and print hello world.
306306
[ws2812](pio/ws2812) | Examples of driving WS2812 addressable RGB LEDs.
307307
[addition](pio/addition) | Add two integers together using PIO. Only around 8 billion times slower than Cortex-M0+.
308+
[uart_pio_dma](pio/uart_pio_dma) | Send and receive data from a UART implemented using the PIO and DMA
308309

309310
### PWM
310311

pio/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ if (TARGET hardware_pio)
2020
add_subdirectory_exclude_platforms(uart_rx)
2121
add_subdirectory_exclude_platforms(uart_tx)
2222
add_subdirectory_exclude_platforms(ws2812)
23+
add_subdirectory_exclude_platforms(uart_pio_dma)
2324
else()
2425
message("Skipping PIO examples as hardware_pio is unavailable on this platform")
2526
endif()

pio/uart_pio_dma/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
add_executable(uart_pio_dma)
2+
pico_generate_pio_header(uart_pio_dma ${CMAKE_CURRENT_LIST_DIR}/../uart_rx/uart_rx.pio)
3+
pico_generate_pio_header(uart_pio_dma ${CMAKE_CURRENT_LIST_DIR}/../uart_tx/uart_tx.pio)
4+
target_sources(uart_pio_dma PRIVATE uart_pio_dma.c)
5+
target_link_libraries(uart_pio_dma PRIVATE
6+
pico_stdlib
7+
hardware_pio
8+
hardware_dma
9+
)
10+
pico_add_extra_outputs(uart_pio_dma)
11+
example_auto_set_url(uart_pio_dma)

pio/uart_pio_dma/uart_pio_dma.c

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
/**
2+
* Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
7+
#include <stdio.h>
8+
#include <string.h>
9+
10+
#include "pico/stdlib.h"
11+
#include "hardware/pio.h"
12+
#include "hardware/uart.h"
13+
#include "hardware/dma.h"
14+
#include "uart_rx.pio.h"
15+
#include "uart_tx.pio.h"
16+
17+
// Sends data via GPIO 4 and receives it on GPIO 5
18+
// Connect these pins with a wire
19+
#define GPIO_TX 4
20+
#define GPIO_RX 5
21+
22+
#define SERIAL_BAUD 921600
23+
#define HARD_UART_INST uart1
24+
25+
#define USE_PIO_FOR_RX 1
26+
#define USE_DMA_FOR_RX 1
27+
#define USE_PIO_FOR_TX 1
28+
#define USE_DMA_FOR_TX 1
29+
30+
#ifndef DMA_IRQ_PRIORITY
31+
#define DMA_IRQ_PRIORITY PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY
32+
#endif
33+
34+
#ifndef PIO_IRQ_PRIORITY
35+
#define PIO_IRQ_PRIORITY PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY
36+
#endif
37+
38+
#define PIO_IRQ_TO_USE 0
39+
#define DMA_IRQ_TO_USE 0
40+
41+
// dma channels
42+
static uint dma_channel_rx;
43+
static uint dma_channel_tx;
44+
45+
// pio hardware
46+
#if USE_PIO_FOR_RX
47+
static PIO pio_hw_rx;
48+
static uint pio_sm_rx;
49+
static uint offset_rx;
50+
#endif
51+
52+
#if USE_PIO_FOR_TX
53+
static PIO pio_hw_tx;
54+
static uint pio_sm_tx;
55+
static uint offset_tx;
56+
#endif
57+
58+
// read request size for progress
59+
static uint32_t read_size;
60+
61+
// PIO interrupt handler, called when the state machine fifo is not empty
62+
// note: shouldn't printf in an irq normally!
63+
static void pio_irq_handler() {
64+
dma_channel_hw_t *dma_chan = dma_channel_hw_addr(dma_channel_rx);
65+
printf("pio_rx dma_rx=%u/%u\n", read_size - dma_chan->transfer_count, read_size);
66+
}
67+
68+
// DMA interrupt handler, called when a DMA channel has transmitted its data
69+
static void dma_irq_handler() {
70+
if (dma_channel_tx >= 0 && dma_irqn_get_channel_status(DMA_IRQ_TO_USE, dma_channel_tx)) {
71+
dma_irqn_acknowledge_channel(DMA_IRQ_TO_USE, dma_channel_tx);
72+
printf("dma_tx done\n");
73+
}
74+
if (dma_channel_rx >= 0 && dma_irqn_get_channel_status(DMA_IRQ_TO_USE, dma_channel_rx)) {
75+
dma_irqn_acknowledge_channel(DMA_IRQ_TO_USE, dma_channel_rx);
76+
printf("dma_rx done\n");
77+
}
78+
}
79+
80+
static void dump_bytes(const char *bptr, uint32_t len) {
81+
unsigned int i = 0;
82+
for (i = 0; i < len;) {
83+
if ((i & 0x0f) == 0) {
84+
printf("\n");
85+
} else if ((i & 0x07) == 0) {
86+
printf(" ");
87+
}
88+
printf("%02x ", bptr[i++]);
89+
}
90+
printf("\n");
91+
}
92+
93+
int main()
94+
{
95+
setup_default_uart();
96+
97+
// setup text we are going to send and what we expect to get back
98+
const char buffer_tx[] = "the quick brown fox jumps over the lazy dog";
99+
100+
// Buffer for receiving data
101+
char buffer_rx[sizeof(buffer_tx) - 1] = {0};
102+
103+
#if !USE_PIO_FOR_RX || !USE_PIO_FOR_TX
104+
uart_init(HARD_UART_INST, SERIAL_BAUD);
105+
#endif
106+
107+
// setup pio for rx
108+
#if USE_PIO_FOR_RX
109+
if (!pio_claim_free_sm_and_add_program_for_gpio_range(&uart_rx_mini_program, &pio_hw_rx, &pio_sm_rx, &offset_rx, GPIO_RX, 1, true)) {
110+
panic("failed to allocate pio for rx");
111+
}
112+
uart_rx_mini_program_init(pio_hw_rx, pio_sm_rx, offset_rx, GPIO_RX, SERIAL_BAUD);
113+
#else
114+
gpio_set_function(GPIO_RX, GPIO_FUNC_UART);
115+
#endif
116+
117+
// setup pio for tx
118+
#if USE_PIO_FOR_TX
119+
if (!pio_claim_free_sm_and_add_program_for_gpio_range(&uart_tx_program, &pio_hw_tx, &pio_sm_tx, &offset_tx, GPIO_TX, 1, true)) {
120+
panic("failed to allocate pio for tx");
121+
}
122+
uart_tx_program_init(pio_hw_tx, pio_sm_tx, offset_tx, GPIO_TX, SERIAL_BAUD);
123+
#else
124+
gpio_set_function(GPIO_TX, GPIO_FUNC_UART);
125+
#endif
126+
127+
// setup pio interrupt
128+
#if USE_PIO_FOR_RX
129+
if (irq_get_exclusive_handler(pio_get_irq_num(pio_hw_rx, PIO_IRQ_TO_USE))) {
130+
panic("PIO IRQ in use");
131+
}
132+
#if USE_DMA_FOR_RX
133+
irq_add_shared_handler(pio_get_irq_num(pio_hw_rx, PIO_IRQ_TO_USE), pio_irq_handler, PIO_IRQ_PRIORITY);
134+
pio_set_irqn_source_enabled(pio_hw_rx, PIO_IRQ_TO_USE, pis_sm0_rx_fifo_not_empty + pio_sm_rx, true);
135+
irq_set_enabled(pio_get_irq_num(pio_hw_rx, PIO_IRQ_TO_USE), true);
136+
#endif
137+
#endif
138+
139+
// add dma handler
140+
#if USE_DMA_FOR_RX || USE_DMA_FOR_TX
141+
if (irq_get_exclusive_handler(dma_get_irq_num(DMA_IRQ_TO_USE))) {
142+
panic("DMA IRQ in use");
143+
}
144+
irq_add_shared_handler(dma_get_irq_num(DMA_IRQ_TO_USE), dma_irq_handler, DMA_IRQ_PRIORITY);
145+
irq_set_enabled(dma_get_irq_num(DMA_IRQ_TO_USE), true);
146+
#endif
147+
148+
// Setup dma for read
149+
#if USE_DMA_FOR_RX
150+
dma_channel_rx = dma_claim_unused_channel(false);
151+
if (dma_channel_rx < 0) {
152+
panic("No free dma channels");
153+
}
154+
dma_channel_config config_rx = dma_channel_get_default_config(dma_channel_rx);
155+
channel_config_set_transfer_data_size(&config_rx, DMA_SIZE_8);
156+
channel_config_set_read_increment(&config_rx, false);
157+
channel_config_set_write_increment(&config_rx, true);
158+
read_size = sizeof(buffer_tx) - 1;
159+
// enable irq for rx
160+
dma_irqn_set_channel_enabled(DMA_IRQ_TO_USE, dma_channel_rx, true);
161+
#if USE_PIO_FOR_RX
162+
// read from pio fifo
163+
channel_config_set_dreq(&config_rx, pio_get_dreq(pio_hw_rx, pio_sm_rx, false));
164+
// 8-bit read from the uppermost byte of the FIFO, as data is left-justified so need to add 3. Don't forget the cast!
165+
dma_channel_configure(dma_channel_rx, &config_rx, buffer_rx, (io_rw_8*)&pio_hw_rx->rxf[pio_sm_rx] + 3, read_size, true); // dma started
166+
#else
167+
// read from uart hardware
168+
channel_config_set_dreq(&config_rx, uart_get_dreq(HARD_UART_INST, false));
169+
dma_channel_configure(dma_channel_rx, &config_rx, buffer_rx, &uart_get_hw(HARD_UART_INST)->dr, read_size, true); // dma started
170+
#endif
171+
#endif
172+
173+
// setup dma for write
174+
#if USE_DMA_FOR_TX
175+
dma_channel_tx = dma_claim_unused_channel(false);
176+
if (dma_channel_tx < 0) {
177+
panic("No free dma channels");
178+
}
179+
dma_channel_config config_tx = dma_channel_get_default_config(dma_channel_tx);
180+
channel_config_set_transfer_data_size(&config_tx, DMA_SIZE_8);
181+
channel_config_set_read_increment(&config_tx, true);
182+
channel_config_set_write_increment(&config_tx, false);
183+
// enable irq for tx
184+
dma_irqn_set_channel_enabled(DMA_IRQ_TO_USE, dma_channel_tx, true);
185+
#if USE_PIO_FOR_RX
186+
// write to pio fifo
187+
channel_config_set_dreq(&config_tx, pio_get_dreq(pio_hw_tx, pio_sm_tx, true));
188+
dma_channel_configure(dma_channel_tx, &config_tx, &pio_hw_rx->txf[pio_sm_tx], buffer_tx, sizeof(buffer_tx) - 1, true); // dma started
189+
#else
190+
// write to uart hardware
191+
channel_config_set_dreq(&config_tx, uart_get_dreq(HARD_UART_INST, true));
192+
dma_channel_configure(dma_channel_tx, &config_tx, &uart_get_hw(HARD_UART_INST)->dr, buffer_tx, sizeof(buffer_tx) - 1, true); // dma started
193+
#endif
194+
#endif
195+
196+
// send data
197+
#if USE_DMA_FOR_TX
198+
dma_channel_wait_for_finish_blocking(dma_channel_tx); // wait for tx
199+
#elif USE_PIO_FOR_TX
200+
// write to the pio fifo
201+
int count_pio_tx = 0;
202+
while(count_pio_tx < sizeof(buffer_tx) - 1) {
203+
uart_tx_program_putc(pio_hw_tx, pio_sm_tx, buffer_tx[count_pio_tx++]);
204+
}
205+
#else
206+
uart_puts(HARD_UART_INST, buffer_tx);
207+
#endif
208+
209+
// Receive the data
210+
#if USE_DMA_FOR_RX
211+
// wait for dma rx
212+
dma_channel_wait_for_finish_blocking(dma_channel_rx);
213+
#elif USE_PIO_FOR_RX
214+
// read from the pio fifo
215+
int count_pio_rx = 0;
216+
while(count_pio_rx < sizeof(buffer_tx) - 1) {
217+
buffer_rx[count_pio_rx++] = uart_rx_program_getc(pio_hw_rx, pio_sm_rx);
218+
}
219+
#else
220+
// use the uart hardware
221+
int count_uart_rx = 0;
222+
while(count_uart_rx < sizeof(buffer_tx) - 1) {
223+
buffer_rx[count_uart_rx++] = uart_getc(HARD_UART_INST);
224+
}
225+
#endif
226+
227+
// check
228+
if (memcmp(buffer_rx, buffer_tx, sizeof(buffer_tx) - 1) == 0) {
229+
printf("Test passed\n");
230+
} else {
231+
printf("buffer_tx: >%s<\n", buffer_tx);
232+
dump_bytes(buffer_tx, sizeof(buffer_tx));
233+
printf("result: >%s<\n", buffer_rx);
234+
dump_bytes(buffer_rx, sizeof(buffer_rx));
235+
printf("Test failed\n");
236+
assert(0);
237+
}
238+
239+
// cleanup
240+
#if USE_DMA_FOR_TX
241+
dma_irqn_set_channel_enabled(DMA_IRQ_TO_USE, dma_channel_tx, false);
242+
dma_channel_unclaim(dma_channel_tx);
243+
#endif
244+
#if USE_DMA_FOR_RX
245+
dma_irqn_set_channel_enabled(DMA_IRQ_TO_USE, dma_channel_rx, false);
246+
dma_channel_unclaim(dma_channel_rx);
247+
#endif
248+
#if USE_DMA_FOR_RX || USE_DMA_FOR_TX
249+
irq_remove_handler(dma_get_irq_num(DMA_IRQ_TO_USE), dma_irq_handler);
250+
if (!irq_has_shared_handler(dma_get_irq_num(DMA_IRQ_TO_USE))) {
251+
irq_set_enabled(dma_get_irq_num(DMA_IRQ_TO_USE), false);
252+
}
253+
#endif
254+
#if USE_PIO_FOR_RX
255+
pio_set_irqn_source_enabled(pio_hw_rx, PIO_IRQ_TO_USE, pis_sm0_rx_fifo_not_empty + pio_sm_rx, false);
256+
irq_remove_handler(pio_get_irq_num(pio_hw_rx, PIO_IRQ_TO_USE), pio_irq_handler);
257+
if (!irq_has_shared_handler(pio_get_irq_num(pio_hw_rx, PIO_IRQ_TO_USE))) {
258+
irq_set_enabled(pio_get_irq_num(pio_hw_rx, PIO_IRQ_TO_USE), false);
259+
}
260+
pio_remove_program_and_unclaim_sm(&uart_rx_mini_program, pio_hw_rx, pio_sm_rx, offset_rx);
261+
#endif
262+
#if USE_PIO_FOR_TX
263+
pio_remove_program_and_unclaim_sm(&uart_tx_program, pio_hw_tx, pio_sm_tx, offset_tx);
264+
#endif
265+
#if !USE_PIO_FOR_RX || !USE_PIO_FOR_TX
266+
uart_deinit(HARD_UART_INST);
267+
#endif
268+
return 0;
269+
}

0 commit comments

Comments
 (0)