diff options
| -rw-r--r-- | examples/rp/Cargo.toml | 1 | ||||
| -rw-r--r-- | examples/rp/src/bin/ws2812-pio.rs | 142 |
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" | |||
| 28 | st7789 = "0.6.1" | 28 | st7789 = "0.6.1" |
| 29 | display-interface = "0.4.1" | 29 | display-interface = "0.4.1" |
| 30 | byte-slice-cast = { version = "1.2.0", default-features = false } | 30 | byte-slice-cast = { version = "1.2.0", default-features = false } |
| 31 | smart-leds = "0.3.0" | ||
| 31 | 32 | ||
| 32 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } | 33 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } |
| 33 | embedded-hal-async = "0.2.0-alpha.0" | 34 | embedded-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 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_rp::gpio::{self, Pin}; | ||
| 8 | use embassy_rp::pio::{ | ||
| 9 | FifoJoin, PioInstance, PioPeripheral, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstance, | ||
| 10 | }; | ||
| 11 | use embassy_rp::pio_instr_util; | ||
| 12 | use embassy_rp::relocate::RelocatedProgram; | ||
| 13 | use embassy_time::{Duration, Timer}; | ||
| 14 | use smart_leds::RGB8; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | pub struct Ws2812<P: PioInstance, S: SmInstance> { | ||
| 17 | sm: PioStateMachineInstance<P, S>, | ||
| 18 | } | ||
| 19 | |||
| 20 | impl<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. | ||
| 100 | fn 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] | ||
| 114 | async 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 | } | ||
