aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorCaleb Jamison <[email protected]>2023-03-11 02:58:28 -0500
committerCaleb Jamison <[email protected]>2023-03-11 02:58:28 -0500
commit12d6e37b3f471186f96275b585f5512704154a17 (patch)
treea4d6dcd298770099bec55e3140f78db5441fa084 /examples
parent055597063f2d3f1156a9f8076c601dd300d85542 (diff)
Example using the PIO to drive WS2812 aka Neopixel RGB leds
This example also uses a pio program compiled at runtime, rather than one built at compile time. There's no reason to do that, but it's probably useful to have an example that does this as well.
Diffstat (limited to 'examples')
-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}