ws2811_spi/
lib.rs

1//! # Use ws2811 leds via spi
2//!
3//! - For usage with `smart-leds`
4//! - Implements the `SmartLedsWrite` trait
5//!
6//! Needs a type implementing the `spi::FullDuplex` trait.
7//!
8//! The spi peripheral should run at 2MHz to 3.8 MHz
9
10#![no_std]
11
12use embedded_hal as hal;
13
14pub mod prerendered;
15
16use hal::spi::{FullDuplex, Mode, Phase, Polarity};
17
18use core::marker::PhantomData;
19
20use smart_leds_trait::{SmartLedsWrite, RGB8};
21
22use nb;
23use nb::block;
24
25/// SPI mode that can be used for this crate
26///
27/// Provided for convenience
28/// Doesn't really matter
29pub const MODE: Mode = Mode {
30    polarity: Polarity::IdleLow,
31    phase: Phase::CaptureOnFirstTransition,
32};
33
34pub mod devices {
35    pub struct Ws2811Rgb;
36    pub struct Ws2811Rbg;
37}
38
39pub struct Ws2811<SPI, DEVICE = devices::Ws2811Rgb> {
40    spi: SPI,
41    device: PhantomData<DEVICE>,
42}
43
44impl<SPI, E> Ws2811<SPI>
45where
46    SPI: FullDuplex<u8, Error = E>,
47{
48    /// Use Ws2811 devices via spi RGB
49    ///
50    /// The SPI bus should run within 1.6 MHz to 3.2 MHz
51    ///
52    /// You may need to look at the datasheet and your own hal to verify this.
53    ///
54    /// Please ensure that the mcu is pretty fast, otherwise weird timing
55    /// issues will occur
56    pub fn new_rgb(spi: SPI) -> Self {
57        Self {
58            spi,
59            device: PhantomData {},
60        }
61    }
62}
63
64impl<SPI, E> Ws2811<SPI, devices::Ws2811Rbg>
65where
66    SPI: FullDuplex<u8, Error = E>,
67{
68    /// Use Ws2811 devices via spi RBG
69    ///
70    /// The SPI bus should run within 1.6 MHz to 3.2 MHz
71    ///
72    /// You may need to look at the datasheet and your own hal to verify this.
73    ///
74    /// Please ensure that the mcu is pretty fast, otherwise weird timing
75    /// issues will occur
76    pub fn new_rbg(spi: SPI) -> Self {
77        Self {
78            spi,
79            device: PhantomData {},
80        }
81    }
82}
83
84impl<SPI, D, E> Ws2811<SPI, D>
85where
86    SPI: FullDuplex<u8, Error = E>,
87{
88    /// Write a single byte for Ws2811 devices
89    fn write_byte(&mut self, mut data: u8) -> Result<(), E> {
90        // Send two bits in one spi byte. High time first, then the low time
91        // The maximum for T0H is 500ns, the minimum for one bit 1063 ns.
92        // These result in the upper and lower spi frequency limits
93        let patterns = [0b1000_1000, 0b1000_1110, 0b11101000, 0b11101110];
94        for _ in 0..4 {
95            let bits = (data & 0b1100_0000) >> 6;
96            block!(self.spi.send(patterns[bits as usize]))?;
97            block!(self.spi.read()).ok();
98            data <<= 2;
99        }
100        Ok(())
101    }
102
103    fn flush(&mut self) -> Result<(), E> {
104        // Should be > 300μs, so for an SPI Freq. of 3.8MHz, we have to send at least 1140 low bits or 140 low bytes
105        for _ in 0..140 {
106            block!(self.spi.send(0))?;
107            block!(self.spi.read()).ok();
108        }
109        Ok(())
110    }
111}
112
113impl<SPI, E> SmartLedsWrite for Ws2811<SPI>
114where
115    SPI: FullDuplex<u8, Error = E>,
116{
117    type Error = E;
118    type Color = RGB8;
119    /// Write all the items of an iterator to a Ws2811 strip
120    fn write<T, I>(&mut self, iterator: T) -> Result<(), E>
121    where
122        T: Iterator<Item = I>,
123        I: Into<Self::Color>,
124    {
125        // We introduce an offset in the fifo here, so there's always one byte in transit
126        // Some MCUs (like the stm32f1) only a one byte fifo, which would result
127        // in overrun error if two bytes need to be stored
128        block!(self.spi.send(0))?;
129
130        if cfg!(feature = "mosi_idle_high") {
131            self.flush()?;
132        }
133
134        for item in iterator {
135            let item = item.into();
136            self.write_byte(item.r)?;
137            self.write_byte(item.g)?;
138            self.write_byte(item.b)?;
139        }
140        self.flush()?;
141        // Now, resolve the offset we introduced at the beginning
142        if cfg!(feature = "fifo_stm32f1") {
143            self.spi.read().ok();
144        } else {
145            block!(self.spi.read())?;
146        }
147
148        Ok(())
149    }
150}
151
152impl<SPI, E> SmartLedsWrite for Ws2811<SPI, devices::Ws2811Rbg>
153where
154    SPI: FullDuplex<u8, Error = E>,
155{
156    type Error = E;
157    type Color = RGB8;
158    /// Write all the items of an iterator to a Ws2811 strip
159    fn write<T, I>(&mut self, iterator: T) -> Result<(), E>
160    where
161        T: Iterator<Item = I>,
162        I: Into<Self::Color>,
163    {
164        // We introduce an offset in the fifo here, so there's always one byte in transit
165        // Some MCUs (like the stm32f1) only a one byte fifo, which would result
166        // in overrun error if two bytes need to be stored
167        block!(self.spi.send(0))?;
168        if cfg!(feature = "mosi_idle_high") {
169            self.flush()?;
170        }
171
172        for item in iterator {
173            let item = item.into();
174            self.write_byte(item.r)?;
175            self.write_byte(item.b)?;
176            self.write_byte(item.g)?;
177        }
178        self.flush()?;
179        // Now, resolve the offset we introduced at the beginning
180        if cfg!(feature = "fifo_stm32f1") {
181            self.spi.read().ok();
182        } else {
183            block!(self.spi.read())?;
184        }
185        Ok(())
186    }
187}