Skip to content

Commit 66bf1ad

Browse files
Add hosted variant intended for linux SBCs
1 parent f752a56 commit 66bf1ad

File tree

4 files changed

+172
-1
lines changed

4 files changed

+172
-1
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ embedded-hal = "1.0.0"
2020

2121
[features]
2222
mosi_idle_high = []
23+
std = []

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ An embedded-hal driver for ws2812 leds using spi as the timing provider.
77

88
![rainbow on stm32f0](./stm32f0_ws2812_spi_rainbow.gif)
99

10-
It provides two variants:
10+
It provides three variants:
1111
- The normal usage
1212

1313
Your spi peripheral has to run betwee 2MHz and 3.8MHz & the SPI data is created on-the-fly.
@@ -18,7 +18,11 @@ It provides two variants:
1818
may want to use this. It creates all the data beforehand & then sends it. This
1919
means that you have to provide a data array that's large enough for all the
2020
spi data.
21+
- Hosted
2122

23+
Intended for device like the raspberry pi or other linux SBCs. Similarly to
24+
prerendered it creates all the data beforehand, but sends it using a single
25+
call.
2226
## It doesn't work!!!
2327
- Do you use the normal variant? Does your spi run at the right frequency?
2428

src/hosted.rs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
//! This dynamically allocates an output buffer and writes out the data in a single call
2+
//!
3+
//! Much better suited for linux or similar environments
4+
5+
use embedded_hal as hal;
6+
7+
use hal::blocking::spi::Write;
8+
use hal::spi::{Mode, Phase, Polarity};
9+
10+
use core::marker::PhantomData;
11+
12+
use smart_leds_trait::{SmartLedsWrite, RGB8, RGBW};
13+
14+
use std::vec;
15+
use std::vec::Vec;
16+
17+
/// SPI mode that can be used for this crate
18+
///
19+
/// Provided for convenience
20+
/// Doesn't really matter
21+
pub const MODE: Mode = Mode {
22+
polarity: Polarity::IdleLow,
23+
phase: Phase::CaptureOnFirstTransition,
24+
};
25+
26+
pub mod devices {
27+
pub struct Ws2812;
28+
pub struct Sk6812w;
29+
}
30+
31+
pub struct Ws2812<SPI, DEVICE = devices::Ws2812> {
32+
spi: SPI,
33+
data: Vec<u8>,
34+
device: PhantomData<DEVICE>,
35+
}
36+
37+
impl<SPI, E> Ws2812<SPI>
38+
where
39+
SPI: Write<u8, Error = E>,
40+
{
41+
/// Use ws2812 devices via spi
42+
///
43+
/// The SPI bus should run within 2 MHz to 3.8 MHz
44+
///
45+
/// You may need to look at the datasheet and your own hal to verify this.
46+
///
47+
pub fn new(spi: SPI) -> Self {
48+
let data = if cfg!(feature = "mosi_idle_high") {
49+
vec![0; 140]
50+
} else {
51+
vec![]
52+
};
53+
54+
Self {
55+
spi,
56+
data,
57+
device: PhantomData {},
58+
}
59+
}
60+
}
61+
62+
impl<SPI, E> Ws2812<SPI, devices::Sk6812w>
63+
where
64+
SPI: Write<u8, Error = E>,
65+
{
66+
/// Use sk6812w devices via spi
67+
///
68+
/// The SPI bus should run within 2.3 MHz to 3.8 MHz at least.
69+
///
70+
/// You may need to look at the datasheet and your own hal to verify this.
71+
///
72+
// The spi frequencies are just the limits, the available timing data isn't
73+
// complete
74+
pub fn new_sk6812w(spi: SPI) -> Self {
75+
let data = if cfg!(feature = "mosi_idle_high") {
76+
vec![0; 140]
77+
} else {
78+
vec![]
79+
};
80+
81+
Self {
82+
spi,
83+
data,
84+
device: PhantomData {},
85+
}
86+
}
87+
}
88+
89+
impl<SPI, D, E> Ws2812<SPI, D>
90+
where
91+
SPI: Write<u8, Error = E>,
92+
{
93+
/// Write a single byte for ws2812 devices
94+
fn write_byte(&mut self, mut data: u8) {
95+
// Send two bits in one spi byte. High time first, then the low time
96+
// The maximum for T0H is 500ns, the minimum for one bit 1063 ns.
97+
// These result in the upper and lower spi frequency limits
98+
let patterns = [0b1000_1000, 0b1000_1110, 0b11101000, 0b11101110];
99+
for _ in 0..4 {
100+
let bits = (data & 0b1100_0000) >> 6;
101+
self.data.push(patterns[bits as usize]);
102+
data <<= 2;
103+
}
104+
}
105+
106+
fn send_data(&mut self) -> Result<(), E> {
107+
self.data.extend_from_slice(&[0; 140]);
108+
self.spi.write(&self.data)?;
109+
self.data.truncate(if cfg!(feature = "mosi_idle_high") {
110+
140
111+
} else {
112+
0
113+
});
114+
Ok(())
115+
}
116+
}
117+
118+
impl<SPI, E> SmartLedsWrite for Ws2812<SPI>
119+
where
120+
SPI: Write<u8, Error = E>,
121+
{
122+
type Error = E;
123+
type Color = RGB8;
124+
/// Write all the items of an iterator to a ws2812 strip
125+
fn write<T, I>(&mut self, iterator: T) -> Result<(), E>
126+
where
127+
T: Iterator<Item = I>,
128+
I: Into<Self::Color>,
129+
{
130+
for item in iterator {
131+
let item = item.into();
132+
self.write_byte(item.g);
133+
self.write_byte(item.r);
134+
self.write_byte(item.b);
135+
}
136+
self.send_data()
137+
}
138+
}
139+
140+
impl<SPI, E> SmartLedsWrite for Ws2812<SPI, devices::Sk6812w>
141+
where
142+
SPI: Write<u8, Error = E>,
143+
{
144+
type Error = E;
145+
type Color = RGBW<u8, u8>;
146+
/// Write all the items of an iterator to a sk6812w strip
147+
fn write<T, I>(&mut self, iterator: T) -> Result<(), E>
148+
where
149+
T: Iterator<Item = I>,
150+
I: Into<Self::Color>,
151+
{
152+
for item in iterator {
153+
let item = item.into();
154+
self.write_byte(item.g);
155+
self.write_byte(item.r);
156+
self.write_byte(item.b);
157+
self.write_byte(item.a.0);
158+
}
159+
self.send_data()
160+
}
161+
}

src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@
1212

1313
#![no_std]
1414

15+
#[cfg(feature = "std")]
16+
extern crate std;
17+
1518
use embedded_hal as hal;
1619

20+
#[cfg(feature = "std")]
21+
pub mod hosted;
1722
pub mod prerendered;
1823

1924
use hal::spi::{Mode, Phase, Polarity, SpiBus};

0 commit comments

Comments
 (0)