1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//! This prerenders the data, so that no calculations have to be performed while sending the data.
//!
//! This approach minimizes timing issues, at the cost of much higher ram usage.
//! It also increases the needed time.

extern crate embedded_hal as hal;

use hal::spi::FullDuplex;

use smart_leds_trait::{Color, SmartLedsWrite};

use nb;
use nb::block;

pub struct Ws2812<'a, SPI> {
    spi: SPI,
    timing: Timing,
    data: &'a mut [u8],
}

impl<'a, SPI, E> Ws2812<'a, SPI>
where
    SPI: FullDuplex<u8, Error = E>,
{
    /// The SPI bus should run exactly with the provided frequency
    ///
    /// You may need to look at the datasheet and your own hal to verify this.
    ///
    /// You need to provide a buffer `data`, whoose length is at least 3 the
    /// length of the led strip.
    /// You can calculate the exact amount for frequencies >= 3 MHz with
    /// "spi frequency / 1_100_00" rounded up
    pub fn new(spi: SPI, timing: Timing, data: &'a mut [u8]) -> Ws2812<'a, SPI> {
        Self { spi, timing, data }
    }

    /// Write a single byte for ws2812 devices
    fn write_byte(
        &mut self,
        mut data: u8,
        serial_data: &mut u32,
        serial_count: &mut u8,
        index: &mut usize,
    ) -> Result<(), E> {
        for _ in 0..8 {
            let pattern = if (data & 0x80) != 0 {
                self.timing.one_pattern
            } else {
                self.timing.zero_pattern
            };
            *serial_count += self.timing.len;
            *serial_data |= pattern << (32 - *serial_count);
            while *serial_count > 7 {
                let data = (*serial_data >> 24) as u8;
                self.data[*index] = data;
                *index += 1;
                *serial_data <<= 8;
                *serial_count -= 8;
            }
            data <<= 1;
        }
        Ok(())
    }
    fn flush(&mut self) -> Result<(), E> {
        for _ in 0..self.timing.flush_bytes {
            block!(self.spi.send(0))?;
            self.spi.read().ok();
        }
        Ok(())
    }
}

impl<SPI, E> SmartLedsWrite for Ws2812<'_, SPI>
where
    SPI: FullDuplex<u8, Error = E>,
{
    type Error = E;
    /// Write all the items of an iterator to a ws2812 strip
    fn write<T>(&mut self, iterator: T) -> Result<(), E>
    where
        T: Iterator<Item = Color>,
    {
        if cfg!(feature = "mosi_idle_high") {
            self.flush()?;
        }

        let mut serial_data: u32 = 0;
        let mut serial_count = 0;
        let mut index = 0;
        for item in iterator {
            self.write_byte(item.g, &mut serial_data, &mut serial_count, &mut index)?;
            self.write_byte(item.r, &mut serial_data, &mut serial_count, &mut index)?;
            self.write_byte(item.b, &mut serial_data, &mut serial_count, &mut index)?;
        }
        for d in self.data.iter().take(index + 1) {
            block!(self.spi.send(*d))?;
            self.spi.read().ok();
        }
        self.flush()?;
        Ok(())
    }
}

#[derive(Debug)]
pub struct Timing {
    one_pattern: u32,
    zero_pattern: u32,
    len: u8,
    flush_bytes: usize,
}

impl Timing {
    /// Create timing values for the provided frequency
    pub fn new(mhz: u32) -> Option<Self> {
        if mhz < 2_000_000 {
            return None;
        }
        static ONE_HIGH: u32 = 1_510_000;
        static ZERO_HIGH: u32 = 5_000_000;
        static TOTAL: u32 = 1_100_000;
        static FLUSH: u32 = 3_000;

        let mut zero_high = mhz / ZERO_HIGH;
        // Make sure we have at least something
        if zero_high == 0 {
            zero_high = 1;
        }

        // Round up
        let one_high = mhz / ONE_HIGH + 1;
        let mut total = mhz / TOTAL + 1;
        // Make sure total is at least one higher than one_high
        if total == one_high {
            total = one_high + 1;
        }
        if total > 28 {
            return None;
        }
        let flush = ((mhz / FLUSH + 1) / 8 + 1) as usize;
        // Create patterns
        let mut one_pattern = 0;
        let mut zero_pattern = 0;
        for _ in 0..one_high {
            one_pattern <<= 1;
            one_pattern |= 1;
        }
        for _ in 0..total - one_high {
            one_pattern <<= 1;
        }
        for _ in 0..zero_high {
            zero_pattern <<= 1;
            zero_pattern |= 1;
        }
        for _ in 0..total - zero_high {
            zero_pattern <<= 1;
        }
        Some(Self {
            one_pattern,
            zero_pattern,
            len: total as u8,
            flush_bytes: flush,
        })
    }
}