Skip to content

Commit 7c10a4b

Browse files
Nashenas88melvyn2
authored andcommitted
Add support for RGBW and custom color layouts.
Adds direct support for RGBW color via smart_leds::RGBA8, and allows clients to implement custom support via new ColorFormat trait.
1 parent 5abb568 commit 7c10a4b

File tree

2 files changed

+115
-14
lines changed

2 files changed

+115
-14
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version = "0.9.0"
44
edition = "2021"
55
license = "Apache-2.0"
66
description = "Driver implementation for the WS2812 smart LED using the RP2040's PIO peripheral."
7-
documentation = "https://docs.rs/ws2812-pio"
7+
documentation = "https://docs.rs/ws2812-pio"
88
repository = "https://github.com/rp-rs/ws2812-pio-rs/"
99

1010
[dependencies]

src/lib.rs

Lines changed: 114 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
//! Bear in mind that you will have to take care of timing requirements
1414
//! yourself then.
1515
16+
use core::marker::PhantomData;
1617
use embedded_hal::timer::CountDown;
1718
use fugit::{ExtU32, HertzU32, MicrosDurationU32};
1819
use rp2040_hal::{
@@ -54,21 +55,47 @@ use smart_leds_trait_0_2::SmartLedsWrite as SmartLedsWrite02;
5455
/// delay_for_at_least_60_microseconds();
5556
/// };
5657
///```
57-
pub struct Ws2812Direct<P, SM, I>
58+
///
59+
/// Typical RGBW usage example:
60+
///```ignore
61+
/// use rp2040_hal::clocks::init_clocks_and_plls;
62+
/// let clocks = init_clocks_and_plls(...);
63+
/// let pins = rp2040_hal::gpio::pin::bank0::Pins::new(...);
64+
///
65+
/// let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
66+
/// let mut ws = Ws2812Direct::<_, _, _, smart_leds::RGBA8>::new(
67+
/// pins.gpio4.into_mode(),
68+
/// &mut pio,
69+
/// sm0,
70+
/// clocks.peripheral_clock.freq(),
71+
/// );
72+
///
73+
/// // Then you will make sure yourself to not write too frequently:
74+
/// loop {
75+
/// use smart_leds::{SmartLedsWrite, RGBA8};
76+
/// let color : RGBA8 = (255, 0, 255, 127).into();
77+
///
78+
/// ws.write([color].iter().copied()).unwrap();
79+
/// delay_for_at_least_60_microseconds();
80+
/// };
81+
///```
82+
pub struct Ws2812Direct<P, SM, I, CF = smart_leds_trait::RGB8>
5883
where
5984
I: AnyPin<Function = P::PinFunction>,
6085
P: PIOExt,
6186
SM: StateMachineIndex,
6287
{
6388
tx: Tx<(P, SM)>,
6489
_pin: I,
90+
_color_format: PhantomData<CF>,
6591
}
6692

67-
impl<P, SM, I> Ws2812Direct<P, SM, I>
93+
impl<P, SM, I, CF> Ws2812Direct<P, SM, I, CF>
6894
where
6995
I: AnyPin<Function = P::PinFunction>,
7096
P: PIOExt,
7197
SM: StateMachineIndex,
98+
CF: ColorFormat,
7299
{
73100
/// Creates a new instance of this driver.
74101
pub fn new(
@@ -134,7 +161,7 @@ where
134161
// OSR config
135162
.out_shift_direction(rp2040_hal::pio::ShiftDirection::Left)
136163
.autopull(true)
137-
.pull_threshold(24)
164+
.pull_threshold(<CF as ColorFormat>::COLOR_BYTES.num_bits())
138165
.clock_divisor_fixed_point(int, frac)
139166
.build(sm);
140167

@@ -146,17 +173,63 @@ where
146173
Self {
147174
tx,
148175
_pin: I::from(pin),
176+
_color_format: PhantomData,
149177
}
150178
}
151179
}
152180

153-
impl<P, SM, I> SmartLedsWrite for Ws2812Direct<P, SM, I>
181+
/// Specify whether to use 3 or 4 bytes per led color.
182+
pub enum ColorBytes {
183+
ThreeBytes,
184+
FourBytes,
185+
}
186+
187+
impl ColorBytes {
188+
const fn num_bits(&self) -> u8 {
189+
match self {
190+
ColorBytes::ThreeBytes => 24,
191+
ColorBytes::FourBytes => 32,
192+
}
193+
}
194+
}
195+
196+
/// Implement this trait to support a user-defined color format.
197+
///
198+
/// smart_leds::RGB8 and smart_leds::RGBA are implemented by the ws2812-pio
199+
/// crate.
200+
pub trait ColorFormat {
201+
/// Select the number of bytes per led.
202+
const COLOR_BYTES: ColorBytes;
203+
204+
/// Map the color to a 32-bit word.
205+
fn to_word(self) -> u32;
206+
}
207+
208+
impl ColorFormat for smart_leds_trait::RGB8 {
209+
const COLOR_BYTES: ColorBytes = ColorBytes::ThreeBytes;
210+
fn to_word(self) -> u32 {
211+
(u32::from(self.g) << 24) | (u32::from(self.r) << 16) | (u32::from(self.b) << 8)
212+
}
213+
}
214+
215+
impl ColorFormat for smart_leds_trait::RGBA<u8> {
216+
const COLOR_BYTES: ColorBytes = ColorBytes::FourBytes;
217+
fn to_word(self) -> u32 {
218+
(u32::from(self.g) << 24)
219+
| (u32::from(self.r) << 16)
220+
| (u32::from(self.b) << 8)
221+
| (u32::from(self.a))
222+
}
223+
}
224+
225+
impl<P, SM, I, CF> SmartLedsWrite for Ws2812Direct<P, SM, I, CF>
154226
where
155227
I: AnyPin<Function = P::PinFunction>,
156228
P: PIOExt,
157229
SM: StateMachineIndex,
230+
CF: ColorFormat,
158231
{
159-
type Color = smart_leds_trait::RGB8;
232+
type Color = CF;
160233
type Error = ();
161234
/// If you call this function, be advised that you will have to wait
162235
/// at least 60 microseconds between calls of this function!
@@ -172,8 +245,7 @@ where
172245
{
173246
for item in iterator {
174247
let color: Self::Color = item.into();
175-
let word =
176-
(u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8);
248+
let word = color.to_word();
177249

178250
while !self.tx.write(word) {
179251
cortex_m::asm::nop();
@@ -237,23 +309,51 @@ where
237309
/// // Do other stuff here...
238310
/// };
239311
///```
240-
pub struct Ws2812<P, SM, C, I>
312+
///
313+
/// Typical RGBW usage example:
314+
///```ignore
315+
/// use rp2040_hal::clocks::init_clocks_and_plls;
316+
/// let clocks = init_clocks_and_plls(...);
317+
/// let pins = rp2040_hal::gpio::pin::bank0::Pins::new(...);
318+
///
319+
/// let timer = Timer::new(pac.TIMER, &mut pac.RESETS);
320+
///
321+
/// let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
322+
/// let mut ws = Ws2812::<_, _, _, _, smart_leds::RGBA8>::new(
323+
/// pins.gpio4.into_mode(),
324+
/// &mut pio,
325+
/// sm0,
326+
/// clocks.peripheral_clock.freq(),
327+
/// timer.count_down(),
328+
/// );
329+
///
330+
/// loop {
331+
/// use smart_leds::{SmartLedsWrite, RGBA8};
332+
/// let color : RGBA8 = (255, 0, 255, 127).into();
333+
///
334+
/// ws.write([color].iter().copied()).unwrap();
335+
///
336+
/// // Do other stuff here...
337+
/// };
338+
///```
339+
pub struct Ws2812<P, SM, C, I, CF = smart_leds_trait::RGB8>
241340
where
242341
C: CountDown,
243342
I: AnyPin<Function = P::PinFunction>,
244343
P: PIOExt,
245344
SM: StateMachineIndex,
246345
{
247-
driver: Ws2812Direct<P, SM, I>,
346+
driver: Ws2812Direct<P, SM, I, CF>,
248347
cd: C,
249348
}
250349

251-
impl<P, SM, C, I> Ws2812<P, SM, C, I>
350+
impl<P, SM, C, I, CF> Ws2812<P, SM, C, I, CF>
252351
where
253352
C: CountDown,
254353
I: AnyPin<Function = P::PinFunction>,
255354
P: PIOExt,
256355
SM: StateMachineIndex,
356+
CF: ColorFormat,
257357
{
258358
/// Creates a new instance of this driver.
259359
pub fn new(
@@ -262,22 +362,23 @@ where
262362
sm: UninitStateMachine<(P, SM)>,
263363
clock_freq: fugit::HertzU32,
264364
cd: C,
265-
) -> Ws2812<P, SM, C, I> {
365+
) -> Ws2812<P, SM, C, I, CF> {
266366
let driver = Ws2812Direct::new(pin, pio, sm, clock_freq);
267367

268368
Self { driver, cd }
269369
}
270370
}
271371

272-
impl<P, SM, I, C> SmartLedsWrite for Ws2812<P, SM, C, I>
372+
impl<P, SM, I, C, CF> SmartLedsWrite for Ws2812<P, SM, C, I, CF>
273373
where
274374
C: CountDown,
275375
C::Time: From<MicrosDurationU32>,
276376
I: AnyPin<Function = P::PinFunction>,
277377
P: PIOExt,
278378
SM: StateMachineIndex,
379+
CF: ColorFormat,
279380
{
280-
type Color = smart_leds_trait::RGB8;
381+
type Color = CF;
281382
type Error = ();
282383
fn write<T, J>(&mut self, iterator: T) -> Result<(), ()>
283384
where

0 commit comments

Comments
 (0)