Skip to content

Commit b22ac95

Browse files
Add an example of using using DMA with PIO (#468)
1 parent ad98889 commit b22ac95

File tree

4 files changed

+287
-0
lines changed

4 files changed

+287
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ App|Description
310310
[uart_tx](pio/uart_tx) | Implement the transmit component of a UART serial port, and print hello world.
311311
[ws2812](pio/ws2812) | Examples of driving WS2812 addressable RGB LEDs.
312312
[addition](pio/addition) | Add two integers together using PIO. Only around 8 billion times slower than Cortex-M0+.
313+
[uart_pio_dma](pio/uart_pio_dma) | Send and receive data from a UART implemented using the PIO and DMA
313314

314315
### PWM
315316

pio/CMakeLists.txt

+1
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

+11
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

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

0 commit comments

Comments
 (0)