aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-rp/Cargo.toml1
-rw-r--r--embassy-rp/src/lib.rs1
-rw-r--r--embassy-rp/src/pio_programs/hd44780.rs203
-rw-r--r--embassy-rp/src/pio_programs/i2s.rs95
-rw-r--r--embassy-rp/src/pio_programs/mod.rs10
-rw-r--r--embassy-rp/src/pio_programs/onewire.rs109
-rw-r--r--embassy-rp/src/pio_programs/pwm.rs121
-rw-r--r--embassy-rp/src/pio_programs/rotary_encoder.rs73
-rw-r--r--embassy-rp/src/pio_programs/stepper.rs147
-rw-r--r--embassy-rp/src/pio_programs/uart.rs185
-rw-r--r--embassy-rp/src/pio_programs/ws2812.rs118
-rw-r--r--examples/rp/Cargo.toml2
-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.rs83
-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.rs223
-rw-r--r--examples/rp/src/bin/pio_ws2812.rs105
-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
30 files changed, 1587 insertions, 1658 deletions
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml
index 29a8a3c53..54de238b3 100644
--- a/embassy-rp/Cargo.toml
+++ b/embassy-rp/Cargo.toml
@@ -144,6 +144,7 @@ rp2040-boot2 = "0.3"
144document-features = "0.2.7" 144document-features = "0.2.7"
145sha2-const-stable = "0.1" 145sha2-const-stable = "0.1"
146rp-binary-info = { version = "0.1.0", optional = true } 146rp-binary-info = { version = "0.1.0", optional = true }
147smart-leds = "0.4.0"
147 148
148[dev-dependencies] 149[dev-dependencies]
149embassy-executor = { version = "0.6.0", path = "../embassy-executor", features = ["arch-std", "executor-thread"] } 150embassy-executor = { version = "0.6.0", path = "../embassy-executor", features = ["arch-std", "executor-thread"] }
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index d402cf793..7ac18c1f8 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -34,6 +34,7 @@ pub mod i2c_slave;
34pub mod multicore; 34pub mod multicore;
35#[cfg(feature = "_rp235x")] 35#[cfg(feature = "_rp235x")]
36pub mod otp; 36pub mod otp;
37pub mod pio_programs;
37pub mod pwm; 38pub mod pwm;
38mod reset; 39mod reset;
39pub mod rom_data; 40pub mod rom_data;
diff --git a/embassy-rp/src/pio_programs/hd44780.rs b/embassy-rp/src/pio_programs/hd44780.rs
new file mode 100644
index 000000000..9bbf44fc4
--- /dev/null
+++ b/embassy-rp/src/pio_programs/hd44780.rs
@@ -0,0 +1,203 @@
1//! [HD44780 display driver](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf)
2
3use crate::dma::{AnyChannel, Channel};
4use crate::pio::{
5 Common, Config, Direction, FifoJoin, Instance, Irq, LoadedProgram, PioPin, ShiftConfig, ShiftDirection,
6 StateMachine,
7};
8use crate::{into_ref, Peripheral, PeripheralRef};
9
10/// This struct represents a HD44780 program that takes command words (<wait:24> <command:4> <0:4>)
11pub struct PioHD44780CommandWordProgram<'a, PIO: Instance> {
12 prg: LoadedProgram<'a, PIO>,
13}
14
15impl<'a, PIO: Instance> PioHD44780CommandWordProgram<'a, PIO> {
16 /// Load the program into the given pio
17 pub fn new(common: &mut Common<'a, PIO>) -> Self {
18 let prg = pio_proc::pio_asm!(
19 r#"
20 .side_set 1 opt
21 .origin 20
22
23 loop:
24 out x, 24
25 delay:
26 jmp x--, delay
27 out pins, 4 side 1
28 out null, 4 side 0
29 jmp !osre, loop
30 irq 0
31 "#,
32 );
33
34 let prg = common.load_program(&prg.program);
35
36 Self { prg }
37 }
38}
39
40/// This struct represents a HD44780 program that takes command sequences (<rs:1> <count:7>, data...)
41pub struct PioHD44780CommandSequenceProgram<'a, PIO: Instance> {
42 prg: LoadedProgram<'a, PIO>,
43}
44
45impl<'a, PIO: Instance> PioHD44780CommandSequenceProgram<'a, PIO> {
46 /// Load the program into the given pio
47 pub fn new(common: &mut Common<'a, PIO>) -> Self {
48 // many side sets are only there to free up a delay bit!
49 let prg = pio_proc::pio_asm!(
50 r#"
51 .origin 27
52 .side_set 1
53
54 .wrap_target
55 pull side 0
56 out x 1 side 0 ; !rs
57 out y 7 side 0 ; #data - 1
58
59 ; rs/rw to e: >= 60ns
60 ; e high time: >= 500ns
61 ; e low time: >= 500ns
62 ; read data valid after e falling: ~5ns
63 ; write data hold after e falling: ~10ns
64
65 loop:
66 pull side 0
67 jmp !x data side 0
68 command:
69 set pins 0b00 side 0
70 jmp shift side 0
71 data:
72 set pins 0b01 side 0
73 shift:
74 out pins 4 side 1 [9]
75 nop side 0 [9]
76 out pins 4 side 1 [9]
77 mov osr null side 0 [7]
78 out pindirs 4 side 0
79 set pins 0b10 side 0
80 busy:
81 nop side 1 [9]
82 jmp pin more side 0 [9]
83 mov osr ~osr side 1 [9]
84 nop side 0 [4]
85 out pindirs 4 side 0
86 jmp y-- loop side 0
87 .wrap
88 more:
89 nop side 1 [9]
90 jmp busy side 0 [9]
91 "#
92 );
93
94 let prg = common.load_program(&prg.program);
95
96 Self { prg }
97 }
98}
99
100/// Pio backed HD44780 driver
101pub struct PioHD44780<'l, P: Instance, const S: usize> {
102 dma: PeripheralRef<'l, AnyChannel>,
103 sm: StateMachine<'l, P, S>,
104
105 buf: [u8; 40],
106}
107
108impl<'l, P: Instance, const S: usize> PioHD44780<'l, P, S> {
109 /// Configure the given state machine to first init, then write data to, a HD44780 display.
110 pub async fn new(
111 common: &mut Common<'l, P>,
112 mut sm: StateMachine<'l, P, S>,
113 mut irq: Irq<'l, P, S>,
114 dma: impl Peripheral<P = impl Channel> + 'l,
115 rs: impl PioPin,
116 rw: impl PioPin,
117 e: impl PioPin,
118 db4: impl PioPin,
119 db5: impl PioPin,
120 db6: impl PioPin,
121 db7: impl PioPin,
122 word_prg: &PioHD44780CommandWordProgram<'l, P>,
123 seq_prg: &PioHD44780CommandSequenceProgram<'l, P>,
124 ) -> PioHD44780<'l, P, S> {
125 into_ref!(dma);
126
127 let rs = common.make_pio_pin(rs);
128 let rw = common.make_pio_pin(rw);
129 let e = common.make_pio_pin(e);
130 let db4 = common.make_pio_pin(db4);
131 let db5 = common.make_pio_pin(db5);
132 let db6 = common.make_pio_pin(db6);
133 let db7 = common.make_pio_pin(db7);
134
135 sm.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]);
136
137 let mut cfg = Config::default();
138 cfg.use_program(&word_prg.prg, &[&e]);
139 cfg.clock_divider = 125u8.into();
140 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
141 cfg.shift_out = ShiftConfig {
142 auto_fill: true,
143 direction: ShiftDirection::Left,
144 threshold: 32,
145 };
146 cfg.fifo_join = FifoJoin::TxOnly;
147 sm.set_config(&cfg);
148
149 sm.set_enable(true);
150 // init to 8 bit thrice
151 sm.tx().push((50000 << 8) | 0x30);
152 sm.tx().push((5000 << 8) | 0x30);
153 sm.tx().push((200 << 8) | 0x30);
154 // init 4 bit
155 sm.tx().push((200 << 8) | 0x20);
156 // set font and lines
157 sm.tx().push((50 << 8) | 0x20);
158 sm.tx().push(0b1100_0000);
159
160 irq.wait().await;
161 sm.set_enable(false);
162
163 let mut cfg = Config::default();
164 cfg.use_program(&seq_prg.prg, &[&e]);
165 cfg.clock_divider = 8u8.into(); // ~64ns/insn
166 cfg.set_jmp_pin(&db7);
167 cfg.set_set_pins(&[&rs, &rw]);
168 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
169 cfg.shift_out.direction = ShiftDirection::Left;
170 cfg.fifo_join = FifoJoin::TxOnly;
171 sm.set_config(&cfg);
172
173 sm.set_enable(true);
174
175 // display on and cursor on and blinking, reset display
176 sm.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await;
177
178 Self {
179 dma: dma.map_into(),
180 sm,
181 buf: [0x20; 40],
182 }
183 }
184
185 /// Write a line to the display
186 pub async fn add_line(&mut self, s: &[u8]) {
187 // move cursor to 0:0, prepare 16 characters
188 self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]);
189 // move line 2 up
190 self.buf.copy_within(22..38, 3);
191 // move cursor to 1:0, prepare 16 characters
192 self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]);
193 // file line 2 with spaces
194 self.buf[22..38].fill(0x20);
195 // copy input line
196 let len = s.len().min(16);
197 self.buf[22..22 + len].copy_from_slice(&s[0..len]);
198 // set cursor to 1:15
199 self.buf[38..].copy_from_slice(&[0x80, 0xcf]);
200
201 self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await;
202 }
203}
diff --git a/embassy-rp/src/pio_programs/i2s.rs b/embassy-rp/src/pio_programs/i2s.rs
new file mode 100644
index 000000000..e3f1f89d4
--- /dev/null
+++ b/embassy-rp/src/pio_programs/i2s.rs
@@ -0,0 +1,95 @@
1//! Pio backed I2s output
2
3use fixed::traits::ToFixed;
4
5use crate::dma::{AnyChannel, Channel, Transfer};
6use crate::pio::{
7 Common, Config, Direction, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine,
8};
9use crate::{into_ref, Peripheral, PeripheralRef};
10
11/// This struct represents an i2s output driver program
12pub struct PioI2sOutProgram<'a, PIO: Instance> {
13 prg: LoadedProgram<'a, PIO>,
14}
15
16impl<'a, PIO: Instance> PioI2sOutProgram<'a, PIO> {
17 /// Load the program into the given pio
18 pub fn new(common: &mut Common<'a, PIO>) -> Self {
19 let prg = pio_proc::pio_asm!(
20 ".side_set 2",
21 " set x, 14 side 0b01", // side 0bWB - W = Word Clock, B = Bit Clock
22 "left_data:",
23 " out pins, 1 side 0b00",
24 " jmp x-- left_data side 0b01",
25 " out pins 1 side 0b10",
26 " set x, 14 side 0b11",
27 "right_data:",
28 " out pins 1 side 0b10",
29 " jmp x-- right_data side 0b11",
30 " out pins 1 side 0b00",
31 );
32
33 let prg = common.load_program(&prg.program);
34
35 Self { prg }
36 }
37}
38
39/// Pio backed I2s output driver
40pub struct PioI2sOut<'a, P: Instance, const S: usize> {
41 dma: PeripheralRef<'a, AnyChannel>,
42 sm: StateMachine<'a, P, S>,
43}
44
45impl<'a, P: Instance, const S: usize> PioI2sOut<'a, P, S> {
46 /// Configure a state machine to output I2s
47 pub fn new(
48 common: &mut Common<'a, P>,
49 mut sm: StateMachine<'a, P, S>,
50 dma: impl Peripheral<P = impl Channel> + 'a,
51 data_pin: impl PioPin,
52 bit_clock_pin: impl PioPin,
53 lr_clock_pin: impl PioPin,
54 sample_rate: u32,
55 bit_depth: u32,
56 channels: u32,
57 program: &PioI2sOutProgram<'a, P>,
58 ) -> Self {
59 into_ref!(dma);
60
61 let data_pin = common.make_pio_pin(data_pin);
62 let bit_clock_pin = common.make_pio_pin(bit_clock_pin);
63 let left_right_clock_pin = common.make_pio_pin(lr_clock_pin);
64
65 let cfg = {
66 let mut cfg = Config::default();
67 cfg.use_program(&program.prg, &[&bit_clock_pin, &left_right_clock_pin]);
68 cfg.set_out_pins(&[&data_pin]);
69 let clock_frequency = sample_rate * bit_depth * channels;
70 cfg.clock_divider = (125_000_000. / clock_frequency as f64 / 2.).to_fixed();
71 cfg.shift_out = ShiftConfig {
72 threshold: 32,
73 direction: ShiftDirection::Left,
74 auto_fill: true,
75 };
76 // join fifos to have twice the time to start the next dma transfer
77 cfg.fifo_join = FifoJoin::TxOnly;
78 cfg
79 };
80 sm.set_config(&cfg);
81 sm.set_pin_dirs(Direction::Out, &[&data_pin, &left_right_clock_pin, &bit_clock_pin]);
82
83 sm.set_enable(true);
84
85 Self {
86 dma: dma.map_into(),
87 sm,
88 }
89 }
90
91 /// Return an in-prograss dma transfer future. Awaiting it will guarentee a complete transfer.
92 pub fn write<'b>(&'b mut self, buff: &'b [u32]) -> Transfer<'b, AnyChannel> {
93 self.sm.tx().dma_push(self.dma.reborrow(), buff)
94 }
95}
diff --git a/embassy-rp/src/pio_programs/mod.rs b/embassy-rp/src/pio_programs/mod.rs
new file mode 100644
index 000000000..74537825b
--- /dev/null
+++ b/embassy-rp/src/pio_programs/mod.rs
@@ -0,0 +1,10 @@
1//! Pre-built pio programs for common interfaces
2
3pub mod hd44780;
4pub mod i2s;
5pub mod onewire;
6pub mod pwm;
7pub mod rotary_encoder;
8pub mod stepper;
9pub mod uart;
10pub mod ws2812;
diff --git a/embassy-rp/src/pio_programs/onewire.rs b/embassy-rp/src/pio_programs/onewire.rs
new file mode 100644
index 000000000..f3bc5fcd7
--- /dev/null
+++ b/embassy-rp/src/pio_programs/onewire.rs
@@ -0,0 +1,109 @@
1//! OneWire pio driver
2
3use crate::pio::{self, Common, Config, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine};
4
5/// This struct represents an onewire driver program
6pub struct PioOneWireProgram<'a, PIO: Instance> {
7 prg: LoadedProgram<'a, PIO>,
8}
9
10impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> {
11 /// Load the program into the given pio
12 pub fn new(common: &mut Common<'a, PIO>) -> Self {
13 let prg = pio_proc::pio_asm!(
14 r#"
15 .wrap_target
16 again:
17 pull block
18 mov x, osr
19 jmp !x, read
20 write:
21 set pindirs, 1
22 set pins, 0
23 loop1:
24 jmp x--,loop1
25 set pindirs, 0 [31]
26 wait 1 pin 0 [31]
27 pull block
28 mov x, osr
29 bytes1:
30 pull block
31 set y, 7
32 set pindirs, 1
33 bit1:
34 set pins, 0 [1]
35 out pins,1 [31]
36 set pins, 1 [20]
37 jmp y--,bit1
38 jmp x--,bytes1
39 set pindirs, 0 [31]
40 jmp again
41 read:
42 pull block
43 mov x, osr
44 bytes2:
45 set y, 7
46 bit2:
47 set pindirs, 1
48 set pins, 0 [1]
49 set pindirs, 0 [5]
50 in pins,1 [10]
51 jmp y--,bit2
52 jmp x--,bytes2
53 .wrap
54 "#,
55 );
56 let prg = common.load_program(&prg.program);
57
58 Self { prg }
59 }
60}
61
62/// Pio backed OneWire driver
63pub struct PioOneWire<'d, PIO: pio::Instance, const SM: usize> {
64 sm: StateMachine<'d, PIO, SM>,
65}
66
67impl<'d, PIO: pio::Instance, const SM: usize> PioOneWire<'d, PIO, SM> {
68 /// Create a new instance the driver
69 pub fn new(
70 common: &mut Common<'d, PIO>,
71 mut sm: StateMachine<'d, PIO, SM>,
72 pin: impl PioPin,
73 program: &PioOneWireProgram<'d, PIO>,
74 ) -> Self {
75 let pin = common.make_pio_pin(pin);
76 let mut cfg = Config::default();
77 cfg.use_program(&program.prg, &[]);
78 cfg.set_out_pins(&[&pin]);
79 cfg.set_in_pins(&[&pin]);
80 cfg.set_set_pins(&[&pin]);
81 cfg.shift_in = ShiftConfig {
82 auto_fill: true,
83 direction: ShiftDirection::Right,
84 threshold: 8,
85 };
86 cfg.clock_divider = 255_u8.into();
87 sm.set_config(&cfg);
88 sm.set_enable(true);
89 Self { sm }
90 }
91
92 /// Write bytes over the wire
93 pub async fn write_bytes(&mut self, bytes: &[u8]) {
94 self.sm.tx().wait_push(250).await;
95 self.sm.tx().wait_push(bytes.len() as u32 - 1).await;
96 for b in bytes {
97 self.sm.tx().wait_push(*b as u32).await;
98 }
99 }
100
101 /// Read bytes from the wire
102 pub async fn read_bytes(&mut self, bytes: &mut [u8]) {
103 self.sm.tx().wait_push(0).await;
104 self.sm.tx().wait_push(bytes.len() as u32 - 1).await;
105 for b in bytes.iter_mut() {
106 *b = (self.sm.rx().wait_pull().await >> 24) as u8;
107 }
108 }
109}
diff --git a/embassy-rp/src/pio_programs/pwm.rs b/embassy-rp/src/pio_programs/pwm.rs
new file mode 100644
index 000000000..c6502387a
--- /dev/null
+++ b/embassy-rp/src/pio_programs/pwm.rs
@@ -0,0 +1,121 @@
1//! PIO backed PWM driver
2
3use core::time::Duration;
4
5use pio::InstructionOperands;
6
7use crate::clocks;
8use crate::gpio::Level;
9use crate::pio::{Common, Config, Direction, Instance, LoadedProgram, Pin, PioPin, StateMachine};
10
11/// This converts the duration provided into the number of cycles the PIO needs to run to make it take the same time
12fn to_pio_cycles(duration: Duration) -> u32 {
13 (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow
14}
15
16/// This struct represents a PWM program loaded into pio instruction memory.
17pub struct PioPwmProgram<'a, PIO: Instance> {
18 prg: LoadedProgram<'a, PIO>,
19}
20
21impl<'a, PIO: Instance> PioPwmProgram<'a, PIO> {
22 /// Load the program into the given pio
23 pub fn new(common: &mut Common<'a, PIO>) -> Self {
24 let prg = pio_proc::pio_asm!(
25 ".side_set 1 opt"
26 "pull noblock side 0"
27 "mov x, osr"
28 "mov y, isr"
29 "countloop:"
30 "jmp x!=y noset"
31 "jmp skip side 1"
32 "noset:"
33 "nop"
34 "skip:"
35 "jmp y-- countloop"
36 );
37
38 let prg = common.load_program(&prg.program);
39
40 Self { prg }
41 }
42}
43
44/// Pio backed PWM output
45pub struct PioPwm<'d, T: Instance, const SM: usize> {
46 sm: StateMachine<'d, T, SM>,
47 pin: Pin<'d, T>,
48}
49
50impl<'d, T: Instance, const SM: usize> PioPwm<'d, T, SM> {
51 /// Configure a state machine as a PWM output
52 pub fn new(
53 pio: &mut Common<'d, T>,
54 mut sm: StateMachine<'d, T, SM>,
55 pin: impl PioPin,
56 program: &PioPwmProgram<'d, T>,
57 ) -> Self {
58 let pin = pio.make_pio_pin(pin);
59 sm.set_pins(Level::High, &[&pin]);
60 sm.set_pin_dirs(Direction::Out, &[&pin]);
61
62 let mut cfg = Config::default();
63 cfg.use_program(&program.prg, &[&pin]);
64
65 sm.set_config(&cfg);
66
67 Self { sm, pin }
68 }
69
70 /// Enable's the PIO program, continuing the wave generation from the PIO program.
71 pub fn start(&mut self) {
72 self.sm.set_enable(true);
73 }
74
75 /// Stops the PIO program, ceasing all signals from the PIN that were generated via PIO.
76 pub fn stop(&mut self) {
77 self.sm.set_enable(false);
78 }
79
80 /// Sets the pwm period, which is the length of time for each pio wave until reset.
81 pub fn set_period(&mut self, duration: Duration) {
82 let is_enabled = self.sm.is_enabled();
83 while !self.sm.tx().empty() {} // Make sure that the queue is empty
84 self.sm.set_enable(false);
85 self.sm.tx().push(to_pio_cycles(duration));
86 unsafe {
87 self.sm.exec_instr(
88 InstructionOperands::PULL {
89 if_empty: false,
90 block: false,
91 }
92 .encode(),
93 );
94 self.sm.exec_instr(
95 InstructionOperands::OUT {
96 destination: ::pio::OutDestination::ISR,
97 bit_count: 32,
98 }
99 .encode(),
100 );
101 };
102 if is_enabled {
103 self.sm.set_enable(true) // Enable if previously enabled
104 }
105 }
106
107 /// Set the number of pio cycles to set the wave on high to.
108 pub fn set_level(&mut self, level: u32) {
109 self.sm.tx().push(level);
110 }
111
112 /// Set the pulse width high time
113 pub fn write(&mut self, duration: Duration) {
114 self.set_level(to_pio_cycles(duration));
115 }
116
117 /// Return the state machine and pin.
118 pub fn release(self) -> (StateMachine<'d, T, SM>, Pin<'d, T>) {
119 (self.sm, self.pin)
120 }
121}
diff --git a/embassy-rp/src/pio_programs/rotary_encoder.rs b/embassy-rp/src/pio_programs/rotary_encoder.rs
new file mode 100644
index 000000000..86423fd31
--- /dev/null
+++ b/embassy-rp/src/pio_programs/rotary_encoder.rs
@@ -0,0 +1,73 @@
1//! PIO backed quadrature encoder
2
3use fixed::traits::ToFixed;
4
5use crate::gpio::Pull;
6use crate::pio::{self, Common, Config, FifoJoin, Instance, LoadedProgram, PioPin, ShiftDirection, StateMachine};
7
8/// This struct represents an Encoder program loaded into pio instruction memory.
9pub struct PioEncoderProgram<'a, PIO: Instance> {
10 prg: LoadedProgram<'a, PIO>,
11}
12
13impl<'a, PIO: Instance> PioEncoderProgram<'a, PIO> {
14 /// Load the program into the given pio
15 pub fn new(common: &mut Common<'a, PIO>) -> Self {
16 let prg = pio_proc::pio_asm!("wait 1 pin 1", "wait 0 pin 1", "in pins, 2", "push",);
17
18 let prg = common.load_program(&prg.program);
19
20 Self { prg }
21 }
22}
23
24/// Pio Backed quadrature encoder reader
25pub struct PioEncoder<'d, T: Instance, const SM: usize> {
26 sm: StateMachine<'d, T, SM>,
27}
28
29impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> {
30 /// Configure a state machine with the loaded [PioEncoderProgram]
31 pub fn new(
32 pio: &mut Common<'d, T>,
33 mut sm: StateMachine<'d, T, SM>,
34 pin_a: impl PioPin,
35 pin_b: impl PioPin,
36 program: &PioEncoderProgram<'d, T>,
37 ) -> Self {
38 let mut pin_a = pio.make_pio_pin(pin_a);
39 let mut pin_b = pio.make_pio_pin(pin_b);
40 pin_a.set_pull(Pull::Up);
41 pin_b.set_pull(Pull::Up);
42 sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]);
43
44 let mut cfg = Config::default();
45 cfg.set_in_pins(&[&pin_a, &pin_b]);
46 cfg.fifo_join = FifoJoin::RxOnly;
47 cfg.shift_in.direction = ShiftDirection::Left;
48 cfg.clock_divider = 10_000.to_fixed();
49 cfg.use_program(&program.prg, &[]);
50 sm.set_config(&cfg);
51 sm.set_enable(true);
52 Self { sm }
53 }
54
55 /// Read a single count from the encoder
56 pub async fn read(&mut self) -> Direction {
57 loop {
58 match self.sm.rx().wait_pull().await {
59 0 => return Direction::CounterClockwise,
60 1 => return Direction::Clockwise,
61 _ => {}
62 }
63 }
64 }
65}
66
67/// Encoder Count Direction
68pub enum Direction {
69 /// Encoder turned clockwise
70 Clockwise,
71 /// Encoder turned counter clockwise
72 CounterClockwise,
73}
diff --git a/embassy-rp/src/pio_programs/stepper.rs b/embassy-rp/src/pio_programs/stepper.rs
new file mode 100644
index 000000000..0d58c754c
--- /dev/null
+++ b/embassy-rp/src/pio_programs/stepper.rs
@@ -0,0 +1,147 @@
1//! Pio Stepper Driver for 5-wire steppers
2
3use core::mem::{self, MaybeUninit};
4
5use fixed::traits::ToFixed;
6use fixed::types::extra::U8;
7use fixed::FixedU32;
8
9use crate::pio::{Common, Config, Direction, Instance, Irq, LoadedProgram, PioPin, StateMachine};
10
11/// This struct represents a Stepper driver program loaded into pio instruction memory.
12pub struct PioStepperProgram<'a, PIO: Instance> {
13 prg: LoadedProgram<'a, PIO>,
14}
15
16impl<'a, PIO: Instance> PioStepperProgram<'a, PIO> {
17 /// Load the program into the given pio
18 pub fn new(common: &mut Common<'a, PIO>) -> Self {
19 let prg = pio_proc::pio_asm!(
20 "pull block",
21 "mov x, osr",
22 "pull block",
23 "mov y, osr",
24 "jmp !x end",
25 "loop:",
26 "jmp !osre step",
27 "mov osr, y",
28 "step:",
29 "out pins, 4 [31]"
30 "jmp x-- loop",
31 "end:",
32 "irq 0 rel"
33 );
34
35 let prg = common.load_program(&prg.program);
36
37 Self { prg }
38 }
39}
40
41/// Pio backed Stepper driver
42pub struct PioStepper<'d, T: Instance, const SM: usize> {
43 irq: Irq<'d, T, SM>,
44 sm: StateMachine<'d, T, SM>,
45}
46
47impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> {
48 /// Configure a state machine to drive a stepper
49 pub fn new(
50 pio: &mut Common<'d, T>,
51 mut sm: StateMachine<'d, T, SM>,
52 irq: Irq<'d, T, SM>,
53 pin0: impl PioPin,
54 pin1: impl PioPin,
55 pin2: impl PioPin,
56 pin3: impl PioPin,
57 program: &PioStepperProgram<'d, T>,
58 ) -> Self {
59 let pin0 = pio.make_pio_pin(pin0);
60 let pin1 = pio.make_pio_pin(pin1);
61 let pin2 = pio.make_pio_pin(pin2);
62 let pin3 = pio.make_pio_pin(pin3);
63 sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]);
64 let mut cfg = Config::default();
65 cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]);
66 cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed();
67 cfg.use_program(&program.prg, &[]);
68 sm.set_config(&cfg);
69 sm.set_enable(true);
70 Self { irq, sm }
71 }
72
73 /// Set pulse frequency
74 pub fn set_frequency(&mut self, freq: u32) {
75 let clock_divider: FixedU32<U8> = (125_000_000 / (freq * 136)).to_fixed();
76 assert!(clock_divider <= 65536, "clkdiv must be <= 65536");
77 assert!(clock_divider >= 1, "clkdiv must be >= 1");
78 self.sm.set_clock_divider(clock_divider);
79 self.sm.clkdiv_restart();
80 }
81
82 /// Full step, one phase
83 pub async fn step(&mut self, steps: i32) {
84 if steps > 0 {
85 self.run(steps, 0b1000_0100_0010_0001_1000_0100_0010_0001).await
86 } else {
87 self.run(-steps, 0b0001_0010_0100_1000_0001_0010_0100_1000).await
88 }
89 }
90
91 /// Full step, two phase
92 pub async fn step2(&mut self, steps: i32) {
93 if steps > 0 {
94 self.run(steps, 0b1001_1100_0110_0011_1001_1100_0110_0011).await
95 } else {
96 self.run(-steps, 0b0011_0110_1100_1001_0011_0110_1100_1001).await
97 }
98 }
99
100 /// Half step
101 pub async fn step_half(&mut self, steps: i32) {
102 if steps > 0 {
103 self.run(steps, 0b1001_1000_1100_0100_0110_0010_0011_0001).await
104 } else {
105 self.run(-steps, 0b0001_0011_0010_0110_0100_1100_1000_1001).await
106 }
107 }
108
109 async fn run(&mut self, steps: i32, pattern: u32) {
110 self.sm.tx().wait_push(steps as u32).await;
111 self.sm.tx().wait_push(pattern).await;
112 let drop = OnDrop::new(|| {
113 self.sm.clear_fifos();
114 unsafe {
115 self.sm.exec_instr(
116 pio::InstructionOperands::JMP {
117 address: 0,
118 condition: pio::JmpCondition::Always,
119 }
120 .encode(),
121 );
122 }
123 });
124 self.irq.wait().await;
125 drop.defuse();
126 }
127}
128
129struct OnDrop<F: FnOnce()> {
130 f: MaybeUninit<F>,
131}
132
133impl<F: FnOnce()> OnDrop<F> {
134 pub fn new(f: F) -> Self {
135 Self { f: MaybeUninit::new(f) }
136 }
137
138 pub fn defuse(self) {
139 mem::forget(self)
140 }
141}
142
143impl<F: FnOnce()> Drop for OnDrop<F> {
144 fn drop(&mut self) {
145 unsafe { self.f.as_ptr().read()() }
146 }
147}
diff --git a/embassy-rp/src/pio_programs/uart.rs b/embassy-rp/src/pio_programs/uart.rs
new file mode 100644
index 000000000..c643f1063
--- /dev/null
+++ b/embassy-rp/src/pio_programs/uart.rs
@@ -0,0 +1,185 @@
1//! Pio backed uart drivers
2
3use core::convert::Infallible;
4
5use embedded_io_async::{ErrorType, Read, Write};
6use fixed::traits::ToFixed;
7
8use crate::clocks::clk_sys_freq;
9use crate::gpio::Level;
10use crate::pio::{
11 Common, Config, Direction as PioDirection, FifoJoin, Instance, LoadedProgram, PioPin, ShiftDirection, StateMachine,
12};
13
14/// This struct represents a uart tx program loaded into pio instruction memory.
15pub struct PioUartTxProgram<'a, PIO: Instance> {
16 prg: LoadedProgram<'a, PIO>,
17}
18
19impl<'a, PIO: Instance> PioUartTxProgram<'a, PIO> {
20 /// Load the uart tx program into the given pio
21 pub fn new(common: &mut Common<'a, PIO>) -> Self {
22 let prg = pio_proc::pio_asm!(
23 r#"
24 .side_set 1 opt
25
26 ; An 8n1 UART transmit program.
27 ; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin.
28
29 pull side 1 [7] ; Assert stop bit, or stall with line in idle state
30 set x, 7 side 0 [7] ; Preload bit counter, assert start bit for 8 clocks
31 bitloop: ; This loop will run 8 times (8n1 UART)
32 out pins, 1 ; Shift 1 bit from OSR to the first OUT pin
33 jmp x-- bitloop [6] ; Each loop iteration is 8 cycles.
34 "#
35 );
36
37 let prg = common.load_program(&prg.program);
38
39 Self { prg }
40 }
41}
42
43/// PIO backed Uart transmitter
44pub struct PioUartTx<'a, PIO: Instance, const SM: usize> {
45 sm_tx: StateMachine<'a, PIO, SM>,
46}
47
48impl<'a, PIO: Instance, const SM: usize> PioUartTx<'a, PIO, SM> {
49 /// Configure a pio state machine to use the loaded tx program.
50 pub fn new(
51 baud: u32,
52 common: &mut Common<'a, PIO>,
53 mut sm_tx: StateMachine<'a, PIO, SM>,
54 tx_pin: impl PioPin,
55 program: &PioUartTxProgram<'a, PIO>,
56 ) -> Self {
57 let tx_pin = common.make_pio_pin(tx_pin);
58 sm_tx.set_pins(Level::High, &[&tx_pin]);
59 sm_tx.set_pin_dirs(PioDirection::Out, &[&tx_pin]);
60
61 let mut cfg = Config::default();
62
63 cfg.set_out_pins(&[&tx_pin]);
64 cfg.use_program(&program.prg, &[&tx_pin]);
65 cfg.shift_out.auto_fill = false;
66 cfg.shift_out.direction = ShiftDirection::Right;
67 cfg.fifo_join = FifoJoin::TxOnly;
68 cfg.clock_divider = (clk_sys_freq() / (8 * baud)).to_fixed();
69 sm_tx.set_config(&cfg);
70 sm_tx.set_enable(true);
71
72 Self { sm_tx }
73 }
74
75 /// Write a single u8
76 pub async fn write_u8(&mut self, data: u8) {
77 self.sm_tx.tx().wait_push(data as u32).await;
78 }
79}
80
81impl<PIO: Instance, const SM: usize> ErrorType for PioUartTx<'_, PIO, SM> {
82 type Error = Infallible;
83}
84
85impl<PIO: Instance, const SM: usize> Write for PioUartTx<'_, PIO, SM> {
86 async fn write(&mut self, buf: &[u8]) -> Result<usize, Infallible> {
87 for byte in buf {
88 self.write_u8(*byte).await;
89 }
90 Ok(buf.len())
91 }
92}
93
94/// This struct represents a Uart Rx program loaded into pio instruction memory.
95pub struct PioUartRxProgram<'a, PIO: Instance> {
96 prg: LoadedProgram<'a, PIO>,
97}
98
99impl<'a, PIO: Instance> PioUartRxProgram<'a, PIO> {
100 /// Load the uart rx program into the given pio
101 pub fn new(common: &mut Common<'a, PIO>) -> Self {
102 let prg = pio_proc::pio_asm!(
103 r#"
104 ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and
105 ; break conditions more gracefully.
106 ; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX.
107
108 start:
109 wait 0 pin 0 ; Stall until start bit is asserted
110 set x, 7 [10] ; Preload bit counter, then delay until halfway through
111 rx_bitloop: ; the first data bit (12 cycles incl wait, set).
112 in pins, 1 ; Shift data bit into ISR
113 jmp x-- rx_bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles
114 jmp pin good_rx_stop ; Check stop bit (should be high)
115
116 irq 4 rel ; Either a framing error or a break. Set a sticky flag,
117 wait 1 pin 0 ; and wait for line to return to idle state.
118 jmp start ; Don't push data if we didn't see good framing.
119
120 good_rx_stop: ; No delay before returning to start; a little slack is
121 in null 24
122 push ; important in case the TX clock is slightly too fast.
123 "#
124 );
125
126 let prg = common.load_program(&prg.program);
127
128 Self { prg }
129 }
130}
131
132/// PIO backed Uart reciever
133pub struct PioUartRx<'a, PIO: Instance, const SM: usize> {
134 sm_rx: StateMachine<'a, PIO, SM>,
135}
136
137impl<'a, PIO: Instance, const SM: usize> PioUartRx<'a, PIO, SM> {
138 /// Configure a pio state machine to use the loaded rx program.
139 pub fn new(
140 baud: u32,
141 common: &mut Common<'a, PIO>,
142 mut sm_rx: StateMachine<'a, PIO, SM>,
143 rx_pin: impl PioPin,
144 program: &PioUartRxProgram<'a, PIO>,
145 ) -> Self {
146 let mut cfg = Config::default();
147 cfg.use_program(&program.prg, &[]);
148
149 let rx_pin = common.make_pio_pin(rx_pin);
150 sm_rx.set_pins(Level::High, &[&rx_pin]);
151 cfg.set_in_pins(&[&rx_pin]);
152 cfg.set_jmp_pin(&rx_pin);
153 sm_rx.set_pin_dirs(PioDirection::In, &[&rx_pin]);
154
155 cfg.clock_divider = (clk_sys_freq() / (8 * baud)).to_fixed();
156 cfg.shift_in.auto_fill = false;
157 cfg.shift_in.direction = ShiftDirection::Right;
158 cfg.shift_in.threshold = 32;
159 cfg.fifo_join = FifoJoin::RxOnly;
160 sm_rx.set_config(&cfg);
161 sm_rx.set_enable(true);
162
163 Self { sm_rx }
164 }
165
166 /// Wait for a single u8
167 pub async fn read_u8(&mut self) -> u8 {
168 self.sm_rx.rx().wait_pull().await as u8
169 }
170}
171
172impl<PIO: Instance, const SM: usize> ErrorType for PioUartRx<'_, PIO, SM> {
173 type Error = Infallible;
174}
175
176impl<PIO: Instance, const SM: usize> Read for PioUartRx<'_, PIO, SM> {
177 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Infallible> {
178 let mut i = 0;
179 while i < buf.len() {
180 buf[i] = self.read_u8().await;
181 i += 1;
182 }
183 Ok(i)
184 }
185}
diff --git a/embassy-rp/src/pio_programs/ws2812.rs b/embassy-rp/src/pio_programs/ws2812.rs
new file mode 100644
index 000000000..875f0209f
--- /dev/null
+++ b/embassy-rp/src/pio_programs/ws2812.rs
@@ -0,0 +1,118 @@
1//! [ws2812](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf)
2
3use embassy_time::Timer;
4use fixed::types::U24F8;
5use smart_leds::RGB8;
6
7use crate::clocks::clk_sys_freq;
8use crate::dma::{AnyChannel, Channel};
9use crate::pio::{
10 Common, Config, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine,
11};
12use crate::{into_ref, Peripheral, PeripheralRef};
13
14const T1: u8 = 2; // start bit
15const T2: u8 = 5; // data bit
16const T3: u8 = 3; // stop bit
17const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32;
18
19/// This struct represents a ws2812 program loaded into pio instruction memory.
20pub struct PioWs2812Program<'a, PIO: Instance> {
21 prg: LoadedProgram<'a, PIO>,
22}
23
24impl<'a, PIO: Instance> PioWs2812Program<'a, PIO> {
25 /// Load the ws2812 program into the given pio
26 pub fn new(common: &mut Common<'a, PIO>) -> Self {
27 let side_set = pio::SideSet::new(false, 1, false);
28 let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set);
29
30 let mut wrap_target = a.label();
31 let mut wrap_source = a.label();
32 let mut do_zero = a.label();
33 a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0);
34 a.bind(&mut wrap_target);
35 // Do stop bit
36 a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0);
37 // Do start bit
38 a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1);
39 // Do data bit = 1
40 a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1);
41 a.bind(&mut do_zero);
42 // Do data bit = 0
43 a.nop_with_delay_and_side_set(T2 - 1, 0);
44 a.bind(&mut wrap_source);
45
46 let prg = a.assemble_with_wrap(wrap_source, wrap_target);
47 let prg = common.load_program(&prg);
48
49 Self { prg }
50 }
51}
52
53/// Pio backed ws2812 driver
54/// Const N is the number of ws2812 leds attached to this pin
55pub struct PioWs2812<'d, P: Instance, const S: usize, const N: usize> {
56 dma: PeripheralRef<'d, AnyChannel>,
57 sm: StateMachine<'d, P, S>,
58}
59
60impl<'d, P: Instance, const S: usize, const N: usize> PioWs2812<'d, P, S, N> {
61 /// Configure a pio state machine to use the loaded ws2812 program.
62 pub fn new(
63 pio: &mut Common<'d, P>,
64 mut sm: StateMachine<'d, P, S>,
65 dma: impl Peripheral<P = impl Channel> + 'd,
66 pin: impl PioPin,
67 program: &PioWs2812Program<'d, P>,
68 ) -> Self {
69 into_ref!(dma);
70
71 // Setup sm0
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(&program.prg, &[&out_pin]);
80
81 // Clock config, measured in kHz to avoid overflows
82 let clock_freq = U24F8::from_num(clk_sys_freq() / 1000);
83 let ws2812_freq = U24F8::from_num(800);
84 let bit_freq = ws2812_freq * CYCLES_PER_BIT;
85 cfg.clock_divider = clock_freq / bit_freq;
86
87 // FIFO config
88 cfg.fifo_join = FifoJoin::TxOnly;
89 cfg.shift_out = ShiftConfig {
90 auto_fill: true,
91 threshold: 24,
92 direction: ShiftDirection::Left,
93 };
94
95 sm.set_config(&cfg);
96 sm.set_enable(true);
97
98 Self {
99 dma: dma.map_into(),
100 sm,
101 }
102 }
103
104 /// Write a buffer of [smart_leds::RGB8] to the ws2812 string
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}
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index 674d331ab..a220b9a77 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -42,7 +42,7 @@ embedded-graphics = "0.7.1"
42st7789 = "0.6.1" 42st7789 = "0.6.1"
43display-interface = "0.4.1" 43display-interface = "0.4.1"
44byte-slice-cast = { version = "1.2.0", default-features = false } 44byte-slice-cast = { version = "1.2.0", default-features = false }
45smart-leds = "0.3.0" 45smart-leds = "0.4.0"
46heapless = "0.8" 46heapless = "0.8"
47usbd-hid = "0.8.1" 47usbd-hid = "0.8.1"
48 48
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..2750f61ae 100644
--- a/examples/rp/src/bin/pio_rotary_encoder.rs
+++ b/examples/rp/src/bin/pio_rotary_encoder.rs
@@ -5,70 +5,30 @@
5 5
6use defmt::info; 6use defmt::info;
7use embassy_executor::Spawner; 7use embassy_executor::Spawner;
8use embassy_rp::gpio::Pull; 8use embassy_rp::bind_interrupts;
9use embassy_rp::peripherals::PIO0; 9use embassy_rp::peripherals::PIO0;
10use embassy_rp::{bind_interrupts, pio}; 10use embassy_rp::pio::{InterruptHandler, Pio};
11use fixed::traits::ToFixed; 11use embassy_rp::pio_programs::rotary_encoder::{Direction, PioEncoder, PioEncoderProgram};
12use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine};
13use {defmt_rtt as _, panic_probe as _}; 12use {defmt_rtt as _, panic_probe as _};
14 13
15bind_interrupts!(struct Irqs { 14bind_interrupts!(struct Irqs {
16 PIO0_IRQ_0 => InterruptHandler<PIO0>; 15 PIO0_IRQ_0 => InterruptHandler<PIO0>;
17}); 16});
18 17
19pub struct PioEncoder<'d, T: Instance, const SM: usize> { 18#[embassy_executor::task]
20 sm: StateMachine<'d, T, SM>, 19async fn encoder_0(mut encoder: PioEncoder<'static, PIO0, 0>) {
21} 20 let mut count = 0;
22 21 loop {
23impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> { 22 info!("Count: {}", count);
24 pub fn new( 23 count += match encoder.read().await {
25 pio: &mut Common<'d, T>, 24 Direction::Clockwise => 1,
26 mut sm: StateMachine<'d, T, SM>, 25 Direction::CounterClockwise => -1,
27 pin_a: impl PioPin, 26 };
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 } 27 }
58} 28}
59 29
60pub enum Direction { 30#[embassy_executor::task]
61 Clockwise, 31async 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; 32 let mut count = 0;
73 loop { 33 loop {
74 info!("Count: {}", count); 34 info!("Count: {}", count);
@@ -78,3 +38,18 @@ async fn main(_spawner: Spawner) {
78 }; 38 };
79 } 39 }
80} 40}
41
42#[embassy_executor::main]
43async fn main(spawner: Spawner) {
44 let p = embassy_rp::init(Default::default());
45 let Pio {
46 mut common, sm0, sm1, ..
47 } = Pio::new(p.PIO0, Irqs);
48
49 let prg = PioEncoderProgram::new(&mut common);
50 let encoder0 = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5, &prg);
51 let encoder1 = PioEncoder::new(&mut common, sm1, p.PIN_6, p.PIN_7, &prg);
52
53 spawner.must_spawn(encoder_0(encoder0));
54 spawner.must_spawn(encoder_1(encoder1));
55}
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..aaf2a524f 100644
--- a/examples/rp/src/bin/pio_uart.rs
+++ b/examples/rp/src/bin/pio_uart.rs
@@ -13,10 +13,10 @@
13use defmt::{info, panic, trace}; 13use defmt::{info, panic, trace};
14use embassy_executor::Spawner; 14use embassy_executor::Spawner;
15use embassy_futures::join::{join, join3}; 15use embassy_futures::join::{join, join3};
16use embassy_rp::bind_interrupts;
17use embassy_rp::peripherals::{PIO0, USB}; 16use embassy_rp::peripherals::{PIO0, USB};
18use embassy_rp::pio::InterruptHandler as PioInterruptHandler; 17use embassy_rp::pio_programs::uart::{PioUartRx, PioUartRxProgram, PioUartTx, PioUartTxProgram};
19use embassy_rp::usb::{Driver, Instance, InterruptHandler}; 18use embassy_rp::usb::{Driver, Instance, InterruptHandler};
19use embassy_rp::{bind_interrupts, pio};
20use embassy_sync::blocking_mutex::raw::NoopRawMutex; 20use embassy_sync::blocking_mutex::raw::NoopRawMutex;
21use embassy_sync::pipe::Pipe; 21use embassy_sync::pipe::Pipe;
22use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; 22use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State};
@@ -25,13 +25,11 @@ use embassy_usb::{Builder, Config};
25use embedded_io_async::{Read, Write}; 25use embedded_io_async::{Read, Write};
26use {defmt_rtt as _, panic_probe as _}; 26use {defmt_rtt as _, panic_probe as _};
27 27
28use crate::uart::PioUart; 28//use crate::uart::PioUart;
29use crate::uart_rx::PioUartRx;
30use crate::uart_tx::PioUartTx;
31 29
32bind_interrupts!(struct Irqs { 30bind_interrupts!(struct Irqs {
33 USBCTRL_IRQ => InterruptHandler<USB>; 31 USBCTRL_IRQ => InterruptHandler<USB>;
34 PIO0_IRQ_0 => PioInterruptHandler<PIO0>; 32 PIO0_IRQ_0 => pio::InterruptHandler<PIO0>;
35}); 33});
36 34
37#[embassy_executor::main] 35#[embassy_executor::main]
@@ -85,8 +83,15 @@ async fn main(_spawner: Spawner) {
85 let usb_fut = usb.run(); 83 let usb_fut = usb.run();
86 84
87 // PIO UART setup 85 // PIO UART setup
88 let uart = PioUart::new(9600, p.PIO0, p.PIN_4, p.PIN_5); 86 let pio::Pio {
89 let (mut uart_tx, mut uart_rx) = uart.split(); 87 mut common, sm0, sm1, ..
88 } = pio::Pio::new(p.PIO0, Irqs);
89
90 let tx_program = PioUartTxProgram::new(&mut common);
91 let mut uart_tx = PioUartTx::new(9600, &mut common, sm0, p.PIN_4, &tx_program);
92
93 let rx_program = PioUartRxProgram::new(&mut common);
94 let mut uart_rx = PioUartRx::new(9600, &mut common, sm1, p.PIN_5, &rx_program);
90 95
91 // Pipe setup 96 // Pipe setup
92 let mut usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new(); 97 let mut usb_pipe: Pipe<NoopRawMutex, 20> = Pipe::new();
@@ -163,8 +168,8 @@ async fn usb_write<'d, T: Instance + 'd>(
163} 168}
164 169
165/// Read from the UART and write it to the USB TX pipe 170/// Read from the UART and write it to the USB TX pipe
166async fn uart_read( 171async fn uart_read<PIO: pio::Instance, const SM: usize>(
167 uart_rx: &mut PioUartRx<'_>, 172 uart_rx: &mut PioUartRx<'_, PIO, SM>,
168 usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, 173 usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>,
169) -> ! { 174) -> ! {
170 let mut buf = [0; 64]; 175 let mut buf = [0; 64];
@@ -180,8 +185,8 @@ async fn uart_read(
180} 185}
181 186
182/// Read from the UART TX pipe and write it to the UART 187/// Read from the UART TX pipe and write it to the UART
183async fn uart_write( 188async fn uart_write<PIO: pio::Instance, const SM: usize>(
184 uart_tx: &mut PioUartTx<'_>, 189 uart_tx: &mut PioUartTx<'_, PIO, SM>,
185 uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, 190 uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>,
186) -> ! { 191) -> ! {
187 let mut buf = [0; 64]; 192 let mut buf = [0; 64];
@@ -192,197 +197,3 @@ async fn uart_write(
192 let _ = uart_tx.write(&data).await; 197 let _ = uart_tx.write(&data).await;
193 } 198 }
194} 199}
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));
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));