RKTK API Docs RKTK Home Repo

rktk_drivers_nrf/rgb/
ws2812_pwm.rs

1//! WS2812 PWM driver for nRF52
2//! Based on https://github.com/embassy-rs/embassy/blob/main/examples/nrf52840/src/bin/pwm_sequence_ws2812b.rs
3
4use core::convert::Infallible;
5
6use bitvec::prelude::*;
7use embassy_nrf::{
8    Peri,
9    gpio::{OutputDrive, Pin},
10    pwm::{
11        Config, Instance, Prescaler, SequenceLoad, SequencePwm, SingleSequenceMode, SingleSequencer,
12    },
13};
14use embassy_time::Timer;
15use rktk::drivers::interface::rgb::{LedRgb, RgbDriver};
16
17/// WS2812 NeoPixel driver using PWM on nRF.
18///
19/// ## Note about `BUFFER_SIZE`
20/// Buffer size should be large enough to hold the number of pixels you want to control.
21/// It should be larger than `led_count * 24 + 100` to ensure enough space for the sequence.
22///
23/// Even buffer size is not enough, pixels will be written, but the driver will log a warning.
24pub struct Ws2812Pwm<const BUFFER_SIZE: usize, PWM: Instance, DATA: Pin> {
25    pwm: Peri<'static, PWM>,
26    pin: Peri<'static, DATA>,
27}
28
29const T1H: u16 = 0x8000 | 13;
30const T0H: u16 = 0x8000 | 7;
31const RES: u16 = 0x8000;
32
33impl<const BUFFER_SIZE: usize, PWM: Instance + 'static, DATA: Pin>
34    Ws2812Pwm<BUFFER_SIZE, PWM, DATA>
35{
36    pub fn new(pwm: Peri<'static, PWM>, data: Peri<'static, DATA>) -> Self {
37        Self { pwm, pin: data }
38    }
39}
40
41impl<const BUFFER_SIZE: usize, PWM: Instance + 'static, DATA: Pin> RgbDriver
42    for Ws2812Pwm<BUFFER_SIZE, PWM, DATA>
43{
44    type Error = Infallible;
45
46    async fn write<I: IntoIterator<Item = LedRgb<u8>>>(
47        &mut self,
48        pixels: I,
49    ) -> Result<(), Self::Error> {
50        let mut pwm_config = Config::default();
51        pwm_config.sequence_load = SequenceLoad::Common;
52        pwm_config.prescaler = Prescaler::Div1; // 16MHz
53        pwm_config.max_duty = 20; // 1.25us (1s / 16Mhz * 20)
54        pwm_config.ch0_drive = OutputDrive::HighDrive;
55        let Ok(mut seq_pwm) =
56            SequencePwm::new_1ch(self.pwm.reborrow(), self.pin.reborrow(), pwm_config)
57        else {
58            // TODO: Handle error
59            return Ok(());
60        };
61
62        let mut words = heapless::Vec::<u16, BUFFER_SIZE>::from_slice(&[RES; 100]).unwrap();
63
64        'outer: for color in pixels {
65            for bit in color[1]
66                .view_bits::<Msb0>()
67                .iter()
68                .chain(color[0].view_bits())
69                .chain(color[2].view_bits())
70            {
71                if words.push(if *bit { T1H } else { T0H }).is_err() {
72                    rktk_log::warn!("WS2812Pwm buffer size is not enough. Increase BUFFER_SIZE.");
73                    break 'outer;
74                }
75            }
76        }
77
78        let seq_config = embassy_nrf::pwm::SequenceConfig::default();
79        let sequencer = SingleSequencer::new(&mut seq_pwm, words.as_slice(), seq_config);
80        let _ = sequencer.start(SingleSequenceMode::Times(1));
81
82        // Wait for a long time is important. Otherwise, the PWM will be stopped before the
83        // sequence is finished.
84        Timer::after_millis(5).await;
85
86        Ok(())
87    }
88}