aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/rp/Cargo.toml1
-rw-r--r--examples/rp/src/bin/ws2812-pio.rs142
2 files changed, 143 insertions, 0 deletions
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index f07684f29..1e8870ed7 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -28,6 +28,7 @@ embedded-graphics = "0.7.1"
28st7789 = "0.6.1" 28st7789 = "0.6.1"
29display-interface = "0.4.1" 29display-interface = "0.4.1"
30byte-slice-cast = { version = "1.2.0", default-features = false } 30byte-slice-cast = { version = "1.2.0", default-features = false }
31smart-leds = "0.3.0"
31 32
32embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } 33embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" }
33embedded-hal-async = "0.2.0-alpha.0" 34embedded-hal-async = "0.2.0-alpha.0"
diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs
new file mode 100644
index 000000000..5f8a3baee
--- /dev/null
+++ b/examples/rp/src/bin/ws2812-pio.rs
@@ -0,0 +1,142 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_rp::gpio::{self, Pin};
8use embassy_rp::pio::{
9 FifoJoin, PioInstance, PioPeripheral, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstance,
10};
11use embassy_rp::pio_instr_util;
12use embassy_rp::relocate::RelocatedProgram;
13use embassy_time::{Duration, Timer};
14use smart_leds::RGB8;
15use {defmt_rtt as _, panic_probe as _};
16pub struct Ws2812<P: PioInstance, S: SmInstance> {
17 sm: PioStateMachineInstance<P, S>,
18}
19
20impl<P: PioInstance, S: SmInstance> Ws2812<P, S> {
21 pub fn new(mut sm: PioStateMachineInstance<P, S>, pin: gpio::AnyPin) -> Self {
22 // Setup sm0
23
24 // prepare the PIO program
25 let side_set = pio::SideSet::new(false, 1, false);
26 let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set);
27
28 const T1: u8 = 2; // start bit
29 const T2: u8 = 5; // data bit
30 const T3: u8 = 3; // stop bit
31 const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32;
32
33 let mut wrap_target = a.label();
34 let mut wrap_source = a.label();
35 let mut do_zero = a.label();
36 a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0);
37 a.bind(&mut wrap_target);
38 // Do stop bit
39 a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0);
40 // Do start bit
41 a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1);
42 // Do data bit = 1
43 a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1);
44 a.bind(&mut do_zero);
45 // Do data bit = 0
46 a.nop_with_delay_and_side_set(T2 - 1, 0);
47 a.bind(&mut wrap_source);
48
49 let prg = a.assemble_with_wrap(wrap_source, wrap_target);
50
51 let relocated = RelocatedProgram::new(&prg);
52 sm.write_instr(relocated.origin() as usize, relocated.code());
53 pio_instr_util::exec_jmp(&mut sm, relocated.origin());
54
55 // Pin config
56 let out_pin = sm.make_pio_pin(pin);
57 sm.set_set_pins(&[&out_pin]);
58 sm.set_sideset_base_pin(&out_pin);
59 sm.set_sideset_count(1);
60
61 // Clock config
62 // TODO CLOCK_FREQ should come from embassy_rp
63 const CLOCK_FREQ: u32 = 125_000_000;
64 const WS2812_FREQ: u32 = 800_000;
65
66 let bit_freq = WS2812_FREQ * CYCLES_PER_BIT;
67 let mut int = CLOCK_FREQ / bit_freq;
68 let rem = CLOCK_FREQ - (int * bit_freq);
69 let frac = (rem * 256) / bit_freq;
70 // 65536.0 is represented as 0 in the pio's clock divider
71 if int == 65536 {
72 int = 0;
73 }
74
75 sm.set_clkdiv((int << 8) | frac);
76 let pio::Wrap { source, target } = relocated.wrap();
77 sm.set_wrap(source, target);
78
79 // FIFO config
80 sm.set_autopull(true);
81 sm.set_fifo_join(FifoJoin::TxOnly);
82 sm.set_pull_threshold(24);
83 sm.set_out_shift_dir(ShiftDirection::Left);
84
85 sm.set_enable(true);
86
87 Self { sm }
88 }
89
90 pub async fn write(&mut self, colors: &[RGB8]) {
91 for color in colors {
92 let word = (u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8);
93 self.sm.wait_push(word).await;
94 }
95 }
96}
97
98/// Input a value 0 to 255 to get a color value
99/// The colours are a transition r - g - b - back to r.
100fn wheel(mut wheel_pos: u8) -> RGB8 {
101 wheel_pos = 255 - wheel_pos;
102 if wheel_pos < 85 {
103 return (255 - wheel_pos * 3, 0, wheel_pos * 3).into();
104 }
105 if wheel_pos < 170 {
106 wheel_pos -= 85;
107 return (0, wheel_pos * 3, 255 - wheel_pos * 3).into();
108 }
109 wheel_pos -= 170;
110 (wheel_pos * 3, 255 - wheel_pos * 3, 0).into()
111}
112
113#[embassy_executor::main]
114async fn main(_spawner: Spawner) {
115 info!("Start");
116 let p = embassy_rp::init(Default::default());
117
118 let (_pio0, sm0, _sm1, _sm2, _sm3) = p.PIO0.split();
119
120 // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit
121 // feather boards for the 2040 both have one built in.
122 const NUM_LEDS: usize = 1;
123 let mut data = [RGB8::default(); NUM_LEDS];
124
125 // For the thing plus, use pin 8
126 // For the feather, use pin 16
127 let mut ws2812 = Ws2812::new(sm0, p.PIN_8.degrade());
128
129 // Loop forever making RGB values and pushing them out to the WS2812.
130 loop {
131 for j in 0..(256 * 5) {
132 debug!("New Colors:");
133 for i in 0..NUM_LEDS {
134 data[i] = wheel((((i * 256) as u16 / NUM_LEDS as u16 + j as u16) & 255) as u8);
135 debug!("R: {} G: {} B: {}", data[i].r, data[i].g, data[i].b);
136 }
137 ws2812.write(&data).await;
138
139 Timer::after(Duration::from_micros(5)).await;
140 }
141 }
142}