Skip to content

Commit 9232a3a

Browse files
committed
Add an example of using using DMA with PIO
Fixes #467
1 parent 1ed4de5 commit 9232a3a

File tree

4 files changed

+335
-0
lines changed

4 files changed

+335
-0
lines changed

README.md

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

240241
### PWM
241242

pio/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ if (NOT PICO_NO_HARDWARE)
1919
add_subdirectory(uart_rx)
2020
add_subdirectory(uart_tx)
2121
add_subdirectory(ws2812)
22+
add_subdirectory(uart_pio_dma)
2223
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: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
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_NUM 0
39+
#define DMA_IRQ_NUM 0
40+
41+
#define DMA_IRQX(N) DMA_IRQ_ ## N
42+
#define DMA_IRQN(N) DMA_IRQX(N)
43+
44+
// dma channels
45+
static uint dma_channel_rx;
46+
static uint dma_channel_tx;
47+
48+
// pio hardware
49+
#if USE_PIO_FOR_RX
50+
static PIO pio_hw_rx;
51+
static uint pio_sm_rx;
52+
static uint offset_rx;
53+
#endif
54+
55+
#if USE_PIO_FOR_TX
56+
static PIO pio_hw_tx;
57+
static uint pio_sm_tx;
58+
static uint offset_tx;
59+
#endif
60+
61+
// read request size for progress
62+
static uint32_t read_size;
63+
64+
// PIO interrupt handler, called when the state machine fifo is not empty
65+
// note: shouldn't printf in an irq normally!
66+
static void pio_irq_handler() {
67+
dma_channel_hw_t *dma_chan = dma_channel_hw_addr(dma_channel_rx);
68+
printf("pio_rx dma_rx=%u/%u\n", read_size - dma_chan->transfer_count, read_size);
69+
}
70+
71+
// DMA interrupt handler, called when a DMA channel has transmitted its data
72+
static void dma_irq_handler() {
73+
if (dma_channel_tx >= 0 && dma_irqn_get_channel_status(DMA_IRQ_NUM, dma_channel_tx)) {
74+
dma_irqn_acknowledge_channel(DMA_IRQ_NUM, dma_channel_tx);
75+
printf("dma_tx done\n");
76+
}
77+
if (dma_channel_rx >= 0 && dma_irqn_get_channel_status(DMA_IRQ_NUM, dma_channel_rx)) {
78+
dma_irqn_acknowledge_channel(DMA_IRQ_NUM, dma_channel_rx);
79+
printf("dma_rx done\n");
80+
}
81+
}
82+
83+
// Return a pointer to pio hardware
84+
static PIO pio_hardware(int n) {
85+
static_assert(NUM_PIOS == 2, "");
86+
const PIO pios[] = {pio0, pio1};
87+
return pios[n];
88+
}
89+
90+
// Return irqn for the pio
91+
static int pio_irqn(PIO pio_hw, int irqn) {
92+
assert(irqn < (PIO1_IRQ_0 - PIO0_IRQ_0));
93+
int count = NUM_PIOS;
94+
while(count--) {
95+
if (pio_hw == pio_hardware(count)) {
96+
return PIO0_IRQ_0 + (PIO1_IRQ_0 - PIO0_IRQ_0) * count + irqn;
97+
}
98+
}
99+
assert(0);
100+
}
101+
102+
// Allocates a pio and statemachine and loads the pio program into memory
103+
// Return true on success
104+
static bool pio_init(const pio_program_t *program, PIO *pio_hw, uint *sm, uint *offset) {
105+
// Find a free pio
106+
int count = NUM_PIOS;
107+
while(count--) {
108+
*pio_hw = pio_hardware(count);
109+
if (pio_can_add_program(*pio_hw, program)) {
110+
break;
111+
}
112+
if (count == 0) {
113+
return false;
114+
}
115+
}
116+
// Find a state machine
117+
*sm = (int8_t)pio_claim_unused_sm(*pio_hw, false);
118+
if (*sm < 0) {
119+
return false;
120+
}
121+
*offset = pio_add_program(*pio_hw, program);
122+
return true;
123+
}
124+
125+
// free up pio resources
126+
static void pio_deinit(const pio_program_t *program, PIO pio_hw, uint sm, uint offset) {
127+
pio_remove_program(pio_hw, program, offset);
128+
pio_sm_unclaim(pio_hw, sm);
129+
}
130+
131+
static void dump_bytes(const char *bptr, uint32_t len) {
132+
unsigned int i = 0;
133+
for (i = 0; i < len;) {
134+
if ((i & 0x0f) == 0) {
135+
printf("\n");
136+
} else if ((i & 0x07) == 0) {
137+
printf(" ");
138+
}
139+
printf("%02x ", bptr[i++]);
140+
}
141+
printf("\n");
142+
}
143+
144+
int main()
145+
{
146+
setup_default_uart();
147+
148+
// setup text we are going to send and what we expect to get back
149+
const char buffer_tx[] = "the quick brown fox jumps over the lazy dog";
150+
151+
// Buffer for receiving data
152+
char buffer_rx[sizeof(buffer_tx) - 1] = {0};
153+
154+
#if !USE_PIO_FOR_RX || !USE_PIO_FOR_TX
155+
uart_init(HARD_UART_INST, SERIAL_BAUD);
156+
#endif
157+
158+
// setup pio for rx
159+
#if USE_PIO_FOR_RX
160+
if (!pio_init(&uart_rx_mini_program, &pio_hw_rx, &pio_sm_rx, &offset_rx)) {
161+
panic("failed to allocate pio for rx");
162+
}
163+
uart_rx_mini_program_init(pio_hw_rx, pio_sm_rx, offset_rx, GPIO_RX, SERIAL_BAUD);
164+
#else
165+
gpio_set_function(GPIO_RX, GPIO_FUNC_UART);
166+
#endif
167+
168+
// setup pio for tx
169+
#if USE_PIO_FOR_TX
170+
if (!pio_init(&uart_tx_program, &pio_hw_tx, &pio_sm_tx, &offset_tx)) {
171+
panic("failed to allocate pio for tx");
172+
}
173+
uart_tx_program_init(pio_hw_tx, pio_sm_tx, offset_tx, GPIO_TX, SERIAL_BAUD);
174+
#else
175+
gpio_set_function(GPIO_TX, GPIO_FUNC_UART);
176+
#endif
177+
178+
// Setup dma for read
179+
#if USE_DMA_FOR_RX
180+
dma_channel_rx = dma_claim_unused_channel(false);
181+
if (dma_channel_rx < 0) {
182+
panic("No free dma channels");
183+
}
184+
dma_channel_config config_rx = dma_channel_get_default_config(dma_channel_rx);
185+
channel_config_set_transfer_data_size(&config_rx, DMA_SIZE_8);
186+
channel_config_set_read_increment(&config_rx, false);
187+
channel_config_set_write_increment(&config_rx, true);
188+
read_size = sizeof(buffer_tx) - 1;
189+
#if USE_PIO_FOR_RX
190+
// read from pio fifo
191+
channel_config_set_dreq(&config_rx, pio_get_dreq(pio_hw_rx, pio_sm_rx, false));
192+
// 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!
193+
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
194+
#else
195+
// read from uart hardware
196+
channel_config_set_dreq(&config_rx, uart_get_dreq(HARD_UART_INST, false));
197+
dma_channel_configure(dma_channel_rx, &config_rx, buffer_rx, &uart_get_hw(HARD_UART_INST)->dr, read_size, true); // dma started
198+
#endif
199+
#endif
200+
201+
// setup dma for write
202+
#if USE_DMA_FOR_TX
203+
dma_channel_tx = dma_claim_unused_channel(false);
204+
if (dma_channel_tx < 0) {
205+
panic("No free dma channels");
206+
}
207+
dma_channel_config config_tx = dma_channel_get_default_config(dma_channel_tx);
208+
channel_config_set_transfer_data_size(&config_tx, DMA_SIZE_8);
209+
channel_config_set_read_increment(&config_tx, true);
210+
channel_config_set_write_increment(&config_tx, false);
211+
#if USE_PIO_FOR_RX
212+
// write to pio fofo
213+
channel_config_set_dreq(&config_tx, pio_get_dreq(pio_hw_tx, pio_sm_tx, true));
214+
dma_channel_configure(dma_channel_tx, &config_tx, &pio_hw_rx->txf[pio_sm_tx], buffer_tx, sizeof(buffer_tx) - 1, true); // dma started
215+
#else
216+
// write to uart hardware
217+
channel_config_set_dreq(&config_tx, uart_get_dreq(HARD_UART_INST, true));
218+
dma_channel_configure(dma_channel_tx, &config_tx, &uart_get_hw(HARD_UART_INST)->dr, buffer_tx, sizeof(buffer_tx) - 1, true); // dma started
219+
#endif
220+
#endif
221+
222+
// setup pio interrupt
223+
#if USE_PIO_FOR_RX
224+
if (irq_get_exclusive_handler(pio_irqn(pio_hw_rx, PIO_IRQ_NUM))) {
225+
panic("PIO IRQ in use");
226+
}
227+
#if USE_DMA_FOR_RX
228+
irq_add_shared_handler(pio_irqn(pio_hw_rx, PIO_IRQ_NUM), pio_irq_handler, PIO_IRQ_PRIORITY);
229+
pio_set_irqn_source_enabled(pio_hw_rx, PIO_IRQ_NUM, pis_sm0_rx_fifo_not_empty + pio_sm_rx, true);
230+
irq_set_enabled(pio_irqn(pio_hw_rx, PIO_IRQ_NUM), true);
231+
#endif
232+
#endif
233+
234+
// setup dma interrupt
235+
#if USE_DMA_FOR_RX || USE_DMA_FOR_TX
236+
if (irq_get_exclusive_handler(DMA_IRQN(DMA_IRQ_NUM))) {
237+
panic("DMA IRQ in use");
238+
}
239+
irq_add_shared_handler(DMA_IRQN(DMA_IRQ_NUM), dma_irq_handler, DMA_IRQ_PRIORITY);
240+
#if USE_DMA_FOR_TX
241+
dma_irqn_set_channel_enabled(DMA_IRQ_NUM, dma_channel_tx, true);
242+
#endif
243+
#if USE_DMA_FOR_RX
244+
dma_irqn_set_channel_enabled(DMA_IRQ_NUM, dma_channel_rx, true);
245+
#endif
246+
irq_set_enabled(DMA_IRQN(DMA_IRQ_NUM), true);
247+
#endif
248+
249+
// send data
250+
#if USE_DMA_FOR_TX
251+
dma_channel_wait_for_finish_blocking(dma_channel_tx); // wait for tx
252+
#elif USE_PIO_FOR_TX
253+
// write to the pio fifo
254+
int count_pio_tx = 0;
255+
while(count_pio_tx < sizeof(buffer_tx) - 1) {
256+
uart_tx_program_putc(pio_hw_tx, pio_sm_tx, buffer_tx[count_pio_tx++]);
257+
}
258+
#else
259+
uart_puts(HARD_UART_INST, buffer_tx);
260+
#endif
261+
262+
// Receive the data
263+
#if USE_DMA_FOR_RX
264+
// wait for dma rx
265+
dma_channel_wait_for_finish_blocking(dma_channel_rx);
266+
#elif USE_PIO_FOR_RX
267+
// read from the pio fifo
268+
int count_pio_rx = 0;
269+
while(count_pio_rx < sizeof(buffer_tx) - 1) {
270+
buffer_rx[count_pio_rx++] = uart_rx_program_getc(pio_hw_rx, pio_sm_rx);
271+
}
272+
#else
273+
// use the uart hardware
274+
int count_uart_rx = 0;
275+
while(count_uart_rx < sizeof(buffer_tx) - 1) {
276+
buffer_rx[count_uart_rx++] = uart_getc(HARD_UART_INST);
277+
}
278+
#endif
279+
280+
// check
281+
if (memcmp(buffer_rx, buffer_tx, sizeof(buffer_tx) - 1) == 0) {
282+
printf("Test passed\n");
283+
} else {
284+
printf("buffer_tx: >%s<\n", buffer_tx);
285+
dump_bytes(buffer_tx, sizeof(buffer_tx));
286+
printf("result: >%s<\n", buffer_rx);
287+
dump_bytes(buffer_rx, sizeof(buffer_rx));
288+
printf("Test failed\n");
289+
assert(0);
290+
}
291+
292+
// cleanup
293+
#if USE_DMA_FOR_TX
294+
dma_irqn_set_channel_enabled(DMA_IRQ_NUM, dma_channel_tx, false);
295+
dma_channel_unclaim(dma_channel_tx);
296+
#endif
297+
#if USE_DMA_FOR_RX
298+
dma_irqn_set_channel_enabled(DMA_IRQ_NUM, dma_channel_rx, false);
299+
dma_channel_unclaim(dma_channel_rx);
300+
#endif
301+
#if USE_DMA_FOR_RX || USE_DMA_FOR_TX
302+
irq_remove_handler(DMA_IRQN(DMA_IRQ_NUM), dma_irq_handler);
303+
if (!irq_has_shared_handler(DMA_IRQN(DMA_IRQ_NUM))) {
304+
irq_set_enabled(DMA_IRQN(DMA_IRQ_NUM), false);
305+
}
306+
#endif
307+
#if USE_PIO_FOR_RX
308+
pio_set_irqn_source_enabled(pio_hw_rx, PIO_IRQ_NUM, pis_sm0_rx_fifo_not_empty + pio_sm_rx, false);
309+
irq_remove_handler(pio_irqn(pio_hw_rx, PIO_IRQ_NUM), pio_irq_handler);
310+
if (!irq_has_shared_handler(pio_irqn(pio_hw_rx, PIO_IRQ_NUM))) {
311+
irq_set_enabled(pio_irqn(pio_hw_rx, PIO_IRQ_NUM), false);
312+
}
313+
pio_deinit(&uart_rx_mini_program, pio_hw_rx, pio_sm_rx, offset_rx);
314+
#endif
315+
#if USE_PIO_FOR_TX
316+
pio_deinit(&uart_tx_program, pio_hw_tx, pio_sm_tx, offset_tx);
317+
#endif
318+
#if !USE_PIO_FOR_RX || !USE_PIO_FOR_TX
319+
uart_deinit(HARD_UART_INST);
320+
#endif
321+
return 0;
322+
}

0 commit comments

Comments
 (0)