diff --git a/CHANGELOG.md b/CHANGELOG.md index bf306ca..f637db4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - Add a `reset_single_transaction` feature for the `prerendered` version, which uses a single SPI transaction for a `write` call. Can be useful if using a DMA-approach or you're experiencing glitches due to the SPI peripheral turning high between the data and the reset. +- Add `pixel_order` to allow for non-standard pixel orders ### Changed - Using the `mosi_idle_high` feature with the `prerendered` version now sends out the first reset together with the data, thus requiring a larger data buffer (but avoids potentially long dead-time between the first reset and the data being sent). diff --git a/src/hosted.rs b/src/hosted.rs index a57cef7..332caa2 100644 --- a/src/hosted.rs +++ b/src/hosted.rs @@ -3,7 +3,7 @@ //! This dynamically allocates an output buffer and writes out the data in a single call. //! Much better suited for linux or similar environments, but may not always work //! -//! Intendded for use with rppal or linux-embedded-hal +//! Intended for use with rppal or linux-embedded-hal use embedded_hal as hal; @@ -16,6 +16,9 @@ use smart_leds_trait::{SmartLedsWrite, RGB8, RGBW}; use std::vec; use std::vec::Vec; +use crate::pixel_order; +use crate::OrderedColors; + /// SPI mode that can be used for this crate /// /// Provided for convenience @@ -30,15 +33,17 @@ pub mod devices { pub struct Sk6812w; } -pub struct Ws2812 { +pub struct Ws2812 { spi: SPI, data: Vec, - device: PhantomData, + _device: PhantomData, + _pixel_order: PhantomData, } -impl Ws2812 +impl Ws2812 where SPI: SpiBus, + PO: OrderedColors, { /// Use ws2812 devices via spi /// @@ -52,7 +57,8 @@ where Self { spi, data, - device: PhantomData {}, + _device: PhantomData {}, + _pixel_order: PhantomData {}, } } } @@ -75,12 +81,13 @@ where Self { spi, data, - device: PhantomData {}, + _device: PhantomData {}, + _pixel_order: PhantomData {}, } } } -impl Ws2812 +impl Ws2812 where SPI: SpiBus, { @@ -105,9 +112,10 @@ where } } -impl SmartLedsWrite for Ws2812 +impl SmartLedsWrite for Ws2812 where SPI: SpiBus, + PO: OrderedColors, { type Error = E; type Color = RGB8; @@ -118,10 +126,11 @@ where I: Into, { for item in iterator { - let item = item.into(); - self.write_byte(item.g); - self.write_byte(item.r); - self.write_byte(item.b); + let color: RGB8 = item.into(); + let ordered_color = PO::order(color); + self.write_byte(ordered_color[0]); + self.write_byte(ordered_color[1]); + self.write_byte(ordered_color[2]); } self.send_data() } @@ -141,6 +150,7 @@ where { for item in iterator { let item = item.into(); + // SK6812W always expects GRBW order self.write_byte(item.g); self.write_byte(item.r); self.write_byte(item.b); diff --git a/src/lib.rs b/src/lib.rs index 3221378..dd1050c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,14 +39,51 @@ pub mod devices { pub struct Sk6812w; } -pub struct Ws2812 { +/// The color order the WS2812-like device expects. In most cases, this is GRB +/// and doesn't need to be specified separately. +/// +/// For SK6812W devices (with a separate white channel) this is unused +pub mod pixel_order { + pub struct RGB; + pub struct RBG; + pub struct GRB; + pub struct GBR; + pub struct BRG; + pub struct BGR; +} + +/// Used to define the pixel order, refer to the pixel_order module +pub trait OrderedColors { + fn order(color: RGB8) -> [u8; 3]; +} + +macro_rules! impl_ordered_colors { + ($struct_name:ident, $r_field:ident, $g_field:ident, $b_field:ident) => { + impl OrderedColors for pixel_order::$struct_name { + fn order(color: RGB8) -> [u8; 3] { + [color.$r_field, color.$g_field, color.$b_field] + } + } + }; +} + +impl_ordered_colors!(RGB, r, g, b); +impl_ordered_colors!(RBG, r, b, g); +impl_ordered_colors!(GRB, g, r, b); +impl_ordered_colors!(GBR, g, b, r); +impl_ordered_colors!(BRG, b, r, g); +impl_ordered_colors!(BGR, b, g, r); + +pub struct Ws2812 { spi: SPI, - device: PhantomData, + _device: PhantomData, + _pixel_order: PhantomData, } -impl Ws2812 +impl Ws2812 where SPI: SpiBus, + PO: OrderedColors, { /// Use ws2812 devices via spi /// @@ -59,12 +96,13 @@ where pub fn new(spi: SPI) -> Self { Self { spi, - device: PhantomData {}, + _device: PhantomData {}, + _pixel_order: PhantomData {}, } } } -impl Ws2812 +impl Ws2812 where SPI: SpiBus, { @@ -81,12 +119,13 @@ where pub fn new_sk6812w(spi: SPI) -> Self { Self { spi, - device: PhantomData {}, + _device: PhantomData {}, + _pixel_order: PhantomData {}, } } } -impl Ws2812 +impl Ws2812 where SPI: SpiBus, { @@ -113,7 +152,7 @@ where } } -impl SmartLedsWrite for Ws2812 +impl SmartLedsWrite for Ws2812 where SPI: SpiBus, { @@ -130,17 +169,18 @@ where } for item in iterator { - let item = item.into(); - self.write_byte(item.g)?; - self.write_byte(item.r)?; - self.write_byte(item.b)?; + let color: RGB8 = item.into(); + let ordered_color = PO::order(color); + self.write_byte(ordered_color[0])?; + self.write_byte(ordered_color[1])?; + self.write_byte(ordered_color[2])?; } self.reset()?; Ok(()) } } -impl SmartLedsWrite for Ws2812 +impl SmartLedsWrite for Ws2812 where SPI: SpiBus, { @@ -158,6 +198,7 @@ where for item in iterator { let item = item.into(); + // SK6812W always expects GRBW order self.write_byte(item.g)?; self.write_byte(item.r)?; self.write_byte(item.b)?; diff --git a/src/prerendered.rs b/src/prerendered.rs index d6124dc..6245355 100644 --- a/src/prerendered.rs +++ b/src/prerendered.rs @@ -13,6 +13,9 @@ use smart_leds_trait::{SmartLedsWrite, RGB8, RGBW}; const RESET_DATA_LEN: usize = 140; +use crate::pixel_order; +use crate::OrderedColors; + /// SPI mode that can be used for this crate /// /// Provided for convenience @@ -33,16 +36,18 @@ pub mod devices { pub struct Sk6812w; } -pub struct Ws2812<'a, SPI, DEVICE = devices::Ws2812> { +pub struct Ws2812<'a, SPI, DEVICE = devices::Ws2812, PIXELORDER = pixel_order::GRB> { spi: SPI, data: &'a mut [u8], index: usize, - device: PhantomData, + _device: PhantomData, + _pixel_order: PhantomData, } -impl<'a, SPI, E> Ws2812<'a, SPI> +impl<'a, SPI, E, PO> Ws2812<'a, SPI, devices::Ws2812, PO> where SPI: SpiBus, + PO: OrderedColors, { /// Use WS2812 devices via SPI /// @@ -63,12 +68,13 @@ where spi, data, index: 0, - device: PhantomData {}, + _device: PhantomData {}, + _pixel_order: PhantomData {}, } } } -impl<'a, SPI, E> Ws2812<'a, SPI, devices::Sk6812w> +impl<'a, SPI, E, PO> Ws2812<'a, SPI, devices::Sk6812w, PO> where SPI: SpiBus, { @@ -93,12 +99,13 @@ where spi, data, index: 0, - device: PhantomData {}, + _device: PhantomData {}, + _pixel_order: PhantomData {}, } } } -impl<'a, SPI, D, E> Ws2812<'a, SPI, D> +impl Ws2812<'_, SPI, D, PO> where SPI: SpiBus, { @@ -148,9 +155,10 @@ where } } -impl<'a, SPI, E> SmartLedsWrite for Ws2812<'a, SPI> +impl SmartLedsWrite for Ws2812<'_, SPI, devices::Ws2812, PO> where SPI: SpiBus, + PO: OrderedColors, { type Error = Error; type Color = RGB8; @@ -167,10 +175,11 @@ where } for item in iterator { - let item = item.into(); - self.write_byte(item.g)?; - self.write_byte(item.r)?; - self.write_byte(item.b)?; + let color: RGB8 = item.into(); + let ordered_color = PO::order(color); + self.write_byte(ordered_color[0])?; + self.write_byte(ordered_color[1])?; + self.write_byte(ordered_color[2])?; } if cfg!(feature = "reset_single_transaction") { @@ -186,7 +195,7 @@ where } } -impl<'a, SPI, E> SmartLedsWrite for Ws2812<'a, SPI, devices::Sk6812w> +impl SmartLedsWrite for Ws2812<'_, SPI, devices::Sk6812w, PO> where SPI: SpiBus, { @@ -206,6 +215,7 @@ where for item in iterator { let item = item.into(); + // SK6812W always expects GRBW order self.write_byte(item.g)?; self.write_byte(item.r)?; self.write_byte(item.b)?;