Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions embassy-rp/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
123 changes: 109 additions & 14 deletions embassy-rp/src/pio_programs/ws2812.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<u8>) -> 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<u8>) -> 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<u8>) -> 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>,
Expand Down Expand Up @@ -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<ORDER>,
}

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>,
Expand Down Expand Up @@ -93,16 +163,19 @@ 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
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;
words[i] = ORDER::pack(colors[i]);
}

// DMA transfer
Expand All @@ -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<ORDER>,
}

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>,
Expand Down Expand Up @@ -156,19 +251,19 @@ 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
pub async fn write(&mut self, colors: &[RGBW<u8>; 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
Expand Down