From 1be6e533165cb773979739c203b745df24ff1ecf Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Tue, 9 May 2023 17:05:39 -0400 Subject: Pin fix, improve fifo handling --- examples/rp/src/bin/pio_ws2812.rs | 132 ++++++++++++++++++++++++++++++++++++++ examples/rp/src/bin/ws2812-pio.rs | 128 ------------------------------------ 2 files changed, 132 insertions(+), 128 deletions(-) create mode 100644 examples/rp/src/bin/pio_ws2812.rs delete mode 100644 examples/rp/src/bin/ws2812-pio.rs diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs new file mode 100644 index 000000000..4915c7e92 --- /dev/null +++ b/examples/rp/src/bin/pio_ws2812.rs @@ -0,0 +1,132 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::pio::{Common, Config, FifoJoin, Instance, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; +use embassy_rp::relocate::RelocatedProgram; +use embassy_time::{Duration, Timer}; +use fixed_macro::fixed; +use smart_leds::RGB8; +use {defmt_rtt as _, panic_probe as _}; +pub struct Ws2812<'d, P: Instance, const S: usize> { + sm: StateMachine<'d, P, S>, +} + +impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { + pub fn new(mut pio: Common<'d, P>, mut sm: StateMachine<'d, P, S>, pin: impl PioPin) -> Self { + // Setup sm0 + + // prepare the PIO program + let side_set = pio::SideSet::new(false, 1, false); + let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set); + + const T1: u8 = 2; // start bit + const T2: u8 = 5; // data bit + const T3: u8 = 3; // stop bit + const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32; + + let mut wrap_target = a.label(); + let mut wrap_source = a.label(); + let mut do_zero = a.label(); + a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0); + a.bind(&mut wrap_target); + // Do stop bit + a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0); + // Do start bit + a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1); + // Do data bit = 1 + a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1); + a.bind(&mut do_zero); + // Do data bit = 0 + a.nop_with_delay_and_side_set(T2 - 1, 0); + a.bind(&mut wrap_source); + + let prg = a.assemble_with_wrap(wrap_source, wrap_target); + let mut cfg = Config::default(); + + // Pin config + let out_pin = pio.make_pio_pin(pin); + cfg.set_out_pins(&[&out_pin]); + cfg.set_set_pins(&[&out_pin]); + + let relocated = RelocatedProgram::new(&prg); + cfg.use_program(&pio.load_program(&relocated), &[&out_pin]); + + // Clock config, measured in kHz to avoid overflows + // TODO CLOCK_FREQ should come from embassy_rp + let clock_freq = fixed!(125_000: U24F8); + let ws2812_freq = fixed!(800: U24F8); + let bit_freq = ws2812_freq * CYCLES_PER_BIT; + cfg.clock_divider = clock_freq / bit_freq; + + // FIFO config + cfg.fifo_join = FifoJoin::TxOnly; + cfg.shift_out = ShiftConfig { + auto_fill: true, + threshold: 24, + direction: ShiftDirection::Left, + }; + + sm.set_config(&cfg); + sm.set_enable(true); + + Self { sm } + } + + pub async fn write(&mut self, colors: &[RGB8]) { + for color in colors { + let word = (u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8); + if !self.sm.tx().try_push(word) { + self.sm.tx().wait_push(word).await; + } + } + } +} + +/// Input a value 0 to 255 to get a color value +/// The colours are a transition r - g - b - back to r. +fn wheel(mut wheel_pos: u8) -> RGB8 { + wheel_pos = 255 - wheel_pos; + if wheel_pos < 85 { + return (255 - wheel_pos * 3, 0, wheel_pos * 3).into(); + } + if wheel_pos < 170 { + wheel_pos -= 85; + return (0, wheel_pos * 3, 255 - wheel_pos * 3).into(); + } + wheel_pos -= 170; + (wheel_pos * 3, 255 - wheel_pos * 3, 0).into() +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Start"); + let p = embassy_rp::init(Default::default()); + + let Pio { common, sm0, .. } = Pio::new(p.PIO0); + + // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit + // feather boards for the 2040 both have one built in. + const NUM_LEDS: usize = 1; + let mut data = [RGB8::default(); NUM_LEDS]; + + // For the thing plus, use pin 8 + // For the feather, use pin 16 + let mut ws2812 = Ws2812::new(common, sm0, p.PIN_8); + + // Loop forever making RGB values and pushing them out to the WS2812. + loop { + for j in 0..(256 * 5) { + debug!("New Colors:"); + for i in 0..NUM_LEDS { + data[i] = wheel((((i * 256) as u16 / NUM_LEDS as u16 + j as u16) & 255) as u8); + debug!("R: {} G: {} B: {}", data[i].r, data[i].g, data[i].b); + } + ws2812.write(&data).await; + + Timer::after(Duration::from_micros(5)).await; + } + } +} diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs deleted file mode 100644 index f4c2d6313..000000000 --- a/examples/rp/src/bin/ws2812-pio.rs +++ /dev/null @@ -1,128 +0,0 @@ -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] - -use defmt::*; -use embassy_executor::Spawner; -use embassy_rp::pio::{Common, Config, FifoJoin, Instance, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; -use embassy_rp::relocate::RelocatedProgram; -use embassy_time::{Duration, Timer}; -use fixed_macro::fixed; -use smart_leds::RGB8; -use {defmt_rtt as _, panic_probe as _}; -pub struct Ws2812<'d, P: Instance, const S: usize> { - sm: StateMachine<'d, P, S>, -} - -impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { - pub fn new(mut pio: Common<'d, P>, mut sm: StateMachine<'d, P, S>, pin: impl PioPin) -> Self { - // Setup sm0 - - // prepare the PIO program - let side_set = pio::SideSet::new(false, 1, false); - let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set); - - const T1: u8 = 2; // start bit - const T2: u8 = 5; // data bit - const T3: u8 = 3; // stop bit - const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32; - - let mut wrap_target = a.label(); - let mut wrap_source = a.label(); - let mut do_zero = a.label(); - a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0); - a.bind(&mut wrap_target); - // Do stop bit - a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0); - // Do start bit - a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1); - // Do data bit = 1 - a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1); - a.bind(&mut do_zero); - // Do data bit = 0 - a.nop_with_delay_and_side_set(T2 - 1, 0); - a.bind(&mut wrap_source); - - let prg = a.assemble_with_wrap(wrap_source, wrap_target); - let mut cfg = Config::default(); - - // Pin config - let out_pin = pio.make_pio_pin(pin); - - let relocated = RelocatedProgram::new(&prg); - cfg.use_program(&pio.load_program(&relocated), &[&out_pin]); - - // Clock config, measured in kHz to avoid overflows - // TODO CLOCK_FREQ should come from embassy_rp - let clock_freq = fixed!(125_000: U24F8); - let ws2812_freq = fixed!(800: U24F8); - let bit_freq = ws2812_freq * CYCLES_PER_BIT; - cfg.clock_divider = clock_freq / bit_freq; - - // FIFO config - cfg.fifo_join = FifoJoin::TxOnly; - cfg.shift_out = ShiftConfig { - auto_fill: true, - threshold: 24, - direction: ShiftDirection::Left, - }; - - sm.set_config(&cfg); - sm.set_enable(true); - - Self { sm } - } - - pub async fn write(&mut self, colors: &[RGB8]) { - for color in colors { - let word = (u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8); - self.sm.tx().wait_push(word).await; - } - } -} - -/// Input a value 0 to 255 to get a color value -/// The colours are a transition r - g - b - back to r. -fn wheel(mut wheel_pos: u8) -> RGB8 { - wheel_pos = 255 - wheel_pos; - if wheel_pos < 85 { - return (255 - wheel_pos * 3, 0, wheel_pos * 3).into(); - } - if wheel_pos < 170 { - wheel_pos -= 85; - return (0, wheel_pos * 3, 255 - wheel_pos * 3).into(); - } - wheel_pos -= 170; - (wheel_pos * 3, 255 - wheel_pos * 3, 0).into() -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - info!("Start"); - let p = embassy_rp::init(Default::default()); - - let Pio { common, sm0, .. } = Pio::new(p.PIO0); - - // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit - // feather boards for the 2040 both have one built in. - const NUM_LEDS: usize = 1; - let mut data = [RGB8::default(); NUM_LEDS]; - - // For the thing plus, use pin 8 - // For the feather, use pin 16 - let mut ws2812 = Ws2812::new(common, sm0, p.PIN_8); - - // Loop forever making RGB values and pushing them out to the WS2812. - loop { - for j in 0..(256 * 5) { - debug!("New Colors:"); - for i in 0..NUM_LEDS { - data[i] = wheel((((i * 256) as u16 / NUM_LEDS as u16 + j as u16) & 255) as u8); - debug!("R: {} G: {} B: {}", data[i].r, data[i].g, data[i].b); - } - ws2812.write(&data).await; - - Timer::after(Duration::from_micros(5)).await; - } - } -} -- cgit From 1ebb742fbf9839ab393c038f320003a9346d37ff Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Fri, 19 May 2023 16:48:47 -0400 Subject: Switch to DMA, use new clocks, don't take ownership of pio common --- examples/rp/src/bin/pio_ws2812.rs | 44 +++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs index 4915c7e92..26422421f 100644 --- a/examples/rp/src/bin/pio_ws2812.rs +++ b/examples/rp/src/bin/pio_ws2812.rs @@ -4,18 +4,30 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::pio::{Common, Config, FifoJoin, Instance, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; use embassy_rp::relocate::RelocatedProgram; +use embassy_rp::{clocks, into_ref, Peripheral, PeripheralRef}; use embassy_time::{Duration, Timer}; +use fixed::types::U24F8; use fixed_macro::fixed; use smart_leds::RGB8; use {defmt_rtt as _, panic_probe as _}; -pub struct Ws2812<'d, P: Instance, const S: usize> { + +pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> { + dma: PeripheralRef<'d, AnyChannel>, sm: StateMachine<'d, P, S>, } -impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { - pub fn new(mut pio: Common<'d, P>, mut sm: StateMachine<'d, P, S>, pin: impl PioPin) -> Self { +impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> { + pub fn new( + pio: &mut Common<'d, P>, + mut sm: StateMachine<'d, P, S>, + dma: impl Peripheral

+ 'd, + pin: impl PioPin, + ) -> Self { + into_ref!(dma); + // Setup sm0 // prepare the PIO program @@ -56,7 +68,7 @@ impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { // Clock config, measured in kHz to avoid overflows // TODO CLOCK_FREQ should come from embassy_rp - let clock_freq = fixed!(125_000: U24F8); + let clock_freq = U24F8::from_num(clocks::clk_sys_freq() / 1000); let ws2812_freq = fixed!(800: U24F8); let bit_freq = ws2812_freq * CYCLES_PER_BIT; cfg.clock_divider = clock_freq / bit_freq; @@ -72,16 +84,22 @@ impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { sm.set_config(&cfg); sm.set_enable(true); - Self { sm } + Self { + dma: dma.map_into(), + sm, + } } - pub async fn write(&mut self, colors: &[RGB8]) { - for color in colors { - let word = (u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8); - if !self.sm.tx().try_push(word) { - self.sm.tx().wait_push(word).await; - } + pub async fn write(&mut self, colors: &[RGB8; N]) { + // Precompute the word bytes from the colors + let mut words = [0u32; N]; + for i in 0..N { + let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8); + words[i] = word; } + + // DMA transfer + self.sm.tx().dma_push(self.dma.reborrow(), &words).await; } } @@ -105,7 +123,7 @@ async fn main(_spawner: Spawner) { info!("Start"); let p = embassy_rp::init(Default::default()); - let Pio { common, sm0, .. } = Pio::new(p.PIO0); + let Pio { mut common, sm0, .. } = Pio::new(p.PIO0); // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit // feather boards for the 2040 both have one built in. @@ -114,7 +132,7 @@ async fn main(_spawner: Spawner) { // For the thing plus, use pin 8 // For the feather, use pin 16 - let mut ws2812 = Ws2812::new(common, sm0, p.PIN_8); + let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16); // Loop forever making RGB values and pushing them out to the WS2812. loop { -- cgit