diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md index 4fab20f082..3b3cb5351b 100644 --- a/embassy-rp/CHANGELOG.md +++ b/embassy-rp/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - rp235x: use msplim for stack guard instead of MPU - Add reset_to_usb_boot for rp235x ([#4705](https://github.com/embassy-rs/embassy/pull/4705)) - Add fix #4822 in PIO onewire. Change to disable the state machine before setting y register ([#4824](https://github.com/embassy-rs/embassy/pull/4824)) +- Add PIO::Ws2812 color order support ## 0.8.0 - 2025-08-26 diff --git a/embassy-rp/src/pio_programs/ws2812.rs b/embassy-rp/src/pio_programs/ws2812.rs index e6851b1a63..0b60353168 100644 --- a/embassy-rp/src/pio_programs/ws2812.rs +++ b/embassy-rp/src/pio_programs/ws2812.rs @@ -1,4 +1,4 @@ -//! [ws2812](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) +//! [ws2812](https://www.sparkfun.com/categories/tags/ws2812) use embassy_time::Timer; use fixed::types::U24F8; @@ -16,6 +16,54 @@ const T2: u8 = 5; // data bit const T3: u8 = 3; // stop bit const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32; +/// Color orders for WS2812B, type RGB8 +pub trait RgbColorOrder { + /// Pack an 8-bit RGB color into a u32 + fn pack(color: RGB8) -> u32; +} + +/// Green, Red, Blue order is the common default for WS2812B +pub struct Grb; +impl RgbColorOrder for Grb { + /// Pack an 8-bit RGB color into a u32 in GRB order + fn pack(color: RGB8) -> u32 { + (u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8) + } +} + +/// Red, Green, Blue is used by some WS2812B implementations +pub struct Rgb; +impl RgbColorOrder for Rgb { + /// Pack an 8-bit RGB color into a u32 in RGB order + fn pack(color: RGB8) -> u32 { + (u32::from(color.r) << 24) | (u32::from(color.g) << 16) | (u32::from(color.b) << 8) + } +} + +/// Color orders RGBW strips +pub trait RgbwColorOrder { + /// Pack an RGB+W color into a u32 + fn pack(color: RGBW) -> u32; +} + +/// Green, Red, Blue, White order is the common default for RGBW strips +pub struct Grbw; +impl RgbwColorOrder for Grbw { + /// Pack an RGB+W color into a u32 in GRBW order + fn pack(color: RGBW) -> u32 { + (u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8) | u32::from(color.a.0) + } +} + +/// Red, Green, Blue, White order +pub struct Rgbw; +impl RgbwColorOrder for Rgbw { + /// Pack an RGB+W color into a u32 in RGBW order + fn pack(color: RGBW) -> u32 { + (u32::from(color.r) << 24) | (u32::from(color.g) << 16) | (u32::from(color.b) << 8) | u32::from(color.a.0) + } +} + /// This struct represents a ws2812 program loaded into pio instruction memory. pub struct PioWs2812Program<'a, PIO: Instance> { prg: LoadedProgram<'a, PIO>, @@ -52,14 +100,36 @@ impl<'a, PIO: Instance> PioWs2812Program<'a, PIO> { /// Pio backed RGB ws2812 driver /// Const N is the number of ws2812 leds attached to this pin -pub struct PioWs2812<'d, P: Instance, const S: usize, const N: usize> { +pub struct PioWs2812<'d, P: Instance, const S: usize, const N: usize, ORDER> +where + ORDER: RgbColorOrder, +{ dma: Peri<'d, AnyChannel>, sm: StateMachine<'d, P, S>, + _order: core::marker::PhantomData, } -impl<'d, P: Instance, const S: usize, const N: usize> PioWs2812<'d, P, S, N> { +impl<'d, P: Instance, const S: usize, const N: usize> PioWs2812<'d, P, S, N, Grb> { /// Configure a pio state machine to use the loaded ws2812 program. + /// Uses the default GRB order. pub fn new( + pio: &mut Common<'d, P>, + sm: StateMachine<'d, P, S>, + dma: Peri<'d, impl Channel>, + pin: Peri<'d, impl PioPin>, + program: &PioWs2812Program<'d, P>, + ) -> Self { + Self::with_color_order(pio, sm, dma, pin, program) + } +} + +impl<'d, P: Instance, const S: usize, const N: usize, ORDER> PioWs2812<'d, P, S, N, ORDER> +where + ORDER: RgbColorOrder, +{ + /// Configure a pio state machine to use the loaded ws2812 program. + /// Uses the specified color order. + pub fn with_color_order( pio: &mut Common<'d, P>, mut sm: StateMachine<'d, P, S>, dma: Peri<'d, impl Channel>, @@ -93,7 +163,11 @@ impl<'d, P: Instance, const S: usize, const N: usize> PioWs2812<'d, P, S, N> { sm.set_config(&cfg); sm.set_enable(true); - Self { dma: dma.into(), sm } + Self { + dma: dma.into(), + sm, + _order: core::marker::PhantomData, + } } /// Write a buffer of [smart_leds::RGB8] to the ws2812 string @@ -101,8 +175,7 @@ impl<'d, P: Instance, const S: usize, const N: usize> PioWs2812<'d, P, S, 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; + words[i] = ORDER::pack(colors[i]); } // DMA transfer @@ -115,14 +188,36 @@ impl<'d, P: Instance, const S: usize, const N: usize> PioWs2812<'d, P, S, N> { /// Pio backed RGBW ws2812 driver /// This version is intended for ws2812 leds with 4 addressable lights /// Const N is the number of ws2812 leds attached to this pin -pub struct RgbwPioWs2812<'d, P: Instance, const S: usize, const N: usize> { +pub struct RgbwPioWs2812<'d, P: Instance, const S: usize, const N: usize, ORDER> +where + ORDER: RgbwColorOrder, +{ dma: Peri<'d, AnyChannel>, sm: StateMachine<'d, P, S>, + _order: core::marker::PhantomData, } -impl<'d, P: Instance, const S: usize, const N: usize> RgbwPioWs2812<'d, P, S, N> { +impl<'d, P: Instance, const S: usize, const N: usize> RgbwPioWs2812<'d, P, S, N, Grbw> { /// Configure a pio state machine to use the loaded ws2812 program. + /// Uses the default GRBW color order pub fn new( + pio: &mut Common<'d, P>, + sm: StateMachine<'d, P, S>, + dma: Peri<'d, impl Channel>, + pin: Peri<'d, impl PioPin>, + program: &PioWs2812Program<'d, P>, + ) -> Self { + Self::with_color_order(pio, sm, dma, pin, program) + } +} + +impl<'d, P: Instance, const S: usize, const N: usize, ORDER> RgbwPioWs2812<'d, P, S, N, ORDER> +where + ORDER: RgbwColorOrder, +{ + /// Configure a pio state machine to use the loaded ws2812 program. + /// Uses the specified color order + pub fn with_color_order( pio: &mut Common<'d, P>, mut sm: StateMachine<'d, P, S>, dma: Peri<'d, impl Channel>, @@ -156,7 +251,11 @@ impl<'d, P: Instance, const S: usize, const N: usize> RgbwPioWs2812<'d, P, S, N> sm.set_config(&cfg); sm.set_enable(true); - Self { dma: dma.into(), sm } + Self { + dma: dma.into(), + sm, + _order: core::marker::PhantomData, + } } /// Write a buffer of [smart_leds::RGBW] to the ws2812 string @@ -164,11 +263,7 @@ impl<'d, P: Instance, const S: usize, const N: usize> RgbwPioWs2812<'d, P, S, 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) - | u32::from(colors[i].a.0); - words[i] = word; + words[i] = ORDER::pack(colors[i]); } // DMA transfer