From 2546d26cf2748a37b74aac4dbdab3ff4df460cc1 Mon Sep 17 00:00:00 2001 From: Robert Williams <1266467+bobdoah@users.noreply.github.com> Date: Sun, 26 Oct 2025 21:33:05 +0000 Subject: [PATCH 1/6] embassy-rp: add color order to ws2812 pio program --- embassy-rp/src/pio_programs/ws2812.rs | 93 ++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 15 deletions(-) diff --git a/embassy-rp/src/pio_programs/ws2812.rs b/embassy-rp/src/pio_programs/ws2812.rs index e6851b1a63..3c16367e69 100644 --- a/embassy-rp/src/pio_programs/ws2812.rs +++ b/embassy-rp/src/pio_programs/ws2812.rs @@ -1,21 +1,67 @@ -//! [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; use smart_leds::{RGB8, RGBW}; -use crate::Peri; use crate::clocks::clk_sys_freq; use crate::dma::{AnyChannel, Channel}; use crate::pio::{ Common, Config, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine, }; +use crate::Peri; 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; +/// This trait defines the contract for color ordering +pub trait ColorOrder { + type ColorType; + + // Pack the type specific struct into a u32 word in the correct order + fn pack(color: Self::ColorType) -> u32; +} + +/// Color orders for WS2812B, type RGB8 +/// Green, Red, Blue order is the common default for WS2812B +pub struct Grb; +impl ColorOrder for Grb { + type ColorType = RGB8; + fn pack(color: Self::ColorType) -> 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 ColorOrder for Rgb { + type ColorType = RGB8; + fn pack(color: Self::ColorType) -> u32 { + (u32::from(color.r) << 24) | (u32::from(color.g) << 16) | (u32::from(color.b) << 8) + } +} + +/// Color orders RGBW strips +/// Green, Red, Blue, White order is the common default for RGBW strips +pub struct Grbw; +impl ColorOrder for Grbw { + type ColorType = RGBW; + fn pack(color: Self::ColorType) -> 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 ColorOrder for Rgbw { + type ColorType = RGBW; + fn pack(color: Self::ColorType) -> 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,12 +98,19 @@ 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 = Grb> +where + ORDER: ColorOrder, +{ 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, ORDER = Grb> PioWs2812<'d, P, S, N, ORDER> +where + ORDER: ColorOrder, +{ /// Configure a pio state machine to use the loaded ws2812 program. pub fn new( pio: &mut Common<'d, P>, @@ -93,7 +146,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 +158,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,12 +171,19 @@ 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 = Grbw> +where + ORDER: ColorOrder>, +{ 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, ORDER = Grbw> RgbwPioWs2812<'d, P, S, N, ORDER> +where + ORDER: ColorOrder>, +{ /// Configure a pio state machine to use the loaded ws2812 program. pub fn new( pio: &mut Common<'d, P>, @@ -156,7 +219,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 +231,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 From 07f45bd12a355a9775e1dac472ee127e47ed3624 Mon Sep 17 00:00:00 2001 From: Robert Williams <1266467+bobdoah@users.noreply.github.com> Date: Mon, 27 Oct 2025 21:44:53 +0000 Subject: [PATCH 2/6] fix: split traits to avoid inference problems --- embassy-rp/src/pio_programs/ws2812.rs | 42 ++++++++++++--------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/embassy-rp/src/pio_programs/ws2812.rs b/embassy-rp/src/pio_programs/ws2812.rs index 3c16367e69..38f95facbe 100644 --- a/embassy-rp/src/pio_programs/ws2812.rs +++ b/embassy-rp/src/pio_programs/ws2812.rs @@ -16,48 +16,44 @@ const T2: u8 = 5; // data bit const T3: u8 = 3; // stop bit const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32; -/// This trait defines the contract for color ordering -pub trait ColorOrder { - type ColorType; - - // Pack the type specific struct into a u32 word in the correct order - fn pack(color: Self::ColorType) -> u32; +/// Color orders for WS2812B, type RGB8 +pub trait RgbColorOrder { + fn pack(color: RGB8) -> u32; } -/// Color orders for WS2812B, type RGB8 /// Green, Red, Blue order is the common default for WS2812B pub struct Grb; -impl ColorOrder for Grb { - type ColorType = RGB8; - fn pack(color: Self::ColorType) -> u32 { +impl RgbColorOrder for Grb { + 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 ColorOrder for Rgb { - type ColorType = RGB8; - fn pack(color: Self::ColorType) -> u32 { +impl RgbColorOrder for Rgb { + 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 { + fn pack(color: RGBW) -> u32; +} + /// Green, Red, Blue, White order is the common default for RGBW strips pub struct Grbw; -impl ColorOrder for Grbw { - type ColorType = RGBW; - fn pack(color: Self::ColorType) -> u32 { +impl RgbwColorOrder for Grbw { + 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 ColorOrder for Rgbw { - type ColorType = RGBW; - fn pack(color: Self::ColorType) -> u32 { +impl RgbwColorOrder for Rgbw { + 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) } } @@ -100,7 +96,7 @@ impl<'a, PIO: Instance> PioWs2812Program<'a, PIO> { /// Const N is the number of ws2812 leds attached to this pin pub struct PioWs2812<'d, P: Instance, const S: usize, const N: usize, ORDER = Grb> where - ORDER: ColorOrder, + ORDER: RgbColorOrder, { dma: Peri<'d, AnyChannel>, sm: StateMachine<'d, P, S>, @@ -109,7 +105,7 @@ where impl<'d, P: Instance, const S: usize, const N: usize, ORDER = Grb> PioWs2812<'d, P, S, N, ORDER> where - ORDER: ColorOrder, + ORDER: RgbColorOrder, { /// Configure a pio state machine to use the loaded ws2812 program. pub fn new( @@ -173,7 +169,7 @@ where /// Const N is the number of ws2812 leds attached to this pin pub struct RgbwPioWs2812<'d, P: Instance, const S: usize, const N: usize, ORDER = Grbw> where - ORDER: ColorOrder>, + ORDER: RgbwColorOrder, { dma: Peri<'d, AnyChannel>, sm: StateMachine<'d, P, S>, @@ -182,7 +178,7 @@ where impl<'d, P: Instance, const S: usize, const N: usize, ORDER = Grbw> RgbwPioWs2812<'d, P, S, N, ORDER> where - ORDER: ColorOrder>, + ORDER: RgbwColorOrder, { /// Configure a pio state machine to use the loaded ws2812 program. pub fn new( From aa6b6e08dcb9105a672cece1a9a17bbf3e89a4be Mon Sep 17 00:00:00 2001 From: Robert Williams <1266467+bobdoah@users.noreply.github.com> Date: Tue, 28 Oct 2025 19:51:57 +0000 Subject: [PATCH 3/6] split into multiple methods --- embassy-rp/src/pio_programs/ws2812.rs | 36 ++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/embassy-rp/src/pio_programs/ws2812.rs b/embassy-rp/src/pio_programs/ws2812.rs index 38f95facbe..20f7d827b0 100644 --- a/embassy-rp/src/pio_programs/ws2812.rs +++ b/embassy-rp/src/pio_programs/ws2812.rs @@ -94,7 +94,7 @@ 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, ORDER = Grb> +pub struct PioWs2812<'d, P: Instance, const S: usize, const N: usize, ORDER> where ORDER: RgbColorOrder, { @@ -103,12 +103,24 @@ where _order: core::marker::PhantomData, } -impl<'d, P: Instance, const S: usize, const N: usize, ORDER = Grb> PioWs2812<'d, P, S, N, ORDER> +impl<'d, P: Instance, const S: usize, const N: usize> PioWs2812<'d, P, S, N, Grb> { + 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. - pub fn new( + pub fn with_color_order( pio: &mut Common<'d, P>, mut sm: StateMachine<'d, P, S>, dma: Peri<'d, impl Channel>, @@ -167,7 +179,7 @@ where /// 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, ORDER = Grbw> +pub struct RgbwPioWs2812<'d, P: Instance, const S: usize, const N: usize, ORDER> where ORDER: RgbwColorOrder, { @@ -176,12 +188,24 @@ where _order: core::marker::PhantomData, } -impl<'d, P: Instance, const S: usize, const N: usize, ORDER = Grbw> RgbwPioWs2812<'d, P, S, N, ORDER> +impl<'d, P: Instance, const S: usize, const N: usize> RgbwPioWs2812<'d, P, S, N, Grbw> { + 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. - pub fn new( + pub fn with_color_order( pio: &mut Common<'d, P>, mut sm: StateMachine<'d, P, S>, dma: Peri<'d, impl Channel>, From 52d514652b2388812ba6547c0e4743938206cf42 Mon Sep 17 00:00:00 2001 From: Robert Williams <1266467+bobdoah@users.noreply.github.com> Date: Wed, 29 Oct 2025 19:59:12 +0000 Subject: [PATCH 4/6] docs: color order methods traits missing docs --- embassy-rp/src/pio_programs/ws2812.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/embassy-rp/src/pio_programs/ws2812.rs b/embassy-rp/src/pio_programs/ws2812.rs index 20f7d827b0..3c4de6b56f 100644 --- a/embassy-rp/src/pio_programs/ws2812.rs +++ b/embassy-rp/src/pio_programs/ws2812.rs @@ -18,12 +18,14 @@ 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) } @@ -32,6 +34,7 @@ impl RgbColorOrder for Grb { /// 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) } @@ -39,12 +42,14 @@ impl RgbColorOrder for Rgb { /// 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) } @@ -53,6 +58,7 @@ impl RgbwColorOrder for Grbw { /// 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) } @@ -104,6 +110,8 @@ where } 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>, @@ -120,6 +128,7 @@ 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>, @@ -189,6 +198,8 @@ where } 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>, @@ -205,6 +216,7 @@ 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>, From 4177e44e9040a0c0c09830bd66f66d188e5ac53a Mon Sep 17 00:00:00 2001 From: Robert Williams <1266467+bobdoah@users.noreply.github.com> Date: Mon, 3 Nov 2025 21:36:38 +0000 Subject: [PATCH 5/6] fix formatting --- embassy-rp/src/pio_programs/ws2812.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/pio_programs/ws2812.rs b/embassy-rp/src/pio_programs/ws2812.rs index 3c4de6b56f..0b60353168 100644 --- a/embassy-rp/src/pio_programs/ws2812.rs +++ b/embassy-rp/src/pio_programs/ws2812.rs @@ -4,12 +4,12 @@ use embassy_time::Timer; use fixed::types::U24F8; use smart_leds::{RGB8, RGBW}; +use crate::Peri; use crate::clocks::clk_sys_freq; use crate::dma::{AnyChannel, Channel}; use crate::pio::{ Common, Config, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine, }; -use crate::Peri; const T1: u8 = 2; // start bit const T2: u8 = 5; // data bit From f5a7d581da5ff631802cc1fab4265b402f4ef9cf Mon Sep 17 00:00:00 2001 From: Robert Williams <1266467+bobdoah@users.noreply.github.com> Date: Mon, 3 Nov 2025 21:49:33 +0000 Subject: [PATCH 6/6] add missing changelog entry --- embassy-rp/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) 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