Skip to content

Commit d4e1f0c

Browse files
committed
Add rp235x pio_dma_pingpong example
1 parent d2dc6c2 commit d4e1f0c

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed

examples/rp235x/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ static_cell = "2.1"
5757
portable-atomic = { version = "1.5", features = ["critical-section"] }
5858
log = "0.4"
5959
embedded-sdmmc = "0.7.0"
60+
num-traits = { version = "0.2.19", default-features = false }
6061

6162
[profile.release]
6263
# Enable generation of debug symbols even on release builds
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
//! This example shows how to feed alternating buffers to the PIO without downtime.
2+
3+
#![no_std]
4+
#![no_main]
5+
use defmt::info;
6+
use embassy_executor::Spawner;
7+
use embassy_rp::bind_interrupts;
8+
use embassy_rp::peripherals::PIO0;
9+
use embassy_rp::pio::program::pio_asm;
10+
use embassy_rp::pio::{Config, Direction, InterruptHandler, Pio};
11+
use {defmt_rtt as _, panic_probe as _};
12+
13+
bind_interrupts!(struct Irqs {
14+
PIO0_IRQ_0 => InterruptHandler<PIO0>;
15+
});
16+
17+
/// The desired samples/second to output
18+
const SAMPLE_RATE: u32 = 16_000;
19+
20+
#[embassy_executor::main]
21+
async fn main(_spawner: Spawner) {
22+
let mut p = embassy_rp::init(Default::default());
23+
let mut pio = Pio::new(p.PIO0, Irqs);
24+
25+
const PIO_OUTPUT_RATE: u32 = 2; // pio program efficiency (clocks per output)
26+
let clock_freq = embassy_rp::clocks::clk_sys_freq();
27+
let divider = clock_freq / PIO_OUTPUT_RATE / SAMPLE_RATE;
28+
info!("PIO base divider: {}", divider);
29+
30+
let pio_program = pio_asm!(
31+
".origin 0"
32+
".wrap_target"
33+
"PULL"
34+
"OUT PINS, 8"
35+
".wrap"
36+
);
37+
38+
let pio_pins = [
39+
&pio.common.make_pio_pin(p.PIN_5),
40+
&pio.common.make_pio_pin(p.PIN_6),
41+
&pio.common.make_pio_pin(p.PIN_7),
42+
&pio.common.make_pio_pin(p.PIN_8),
43+
&pio.common.make_pio_pin(p.PIN_9),
44+
&pio.common.make_pio_pin(p.PIN_10),
45+
&pio.common.make_pio_pin(p.PIN_11),
46+
&pio.common.make_pio_pin(p.PIN_12),
47+
];
48+
49+
let mut cfg = Config::default();
50+
cfg.use_program(&pio.common.load_program(&pio_program.program), &[]);
51+
cfg.clock_divider = (divider as u16).into();
52+
cfg.set_out_pins(&pio_pins);
53+
54+
pio.sm0.set_pin_dirs(Direction::Out, &pio_pins);
55+
pio.sm0.set_config(&cfg);
56+
pio.sm0.set_enable(true);
57+
58+
let tx = pio.sm0.tx();
59+
60+
let mut buffer_1 = [0x0u8; 128];
61+
let mut buffer_2 = [0x0u8; 128];
62+
63+
let mut sample_index = 0usize;
64+
tx.dma_push_ping_pong(
65+
p.DMA_CH0.reborrow(),
66+
p.DMA_CH1.reborrow(),
67+
&mut buffer_1,
68+
&mut buffer_2,
69+
|buf| {
70+
info!("In start of fill callback, index={}", sample_index);
71+
if sample_index > 100_000 {
72+
buf.iter_mut().for_each(|b| *b = 0);
73+
return core::ops::ControlFlow::Break(());
74+
}
75+
76+
for b in buf.iter_mut() {
77+
// generate a 440hz sine wave
78+
let time = sample_index as f32 / SAMPLE_RATE as f32;
79+
let wave = fast_sin(time * 440. * core::f32::consts::PI * 2.);
80+
81+
// convert [-1, 1] to [0, 255]
82+
*b = ((wave + 1.) / 2. * 256.) as u8;
83+
84+
sample_index += 1;
85+
}
86+
87+
core::ops::ControlFlow::Continue(())
88+
},
89+
)
90+
.await;
91+
92+
// push a zero to reset the pin state
93+
tx.dma_push(p.DMA_CH0, &[0u8; 1], false).await;
94+
}
95+
96+
/// Based on https://bmtechjournal.wordpress.com/2020/05/27/super-fast-quadratic-sinusoid-approximation/
97+
fn fast_sin(x: f32) -> f32 {
98+
use num_traits::float::FloatCore as _;
99+
100+
let fake_sin_2 = |x: f32| 2.0 * x * (1.0 - (2.0 * x).abs());
101+
let range_limiter_2 = |x: f32| x - x.floor() - 0.5;
102+
103+
-4.0 * fake_sin_2(range_limiter_2(x / (2.0 * core::f32::consts::PI)))
104+
}

0 commit comments

Comments
 (0)