aboutsummaryrefslogtreecommitdiff
path: root/examples/rp23
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2024-10-13 19:41:57 +0000
committerGitHub <[email protected]>2024-10-13 19:41:57 +0000
commiteea08d761d6cf7052efe96a5a38bdcd82a9498ed (patch)
tree9e1e71032be228d9884981e7fd6ca461e0fe4742 /examples/rp23
parent7b09e886458461c4cf84abf31f854f1f881a7f69 (diff)
parent70bd158d03afa92edb0ce23d05860bde1651cf61 (diff)
Merge pull request #3407 from CBJamo/pio_programs
rp: Move pio programs into embassy-rp
Diffstat (limited to 'examples/rp23')
-rw-r--r--examples/rp23/src/bin/pio_hd44780.rs201
-rw-r--r--examples/rp23/src/bin/pio_i2s.rs78
-rw-r--r--examples/rp23/src/bin/pio_onewire.rs88
-rw-r--r--examples/rp23/src/bin/pio_pwm.rs90
-rw-r--r--examples/rp23/src/bin/pio_rotary_encoder.rs83
-rw-r--r--examples/rp23/src/bin/pio_servo.rs96
-rw-r--r--examples/rp23/src/bin/pio_stepper.rs135
-rw-r--r--examples/rp23/src/bin/pio_uart.rs202
-rw-r--r--examples/rp23/src/bin/pio_ws2812.rs105
9 files changed, 394 insertions, 684 deletions
diff --git a/examples/rp23/src/bin/pio_hd44780.rs b/examples/rp23/src/bin/pio_hd44780.rs
index 5a6d7a9c5..c6f5f6db0 100644
--- a/examples/rp23/src/bin/pio_hd44780.rs
+++ b/examples/rp23/src/bin/pio_hd44780.rs
@@ -7,14 +7,12 @@
7use core::fmt::Write; 7use core::fmt::Write;
8 8
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_rp::bind_interrupts;
10use embassy_rp::block::ImageDef; 11use embassy_rp::block::ImageDef;
11use embassy_rp::dma::{AnyChannel, Channel};
12use embassy_rp::peripherals::PIO0; 12use embassy_rp::peripherals::PIO0;
13use embassy_rp::pio::{ 13use embassy_rp::pio::{InterruptHandler, Pio};
14 Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, 14use embassy_rp::pio_programs::hd44780::{PioHD44780, PioHD44780CommandSequenceProgram, PioHD44780CommandWordProgram};
15};
16use embassy_rp::pwm::{self, Pwm}; 15use embassy_rp::pwm::{self, Pwm};
17use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef};
18use embassy_time::{Instant, Timer}; 16use embassy_time::{Instant, Timer};
19use {defmt_rtt as _, panic_probe as _}; 17use {defmt_rtt as _, panic_probe as _};
20 18
@@ -48,8 +46,27 @@ async fn main(_spawner: Spawner) {
48 c 46 c
49 }); 47 });
50 48
51 let mut hd = HD44780::new( 49 let Pio {
52 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, 50 mut common, sm0, irq0, ..
51 } = Pio::new(p.PIO0, Irqs);
52
53 let word_prg = PioHD44780CommandWordProgram::new(&mut common);
54 let seq_prg = PioHD44780CommandSequenceProgram::new(&mut common);
55
56 let mut hd = PioHD44780::new(
57 &mut common,
58 sm0,
59 irq0,
60 p.DMA_CH3,
61 p.PIN_0,
62 p.PIN_1,
63 p.PIN_2,
64 p.PIN_3,
65 p.PIN_4,
66 p.PIN_5,
67 p.PIN_6,
68 &word_prg,
69 &seq_prg,
53 ) 70 )
54 .await; 71 .await;
55 72
@@ -73,173 +90,3 @@ async fn main(_spawner: Spawner) {
73 Timer::after_secs(1).await; 90 Timer::after_secs(1).await;
74 } 91 }
75} 92}
76
77pub struct HD44780<'l> {
78 dma: PeripheralRef<'l, AnyChannel>,
79 sm: StateMachine<'l, PIO0, 0>,
80
81 buf: [u8; 40],
82}
83
84impl<'l> HD44780<'l> {
85 pub async fn new(
86 pio: impl Peripheral<P = PIO0> + 'l,
87 irq: Irqs,
88 dma: impl Peripheral<P = impl Channel> + 'l,
89 rs: impl PioPin,
90 rw: impl PioPin,
91 e: impl PioPin,
92 db4: impl PioPin,
93 db5: impl PioPin,
94 db6: impl PioPin,
95 db7: impl PioPin,
96 ) -> HD44780<'l> {
97 into_ref!(dma);
98
99 let Pio {
100 mut common,
101 mut irq0,
102 mut sm0,
103 ..
104 } = Pio::new(pio, irq);
105
106 // takes command words (<wait:24> <command:4> <0:4>)
107 let prg = pio_proc::pio_asm!(
108 r#"
109 .side_set 1 opt
110 .origin 20
111
112 loop:
113 out x, 24
114 delay:
115 jmp x--, delay
116 out pins, 4 side 1
117 out null, 4 side 0
118 jmp !osre, loop
119 irq 0
120 "#,
121 );
122
123 let rs = common.make_pio_pin(rs);
124 let rw = common.make_pio_pin(rw);
125 let e = common.make_pio_pin(e);
126 let db4 = common.make_pio_pin(db4);
127 let db5 = common.make_pio_pin(db5);
128 let db6 = common.make_pio_pin(db6);
129 let db7 = common.make_pio_pin(db7);
130
131 sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]);
132
133 let mut cfg = Config::default();
134 cfg.use_program(&common.load_program(&prg.program), &[&e]);
135 cfg.clock_divider = 125u8.into();
136 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
137 cfg.shift_out = ShiftConfig {
138 auto_fill: true,
139 direction: ShiftDirection::Left,
140 threshold: 32,
141 };
142 cfg.fifo_join = FifoJoin::TxOnly;
143 sm0.set_config(&cfg);
144
145 sm0.set_enable(true);
146 // init to 8 bit thrice
147 sm0.tx().push((50000 << 8) | 0x30);
148 sm0.tx().push((5000 << 8) | 0x30);
149 sm0.tx().push((200 << 8) | 0x30);
150 // init 4 bit
151 sm0.tx().push((200 << 8) | 0x20);
152 // set font and lines
153 sm0.tx().push((50 << 8) | 0x20);
154 sm0.tx().push(0b1100_0000);
155
156 irq0.wait().await;
157 sm0.set_enable(false);
158
159 // takes command sequences (<rs:1> <count:7>, data...)
160 // many side sets are only there to free up a delay bit!
161 let prg = pio_proc::pio_asm!(
162 r#"
163 .origin 27
164 .side_set 1
165
166 .wrap_target
167 pull side 0
168 out x 1 side 0 ; !rs
169 out y 7 side 0 ; #data - 1
170
171 ; rs/rw to e: >= 60ns
172 ; e high time: >= 500ns
173 ; e low time: >= 500ns
174 ; read data valid after e falling: ~5ns
175 ; write data hold after e falling: ~10ns
176
177 loop:
178 pull side 0
179 jmp !x data side 0
180 command:
181 set pins 0b00 side 0
182 jmp shift side 0
183 data:
184 set pins 0b01 side 0
185 shift:
186 out pins 4 side 1 [9]
187 nop side 0 [9]
188 out pins 4 side 1 [9]
189 mov osr null side 0 [7]
190 out pindirs 4 side 0
191 set pins 0b10 side 0
192 busy:
193 nop side 1 [9]
194 jmp pin more side 0 [9]
195 mov osr ~osr side 1 [9]
196 nop side 0 [4]
197 out pindirs 4 side 0
198 jmp y-- loop side 0
199 .wrap
200 more:
201 nop side 1 [9]
202 jmp busy side 0 [9]
203 "#
204 );
205
206 let mut cfg = Config::default();
207 cfg.use_program(&common.load_program(&prg.program), &[&e]);
208 cfg.clock_divider = 8u8.into(); // ~64ns/insn
209 cfg.set_jmp_pin(&db7);
210 cfg.set_set_pins(&[&rs, &rw]);
211 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
212 cfg.shift_out.direction = ShiftDirection::Left;
213 cfg.fifo_join = FifoJoin::TxOnly;
214 sm0.set_config(&cfg);
215
216 sm0.set_enable(true);
217
218 // display on and cursor on and blinking, reset display
219 sm0.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await;
220
221 Self {
222 dma: dma.map_into(),
223 sm: sm0,
224 buf: [0x20; 40],
225 }
226 }
227
228 pub async fn add_line(&mut self, s: &[u8]) {
229 // move cursor to 0:0, prepare 16 characters
230 self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]);
231 // move line 2 up
232 self.buf.copy_within(22..38, 3);
233 // move cursor to 1:0, prepare 16 characters
234 self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]);
235 // file line 2 with spaces
236 self.buf[22..38].fill(0x20);
237 // copy input line
238 let len = s.len().min(16);
239 self.buf[22..22 + len].copy_from_slice(&s[0..len]);
240 // set cursor to 1:15
241 self.buf[38..].copy_from_slice(&[0x80, 0xcf]);
242
243 self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await;
244 }
245}
diff --git a/examples/rp23/src/bin/pio_i2s.rs b/examples/rp23/src/bin/pio_i2s.rs
index 46e5eac88..1fd34357b 100644
--- a/examples/rp23/src/bin/pio_i2s.rs
+++ b/examples/rp23/src/bin/pio_i2s.rs
@@ -13,11 +13,12 @@
13use core::mem; 13use core::mem;
14 14
15use embassy_executor::Spawner; 15use embassy_executor::Spawner;
16use embassy_rp::bind_interrupts;
16use embassy_rp::block::ImageDef; 17use embassy_rp::block::ImageDef;
18use embassy_rp::gpio::{Input, Pull};
17use embassy_rp::peripherals::PIO0; 19use embassy_rp::peripherals::PIO0;
18use embassy_rp::pio::{Config, FifoJoin, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; 20use embassy_rp::pio::{InterruptHandler, Pio};
19use embassy_rp::{bind_interrupts, Peripheral}; 21use embassy_rp::pio_programs::i2s::{PioI2sOut, PioI2sOutProgram};
20use fixed::traits::ToFixed;
21use static_cell::StaticCell; 22use static_cell::StaticCell;
22use {defmt_rtt as _, panic_probe as _}; 23use {defmt_rtt as _, panic_probe as _};
23 24
@@ -30,63 +31,36 @@ bind_interrupts!(struct Irqs {
30}); 31});
31 32
32const SAMPLE_RATE: u32 = 48_000; 33const SAMPLE_RATE: u32 = 48_000;
34const BIT_DEPTH: u32 = 16;
35const CHANNELS: u32 = 2;
33 36
34#[embassy_executor::main] 37#[embassy_executor::main]
35async fn main(_spawner: Spawner) { 38async fn main(_spawner: Spawner) {
36 let p = embassy_rp::init(Default::default()); 39 let p = embassy_rp::init(Default::default());
37 40
38 // Setup pio state machine for i2s output 41 // Setup pio state machine for i2s output
39 let mut pio = Pio::new(p.PIO0, Irqs); 42 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
40
41 #[rustfmt::skip]
42 let pio_program = pio_proc::pio_asm!(
43 ".side_set 2",
44 " set x, 14 side 0b01", // side 0bWB - W = Word Clock, B = Bit Clock
45 "left_data:",
46 " out pins, 1 side 0b00",
47 " jmp x-- left_data side 0b01",
48 " out pins 1 side 0b10",
49 " set x, 14 side 0b11",
50 "right_data:",
51 " out pins 1 side 0b10",
52 " jmp x-- right_data side 0b11",
53 " out pins 1 side 0b00",
54 );
55 43
56 let bit_clock_pin = p.PIN_18; 44 let bit_clock_pin = p.PIN_18;
57 let left_right_clock_pin = p.PIN_19; 45 let left_right_clock_pin = p.PIN_19;
58 let data_pin = p.PIN_20; 46 let data_pin = p.PIN_20;
59 47
60 let data_pin = pio.common.make_pio_pin(data_pin); 48 let program = PioI2sOutProgram::new(&mut common);
61 let bit_clock_pin = pio.common.make_pio_pin(bit_clock_pin); 49 let mut i2s = PioI2sOut::new(
62 let left_right_clock_pin = pio.common.make_pio_pin(left_right_clock_pin); 50 &mut common,
63 51 sm0,
64 let cfg = { 52 p.DMA_CH0,
65 let mut cfg = Config::default(); 53 data_pin,
66 cfg.use_program( 54 bit_clock_pin,
67 &pio.common.load_program(&pio_program.program), 55 left_right_clock_pin,
68 &[&bit_clock_pin, &left_right_clock_pin], 56 SAMPLE_RATE,
69 ); 57 BIT_DEPTH,
70 cfg.set_out_pins(&[&data_pin]); 58 CHANNELS,
71 const BIT_DEPTH: u32 = 16; 59 &program,
72 const CHANNELS: u32 = 2;
73 let clock_frequency = SAMPLE_RATE * BIT_DEPTH * CHANNELS;
74 cfg.clock_divider = (125_000_000. / clock_frequency as f64 / 2.).to_fixed();
75 cfg.shift_out = ShiftConfig {
76 threshold: 32,
77 direction: ShiftDirection::Left,
78 auto_fill: true,
79 };
80 // join fifos to have twice the time to start the next dma transfer
81 cfg.fifo_join = FifoJoin::TxOnly;
82 cfg
83 };
84 pio.sm0.set_config(&cfg);
85 pio.sm0.set_pin_dirs(
86 embassy_rp::pio::Direction::Out,
87 &[&data_pin, &left_right_clock_pin, &bit_clock_pin],
88 ); 60 );
89 61
62 let fade_input = Input::new(p.PIN_0, Pull::Up);
63
90 // create two audio buffers (back and front) which will take turns being 64 // create two audio buffers (back and front) which will take turns being
91 // filled with new audio data and being sent to the pio fifo using dma 65 // filled with new audio data and being sent to the pio fifo using dma
92 const BUFFER_SIZE: usize = 960; 66 const BUFFER_SIZE: usize = 960;
@@ -95,20 +69,16 @@ async fn main(_spawner: Spawner) {
95 let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE); 69 let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE);
96 70
97 // start pio state machine 71 // start pio state machine
98 pio.sm0.set_enable(true);
99 let tx = pio.sm0.tx();
100 let mut dma_ref = p.DMA_CH0.into_ref();
101
102 let mut fade_value: i32 = 0; 72 let mut fade_value: i32 = 0;
103 let mut phase: i32 = 0; 73 let mut phase: i32 = 0;
104 74
105 loop { 75 loop {
106 // trigger transfer of front buffer data to the pio fifo 76 // trigger transfer of front buffer data to the pio fifo
107 // but don't await the returned future, yet 77 // but don't await the returned future, yet
108 let dma_future = tx.dma_push(dma_ref.reborrow(), front_buffer); 78 let dma_future = i2s.write(front_buffer);
109 79
110 // fade in audio 80 // fade in audio when bootsel is pressed
111 let fade_target = i32::MAX; 81 let fade_target = if fade_input.is_low() { i32::MAX } else { 0 };
112 82
113 // fill back buffer with fresh audio samples before awaiting the dma future 83 // fill back buffer with fresh audio samples before awaiting the dma future
114 for s in back_buffer.iter_mut() { 84 for s in back_buffer.iter_mut() {
diff --git a/examples/rp23/src/bin/pio_onewire.rs b/examples/rp23/src/bin/pio_onewire.rs
new file mode 100644
index 000000000..7f227d04b
--- /dev/null
+++ b/examples/rp23/src/bin/pio_onewire.rs
@@ -0,0 +1,88 @@
1//! This example shows how you can use PIO to read a `DS18B20` one-wire temperature sensor.
2
3#![no_std]
4#![no_main]
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_rp::bind_interrupts;
8use embassy_rp::block::ImageDef;
9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{self, InterruptHandler, Pio};
11use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram};
12use embassy_time::Timer;
13use {defmt_rtt as _, panic_probe as _};
14
15#[link_section = ".start_block"]
16#[used]
17pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
18
19bind_interrupts!(struct Irqs {
20 PIO0_IRQ_0 => InterruptHandler<PIO0>;
21});
22
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) {
25 let p = embassy_rp::init(Default::default());
26 let mut pio = Pio::new(p.PIO0, Irqs);
27
28 let prg = PioOneWireProgram::new(&mut pio.common);
29 let onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg);
30
31 let mut sensor = Ds18b20::new(onewire);
32
33 loop {
34 sensor.start().await; // Start a new measurement
35 Timer::after_secs(1).await; // Allow 1s for the measurement to finish
36 match sensor.temperature().await {
37 Ok(temp) => info!("temp = {:?} deg C", temp),
38 _ => error!("sensor error"),
39 }
40 Timer::after_secs(1).await;
41 }
42}
43
44/// DS18B20 temperature sensor driver
45pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> {
46 wire: PioOneWire<'d, PIO, SM>,
47}
48
49impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> {
50 pub fn new(wire: PioOneWire<'d, PIO, SM>) -> Self {
51 Self { wire }
52 }
53
54 /// Calculate CRC8 of the data
55 fn crc8(data: &[u8]) -> u8 {
56 let mut temp;
57 let mut data_byte;
58 let mut crc = 0;
59 for b in data {
60 data_byte = *b;
61 for _ in 0..8 {
62 temp = (crc ^ data_byte) & 0x01;
63 crc >>= 1;
64 if temp != 0 {
65 crc ^= 0x8C;
66 }
67 data_byte >>= 1;
68 }
69 }
70 crc
71 }
72
73 /// Start a new measurement. Allow at least 1000ms before getting `temperature`.
74 pub async fn start(&mut self) {
75 self.wire.write_bytes(&[0xCC, 0x44]).await;
76 }
77
78 /// Read the temperature. Ensure >1000ms has passed since `start` before calling this.
79 pub async fn temperature(&mut self) -> Result<f32, ()> {
80 self.wire.write_bytes(&[0xCC, 0xBE]).await;
81 let mut data = [0; 9];
82 self.wire.read_bytes(&mut data).await;
83 match Self::crc8(&data) == 0 {
84 true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.),
85 false => Err(()),
86 }
87 }
88}
diff --git a/examples/rp23/src/bin/pio_pwm.rs b/examples/rp23/src/bin/pio_pwm.rs
index 3cffd213d..11af62a7a 100644
--- a/examples/rp23/src/bin/pio_pwm.rs
+++ b/examples/rp23/src/bin/pio_pwm.rs
@@ -5,13 +5,12 @@
5use core::time::Duration; 5use core::time::Duration;
6 6
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_rp::bind_interrupts;
8use embassy_rp::block::ImageDef; 9use embassy_rp::block::ImageDef;
9use embassy_rp::gpio::Level;
10use embassy_rp::peripherals::PIO0; 10use embassy_rp::peripherals::PIO0;
11use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; 11use embassy_rp::pio::{InterruptHandler, Pio};
12use embassy_rp::{bind_interrupts, clocks}; 12use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram};
13use embassy_time::Timer; 13use embassy_time::Timer;
14use pio::InstructionOperands;
15use {defmt_rtt as _, panic_probe as _}; 14use {defmt_rtt as _, panic_probe as _};
16 15
17#[link_section = ".start_block"] 16#[link_section = ".start_block"]
@@ -24,93 +23,14 @@ bind_interrupts!(struct Irqs {
24 PIO0_IRQ_0 => InterruptHandler<PIO0>; 23 PIO0_IRQ_0 => InterruptHandler<PIO0>;
25}); 24});
26 25
27pub fn to_pio_cycles(duration: Duration) -> u32 {
28 (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
29}
30
31pub struct PwmPio<'d, T: Instance, const SM: usize> {
32 sm: StateMachine<'d, T, SM>,
33}
34
35impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
36 pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
37 let prg = pio_proc::pio_asm!(
38 ".side_set 1 opt"
39 "pull noblock side 0"
40 "mov x, osr"
41 "mov y, isr"
42 "countloop:"
43 "jmp x!=y noset"
44 "jmp skip side 1"
45 "noset:"
46 "nop"
47 "skip:"
48 "jmp y-- countloop"
49 );
50
51 pio.load_program(&prg.program);
52 let pin = pio.make_pio_pin(pin);
53 sm.set_pins(Level::High, &[&pin]);
54 sm.set_pin_dirs(Direction::Out, &[&pin]);
55
56 let mut cfg = Config::default();
57 cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
58
59 sm.set_config(&cfg);
60
61 Self { sm }
62 }
63
64 pub fn start(&mut self) {
65 self.sm.set_enable(true);
66 }
67
68 pub fn stop(&mut self) {
69 self.sm.set_enable(false);
70 }
71
72 pub fn set_period(&mut self, duration: Duration) {
73 let is_enabled = self.sm.is_enabled();
74 while !self.sm.tx().empty() {} // Make sure that the queue is empty
75 self.sm.set_enable(false);
76 self.sm.tx().push(to_pio_cycles(duration));
77 unsafe {
78 self.sm.exec_instr(
79 InstructionOperands::PULL {
80 if_empty: false,
81 block: false,
82 }
83 .encode(),
84 );
85 self.sm.exec_instr(
86 InstructionOperands::OUT {
87 destination: ::pio::OutDestination::ISR,
88 bit_count: 32,
89 }
90 .encode(),
91 );
92 };
93 if is_enabled {
94 self.sm.set_enable(true) // Enable if previously enabled
95 }
96 }
97
98 pub fn set_level(&mut self, level: u32) {
99 self.sm.tx().push(level);
100 }
101
102 pub fn write(&mut self, duration: Duration) {
103 self.set_level(to_pio_cycles(duration));
104 }
105}
106
107#[embassy_executor::main] 26#[embassy_executor::main]
108async fn main(_spawner: Spawner) { 27async fn main(_spawner: Spawner) {
109 let p = embassy_rp::init(Default::default()); 28 let p = embassy_rp::init(Default::default());
110 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); 29 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
111 30
112 // Note that PIN_25 is the led pin on the Pico 31 // Note that PIN_25 is the led pin on the Pico
113 let mut pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_25); 32 let prg = PioPwmProgram::new(&mut common);
33 let mut pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_25, &prg);
114 pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL)); 34 pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL));
115 pwm_pio.start(); 35 pwm_pio.start();
116 36
diff --git a/examples/rp23/src/bin/pio_rotary_encoder.rs b/examples/rp23/src/bin/pio_rotary_encoder.rs
index 9542d63b7..2bb0e67f9 100644
--- a/examples/rp23/src/bin/pio_rotary_encoder.rs
+++ b/examples/rp23/src/bin/pio_rotary_encoder.rs
@@ -5,12 +5,11 @@
5 5
6use defmt::info; 6use defmt::info;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_rp::bind_interrupts;
8use embassy_rp::block::ImageDef; 9use embassy_rp::block::ImageDef;
9use embassy_rp::gpio::Pull;
10use embassy_rp::peripherals::PIO0; 10use embassy_rp::peripherals::PIO0;
11use embassy_rp::{bind_interrupts, pio}; 11use embassy_rp::pio::{InterruptHandler, Pio};
12use fixed::traits::ToFixed; 12use embassy_rp::pio_programs::rotary_encoder::{Direction, PioEncoder, PioEncoderProgram};
13use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine};
14use {defmt_rtt as _, panic_probe as _}; 13use {defmt_rtt as _, panic_probe as _};
15 14
16#[link_section = ".start_block"] 15#[link_section = ".start_block"]
@@ -21,59 +20,20 @@ bind_interrupts!(struct Irqs {
21 PIO0_IRQ_0 => InterruptHandler<PIO0>; 20 PIO0_IRQ_0 => InterruptHandler<PIO0>;
22}); 21});
23 22
24pub struct PioEncoder<'d, T: Instance, const SM: usize> { 23#[embassy_executor::task]
25 sm: StateMachine<'d, T, SM>, 24async fn encoder_0(mut encoder: PioEncoder<'static, PIO0, 0>) {
26} 25 let mut count = 0;
27 26 loop {
28impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> { 27 info!("Count: {}", count);
29 pub fn new( 28 count += match encoder.read().await {
30 pio: &mut Common<'d, T>, 29 Direction::Clockwise => 1,
31 mut sm: StateMachine<'d, T, SM>, 30 Direction::CounterClockwise => -1,
32 pin_a: impl PioPin, 31 };
33 pin_b: impl PioPin,
34 ) -> Self {
35 let mut pin_a = pio.make_pio_pin(pin_a);
36 let mut pin_b = pio.make_pio_pin(pin_b);
37 pin_a.set_pull(Pull::Up);
38 pin_b.set_pull(Pull::Up);
39 sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]);
40
41 let prg = pio_proc::pio_asm!("wait 1 pin 1", "wait 0 pin 1", "in pins, 2", "push",);
42
43 let mut cfg = Config::default();
44 cfg.set_in_pins(&[&pin_a, &pin_b]);
45 cfg.fifo_join = FifoJoin::RxOnly;
46 cfg.shift_in.direction = ShiftDirection::Left;
47 cfg.clock_divider = 10_000.to_fixed();
48 cfg.use_program(&pio.load_program(&prg.program), &[]);
49 sm.set_config(&cfg);
50 sm.set_enable(true);
51 Self { sm }
52 }
53
54 pub async fn read(&mut self) -> Direction {
55 loop {
56 match self.sm.rx().wait_pull().await {
57 0 => return Direction::CounterClockwise,
58 1 => return Direction::Clockwise,
59 _ => {}
60 }
61 }
62 } 32 }
63} 33}
64 34
65pub enum Direction { 35#[embassy_executor::task]
66 Clockwise, 36async fn encoder_1(mut encoder: PioEncoder<'static, PIO0, 1>) {
67 CounterClockwise,
68}
69
70#[embassy_executor::main]
71async fn main(_spawner: Spawner) {
72 let p = embassy_rp::init(Default::default());
73 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
74
75 let mut encoder = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5);
76
77 let mut count = 0; 37 let mut count = 0;
78 loop { 38 loop {
79 info!("Count: {}", count); 39 info!("Count: {}", count);
@@ -83,3 +43,18 @@ async fn main(_spawner: Spawner) {
83 }; 43 };
84 } 44 }
85} 45}
46
47#[embassy_executor::main]
48async fn main(spawner: Spawner) {
49 let p = embassy_rp::init(Default::default());
50 let Pio {
51 mut common, sm0, sm1, ..
52 } = Pio::new(p.PIO0, Irqs);
53
54 let prg = PioEncoderProgram::new(&mut common);
55 let encoder0 = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5, &prg);
56 let encoder1 = PioEncoder::new(&mut common, sm1, p.PIN_6, p.PIN_7, &prg);
57
58 spawner.must_spawn(encoder_0(encoder0));
59 spawner.must_spawn(encoder_1(encoder1));
60}
diff --git a/examples/rp23/src/bin/pio_servo.rs b/examples/rp23/src/bin/pio_servo.rs
index 3202ab475..4e94103f1 100644
--- a/examples/rp23/src/bin/pio_servo.rs
+++ b/examples/rp23/src/bin/pio_servo.rs
@@ -5,13 +5,12 @@
5use core::time::Duration; 5use core::time::Duration;
6 6
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_rp::bind_interrupts;
8use embassy_rp::block::ImageDef; 9use embassy_rp::block::ImageDef;
9use embassy_rp::gpio::Level;
10use embassy_rp::peripherals::PIO0; 10use embassy_rp::peripherals::PIO0;
11use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; 11use embassy_rp::pio::{Instance, InterruptHandler, Pio};
12use embassy_rp::{bind_interrupts, clocks}; 12use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram};
13use embassy_time::Timer; 13use embassy_time::Timer;
14use pio::InstructionOperands;
15use {defmt_rtt as _, panic_probe as _}; 14use {defmt_rtt as _, panic_probe as _};
16 15
17#[link_section = ".start_block"] 16#[link_section = ".start_block"]
@@ -27,88 +26,8 @@ bind_interrupts!(struct Irqs {
27 PIO0_IRQ_0 => InterruptHandler<PIO0>; 26 PIO0_IRQ_0 => InterruptHandler<PIO0>;
28}); 27});
29 28
30pub fn to_pio_cycles(duration: Duration) -> u32 {
31 (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
32}
33
34pub struct PwmPio<'d, T: Instance, const SM: usize> {
35 sm: StateMachine<'d, T, SM>,
36}
37
38impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> {
39 pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self {
40 let prg = pio_proc::pio_asm!(
41 ".side_set 1 opt"
42 "pull noblock side 0"
43 "mov x, osr"
44 "mov y, isr"
45 "countloop:"
46 "jmp x!=y noset"
47 "jmp skip side 1"
48 "noset:"
49 "nop"
50 "skip:"
51 "jmp y-- countloop"
52 );
53
54 pio.load_program(&prg.program);
55 let pin = pio.make_pio_pin(pin);
56 sm.set_pins(Level::High, &[&pin]);
57 sm.set_pin_dirs(Direction::Out, &[&pin]);
58
59 let mut cfg = Config::default();
60 cfg.use_program(&pio.load_program(&prg.program), &[&pin]);
61
62 sm.set_config(&cfg);
63
64 Self { sm }
65 }
66
67 pub fn start(&mut self) {
68 self.sm.set_enable(true);
69 }
70
71 pub fn stop(&mut self) {
72 self.sm.set_enable(false);
73 }
74
75 pub fn set_period(&mut self, duration: Duration) {
76 let is_enabled = self.sm.is_enabled();
77 while !self.sm.tx().empty() {} // Make sure that the queue is empty
78 self.sm.set_enable(false);
79 self.sm.tx().push(to_pio_cycles(duration));
80 unsafe {
81 self.sm.exec_instr(
82 InstructionOperands::PULL {
83 if_empty: false,
84 block: false,
85 }
86 .encode(),
87 );
88 self.sm.exec_instr(
89 InstructionOperands::OUT {
90 destination: ::pio::OutDestination::ISR,
91 bit_count: 32,
92 }
93 .encode(),
94 );
95 };
96 if is_enabled {
97 self.sm.set_enable(true) // Enable if previously enabled
98 }
99 }
100
101 pub fn set_level(&mut self, level: u32) {
102 self.sm.tx().push(level);
103 }
104
105 pub fn write(&mut self, duration: Duration) {
106 self.set_level(to_pio_cycles(duration));
107 }
108}
109
110pub struct ServoBuilder<'d, T: Instance, const SM: usize> { 29pub struct ServoBuilder<'d, T: Instance, const SM: usize> {
111 pwm: PwmPio<'d, T, SM>, 30 pwm: PioPwm<'d, T, SM>,
112 period: Duration, 31 period: Duration,
113 min_pulse_width: Duration, 32 min_pulse_width: Duration,
114 max_pulse_width: Duration, 33 max_pulse_width: Duration,
@@ -116,7 +35,7 @@ pub struct ServoBuilder<'d, T: Instance, const SM: usize> {
116} 35}
117 36
118impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> { 37impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> {
119 pub fn new(pwm: PwmPio<'d, T, SM>) -> Self { 38 pub fn new(pwm: PioPwm<'d, T, SM>) -> Self {
120 Self { 39 Self {
121 pwm, 40 pwm,
122 period: Duration::from_micros(REFRESH_INTERVAL), 41 period: Duration::from_micros(REFRESH_INTERVAL),
@@ -158,7 +77,7 @@ impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> {
158} 77}
159 78
160pub struct Servo<'d, T: Instance, const SM: usize> { 79pub struct Servo<'d, T: Instance, const SM: usize> {
161 pwm: PwmPio<'d, T, SM>, 80 pwm: PioPwm<'d, T, SM>,
162 min_pulse_width: Duration, 81 min_pulse_width: Duration,
163 max_pulse_width: Duration, 82 max_pulse_width: Duration,
164 max_degree_rotation: u64, 83 max_degree_rotation: u64,
@@ -195,7 +114,8 @@ async fn main(_spawner: Spawner) {
195 let p = embassy_rp::init(Default::default()); 114 let p = embassy_rp::init(Default::default());
196 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); 115 let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs);
197 116
198 let pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_1); 117 let prg = PioPwmProgram::new(&mut common);
118 let pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_1, &prg);
199 let mut servo = ServoBuilder::new(pwm_pio) 119 let mut servo = ServoBuilder::new(pwm_pio)
200 .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo 120 .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo
201 .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment. 121 .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment.
diff --git a/examples/rp23/src/bin/pio_stepper.rs b/examples/rp23/src/bin/pio_stepper.rs
index 5e87da6eb..4fabe78ca 100644
--- a/examples/rp23/src/bin/pio_stepper.rs
+++ b/examples/rp23/src/bin/pio_stepper.rs
@@ -3,18 +3,15 @@
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::block::ImageDef; 10use embassy_rp::block::ImageDef;
12use embassy_rp::peripherals::PIO0; 11use embassy_rp::peripherals::PIO0;
13use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Irq, Pio, PioPin, StateMachine}; 12use embassy_rp::pio::{InterruptHandler, Pio};
13use embassy_rp::pio_programs::stepper::{PioStepper, PioStepperProgram};
14use embassy_time::{with_timeout, Duration, Timer}; 14use embassy_time::{with_timeout, Duration, Timer};
15use fixed::traits::ToFixed;
16use fixed::types::extra::U8;
17use fixed::FixedU32;
18use {defmt_rtt as _, panic_probe as _}; 15use {defmt_rtt as _, panic_probe as _};
19 16
20#[link_section = ".start_block"] 17#[link_section = ".start_block"]
@@ -25,126 +22,6 @@ bind_interrupts!(struct Irqs {
25 PIO0_IRQ_0 => InterruptHandler<PIO0>; 22 PIO0_IRQ_0 => InterruptHandler<PIO0>;
26}); 23});
27 24
28pub struct PioStepper<'d, T: Instance, const SM: usize> {
29 irq: Irq<'d, T, SM>,
30 sm: StateMachine<'d, T, SM>,
31}
32
33impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> {
34 pub fn new(
35 pio: &mut Common<'d, T>,
36 mut sm: StateMachine<'d, T, SM>,
37 irq: Irq<'d, T, SM>,
38 pin0: impl PioPin,
39 pin1: impl PioPin,
40 pin2: impl PioPin,
41 pin3: impl PioPin,
42 ) -> Self {
43 let prg = pio_proc::pio_asm!(
44 "pull block",
45 "mov x, osr",
46 "pull block",
47 "mov y, osr",
48 "jmp !x end",
49 "loop:",
50 "jmp !osre step",
51 "mov osr, y",
52 "step:",
53 "out pins, 4 [31]"
54 "jmp x-- loop",
55 "end:",
56 "irq 0 rel"
57 );
58 let pin0 = pio.make_pio_pin(pin0);
59 let pin1 = pio.make_pio_pin(pin1);
60 let pin2 = pio.make_pio_pin(pin2);
61 let pin3 = pio.make_pio_pin(pin3);
62 sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]);
63 let mut cfg = Config::default();
64 cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]);
65 cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed();
66 cfg.use_program(&pio.load_program(&prg.program), &[]);
67 sm.set_config(&cfg);
68 sm.set_enable(true);
69 Self { irq, sm }
70 }
71
72 // Set pulse frequency
73 pub fn set_frequency(&mut self, freq: u32) {
74 let clock_divider: FixedU32<U8> = (125_000_000 / (freq * 136)).to_fixed();
75 assert!(clock_divider <= 65536, "clkdiv must be <= 65536");
76 assert!(clock_divider >= 1, "clkdiv must be >= 1");
77 self.sm.set_clock_divider(clock_divider);
78 self.sm.clkdiv_restart();
79 }
80
81 // Full step, one phase
82 pub async fn step(&mut self, steps: i32) {
83 if steps > 0 {
84 self.run(steps, 0b1000_0100_0010_0001_1000_0100_0010_0001).await
85 } else {
86 self.run(-steps, 0b0001_0010_0100_1000_0001_0010_0100_1000).await
87 }
88 }
89
90 // Full step, two phase
91 pub async fn step2(&mut self, steps: i32) {
92 if steps > 0 {
93 self.run(steps, 0b1001_1100_0110_0011_1001_1100_0110_0011).await
94 } else {
95 self.run(-steps, 0b0011_0110_1100_1001_0011_0110_1100_1001).await
96 }
97 }
98
99 // Half step
100 pub async fn step_half(&mut self, steps: i32) {
101 if steps > 0 {
102 self.run(steps, 0b1001_1000_1100_0100_0110_0010_0011_0001).await
103 } else {
104 self.run(-steps, 0b0001_0011_0010_0110_0100_1100_1000_1001).await
105 }
106 }
107
108 async fn run(&mut self, steps: i32, pattern: u32) {
109 self.sm.tx().wait_push(steps as u32).await;
110 self.sm.tx().wait_push(pattern).await;
111 let drop = OnDrop::new(|| {
112 self.sm.clear_fifos();
113 unsafe {
114 self.sm.exec_instr(
115 pio::InstructionOperands::JMP {
116 address: 0,
117 condition: pio::JmpCondition::Always,
118 }
119 .encode(),
120 );
121 }
122 });
123 self.irq.wait().await;
124 drop.defuse();
125 }
126}
127
128struct OnDrop<F: FnOnce()> {
129 f: MaybeUninit<F>,
130}
131
132impl<F: FnOnce()> OnDrop<F> {
133 pub fn new(f: F) -> Self {
134 Self { f: MaybeUninit::new(f) }
135 }
136
137 pub fn defuse(self) {
138 mem::forget(self)
139 }
140}
141
142impl<F: FnOnce()> Drop for OnDrop<F> {
143 fn drop(&mut self) {
144 unsafe { self.f.as_ptr().read()() }
145 }
146}
147
148#[embassy_executor::main] 25#[embassy_executor::main]
149async fn main(_spawner: Spawner) { 26async fn main(_spawner: Spawner) {
150 let p = embassy_rp::init(Default::default()); 27 let p = embassy_rp::init(Default::default());
@@ -152,14 +29,18 @@ async fn main(_spawner: Spawner) {
152 mut common, irq0, sm0, .. 29 mut common, irq0, sm0, ..
153 } = Pio::new(p.PIO0, Irqs); 30 } = Pio::new(p.PIO0, Irqs);
154 31
155 let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7); 32 let prg = PioStepperProgram::new(&mut common);
33 let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7, &prg);
156 stepper.set_frequency(120); 34 stepper.set_frequency(120);
157 loop { 35 loop {
158 info!("CW full steps"); 36 info!("CW full steps");
159 stepper.step(1000).await; 37 stepper.step(1000).await;
160 38
161 info!("CCW full steps, drop after 1 sec"); 39 info!("CCW full steps, drop after 1 sec");
162 if let Err(_) = with_timeout(Duration::from_secs(1), stepper.step(i32::MIN)).await { 40 if with_timeout(Duration::from_secs(1), stepper.step(-i32::MAX))
41 .await
42 .is_err()
43 {
163 info!("Time's up!"); 44 info!("Time's up!");
164 Timer::after(Duration::from_secs(1)).await; 45 Timer::after(Duration::from_secs(1)).await;
165 } 46 }
diff --git a/examples/rp23/src/bin/pio_uart.rs b/examples/rp23/src/bin/pio_uart.rs
new file mode 100644
index 000000000..f8398c22a
--- /dev/null
+++ b/examples/rp23/src/bin/pio_uart.rs
@@ -0,0 +1,202 @@
1//! This example shows how to use the PIO module in the RP2040 chip to implement a duplex UART.
2//! The PIO module is a very powerful peripheral that can be used to implement many different
3//! protocols. It is a very flexible state machine that can be programmed to do almost anything.
4//!
5//! This example opens up a USB device that implements a CDC ACM serial port. It then uses the
6//! PIO module to implement a UART that is connected to the USB serial port. This allows you to
7//! communicate with a device connected to the RP2040 over USB serial.
8
9#![no_std]
10#![no_main]
11#![allow(async_fn_in_trait)]
12
13use defmt::{info, panic, trace};
14use embassy_executor::Spawner;
15use embassy_futures::join::{join, join3};
16use embassy_rp::block::ImageDef;
17use embassy_rp::peripherals::{PIO0, USB};
18use embassy_rp::pio_programs::uart::{PioUartRx, PioUartRxProgram, PioUartTx, PioUartTxProgram};
19use embassy_rp::usb::{Driver, Instance, InterruptHandler};
20use embassy_rp::{bind_interrupts, pio};
21use embassy_sync::blocking_mutex::raw::NoopRawMutex;
22use embassy_sync::pipe::Pipe;
23use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State};
24use embassy_usb::driver::EndpointError;
25use embassy_usb::{Builder, Config};
26use embedded_io_async::{Read, Write};
27use {defmt_rtt as _, panic_probe as _};
28
29#[link_section = ".start_block"]
30#[used]
31pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
32
33bind_interrupts!(struct Irqs {
34 USBCTRL_IRQ => InterruptHandler<USB>;
35 PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;
36});
37
38#[embassy_executor::main]
39async fn main(_spawner: Spawner) {
40 info!("Hello there!");
41
42 let p = embassy_rp::init(Default::default());
43
44 // Create the driver, from the HAL.
45 let driver = Driver::new(p.USB, Irqs);
46
47 // Create embassy-usb Config
48 let mut config = Config::new(0xc0de, 0xcafe);
49 config.manufacturer = Some("Embassy");
50 config.product = Some("PIO UART example");
51 config.serial_number = Some("12345678");
52 config.max_power = 100;
53 config.max_packet_size_0 = 64;
54
55 // Required for windows compatibility.
56 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
57 config.device_class = 0xEF;
58 config.device_sub_class = 0x02;
59 config.device_protocol = 0x01;
60 config.composite_with_iads = true;
61
62 // Create embassy-usb DeviceBuilder using the driver and config.
63 // It needs some buffers for building the descriptors.
64 let mut config_descriptor = [0; 256];
65 let mut bos_descriptor = [0; 256];
66 let mut control_buf = [0; 64];
67
68 let mut state = State::new();
69
70 let mut builder = Builder::new(
71 driver,
72 config,
73 &mut config_descriptor,
74 &mut bos_descriptor,
75 &mut [], // no msos descriptors
76 &mut control_buf,
77 );
78
79 // Create classes on the builder.
80 let class = CdcAcmClass::new(&mut builder, &mut state, 64);
81
82 // Build the builder.
83 let mut usb = builder.build();
84
85 // Run the USB device.
86 let usb_fut = usb.run();
87
88 // PIO UART setup
89 let pio::Pio {
90 mut common, sm0, sm1, ..
91 } = pio::Pio::new(p.PIO0, Irqs);
92
93 let tx_program = PioUartTxProgram::new(&mut common);
94 let mut uart_tx = PioUartTx::new(9600, &mut common, sm0, p.PIN_4, &tx_program);
95
96 let rx_program = PioUartRxProgram::new(&mut common);
97 let mut uart_rx = PioUartRx::new(9600, &mut common, sm1, p.PIN_5, &rx_program);
98
99 // Pipe setup
100 let mut usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new();
101 let (mut usb_pipe_reader, mut usb_pipe_writer) = usb_pipe.split();
102
103 let mut uart_pipe: Pipe<NoopRawMutex, 20> = Pipe::new();
104 let (mut uart_pipe_reader, mut uart_pipe_writer) = uart_pipe.split();
105
106 let (mut usb_tx, mut usb_rx) = class.split();
107
108 // Read + write from USB
109 let usb_future = async {
110 loop {
111 info!("Wait for USB connection");
112 usb_rx.wait_connection().await;
113 info!("Connected");
114 let _ = join(
115 usb_read(&mut usb_rx, &mut uart_pipe_writer),
116 usb_write(&mut usb_tx, &mut usb_pipe_reader),
117 )
118 .await;
119 info!("Disconnected");
120 }
121 };
122
123 // Read + write from UART
124 let uart_future = join(
125 uart_read(&mut uart_rx, &mut usb_pipe_writer),
126 uart_write(&mut uart_tx, &mut uart_pipe_reader),
127 );
128
129 // Run everything concurrently.
130 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
131 join3(usb_fut, usb_future, uart_future).await;
132}
133
134struct Disconnected {}
135
136impl From<EndpointError> for Disconnected {
137 fn from(val: EndpointError) -> Self {
138 match val {
139 EndpointError::BufferOverflow => panic!("Buffer overflow"),
140 EndpointError::Disabled => Disconnected {},
141 }
142 }
143}
144
145/// Read from the USB and write it to the UART TX pipe
146async fn usb_read<'d, T: Instance + 'd>(
147 usb_rx: &mut Receiver<'d, Driver<'d, T>>,
148 uart_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>,
149) -> Result<(), Disconnected> {
150 let mut buf = [0; 64];
151 loop {
152 let n = usb_rx.read_packet(&mut buf).await?;
153 let data = &buf[..n];
154 trace!("USB IN: {:x}", data);
155 (*uart_pipe_writer).write(data).await;
156 }
157}
158
159/// Read from the USB TX pipe and write it to the USB
160async fn usb_write<'d, T: Instance + 'd>(
161 usb_tx: &mut Sender<'d, Driver<'d, T>>,
162 usb_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>,
163) -> Result<(), Disconnected> {
164 let mut buf = [0; 64];
165 loop {
166 let n = (*usb_pipe_reader).read(&mut buf).await;
167 let data = &buf[..n];
168 trace!("USB OUT: {:x}", data);
169 usb_tx.write_packet(&data).await?;
170 }
171}
172
173/// Read from the UART and write it to the USB TX pipe
174async fn uart_read<PIO: pio::Instance, const SM: usize>(
175 uart_rx: &mut PioUartRx<'_, PIO, SM>,
176 usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>,
177) -> ! {
178 let mut buf = [0; 64];
179 loop {
180 let n = uart_rx.read(&mut buf).await.expect("UART read error");
181 if n == 0 {
182 continue;
183 }
184 let data = &buf[..n];
185 trace!("UART IN: {:x}", buf);
186 (*usb_pipe_writer).write(data).await;
187 }
188}
189
190/// Read from the UART TX pipe and write it to the UART
191async fn uart_write<PIO: pio::Instance, const SM: usize>(
192 uart_tx: &mut PioUartTx<'_, PIO, SM>,
193 uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>,
194) -> ! {
195 let mut buf = [0; 64];
196 loop {
197 let n = (*uart_pipe_reader).read(&mut buf).await;
198 let data = &buf[..n];
199 trace!("UART OUT: {:x}", data);
200 let _ = uart_tx.write(&data).await;
201 }
202}
diff --git a/examples/rp23/src/bin/pio_ws2812.rs b/examples/rp23/src/bin/pio_ws2812.rs
index 1f1984c4d..4d258234e 100644
--- a/examples/rp23/src/bin/pio_ws2812.rs
+++ b/examples/rp23/src/bin/pio_ws2812.rs
@@ -6,16 +6,12 @@
6 6
7use defmt::*; 7use defmt::*;
8use embassy_executor::Spawner; 8use embassy_executor::Spawner;
9use embassy_rp::bind_interrupts;
9use embassy_rp::block::ImageDef; 10use embassy_rp::block::ImageDef;
10use embassy_rp::dma::{AnyChannel, Channel};
11use embassy_rp::peripherals::PIO0; 11use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{ 12use embassy_rp::pio::{InterruptHandler, Pio};
13 Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, 13use embassy_rp::pio_programs::ws2812::{PioWs2812, PioWs2812Program};
14}; 14use embassy_time::{Duration, Ticker};
15use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef};
16use embassy_time::{Duration, Ticker, Timer};
17use fixed::types::U24F8;
18use fixed_macro::fixed;
19use smart_leds::RGB8; 15use smart_leds::RGB8;
20use {defmt_rtt as _, panic_probe as _}; 16use {defmt_rtt as _, panic_probe as _};
21 17
@@ -27,96 +23,6 @@ bind_interrupts!(struct Irqs {
27 PIO0_IRQ_0 => InterruptHandler<PIO0>; 23 PIO0_IRQ_0 => InterruptHandler<PIO0>;
28}); 24});
29 25
30pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> {
31 dma: PeripheralRef<'d, AnyChannel>,
32 sm: StateMachine<'d, P, S>,
33}
34
35impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> {
36 pub fn new(
37 pio: &mut Common<'d, P>,
38 mut sm: StateMachine<'d, P, S>,
39 dma: impl Peripheral<P = impl Channel> + 'd,
40 pin: impl PioPin,
41 ) -> Self {
42 into_ref!(dma);
43
44 // Setup sm0
45
46 // prepare the PIO program
47 let side_set = pio::SideSet::new(false, 1, false);
48 let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set);
49
50 const T1: u8 = 2; // start bit
51 const T2: u8 = 5; // data bit
52 const T3: u8 = 3; // stop bit
53 const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32;
54
55 let mut wrap_target = a.label();
56 let mut wrap_source = a.label();
57 let mut do_zero = a.label();
58 a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0);
59 a.bind(&mut wrap_target);
60 // Do stop bit
61 a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0);
62 // Do start bit
63 a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1);
64 // Do data bit = 1
65 a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1);
66 a.bind(&mut do_zero);
67 // Do data bit = 0
68 a.nop_with_delay_and_side_set(T2 - 1, 0);
69 a.bind(&mut wrap_source);
70
71 let prg = a.assemble_with_wrap(wrap_source, wrap_target);
72 let mut cfg = Config::default();
73
74 // Pin config
75 let out_pin = pio.make_pio_pin(pin);
76 cfg.set_out_pins(&[&out_pin]);
77 cfg.set_set_pins(&[&out_pin]);
78
79 cfg.use_program(&pio.load_program(&prg), &[&out_pin]);
80
81 // Clock config, measured in kHz to avoid overflows
82 // TODO CLOCK_FREQ should come from embassy_rp
83 let clock_freq = U24F8::from_num(clocks::clk_sys_freq() / 1000);
84 let ws2812_freq = fixed!(800: U24F8);
85 let bit_freq = ws2812_freq * CYCLES_PER_BIT;
86 cfg.clock_divider = clock_freq / bit_freq;
87
88 // FIFO config
89 cfg.fifo_join = FifoJoin::TxOnly;
90 cfg.shift_out = ShiftConfig {
91 auto_fill: true,
92 threshold: 24,
93 direction: ShiftDirection::Left,
94 };
95
96 sm.set_config(&cfg);
97 sm.set_enable(true);
98
99 Self {
100 dma: dma.map_into(),
101 sm,
102 }
103 }
104
105 pub async fn write(&mut self, colors: &[RGB8; N]) {
106 // Precompute the word bytes from the colors
107 let mut words = [0u32; N];
108 for i in 0..N {
109 let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8);
110 words[i] = word;
111 }
112
113 // DMA transfer
114 self.sm.tx().dma_push(self.dma.reborrow(), &words).await;
115
116 Timer::after_micros(55).await;
117 }
118}
119
120/// Input a value 0 to 255 to get a color value 26/// Input a value 0 to 255 to get a color value
121/// The colours are a transition r - g - b - back to r. 27/// The colours are a transition r - g - b - back to r.
122fn wheel(mut wheel_pos: u8) -> RGB8 { 28fn wheel(mut wheel_pos: u8) -> RGB8 {
@@ -147,7 +53,8 @@ async fn main(_spawner: Spawner) {
147 // Common neopixel pins: 53 // Common neopixel pins:
148 // Thing plus: 8 54 // Thing plus: 8
149 // Adafruit Feather: 16; Adafruit Feather+RFM95: 4 55 // Adafruit Feather: 16; Adafruit Feather+RFM95: 4
150 let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16); 56 let program = PioWs2812Program::new(&mut common);
57 let mut ws2812 = PioWs2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16, &program);
151 58
152 // Loop forever making RGB values and pushing them out to the WS2812. 59 // Loop forever making RGB values and pushing them out to the WS2812.
153 let mut ticker = Ticker::every(Duration::from_millis(10)); 60 let mut ticker = Ticker::every(Duration::from_millis(10));