aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/rp/src/bin/pio_stepper.rs169
1 files changed, 169 insertions, 0 deletions
diff --git a/examples/rp/src/bin/pio_stepper.rs b/examples/rp/src/bin/pio_stepper.rs
new file mode 100644
index 000000000..02fb20699
--- /dev/null
+++ b/examples/rp/src/bin/pio_stepper.rs
@@ -0,0 +1,169 @@
1//! This example shows how to use the PIO module in the RP2040 to implement a stepper motor driver
2//! for a 5-wire stepper such as the 28BYJ-48. You can halt an ongoing rotation by dropping the future.
3
4#![no_std]
5#![no_main]
6#![feature(type_alias_impl_trait)]
7use core::mem::{self, MaybeUninit};
8
9use defmt::info;
10use embassy_executor::Spawner;
11use embassy_rp::bind_interrupts;
12use embassy_rp::peripherals::PIO0;
13use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Irq, Pio, PioPin, StateMachine};
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 _};
19
20bind_interrupts!(struct Irqs {
21 PIO0_IRQ_0 => InterruptHandler<PIO0>;
22});
23
24pub struct PioStepper<'d, T: Instance, const SM: usize> {
25 irq: Irq<'d, T, SM>,
26 sm: StateMachine<'d, T, SM>,
27}
28
29impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> {
30 pub fn new(
31 pio: &mut Common<'d, T>,
32 mut sm: StateMachine<'d, T, SM>,
33 irq: Irq<'d, T, SM>,
34 pin0: impl PioPin,
35 pin1: impl PioPin,
36 pin2: impl PioPin,
37 pin3: impl PioPin,
38 ) -> Self {
39 let prg = pio_proc::pio_asm!(
40 "pull block",
41 "mov x, osr",
42 "pull block",
43 "mov y, osr",
44 "jmp !x end",
45 "loop:",
46 "jmp !osre step",
47 "mov osr, y",
48 "step:",
49 "out pins, 4 [31]"
50 "jmp x-- loop",
51 "end:",
52 "irq 0 rel"
53 );
54 let pin0 = pio.make_pio_pin(pin0);
55 let pin1 = pio.make_pio_pin(pin1);
56 let pin2 = pio.make_pio_pin(pin2);
57 let pin3 = pio.make_pio_pin(pin3);
58 sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]);
59 let mut cfg = Config::default();
60 cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]);
61 cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed();
62 cfg.use_program(&pio.load_program(&prg.program), &[]);
63 sm.set_config(&cfg);
64 sm.set_enable(true);
65 Self { irq, sm }
66 }
67
68 // Set pulse frequency
69 pub fn set_frequency(&mut self, freq: u32) {
70 let clock_divider: FixedU32<U8> = (125_000_000 / (freq * 136)).to_fixed();
71 assert!(clock_divider <= 65536, "clkdiv must be <= 65536");
72 assert!(clock_divider >= 1, "clkdiv must be >= 1");
73 T::PIO.sm(SM).clkdiv().write(|w| w.0 = clock_divider.to_bits() << 8);
74 self.sm.clkdiv_restart();
75 }
76
77 // Full step, one phase
78 pub async fn step(&mut self, steps: i32) {
79 if steps > 0 {
80 self.run(steps, 0b1000_0100_0010_0001_1000_0100_0010_0001).await
81 } else {
82 self.run(-steps, 0b0001_0010_0100_1000_0001_0010_0100_1000).await
83 }
84 }
85
86 // Full step, two phase
87 pub async fn step2(&mut self, steps: i32) {
88 if steps > 0 {
89 self.run(steps, 0b1001_1100_0110_0011_1001_1100_0110_0011).await
90 } else {
91 self.run(-steps, 0b0011_0110_1100_1001_0011_0110_1100_1001).await
92 }
93 }
94
95 // Half step
96 pub async fn step_half(&mut self, steps: i32) {
97 if steps > 0 {
98 self.run(steps, 0b1001_1000_1100_0100_0110_0010_0011_0001).await
99 } else {
100 self.run(-steps, 0b0001_0011_0010_0110_0100_1100_1000_1001).await
101 }
102 }
103
104 async fn run(&mut self, steps: i32, pattern: u32) {
105 self.sm.tx().wait_push(steps as u32).await;
106 self.sm.tx().wait_push(pattern).await;
107 let drop = OnDrop::new(|| {
108 self.sm.clear_fifos();
109 unsafe {
110 self.sm.exec_instr(
111 pio::InstructionOperands::JMP {
112 address: 0,
113 condition: pio::JmpCondition::Always,
114 }
115 .encode(),
116 );
117 }
118 });
119 self.irq.wait().await;
120 drop.defuse();
121 }
122}
123
124struct OnDrop<F: FnOnce()> {
125 f: MaybeUninit<F>,
126}
127
128impl<F: FnOnce()> OnDrop<F> {
129 pub fn new(f: F) -> Self {
130 Self { f: MaybeUninit::new(f) }
131 }
132
133 pub fn defuse(self) {
134 mem::forget(self)
135 }
136}
137
138impl<F: FnOnce()> Drop for OnDrop<F> {
139 fn drop(&mut self) {
140 unsafe { self.f.as_ptr().read()() }
141 }
142}
143
144#[embassy_executor::main]
145async fn main(_spawner: Spawner) {
146 let p = embassy_rp::init(Default::default());
147 let Pio {
148 mut common, irq0, sm0, ..
149 } = Pio::new(p.PIO0, Irqs);
150
151 let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7);
152 stepper.set_frequency(120);
153 loop {
154 info!("CW full steps");
155 stepper.step(1000).await;
156
157 info!("CCW full steps, drop after 1 sec");
158 if let Err(_) = with_timeout(Duration::from_secs(1), stepper.step(i32::MIN)).await {
159 info!("Time's up!");
160 Timer::after(Duration::from_secs(1)).await;
161 }
162
163 info!("CW half steps");
164 stepper.step_half(1000).await;
165
166 info!("CCW half steps");
167 stepper.step_half(-1000).await;
168 }
169}