aboutsummaryrefslogtreecommitdiff
path: root/examples/rp/src/bin
diff options
context:
space:
mode:
authorCaleb Jamison <[email protected]>2024-10-09 10:04:35 -0400
committerCaleb Jamison <[email protected]>2024-10-09 10:18:00 -0400
commit57c1fbf3089e2a2dc9fe5b7d1f1e094596566395 (patch)
tree833856b7da855b8de56dec1494c2da88ac29e415 /examples/rp/src/bin
parent456c226b29799f7db56ab60b0ae3d95cc7d6a32a (diff)
Move pio programs into embassy-rp
Diffstat (limited to 'examples/rp/src/bin')
-rw-r--r--examples/rp/src/bin/pio_hd44780.rs201
-rw-r--r--examples/rp/src/bin/pio_i2s.rs71
-rw-r--r--examples/rp/src/bin/pio_onewire.rs98
-rw-r--r--examples/rp/src/bin/pio_pwm.rs90
-rw-r--r--examples/rp/src/bin/pio_rotary_encoder.rs85
-rw-r--r--examples/rp/src/bin/pio_servo.rs96
-rw-r--r--examples/rp/src/bin/pio_stepper.rs135
-rw-r--r--examples/rp/src/bin/pio_uart.rs222
-rw-r--r--examples/rp/src/bin/pio_ws2812.rs105
9 files changed, 131 insertions, 972 deletions
diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs
index 6c02630e0..164e6f8d3 100644
--- a/examples/rp/src/bin/pio_hd44780.rs
+++ b/examples/rp/src/bin/pio_hd44780.rs
@@ -7,13 +7,11 @@
7use core::fmt::Write; 7use core::fmt::Write;
8 8
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_rp::dma::{AnyChannel, Channel}; 10use embassy_rp::bind_interrupts;
11use embassy_rp::peripherals::PIO0; 11use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{ 12use embassy_rp::pio::{InterruptHandler, Pio};
13 Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, 13use embassy_rp::pio_programs::hd44780::{PioHD44780, PioHD44780CommandSequenceProgram, PioHD44780CommandWordProgram};
14};
15use embassy_rp::pwm::{self, Pwm}; 14use embassy_rp::pwm::{self, Pwm};
16use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef};
17use embassy_time::{Instant, Timer}; 15use embassy_time::{Instant, Timer};
18use {defmt_rtt as _, panic_probe as _}; 16use {defmt_rtt as _, panic_probe as _};
19 17
@@ -43,8 +41,27 @@ async fn main(_spawner: Spawner) {
43 c 41 c
44 }); 42 });
45 43
46 let mut hd = HD44780::new( 44 let Pio {
47 p.PIO0, Irqs, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6, 45 mut common, sm0, irq0, ..
46 } = Pio::new(p.PIO0, Irqs);
47
48 let word_prg = PioHD44780CommandWordProgram::new(&mut common);
49 let seq_prg = PioHD44780CommandSequenceProgram::new(&mut common);
50
51 let mut hd = PioHD44780::new(
52 &mut common,
53 sm0,
54 irq0,
55 p.DMA_CH3,
56 p.PIN_0,
57 p.PIN_1,
58 p.PIN_2,
59 p.PIN_3,
60 p.PIN_4,
61 p.PIN_5,
62 p.PIN_6,
63 &word_prg,
64 &seq_prg,
48 ) 65 )
49 .await; 66 .await;
50 67
@@ -68,173 +85,3 @@ async fn main(_spawner: Spawner) {
68 Timer::after_secs(1).await; 85 Timer::after_secs(1).await;
69 } 86 }
70} 87}
71
72pub struct HD44780<'l> {
73 dma: PeripheralRef<'l, AnyChannel>,
74 sm: StateMachine<'l, PIO0, 0>,
75
76 buf: [u8; 40],
77}
78
79impl<'l> HD44780<'l> {
80 pub async fn new(
81 pio: impl Peripheral<P = PIO0> + 'l,
82 irq: Irqs,
83 dma: impl Peripheral<P = impl Channel> + 'l,
84 rs: impl PioPin,
85 rw: impl PioPin,
86 e: impl PioPin,
87 db4: impl PioPin,
88 db5: impl PioPin,
89 db6: impl PioPin,
90 db7: impl PioPin,
91 ) -> HD44780<'l> {
92 into_ref!(dma);
93
94 let Pio {
95 mut common,
96 mut irq0,
97 mut sm0,
98 ..
99 } = Pio::new(pio, irq);
100
101 // takes command words (<wait:24> <command:4> <0:4>)
102 let prg = pio_proc::pio_asm!(
103 r#"
104 .side_set 1 opt
105 .origin 20
106
107 loop:
108 out x, 24
109 delay:
110 jmp x--, delay
111 out pins, 4 side 1
112 out null, 4 side 0
113 jmp !osre, loop
114 irq 0
115 "#,
116 );
117
118 let rs = common.make_pio_pin(rs);
119 let rw = common.make_pio_pin(rw);
120 let e = common.make_pio_pin(e);
121 let db4 = common.make_pio_pin(db4);
122 let db5 = common.make_pio_pin(db5);
123 let db6 = common.make_pio_pin(db6);
124 let db7 = common.make_pio_pin(db7);
125
126 sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]);
127
128 let mut cfg = Config::default();
129 cfg.use_program(&common.load_program(&prg.program), &[&e]);
130 cfg.clock_divider = 125u8.into();
131 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
132 cfg.shift_out = ShiftConfig {
133 auto_fill: true,
134 direction: ShiftDirection::Left,
135 threshold: 32,
136 };
137 cfg.fifo_join = FifoJoin::TxOnly;
138 sm0.set_config(&cfg);
139
140 sm0.set_enable(true);
141 // init to 8 bit thrice
142 sm0.tx().push((50000 << 8) | 0x30);
143 sm0.tx().push((5000 << 8) | 0x30);
144 sm0.tx().push((200 << 8) | 0x30);
145 // init 4 bit
146 sm0.tx().push((200 << 8) | 0x20);
147 // set font and lines
148 sm0.tx().push((50 << 8) | 0x20);
149 sm0.tx().push(0b1100_0000);
150
151 irq0.wait().await;
152 sm0.set_enable(false);
153
154 // takes command sequences (<rs:1> <count:7>, data...)
155 // many side sets are only there to free up a delay bit!
156 let prg = pio_proc::pio_asm!(
157 r#"
158 .origin 27
159 .side_set 1
160
161 .wrap_target
162 pull side 0
163 out x 1 side 0 ; !rs
164 out y 7 side 0 ; #data - 1
165
166 ; rs/rw to e: >= 60ns
167 ; e high time: >= 500ns
168 ; e low time: >= 500ns
169 ; read data valid after e falling: ~5ns
170 ; write data hold after e falling: ~10ns
171
172 loop:
173 pull side 0
174 jmp !x data side 0
175 command:
176 set pins 0b00 side 0
177 jmp shift side 0
178 data:
179 set pins 0b01 side 0
180 shift:
181 out pins 4 side 1 [9]
182 nop side 0 [9]
183 out pins 4 side 1 [9]
184 mov osr null side 0 [7]
185 out pindirs 4 side 0
186 set pins 0b10 side 0
187 busy:
188 nop side 1 [9]
189 jmp pin more side 0 [9]
190 mov osr ~osr side 1 [9]
191 nop side 0 [4]
192 out pindirs 4 side 0
193 jmp y-- loop side 0
194 .wrap
195 more:
196 nop side 1 [9]
197 jmp busy side 0 [9]
198 "#
199 );
200
201 let mut cfg = Config::default();
202 cfg.use_program(&common.load_program(&prg.program), &[&e]);
203 cfg.clock_divider = 8u8.into(); // ~64ns/insn
204 cfg.set_jmp_pin(&db7);
205 cfg.set_set_pins(&[&rs, &rw]);
206 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
207 cfg.shift_out.direction = ShiftDirection::Left;
208 cfg.fifo_join = FifoJoin::TxOnly;
209 sm0.set_config(&cfg);
210
211 sm0.set_enable(true);
212
213 // display on and cursor on and blinking, reset display
214 sm0.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await;
215
216 Self {
217 dma: dma.map_into(),
218 sm: sm0,
219 buf: [0x20; 40],
220 }
221 }
222
223 pub async fn add_line(&mut self, s: &[u8]) {
224 // move cursor to 0:0, prepare 16 characters
225 self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]);
226 // move line 2 up
227 self.buf.copy_within(22..38, 3);
228 // move cursor to 1:0, prepare 16 characters
229 self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]);
230 // file line 2 with spaces
231 self.buf[22..38].fill(0x20);
232 // copy input line
233 let len = s.len().min(16);
234 self.buf[22..22 + len].copy_from_slice(&s[0..len]);
235 // set cursor to 1:15
236 self.buf[38..].copy_from_slice(&[0x80, 0xcf]);
237
238 self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await;
239 }
240}
diff --git a/examples/rp/src/bin/pio_i2s.rs b/examples/rp/src/bin/pio_i2s.rs
index cf60e5b30..447100ddf 100644
--- a/examples/rp/src/bin/pio_i2s.rs
+++ b/examples/rp/src/bin/pio_i2s.rs
@@ -13,10 +13,10 @@
13use core::mem; 13use core::mem;
14 14
15use embassy_executor::Spawner; 15use embassy_executor::Spawner;
16use embassy_rp::bind_interrupts;
16use embassy_rp::peripherals::PIO0; 17use embassy_rp::peripherals::PIO0;
17use embassy_rp::pio::{Config, FifoJoin, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; 18use embassy_rp::pio::{InterruptHandler, Pio};
18use embassy_rp::{bind_interrupts, Peripheral}; 19use embassy_rp::pio_programs::i2s::{PioI2sOut, PioI2sOutProgram};
19use fixed::traits::ToFixed;
20use static_cell::StaticCell; 20use static_cell::StaticCell;
21use {defmt_rtt as _, panic_probe as _}; 21use {defmt_rtt as _, panic_probe as _};
22 22
@@ -25,61 +25,32 @@ bind_interrupts!(struct Irqs {
25}); 25});
26 26
27const SAMPLE_RATE: u32 = 48_000; 27const SAMPLE_RATE: u32 = 48_000;
28const BIT_DEPTH: u32 = 16;
29const CHANNELS: u32 = 2;
28 30
29#[embassy_executor::main] 31#[embassy_executor::main]
30async fn main(_spawner: Spawner) { 32async fn main(_spawner: Spawner) {
31 let mut p = embassy_rp::init(Default::default()); 33 let mut p = embassy_rp::init(Default::default());
32 34
33 // Setup pio state machine for i2s output 35 // Setup pio state machine for i2s output
34 let mut pio = Pio::new(p.PIO0, Irqs); 36 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
35
36 #[rustfmt::skip]
37 let pio_program = pio_proc::pio_asm!(
38 ".side_set 2",
39 " set x, 14 side 0b01", // side 0bWB - W = Word Clock, B = Bit Clock
40 "left_data:",
41 " out pins, 1 side 0b00",
42 " jmp x-- left_data side 0b01",
43 " out pins 1 side 0b10",
44 " set x, 14 side 0b11",
45 "right_data:",
46 " out pins 1 side 0b10",
47 " jmp x-- right_data side 0b11",
48 " out pins 1 side 0b00",
49 );
50 37
51 let bit_clock_pin = p.PIN_18; 38 let bit_clock_pin = p.PIN_18;
52 let left_right_clock_pin = p.PIN_19; 39 let left_right_clock_pin = p.PIN_19;
53 let data_pin = p.PIN_20; 40 let data_pin = p.PIN_20;
54 41
55 let data_pin = pio.common.make_pio_pin(data_pin); 42 let program = PioI2sOutProgram::new(&mut common);
56 let bit_clock_pin = pio.common.make_pio_pin(bit_clock_pin); 43 let mut i2s = PioI2sOut::new(
57 let left_right_clock_pin = pio.common.make_pio_pin(left_right_clock_pin); 44 &mut common,
58 45 sm0,
59 let cfg = { 46 p.DMA_CH0,
60 let mut cfg = Config::default(); 47 data_pin,
61 cfg.use_program( 48 bit_clock_pin,
62 &pio.common.load_program(&pio_program.program), 49 left_right_clock_pin,
63 &[&bit_clock_pin, &left_right_clock_pin], 50 SAMPLE_RATE,
64 ); 51 BIT_DEPTH,
65 cfg.set_out_pins(&[&data_pin]); 52 CHANNELS,
66 const BIT_DEPTH: u32 = 16; 53 &program,
67 const CHANNELS: u32 = 2;
68 let clock_frequency = SAMPLE_RATE * BIT_DEPTH * CHANNELS;
69 cfg.clock_divider = (125_000_000. / clock_frequency as f64 / 2.).to_fixed();
70 cfg.shift_out = ShiftConfig {
71 threshold: 32,
72 direction: ShiftDirection::Left,
73 auto_fill: true,
74 };
75 // join fifos to have twice the time to start the next dma transfer
76 cfg.fifo_join = FifoJoin::TxOnly;
77 cfg
78 };
79 pio.sm0.set_config(&cfg);
80 pio.sm0.set_pin_dirs(
81 embassy_rp::pio::Direction::Out,
82 &[&data_pin, &left_right_clock_pin, &bit_clock_pin],
83 ); 54 );
84 55
85 // create two audio buffers (back and front) which will take turns being 56 // create two audio buffers (back and front) which will take turns being
@@ -90,17 +61,13 @@ async fn main(_spawner: Spawner) {
90 let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE); 61 let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE);
91 62
92 // start pio state machine 63 // start pio state machine
93 pio.sm0.set_enable(true);
94 let tx = pio.sm0.tx();
95 let mut dma_ref = p.DMA_CH0.into_ref();
96
97 let mut fade_value: i32 = 0; 64 let mut fade_value: i32 = 0;
98 let mut phase: i32 = 0; 65 let mut phase: i32 = 0;
99 66
100 loop { 67 loop {
101 // trigger transfer of front buffer data to the pio fifo 68 // trigger transfer of front buffer data to the pio fifo
102 // but don't await the returned future, yet 69 // but don't await the returned future, yet
103 let dma_future = tx.dma_push(dma_ref.reborrow(), front_buffer); 70 let dma_future = i2s.write(front_buffer);
104 71
105 // fade in audio when bootsel is pressed 72 // fade in audio when bootsel is pressed
106 let fade_target = if p.BOOTSEL.is_pressed() { i32::MAX } else { 0 }; 73 let fade_target = if p.BOOTSEL.is_pressed() { i32::MAX } else { 0 };
diff --git a/examples/rp/src/bin/pio_onewire.rs b/examples/rp/src/bin/pio_onewire.rs
index 5076101ec..991510851 100644
--- a/examples/rp/src/bin/pio_onewire.rs
+++ b/examples/rp/src/bin/pio_onewire.rs
@@ -6,7 +6,8 @@ use defmt::*;
6use embassy_executor::Spawner; 6use embassy_executor::Spawner;
7use embassy_rp::bind_interrupts; 7use embassy_rp::bind_interrupts;
8use embassy_rp::peripherals::PIO0; 8use embassy_rp::peripherals::PIO0;
9use embassy_rp::pio::{self, Common, Config, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; 9use embassy_rp::pio::{self, InterruptHandler, Pio};
10use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram};
10use embassy_time::Timer; 11use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _}; 12use {defmt_rtt as _, panic_probe as _};
12 13
@@ -18,7 +19,11 @@ bind_interrupts!(struct Irqs {
18async fn main(_spawner: Spawner) { 19async fn main(_spawner: Spawner) {
19 let p = embassy_rp::init(Default::default()); 20 let p = embassy_rp::init(Default::default());
20 let mut pio = Pio::new(p.PIO0, Irqs); 21 let mut pio = Pio::new(p.PIO0, Irqs);
21 let mut sensor = Ds18b20::new(&mut pio.common, pio.sm0, p.PIN_2); 22
23 let prg = PioOneWireProgram::new(&mut pio.common);
24 let onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg);
25
26 let mut sensor = Ds18b20::new(onewire);
22 27
23 loop { 28 loop {
24 sensor.start().await; // Start a new measurement 29 sensor.start().await; // Start a new measurement
@@ -33,89 +38,12 @@ async fn main(_spawner: Spawner) {
33 38
34/// DS18B20 temperature sensor driver 39/// DS18B20 temperature sensor driver
35pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> { 40pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> {
36 sm: StateMachine<'d, PIO, SM>, 41 wire: PioOneWire<'d, PIO, SM>,
37} 42}
38 43
39impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> { 44impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> {
40 /// Create a new instance the driver 45 pub fn new(wire: PioOneWire<'d, PIO, SM>) -> Self {
41 pub fn new(common: &mut Common<'d, PIO>, mut sm: StateMachine<'d, PIO, SM>, pin: impl PioPin) -> Self { 46 Self { wire }
42 let prg = pio_proc::pio_asm!(
43 r#"
44 .wrap_target
45 again:
46 pull block
47 mov x, osr
48 jmp !x, read
49 write:
50 set pindirs, 1
51 set pins, 0
52 loop1:
53 jmp x--,loop1
54 set pindirs, 0 [31]
55 wait 1 pin 0 [31]
56 pull block
57 mov x, osr
58 bytes1:
59 pull block
60 set y, 7
61 set pindirs, 1
62 bit1:
63 set pins, 0 [1]
64 out pins,1 [31]
65 set pins, 1 [20]
66 jmp y--,bit1
67 jmp x--,bytes1
68 set pindirs, 0 [31]
69 jmp again
70 read:
71 pull block
72 mov x, osr
73 bytes2:
74 set y, 7
75 bit2:
76 set pindirs, 1
77 set pins, 0 [1]
78 set pindirs, 0 [5]
79 in pins,1 [10]
80 jmp y--,bit2
81 jmp x--,bytes2
82 .wrap
83 "#,
84 );
85
86 let pin = common.make_pio_pin(pin);
87 let mut cfg = Config::default();
88 cfg.use_program(&common.load_program(&prg.program), &[]);
89 cfg.set_out_pins(&[&pin]);
90 cfg.set_in_pins(&[&pin]);
91 cfg.set_set_pins(&[&pin]);
92 cfg.shift_in = ShiftConfig {
93 auto_fill: true,
94 direction: ShiftDirection::Right,
95 threshold: 8,
96 };
97 cfg.clock_divider = 255_u8.into();
98 sm.set_config(&cfg);
99 sm.set_enable(true);
100 Self { sm }
101 }
102
103 /// Write bytes over the wire
104 async fn write_bytes(&mut self, bytes: &[u8]) {
105 self.sm.tx().wait_push(250).await;
106 self.sm.tx().wait_push(bytes.len() as u32 - 1).await;
107 for b in bytes {
108 self.sm.tx().wait_push(*b as u32).await;
109 }
110 }
111
112 /// Read bytes from the wire
113 async fn read_bytes(&mut self, bytes: &mut [u8]) {
114 self.sm.tx().wait_push(0).await;
115 self.sm.tx().wait_push(bytes.len() as u32 - 1).await;
116 for b in bytes.iter_mut() {
117 *b = (self.sm.rx().wait_pull().await >> 24) as u8;
118 }
119 } 47 }
120 48
121 /// Calculate CRC8 of the data 49 /// Calculate CRC8 of the data
@@ -139,14 +67,14 @@ impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> {
139 67
140 /// Start a new measurement. Allow at least 1000ms before getting `temperature`. 68 /// Start a new measurement. Allow at least 1000ms before getting `temperature`.
141 pub async fn start(&mut self) { 69 pub async fn start(&mut self) {
142 self.write_bytes(&[0xCC, 0x44]).await; 70 self.wire.write_bytes(&[0xCC, 0x44]).await;
143 } 71 }
144 72
145 /// Read the temperature. Ensure >1000ms has passed since `start` before calling this. 73 /// Read the temperature. Ensure >1000ms has passed since `start` before calling this.
146 pub async fn temperature(&mut self) -> Result<f32, ()> { 74 pub async fn temperature(&mut self) -> Result<f32, ()> {
147 self.write_bytes(&[0xCC, 0xBE]).await; 75 self.wire.write_bytes(&[0xCC, 0xBE]).await;
148 let mut data = [0; 9]; 76 let mut data = [0; 9];
149 self.read_bytes(&mut data).await; 77 self.wire.read_bytes(&mut data).await;
150 match Self::crc8(&data) == 0 { 78 match Self::crc8(&data) == 0 {
151 true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.), 79 true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.),
152 false => Err(()), 80 false => Err(()),
diff --git a/examples/rp/src/bin/pio_pwm.rs b/examples/rp/src/bin/pio_pwm.rs
index 23d63d435..7eabb2289 100644
--- a/examples/rp/src/bin/pio_pwm.rs
+++ b/examples/rp/src/bin/pio_pwm.rs
@@ -5,12 +5,11 @@
5use core::time::Duration; 5use core::time::Duration;
6 6
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_rp::gpio::Level; 8use embassy_rp::bind_interrupts;
9use embassy_rp::peripherals::PIO0; 9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; 10use embassy_rp::pio::{InterruptHandler, Pio};
11use embassy_rp::{bind_interrupts, clocks}; 11use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram};
12use embassy_time::Timer; 12use embassy_time::Timer;
13use pio::InstructionOperands;
14use {defmt_rtt as _, panic_probe as _}; 13use {defmt_rtt as _, panic_probe as _};
15 14
16const REFRESH_INTERVAL: u64 = 20000; 15const REFRESH_INTERVAL: u64 = 20000;
@@ -19,93 +18,14 @@ bind_interrupts!(struct Irqs {
19 PIO0_IRQ_0 => InterruptHandler<PIO0>; 18 PIO0_IRQ_0 => InterruptHandler<PIO0>;
20}); 19});
21 20
22pub fn to_pio_cycles(duration: Duration) -> u32 {
23 (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
24}
25
26pub struct PwmPio<'d, T: Instance, const SM: usize> {
27 sm: StateMachine<'d, T, SM>,
28}
29
30impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
31 pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
32 let prg = pio_proc::pio_asm!(
33 ".side_set 1 opt"
34 "pull noblock side 0"
35 "mov x, osr"
36 "mov y, isr"
37 "countloop:"
38 "jmp x!=y noset"
39 "jmp skip side 1"
40 "noset:"
41 "nop"
42 "skip:"
43 "jmp y-- countloop"
44 );
45
46 pio.load_program(&prg.program);
47 let pin = pio.make_pio_pin(pin);
48 sm.set_pins(Level::High, &[&pin]);
49 sm.set_pin_dirs(Direction::Out, &[&pin]);
50
51 let mut cfg = Config::default();
52 cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
53
54 sm.set_config(&cfg);
55
56 Self { sm }
57 }
58
59 pub fn start(&mut self) {
60 self.sm.set_enable(true);
61 }
62
63 pub fn stop(&mut self) {
64 self.sm.set_enable(false);
65 }
66
67 pub fn set_period(&mut self, duration: Duration) {
68 let is_enabled = self.sm.is_enabled();
69 while !self.sm.tx().empty() {} // Make sure that the queue is empty
70 self.sm.set_enable(false);
71 self.sm.tx().push(to_pio_cycles(duration));
72 unsafe {
73 self.sm.exec_instr(
74 InstructionOperands::PULL {
75 if_empty: false,
76 block: false,
77 }
78 .encode(),
79 );
80 self.sm.exec_instr(
81 InstructionOperands::OUT {
82 destination: ::pio::OutDestination::ISR,
83 bit_count: 32,
84 }
85 .encode(),
86 );
87 };
88 if is_enabled {
89 self.sm.set_enable(true) // Enable if previously enabled
90 }
91 }
92
93 pub fn set_level(&mut self, level: u32) {
94 self.sm.tx().push(level);
95 }
96
97 pub fn write(&mut self, duration: Duration) {
98 self.set_level(to_pio_cycles(duration));
99 }
100}
101
102#[embassy_executor::main] 21#[embassy_executor::main]
103async fn main(_spawner: Spawner) { 22async fn main(_spawner: Spawner) {
104 let p = embassy_rp::init(Default::default()); 23 let p = embassy_rp::init(Default::default());
105 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); 24 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
106 25
107 // Note that PIN_25 is the led pin on the Pico 26 // Note that PIN_25 is the led pin on the Pico
108 let mut pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_25); 27 let prg = PioPwmProgram::new(&mut common);
28 let mut pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_25, &prg);
109 pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL)); 29 pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL));
110 pwm_pio.start(); 30 pwm_pio.start();
111 31
diff --git a/examples/rp/src/bin/pio_rotary_encoder.rs b/examples/rp/src/bin/pio_rotary_encoder.rs
index 58bdadbc0..a7ecc8d0e 100644
--- a/examples/rp/src/bin/pio_rotary_encoder.rs
+++ b/examples/rp/src/bin/pio_rotary_encoder.rs
@@ -5,70 +5,32 @@
5 5
6use defmt::info; 6use defmt::info;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_rp::gpio::Pull;
9use embassy_rp::peripherals::PIO0; 8use embassy_rp::peripherals::PIO0;
10use embassy_rp::{bind_interrupts, pio}; 9use embassy_rp::{
11use fixed::traits::ToFixed; 10 bind_interrupts,
12use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine}; 11 pio::{InterruptHandler, Pio},
12 pio_programs::rotary_encoder::{Direction, PioEncoder, PioEncoderProgram},
13};
13use {defmt_rtt as _, panic_probe as _}; 14use {defmt_rtt as _, panic_probe as _};
14 15
15bind_interrupts!(struct Irqs { 16bind_interrupts!(struct Irqs {
16 PIO0_IRQ_0 => InterruptHandler<PIO0>; 17 PIO0_IRQ_0 => InterruptHandler<PIO0>;
17}); 18});
18 19
19pub struct PioEncoder<'d, T: Instance, const SM: usize> { 20#[embassy_executor::task]
20 sm: StateMachine<'d, T, SM>, 21async fn encoder_0(mut encoder: PioEncoder<'static, PIO0, 0>) {
21} 22 let mut count = 0;
22 23 loop {
23impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> { 24 info!("Count: {}", count);
24 pub fn new( 25 count += match encoder.read().await {
25 pio: &mut Common<'d, T>, 26 Direction::Clockwise => 1,
26 mut sm: StateMachine<'d, T, SM>, 27 Direction::CounterClockwise => -1,
27 pin_a: impl PioPin, 28 };
28 pin_b: impl PioPin,
29 ) -> Self {
30 let mut pin_a = pio.make_pio_pin(pin_a);
31 let mut pin_b = pio.make_pio_pin(pin_b);
32 pin_a.set_pull(Pull::Up);
33 pin_b.set_pull(Pull::Up);
34 sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]);
35
36 let prg = pio_proc::pio_asm!("wait 1 pin 1", "wait 0 pin 1", "in pins, 2", "push",);
37
38 let mut cfg = Config::default();
39 cfg.set_in_pins(&[&pin_a, &pin_b]);
40 cfg.fifo_join = FifoJoin::RxOnly;
41 cfg.shift_in.direction = ShiftDirection::Left;
42 cfg.clock_divider = 10_000.to_fixed();
43 cfg.use_program(&pio.load_program(&prg.program), &[]);
44 sm.set_config(&cfg);
45 sm.set_enable(true);
46 Self { sm }
47 }
48
49 pub async fn read(&mut self) -> Direction {
50 loop {
51 match self.sm.rx().wait_pull().await {
52 0 => return Direction::CounterClockwise,
53 1 => return Direction::Clockwise,
54 _ => {}
55 }
56 }
57 } 29 }
58} 30}
59 31
60pub enum Direction { 32#[embassy_executor::task]
61 Clockwise, 33async fn encoder_1(mut encoder: PioEncoder<'static, PIO0, 1>) {
62 CounterClockwise,
63}
64
65#[embassy_executor::main]
66async fn main(_spawner: Spawner) {
67 let p = embassy_rp::init(Default::default());
68 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
69
70 let mut encoder = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5);
71
72 let mut count = 0; 34 let mut count = 0;
73 loop { 35 loop {
74 info!("Count: {}", count); 36 info!("Count: {}", count);
@@ -78,3 +40,18 @@ async fn main(_spawner: Spawner) {
78 }; 40 };
79 } 41 }
80} 42}
43
44#[embassy_executor::main]
45async fn main(spawner: Spawner) {
46 let p = embassy_rp::init(Default::default());
47 let Pio {
48 mut common, sm0, sm1, ..
49 } = Pio::new(p.PIO0, Irqs);
50
51 let prg = PioEncoderProgram::new(&mut common);
52 let encoder0 = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5, &prg);
53 let encoder1 = PioEncoder::new(&mut common, sm1, p.PIN_6, p.PIN_7, &prg);
54
55 spawner.must_spawn(encoder_0(encoder0));
56 spawner.must_spawn(encoder_1(encoder1));
57}
diff --git a/examples/rp/src/bin/pio_servo.rs b/examples/rp/src/bin/pio_servo.rs
index a79540479..c52ee7492 100644
--- a/examples/rp/src/bin/pio_servo.rs
+++ b/examples/rp/src/bin/pio_servo.rs
@@ -5,12 +5,11 @@
5use core::time::Duration; 5use core::time::Duration;
6 6
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_rp::gpio::Level; 8use embassy_rp::bind_interrupts;
9use embassy_rp::peripherals::PIO0; 9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; 10use embassy_rp::pio::{Instance, InterruptHandler, Pio};
11use embassy_rp::{bind_interrupts, clocks}; 11use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram};
12use embassy_time::Timer; 12use embassy_time::Timer;
13use pio::InstructionOperands;
14use {defmt_rtt as _, panic_probe as _}; 13use {defmt_rtt as _, panic_probe as _};
15 14
16const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo 15const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo
@@ -22,88 +21,8 @@ bind_interrupts!(struct Irqs {
22 PIO0_IRQ_0 => InterruptHandler<PIO0>; 21 PIO0_IRQ_0 => InterruptHandler<PIO0>;
23}); 22});
24 23
25pub fn to_pio_cycles(duration: Duration) -> u32 {
26 (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
27}
28
29pub struct PwmPio<'d, T: Instance, const SM: usize> {
30 sm: StateMachine<'d, T, SM>,
31}
32
33impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
34 pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
35 let prg = pio_proc::pio_asm!(
36 ".side_set 1 opt"
37 "pull noblock side 0"
38 "mov x, osr"
39 "mov y, isr"
40 "countloop:"
41 "jmp x!=y noset"
42 "jmp skip side 1"
43 "noset:"
44 "nop"
45 "skip:"
46 "jmp y-- countloop"
47 );
48
49 pio.load_program(&prg.program);
50 let pin = pio.make_pio_pin(pin);
51 sm.set_pins(Level::High, &[&pin]);
52 sm.set_pin_dirs(Direction::Out, &[&pin]);
53
54 let mut cfg = Config::default();
55 cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
56
57 sm.set_config(&cfg);
58
59 Self { sm }
60 }
61
62 pub fn start(&mut self) {
63 self.sm.set_enable(true);
64 }
65
66 pub fn stop(&mut self) {
67 self.sm.set_enable(false);
68 }
69
70 pub fn set_period(&mut self, duration: Duration) {
71 let is_enabled = self.sm.is_enabled();
72 while !self.sm.tx().empty() {} // Make sure that the queue is empty
73 self.sm.set_enable(false);
74 self.sm.tx().push(to_pio_cycles(duration));
75 unsafe {
76 self.sm.exec_instr(
77 InstructionOperands::PULL {
78 if_empty: false,
79 block: false,
80 }
81 .encode(),
82 );
83 self.sm.exec_instr(
84 InstructionOperands::OUT {
85 destination: ::pio::OutDestination::ISR,
86 bit_count: 32,
87 }
88 .encode(),
89 );
90 };
91 if is_enabled {
92 self.sm.set_enable(true) // Enable if previously enabled
93 }
94 }
95
96 pub fn set_level(&mut self, level: u32) {
97 self.sm.tx().push(level);
98 }
99
100 pub fn write(&mut self, duration: Duration) {
101 self.set_level(to_pio_cycles(duration));
102 }
103}
104
105pub struct ServoBuilder<'d, T: Instance, const SM: usize> { 24pub struct ServoBuilder<'d, T: Instance, const SM: usize> {
106 pwm: PwmPio<'d, T, SM>, 25 pwm: PioPwm<'d, T, SM>,
107 period: Duration, 26 period: Duration,
108 min_pulse_width: Duration, 27 min_pulse_width: Duration,
109 max_pulse_width: Duration, 28 max_pulse_width: Duration,
@@ -111,7 +30,7 @@ pub struct ServoBuilder<'d, T: Instance, const SM: usize> {
111} 30}
112 31
113impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> { 32impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> {
114 pub fn new(pwm: PwmPio<'d, T, SM>) -> Self { 33 pub fn new(pwm: PioPwm<'d, T, SM>) -> Self {
115 Self { 34 Self {
116 pwm, 35 pwm,
117 period: Duration::from_micros(REFRESH_INTERVAL), 36 period: Duration::from_micros(REFRESH_INTERVAL),
@@ -153,7 +72,7 @@ impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> {
153} 72}
154 73
155pub struct Servo<'d, T: Instance, const SM: usize> { 74pub struct Servo<'d, T: Instance, const SM: usize> {
156 pwm: PwmPio<'d, T, SM>, 75 pwm: PioPwm<'d, T, SM>,
157 min_pulse_width: Duration, 76 min_pulse_width: Duration,
158 max_pulse_width: Duration, 77 max_pulse_width: Duration,
159 max_degree_rotation: u64, 78 max_degree_rotation: u64,
@@ -190,7 +109,8 @@ async fn main(_spawner: Spawner) {
190 let p = embassy_rp::init(Default::default()); 109 let p = embassy_rp::init(Default::default());
191 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); 110 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
192 111
193 let pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_1); 112 let prg = PioPwmProgram::new(&mut common);
113 let pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_1, &prg);
194 let mut servo = ServoBuilder::new(pwm_pio) 114 let mut servo = ServoBuilder::new(pwm_pio)
195 .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo 115 .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo
196 .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment. 116 .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment.
diff --git a/examples/rp/src/bin/pio_stepper.rs b/examples/rp/src/bin/pio_stepper.rs
index 6ee45a414..3862c248b 100644
--- a/examples/rp/src/bin/pio_stepper.rs
+++ b/examples/rp/src/bin/pio_stepper.rs
@@ -3,143 +3,20 @@
3 3
4#![no_std] 4#![no_std]
5#![no_main] 5#![no_main]
6use core::mem::{self, MaybeUninit};
7 6
8use defmt::info; 7use defmt::info;
9use embassy_executor::Spawner; 8use embassy_executor::Spawner;
10use embassy_rp::bind_interrupts; 9use embassy_rp::bind_interrupts;
11use embassy_rp::peripherals::PIO0; 10use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Irq, Pio, PioPin, StateMachine}; 11use embassy_rp::pio::{InterruptHandler, Pio};
12use embassy_rp::pio_programs::stepper::{PioStepper, PioStepperProgram};
13use embassy_time::{with_timeout, Duration, Timer}; 13use embassy_time::{with_timeout, Duration, Timer};
14use fixed::traits::ToFixed;
15use fixed::types::extra::U8;
16use fixed::FixedU32;
17use {defmt_rtt as _, panic_probe as _}; 14use {defmt_rtt as _, panic_probe as _};
18 15
19bind_interrupts!(struct Irqs { 16bind_interrupts!(struct Irqs {
20 PIO0_IRQ_0 => InterruptHandler<PIO0>; 17 PIO0_IRQ_0 => InterruptHandler<PIO0>;
21}); 18});
22 19
23pub struct PioStepper<'d, T: Instance, const SM: usize> {
24 irq: Irq<'d, T, SM>,
25 sm: StateMachine<'d, T, SM>,
26}
27
28impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> {
29 pub fn new(
30 pio: &mut Common<'d, T>,
31 mut sm: StateMachine<'d, T, SM>,
32 irq: Irq<'d, T, SM>,
33 pin0: impl PioPin,
34 pin1: impl PioPin,
35 pin2: impl PioPin,
36 pin3: impl PioPin,
37 ) -> Self {
38 let prg = pio_proc::pio_asm!(
39 "pull block",
40 "mov x, osr",
41 "pull block",
42 "mov y, osr",
43 "jmp !x end",
44 "loop:",
45 "jmp !osre step",
46 "mov osr, y",
47 "step:",
48 "out pins, 4 [31]"
49 "jmp x-- loop",
50 "end:",
51 "irq 0 rel"
52 );
53 let pin0 = pio.make_pio_pin(pin0);
54 let pin1 = pio.make_pio_pin(pin1);
55 let pin2 = pio.make_pio_pin(pin2);
56 let pin3 = pio.make_pio_pin(pin3);
57 sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]);
58 let mut cfg = Config::default();
59 cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]);
60 cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed();
61 cfg.use_program(&pio.load_program(&prg.program), &[]);
62 sm.set_config(&cfg);
63 sm.set_enable(true);
64 Self { irq, sm }
65 }
66
67 // Set pulse frequency
68 pub fn set_frequency(&mut self, freq: u32) {
69 let clock_divider: FixedU32<U8> = (125_000_000 / (freq * 136)).to_fixed();
70 assert!(clock_divider <= 65536, "clkdiv must be <= 65536");
71 assert!(clock_divider >= 1, "clkdiv must be >= 1");
72 self.sm.set_clock_divider(clock_divider);
73 self.sm.clkdiv_restart();
74 }
75
76 // Full step, one phase
77 pub async fn step(&mut self, steps: i32) {
78 if steps > 0 {
79 self.run(steps, 0b1000_0100_0010_0001_1000_0100_0010_0001).await
80 } else {
81 self.run(-steps, 0b0001_0010_0100_1000_0001_0010_0100_1000).await
82 }
83 }
84
85 // Full step, two phase
86 pub async fn step2(&mut self, steps: i32) {
87 if steps > 0 {
88 self.run(steps, 0b1001_1100_0110_0011_1001_1100_0110_0011).await
89 } else {
90 self.run(-steps, 0b0011_0110_1100_1001_0011_0110_1100_1001).await
91 }
92 }
93
94 // Half step
95 pub async fn step_half(&mut self, steps: i32) {
96 if steps > 0 {
97 self.run(steps, 0b1001_1000_1100_0100_0110_0010_0011_0001).await
98 } else {
99 self.run(-steps, 0b0001_0011_0010_0110_0100_1100_1000_1001).await
100 }
101 }
102
103 async fn run(&mut self, steps: i32, pattern: u32) {
104 self.sm.tx().wait_push(steps as u32).await;
105 self.sm.tx().wait_push(pattern).await;
106 let drop = OnDrop::new(|| {
107 self.sm.clear_fifos();
108 unsafe {
109 self.sm.exec_instr(
110 pio::InstructionOperands::JMP {
111 address: 0,
112 condition: pio::JmpCondition::Always,
113 }
114 .encode(),
115 );
116 }
117 });
118 self.irq.wait().await;
119 drop.defuse();
120 }
121}
122
123struct OnDrop<F: FnOnce()> {
124 f: MaybeUninit<F>,
125}
126
127impl<F: FnOnce()> OnDrop<F> {
128 pub fn new(f: F) -> Self {
129 Self { f: MaybeUninit::new(f) }
130 }
131
132 pub fn defuse(self) {
133 mem::forget(self)
134 }
135}
136
137impl<F: FnOnce()> Drop for OnDrop<F> {
138 fn drop(&mut self) {
139 unsafe { self.f.as_ptr().read()() }
140 }
141}
142
143#[embassy_executor::main] 20#[embassy_executor::main]
144async fn main(_spawner: Spawner) { 21async fn main(_spawner: Spawner) {
145 let p = embassy_rp::init(Default::default()); 22 let p = embassy_rp::init(Default::default());
@@ -147,14 +24,18 @@ async fn main(_spawner: Spawner) {
147 mut common, irq0, sm0, .. 24 mut common, irq0, sm0, ..
148 } = Pio::new(p.PIO0, Irqs); 25 } = Pio::new(p.PIO0, Irqs);
149 26
150 let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7); 27 let prg = PioStepperProgram::new(&mut common);
28 let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7, &prg);
151 stepper.set_frequency(120); 29 stepper.set_frequency(120);
152 loop { 30 loop {
153 info!("CW full steps"); 31 info!("CW full steps");
154 stepper.step(1000).await; 32 stepper.step(1000).await;
155 33
156 info!("CCW full steps, drop after 1 sec"); 34 info!("CCW full steps, drop after 1 sec");
157 if let Err(_) = with_timeout(Duration::from_secs(1), stepper.step(-i32::MAX)).await { 35 if with_timeout(Duration::from_secs(1), stepper.step(-i32::MAX))
36 .await
37 .is_err()
38 {
158 info!("Time's up!"); 39 info!("Time's up!");
159 Timer::after(Duration::from_secs(1)).await; 40 Timer::after(Duration::from_secs(1)).await;
160 } 41 }
diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs
index 53b696309..b9e01b0ac 100644
--- a/examples/rp/src/bin/pio_uart.rs
+++ b/examples/rp/src/bin/pio_uart.rs
@@ -15,7 +15,8 @@ use embassy_executor::Spawner;
15use embassy_futures::join::{join, join3}; 15use embassy_futures::join::{join, join3};
16use embassy_rp::bind_interrupts; 16use embassy_rp::bind_interrupts;
17use embassy_rp::peripherals::{PIO0, USB}; 17use embassy_rp::peripherals::{PIO0, USB};
18use embassy_rp::pio::InterruptHandler as PioInterruptHandler; 18use embassy_rp::pio;
19use embassy_rp::pio_programs::uart::{PioUartRx, PioUartRxProgram, PioUartTx, PioUartTxProgram};
19use embassy_rp::usb::{Driver, Instance, InterruptHandler}; 20use embassy_rp::usb::{Driver, Instance, InterruptHandler};
20use embassy_sync::blocking_mutex::raw::NoopRawMutex; 21use embassy_sync::blocking_mutex::raw::NoopRawMutex;
21use embassy_sync::pipe::Pipe; 22use embassy_sync::pipe::Pipe;
@@ -25,13 +26,11 @@ use embassy_usb::{Builder, Config};
25use embedded_io_async::{Read, Write}; 26use embedded_io_async::{Read, Write};
26use {defmt_rtt as _, panic_probe as _}; 27use {defmt_rtt as _, panic_probe as _};
27 28
28use crate::uart::PioUart; 29//use crate::uart::PioUart;
29use crate::uart_rx::PioUartRx;
30use crate::uart_tx::PioUartTx;
31 30
32bind_interrupts!(struct Irqs { 31bind_interrupts!(struct Irqs {
33 USBCTRL_IRQ => InterruptHandler<USB>; 32 USBCTRL_IRQ => InterruptHandler<USB>;
34 PIO0_IRQ_0 => PioInterruptHandler<PIO0>; 33 PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;
35}); 34});
36 35
37#[embassy_executor::main] 36#[embassy_executor::main]
@@ -85,8 +84,15 @@ async fn main(_spawner: Spawner) {
85 let usb_fut = usb.run(); 84 let usb_fut = usb.run();
86 85
87 // PIO UART setup 86 // PIO UART setup
88 let uart = PioUart::new(9600, p.PIO0, p.PIN_4, p.PIN_5); 87 let pio::Pio {
89 let (mut uart_tx, mut uart_rx) = uart.split(); 88 mut common, sm0, sm1, ..
89 } = pio::Pio::new(p.PIO0, Irqs);
90
91 let tx_program = PioUartTxProgram::new(&mut common);
92 let mut uart_tx = PioUartTx::new(9600, &mut common, sm0, p.PIN_4, &tx_program);
93
94 let rx_program = PioUartRxProgram::new(&mut common);
95 let mut uart_rx = PioUartRx::new(9600, &mut common, sm1, p.PIN_5, &rx_program);
90 96
91 // Pipe setup 97 // Pipe setup
92 let mut usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new(); 98 let mut usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new();
@@ -163,8 +169,8 @@ async fn usb_write<'d, T: Instance + 'd>(
163} 169}
164 170
165/// Read from the UART and write it to the USB TX pipe 171/// Read from the UART and write it to the USB TX pipe
166async fn uart_read( 172async fn uart_read<PIO: pio::Instance, const SM: usize>(
167 uart_rx: &mut PioUartRx<'_>, 173 uart_rx: &mut PioUartRx<'_, PIO, SM>,
168 usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, 174 usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>,
169) -> ! { 175) -> ! {
170 let mut buf = [0; 64]; 176 let mut buf = [0; 64];
@@ -180,8 +186,8 @@ async fn uart_read(
180} 186}
181 187
182/// Read from the UART TX pipe and write it to the UART 188/// Read from the UART TX pipe and write it to the UART
183async fn uart_write( 189async fn uart_write<PIO: pio::Instance, const SM: usize>(
184 uart_tx: &mut PioUartTx<'_>, 190 uart_tx: &mut PioUartTx<'_, PIO, SM>,
185 uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, 191 uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>,
186) -> ! { 192) -> ! {
187 let mut buf = [0; 64]; 193 let mut buf = [0; 64];
@@ -192,197 +198,3 @@ async fn uart_write(
192 let _ = uart_tx.write(&data).await; 198 let _ = uart_tx.write(&data).await;
193 } 199 }
194} 200}
195
196mod uart {
197 use embassy_rp::peripherals::PIO0;
198 use embassy_rp::pio::{Pio, PioPin};
199 use embassy_rp::Peripheral;
200
201 use crate::uart_rx::PioUartRx;
202 use crate::uart_tx::PioUartTx;
203 use crate::Irqs;
204
205 pub struct PioUart<'a> {
206 tx: PioUartTx<'a>,
207 rx: PioUartRx<'a>,
208 }
209
210 impl<'a> PioUart<'a> {
211 pub fn new(
212 baud: u64,
213 pio: impl Peripheral<P = PIO0> + 'a,
214 tx_pin: impl PioPin,
215 rx_pin: impl PioPin,
216 ) -> PioUart<'a> {
217 let Pio {
218 mut common, sm0, sm1, ..
219 } = Pio::new(pio, Irqs);
220
221 let tx = PioUartTx::new(&mut common, sm0, tx_pin, baud);
222 let rx = PioUartRx::new(&mut common, sm1, rx_pin, baud);
223
224 PioUart { tx, rx }
225 }
226
227 pub fn split(self) -> (PioUartTx<'a>, PioUartRx<'a>) {
228 (self.tx, self.rx)
229 }
230 }
231}
232
233mod uart_tx {
234 use core::convert::Infallible;
235
236 use embassy_rp::gpio::Level;
237 use embassy_rp::peripherals::PIO0;
238 use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine};
239 use embedded_io_async::{ErrorType, Write};
240 use fixed::traits::ToFixed;
241 use fixed_macro::types::U56F8;
242
243 pub struct PioUartTx<'a> {
244 sm_tx: StateMachine<'a, PIO0, 0>,
245 }
246
247 impl<'a> PioUartTx<'a> {
248 pub fn new(
249 common: &mut Common<'a, PIO0>,
250 mut sm_tx: StateMachine<'a, PIO0, 0>,
251 tx_pin: impl PioPin,
252 baud: u64,
253 ) -> Self {
254 let prg = pio_proc::pio_asm!(
255 r#"
256 .side_set 1 opt
257
258 ; An 8n1 UART transmit program.
259 ; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin.
260
261 pull side 1 [7] ; Assert stop bit, or stall with line in idle state
262 set x, 7 side 0 [7] ; Preload bit counter, assert start bit for 8 clocks
263 bitloop: ; This loop will run 8 times (8n1 UART)
264 out pins, 1 ; Shift 1 bit from OSR to the first OUT pin
265 jmp x-- bitloop [6] ; Each loop iteration is 8 cycles.
266 "#
267 );
268 let tx_pin = common.make_pio_pin(tx_pin);
269 sm_tx.set_pins(Level::High, &[&tx_pin]);
270 sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]);
271
272 let mut cfg = Config::default();
273
274 cfg.set_out_pins(&[&tx_pin]);
275 cfg.use_program(&common.load_program(&prg.program), &[&tx_pin]);
276 cfg.shift_out.auto_fill = false;
277 cfg.shift_out.direction = ShiftDirection::Right;
278 cfg.fifo_join = FifoJoin::TxOnly;
279 cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed();
280 sm_tx.set_config(&cfg);
281 sm_tx.set_enable(true);
282
283 Self { sm_tx }
284 }
285
286 pub async fn write_u8(&mut self, data: u8) {
287 self.sm_tx.tx().wait_push(data as u32).await;
288 }
289 }
290
291 impl ErrorType for PioUartTx<'_> {
292 type Error = Infallible;
293 }
294
295 impl Write for PioUartTx<'_> {
296 async fn write(&mut self, buf: &[u8]) -> Result<usize, Infallible> {
297 for byte in buf {
298 self.write_u8(*byte).await;
299 }
300 Ok(buf.len())
301 }
302 }
303}
304
305mod uart_rx {
306 use core::convert::Infallible;
307
308 use embassy_rp::gpio::Level;
309 use embassy_rp::peripherals::PIO0;
310 use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine};
311 use embedded_io_async::{ErrorType, Read};
312 use fixed::traits::ToFixed;
313 use fixed_macro::types::U56F8;
314
315 pub struct PioUartRx<'a> {
316 sm_rx: StateMachine<'a, PIO0, 1>,
317 }
318
319 impl<'a> PioUartRx<'a> {
320 pub fn new(
321 common: &mut Common<'a, PIO0>,
322 mut sm_rx: StateMachine<'a, PIO0, 1>,
323 rx_pin: impl PioPin,
324 baud: u64,
325 ) -> Self {
326 let prg = pio_proc::pio_asm!(
327 r#"
328 ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and
329 ; break conditions more gracefully.
330 ; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.
331
332 start:
333 wait 0 pin 0 ; Stall until start bit is asserted
334 set x, 7 [10] ; Preload bit counter, then delay until halfway through
335 rx_bitloop: ; the first data bit (12 cycles incl wait, set).
336 in pins, 1 ; Shift data bit into ISR
337 jmp x-- rx_bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles
338 jmp pin good_rx_stop ; Check stop bit (should be high)
339
340 irq 4 rel ; Either a framing error or a break. Set a sticky flag,
341 wait 1 pin 0 ; and wait for line to return to idle state.
342 jmp start ; Don't push data if we didn't see good framing.
343
344 good_rx_stop: ; No delay before returning to start; a little slack is
345 in null 24
346 push ; important in case the TX clock is slightly too fast.
347 "#
348 );
349 let mut cfg = Config::default();
350 cfg.use_program(&common.load_program(&prg.program), &[]);
351
352 let rx_pin = common.make_pio_pin(rx_pin);
353 sm_rx.set_pins(Level::High, &[&rx_pin]);
354 cfg.set_in_pins(&[&rx_pin]);
355 cfg.set_jmp_pin(&rx_pin);
356 sm_rx.set_pin_dirs(Direction::In, &[&rx_pin]);
357
358 cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed();
359 cfg.shift_in.auto_fill = false;
360 cfg.shift_in.direction = ShiftDirection::Right;
361 cfg.shift_in.threshold = 32;
362 cfg.fifo_join = FifoJoin::RxOnly;
363 sm_rx.set_config(&cfg);
364 sm_rx.set_enable(true);
365
366 Self { sm_rx }
367 }
368
369 pub async fn read_u8(&mut self) -> u8 {
370 self.sm_rx.rx().wait_pull().await as u8
371 }
372 }
373
374 impl ErrorType for PioUartRx<'_> {
375 type Error = Infallible;
376 }
377
378 impl Read for PioUartRx<'_> {
379 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Infallible> {
380 let mut i = 0;
381 while i < buf.len() {
382 buf[i] = self.read_u8().await;
383 i += 1;
384 }
385 Ok(i)
386 }
387 }
388}
diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs
index ac145933c..d1fcfc471 100644
--- a/examples/rp/src/bin/pio_ws2812.rs
+++ b/examples/rp/src/bin/pio_ws2812.rs
@@ -6,15 +6,11 @@
6 6
7use defmt::*; 7use defmt::*;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_rp::dma::{AnyChannel, Channel}; 9use embassy_rp::bind_interrupts;
10use embassy_rp::peripherals::PIO0; 10use embassy_rp::peripherals::PIO0;
11use embassy_rp::pio::{ 11use embassy_rp::pio::{InterruptHandler, Pio};
12 Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, 12use embassy_rp::pio_programs::ws2812::{PioWs2812, PioWs2812Program};
13}; 13use embassy_time::{Duration, Ticker};
14use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef};
15use embassy_time::{Duration, Ticker, Timer};
16use fixed::types::U24F8;
17use fixed_macro::fixed;
18use smart_leds::RGB8; 14use smart_leds::RGB8;
19use {defmt_rtt as _, panic_probe as _}; 15use {defmt_rtt as _, panic_probe as _};
20 16
@@ -22,96 +18,6 @@ bind_interrupts!(struct Irqs {
22 PIO0_IRQ_0 => InterruptHandler<PIO0>; 18 PIO0_IRQ_0 => InterruptHandler<PIO0>;
23}); 19});
24 20
25pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> {
26 dma: PeripheralRef<'d, AnyChannel>,
27 sm: StateMachine<'d, P, S>,
28}
29
30impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> {
31 pub fn new(
32 pio: &mut Common<'d, P>,
33 mut sm: StateMachine<'d, P, S>,
34 dma: impl Peripheral<P = impl Channel> + 'd,
35 pin: impl PioPin,
36 ) -> Self {
37 into_ref!(dma);
38
39 // Setup sm0
40
41 // prepare the PIO program
42 let side_set = pio::SideSet::new(false, 1, false);
43 let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set);
44
45 const T1: u8 = 2; // start bit
46 const T2: u8 = 5; // data bit
47 const T3: u8 = 3; // stop bit
48 const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32;
49
50 let mut wrap_target = a.label();
51 let mut wrap_source = a.label();
52 let mut do_zero = a.label();
53 a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0);
54 a.bind(&mut wrap_target);
55 // Do stop bit
56 a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0);
57 // Do start bit
58 a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1);
59 // Do data bit = 1
60 a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1);
61 a.bind(&mut do_zero);
62 // Do data bit = 0
63 a.nop_with_delay_and_side_set(T2 - 1, 0);
64 a.bind(&mut wrap_source);
65
66 let prg = a.assemble_with_wrap(wrap_source, wrap_target);
67 let mut cfg = Config::default();
68
69 // Pin config
70 let out_pin = pio.make_pio_pin(pin);
71 cfg.set_out_pins(&[&out_pin]);
72 cfg.set_set_pins(&[&out_pin]);
73
74 cfg.use_program(&pio.load_program(&prg), &[&out_pin]);
75
76 // Clock config, measured in kHz to avoid overflows
77 // TODO CLOCK_FREQ should come from embassy_rp
78 let clock_freq = U24F8::from_num(clocks::clk_sys_freq() / 1000);
79 let ws2812_freq = fixed!(800: U24F8);
80 let bit_freq = ws2812_freq * CYCLES_PER_BIT;
81 cfg.clock_divider = clock_freq / bit_freq;
82
83 // FIFO config
84 cfg.fifo_join = FifoJoin::TxOnly;
85 cfg.shift_out = ShiftConfig {
86 auto_fill: true,
87 threshold: 24,
88 direction: ShiftDirection::Left,
89 };
90
91 sm.set_config(&cfg);
92 sm.set_enable(true);
93
94 Self {
95 dma: dma.map_into(),
96 sm,
97 }
98 }
99
100 pub async fn write(&mut self, colors: &[RGB8; N]) {
101 // Precompute the word bytes from the colors
102 let mut words = [0u32; N];
103 for i in 0..N {
104 let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8);
105 words[i] = word;
106 }
107
108 // DMA transfer
109 self.sm.tx().dma_push(self.dma.reborrow(), &words).await;
110
111 Timer::after_micros(55).await;
112 }
113}
114
115/// Input a value 0 to 255 to get a color value 21/// Input a value 0 to 255 to get a color value
116/// The colours are a transition r - g - b - back to r. 22/// The colours are a transition r - g - b - back to r.
117fn wheel(mut wheel_pos: u8) -> RGB8 { 23fn wheel(mut wheel_pos: u8) -> RGB8 {
@@ -142,7 +48,8 @@ async fn main(_spawner: Spawner) {
142 // Common neopixel pins: 48 // Common neopixel pins:
143 // Thing plus: 8 49 // Thing plus: 8
144 // Adafruit Feather: 16; Adafruit Feather+RFM95: 4 50 // Adafruit Feather: 16; Adafruit Feather+RFM95: 4
145 let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16); 51 let program = PioWs2812Program::new(&mut common);
52 let mut ws2812 = PioWs2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16, &program);
146 53
147 // Loop forever making RGB values and pushing them out to the WS2812. 54 // Loop forever making RGB values and pushing them out to the WS2812.
148 let mut ticker = Ticker::every(Duration::from_millis(10)); 55 let mut ticker = Ticker::every(Duration::from_millis(10));