ws2811_spi/
prerendered.rs

1//! This prerenders the data, so that no calculations have to be performed while sending the data.
2//!
3//! This approach minimizes timing issues, at the cost of much higher ram usage.
4//! It also increases the needed time.
5
6use embedded_hal as hal;
7
8use hal::spi::{FullDuplex, Mode, Phase, Polarity};
9
10use core::marker::PhantomData;
11
12use smart_leds_trait::{SmartLedsWrite, RGB8, RGBW};
13
14use nb;
15use nb::block;
16
17/// SPI mode that can be used for this crate
18///
19/// Provided for convenience
20/// Doesn't really matter
21pub const MODE: Mode = Mode {
22    polarity: Polarity::IdleLow,
23    phase: Phase::CaptureOnFirstTransition,
24};
25
26#[derive(Debug)]
27pub enum Error<E> {
28    OutOfBounds,
29    Spi(E),
30}
31
32pub mod devices {
33    pub struct Ws2811;
34}
35
36pub struct Ws2811<'a, SPI, DEVICE = devices::Ws2811> {
37    spi: SPI,
38    data: &'a mut [u8],
39    index: usize,
40    device: PhantomData<DEVICE>,
41}
42
43impl<'a, SPI, E> Ws2811<'a, SPI>
44where
45    SPI: FullDuplex<u8, Error = E>,
46{
47    /// Use Ws2811 devices via spi
48    ///
49    /// The SPI bus should run within 2 MHz to 3.8 MHz
50    ///
51    /// You may need to look at the datasheet and your own hal to verify this.
52    ///
53    /// You need to provide a buffer `data`, whoose length is at least 12 * the
54    /// length of the led strip + 20 byes (or 40, if using the `mosi_idle_high` feature)
55    ///
56    /// Please ensure that the mcu is pretty fast, otherwise weird timing
57    /// issues will occur
58    pub fn new(spi: SPI, data: &'a mut [u8]) -> Self {
59        Self {
60            spi,
61            data,
62            index: 0,
63            device: PhantomData {},
64        }
65    }
66}
67
68impl<'a, SPI, D, E> Ws2811<'a, SPI, D>
69where
70    SPI: FullDuplex<u8, Error = E>,
71{
72    /// Write a single byte for Ws2811 devices
73    fn write_byte(&mut self, mut data: u8) -> Result<(), Error<E>> {
74        // Send two bits in one spi byte. High time first, then the low time
75        // The maximum for T0H is 500ns, the minimum for one bit 1063 ns.
76        // These result in the upper and lower spi frequency limits
77        let patterns = [0b1000_1000, 0b1000_1110, 0b11101000, 0b11101110];
78
79        if self.index > self.data.len() - 4 {
80            return Err(Error::OutOfBounds);
81        }
82        for _ in 0..4 {
83            let bits = (data & 0b1100_0000) >> 6;
84            self.data[self.index] = patterns[bits as usize];
85            self.index += 1;
86            data <<= 2;
87        }
88        Ok(())
89    }
90
91    fn send_data(&mut self) -> Result<(), E> {
92        // We introduce an offset in the fifo here, so there's always one byte in transit
93        // Some MCUs (like the stm32f1) only a one byte fifo, which would result
94        // in overrun error if two bytes need to be stored
95        block!(self.spi.send(0))?;
96        if cfg!(feature = "mosi_idle_high") {
97            for _ in 0..140 {
98                block!(self.spi.send(0))?;
99                block!(self.spi.read())?;
100            }
101        }
102        for b in self.data[..self.index].iter() {
103            block!(self.spi.send(*b))?;
104            block!(self.spi.read())?;
105        }
106        for _ in 0..140 {
107            block!(self.spi.send(0))?;
108            block!(self.spi.read())?;
109        }
110        // Now, resolve the offset we introduced at the beginning
111        block!(self.spi.read())?;
112        Ok(())
113    }
114}
115
116impl<'a, SPI, E> SmartLedsWrite for Ws2811<'a, SPI>
117where
118    SPI: FullDuplex<u8, Error = E>,
119{
120    type Error = Error<E>;
121    type Color = RGB8;
122    /// Write all the items of an iterator to a Ws2811 strip
123    fn write<T, I>(&mut self, iterator: T) -> Result<(), Error<E>>
124    where
125        T: Iterator<Item = I>,
126        I: Into<Self::Color>,
127    {
128        self.index = 0;
129
130        for item in iterator {
131            let item = item.into();
132            self.write_byte(item.r)?;
133            self.write_byte(item.b)?;
134            self.write_byte(item.g)?;
135        }
136        self.send_data().map_err(|e| Error::Spi(e))
137    }
138}