Skip to content

Commit 9645550

Browse files
Merge pull request #27 from smart-leds-rs/dev/hosted
Add `hosted` variant intended for linux SBCs
2 parents f752a56 + 4d069a8 commit 9645550

File tree

5 files changed

+163
-2
lines changed

5 files changed

+163
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77
## [Unreleased]
8+
### Added
9+
- Add a `hosted` variant intended for SBCs where the whole data transmission needs to be done in a single call
810

911
### Changed
1012
- Increased reset time from ~50μs to ~300μs, to deal with more/newer variants

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: 6 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,6 +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
22+
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.
2126

2227
## It doesn't work!!!
2328
- Do you use the normal variant? Does your spi run at the right frequency?

src/hosted.rs

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

src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
// Timings for ws2812 from https://cpldcpu.files.wordpress.com/2014/01/ws2812_timing_table.png
1111
// Timings for sk6812 from https://cpldcpu.wordpress.com/2016/03/09/the-sk6812-another-intelligent-rgb-led/
1212

13-
#![no_std]
13+
#![cfg_attr(not(feature = "std"), no_std)]
1414

1515
use embedded_hal as hal;
1616

17+
#[cfg(feature = "std")]
18+
pub mod hosted;
1719
pub mod prerendered;
1820

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

0 commit comments

Comments
 (0)