Skip to content

Commit 9a51348

Browse files
Merge pull request #40 from smart-leds-rs/reset_prerendered_full
Optionally include reset sequences in prerendered transactions
2 parents 4543138 + 9377392 commit 9a51348

File tree

5 files changed

+84
-30
lines changed

5 files changed

+84
-30
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ 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 `reset_single_transaction` feature for the `prerendered` version, which uses a single SPI transaction for a `write` call. Can be useful if using a DMA-approach or you're experiencing glitches due to the SPI peripheral turning high between the data and the reset.
10+
11+
### Changed
12+
- Using the `mosi_idle_high` feature with the `prerendered` version now sends out the first reset together with the data, thus requiring a larger data buffer (but avoids potentially long dead-time between the first reset and the data being sent).
813

914
## [0.5.0] - 2024-07-29
1015
### Added

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,6 @@ embedded-hal = "1.0.0"
2020

2121
[features]
2222
mosi_idle_high = []
23+
# Send the reset command in one single transaction
24+
reset_single_transaction = []
2325
std = []

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ It provides three variants:
3939
It may also be a timing issue with the first bit being sent, this is the case
4040
on the stm32f030 with 2MHz.
4141

42-
You could try using the `mosi_idle_high` feature, it might help.
42+
You could try using the `mosi_idle_high` and `reset_single_transaction` features, they might help.
4343

4444
If this does not help you can try different `embedded_hal::spi::Mode` parameters. For example on
4545
stm32f411 (as used for example on the Black Pill board) you have to set `phase` to

src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ where
104104
Ok(())
105105
}
106106

107-
fn flush(&mut self) -> Result<(), E> {
107+
fn reset(&mut self) -> Result<(), E> {
108108
// 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
109109
for _ in 0..140 {
110110
self.spi.write(from_ref(&0))?;
@@ -126,7 +126,7 @@ where
126126
I: Into<Self::Color>,
127127
{
128128
if cfg!(feature = "mosi_idle_high") {
129-
self.flush()?;
129+
self.reset()?;
130130
}
131131

132132
for item in iterator {
@@ -135,7 +135,7 @@ where
135135
self.write_byte(item.r)?;
136136
self.write_byte(item.b)?;
137137
}
138-
self.flush()?;
138+
self.reset()?;
139139
Ok(())
140140
}
141141
}
@@ -153,7 +153,7 @@ where
153153
I: Into<Self::Color>,
154154
{
155155
if cfg!(feature = "mosi_idle_high") {
156-
self.flush()?;
156+
self.reset()?;
157157
}
158158

159159
for item in iterator {
@@ -163,7 +163,7 @@ where
163163
self.write_byte(item.b)?;
164164
self.write_byte(item.a.0)?;
165165
}
166-
self.flush()?;
166+
self.reset()?;
167167
Ok(())
168168
}
169169
}

src/prerendered.rs

Lines changed: 71 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ use embedded_hal as hal;
88
use hal::spi::{Mode, Phase, Polarity, SpiBus};
99

1010
use core::marker::PhantomData;
11-
use core::slice::from_ref;
1211

1312
use smart_leds_trait::{SmartLedsWrite, RGB8, RGBW};
1413

14+
const RESET_DATA_LEN: usize = 140;
15+
1516
/// SPI mode that can be used for this crate
1617
///
1718
/// Provided for convenience
@@ -43,16 +44,19 @@ impl<'a, SPI, E> Ws2812<'a, SPI>
4344
where
4445
SPI: SpiBus<u8, Error = E>,
4546
{
46-
/// Use ws2812 devices via spi
47+
/// Use WS2812 devices via SPI
4748
///
4849
/// The SPI bus should run within 2 MHz to 3.8 MHz
4950
///
50-
/// You may need to look at the datasheet and your own hal to verify this.
51+
/// You may need to look at the datasheet and your own HAL to verify this.
5152
///
52-
/// You need to provide a buffer `data`, whoose length is at least 12 * the
53-
/// length of the led strip + 20 byes (or 40, if using the `mosi_idle_high` feature)
53+
/// You need to provide a buffer `data`, whose length is at least 12 * the
54+
/// length of the led strip
55+
/// - + 140 bytes if using the `reset_single_transaction` feature
56+
/// - + 140 bytes if using the `mosi_idle_high` feature
57+
/// - + 280 bytes if using the `mosi_idle_high` and `reset_single_transaction` features
5458
///
55-
/// Please ensure that the mcu is pretty fast, otherwise weird timing
59+
/// Please ensure that the MCU is pretty fast, otherwise weird timing
5660
/// issues will occur
5761
pub fn new(spi: SPI, data: &'a mut [u8]) -> Self {
5862
Self {
@@ -68,18 +72,21 @@ impl<'a, SPI, E> Ws2812<'a, SPI, devices::Sk6812w>
6872
where
6973
SPI: SpiBus<u8, Error = E>,
7074
{
71-
/// Use sk6812w devices via spi
75+
/// Use SK6812W devices via SPI
7276
///
7377
/// The SPI bus should run within 2.3 MHz to 3.8 MHz at least.
7478
///
75-
/// You may need to look at the datasheet and your own hal to verify this.
79+
/// You may need to look at the datasheet and your own HAL to verify this.
7680
///
77-
/// You need to provide a buffer `data`, whoose length is at least 12 * the
81+
/// You need to provide a buffer `data`, whose length is at least 16 * the
7882
/// length of the led strip
83+
/// - + 140 bytes if using the `reset_single_transaction` feature
84+
/// - + 140 bytes if using the `mosi_idle_high` feature
85+
/// - + 280 bytes if using the `mosi_idle_high` and `reset_single_transaction` features
7986
///
80-
/// Please ensure that the mcu is pretty fast, otherwise weird timing
87+
/// Please ensure that the MCU is pretty fast, otherwise weird timing
8188
/// issues will occur
82-
// The spi frequencies are just the limits, the available timing data isn't
89+
// The SPI frequencies are just the limits, the available timing data isn't
8390
// complete
8491
pub fn new_sk6812w(spi: SPI, data: &'a mut [u8]) -> Self {
8592
Self {
@@ -95,7 +102,7 @@ impl<'a, SPI, D, E> Ws2812<'a, SPI, D>
95102
where
96103
SPI: SpiBus<u8, Error = E>,
97104
{
98-
/// Write a single byte for ws2812 devices
105+
/// Write a single byte for WS2812-like devices
99106
fn write_byte(&mut self, mut data: u8) -> Result<(), Error<E>> {
100107
// Send two bits in one spi byte. High time first, then the low time
101108
// The maximum for T0H is 500ns, the minimum for one bit 1063 ns.
@@ -114,19 +121,31 @@ where
114121
Ok(())
115122
}
116123

117-
fn send_data(&mut self) -> Result<(), E> {
118-
if cfg!(feature = "mosi_idle_high") {
119-
for _ in 0..140 {
120-
self.spi.write(from_ref(&0))?;
121-
}
124+
/// Add a reset sequence (140 zeroes) to the data buffer
125+
// Is always used for `mosi_idle_high`, as otherwise the time required to fill the buffer can lead to idle cycles on the SPI bus
126+
fn write_reset(&mut self) -> Result<(), Error<E>> {
127+
if self.index + RESET_DATA_LEN > self.data.len() {
128+
return Err(Error::OutOfBounds);
129+
}
130+
for _ in 0..RESET_DATA_LEN {
131+
self.data[self.index] = 0;
132+
self.index += 1;
122133
}
134+
Ok(())
135+
}
123136

124-
self.spi.write(&self.data[..self.index])?;
125-
for _ in 0..140 {
126-
self.spi.write(from_ref(&0))?;
137+
/// Send a reset sequence (140 zeroes) on the bus
138+
fn send_reset(&mut self) -> Result<(), Error<E>> {
139+
for _ in 0..RESET_DATA_LEN {
140+
self.spi.write(&[0]).map_err(Error::Spi)?;
127141
}
142+
128143
Ok(())
129144
}
145+
146+
fn send_data(&mut self) -> Result<(), E> {
147+
self.spi.write(&self.data[..self.index])
148+
}
130149
}
131150

132151
impl<'a, SPI, E> SmartLedsWrite for Ws2812<'a, SPI>
@@ -135,21 +154,35 @@ where
135154
{
136155
type Error = Error<E>;
137156
type Color = RGB8;
138-
/// Write all the items of an iterator to a ws2812 strip
157+
/// Write all the items of an iterator to a WS2812 strip
139158
fn write<T, I>(&mut self, iterator: T) -> Result<(), Error<E>>
140159
where
141160
T: IntoIterator<Item = I>,
142161
I: Into<Self::Color>,
143162
{
144163
self.index = 0;
145164

165+
if cfg!(feature = "mosi_idle_high") {
166+
self.write_reset()?;
167+
}
168+
146169
for item in iterator {
147170
let item = item.into();
148171
self.write_byte(item.g)?;
149172
self.write_byte(item.r)?;
150173
self.write_byte(item.b)?;
151174
}
152-
self.send_data().map_err(|e| Error::Spi(e))
175+
176+
if cfg!(feature = "reset_single_transaction") {
177+
self.write_reset()?;
178+
}
179+
180+
self.send_data().map_err(Error::Spi)?;
181+
182+
if !cfg!(feature = "reset_single_transaction") {
183+
self.send_reset()?;
184+
}
185+
Ok(())
153186
}
154187
}
155188

@@ -159,21 +192,35 @@ where
159192
{
160193
type Error = Error<E>;
161194
type Color = RGBW<u8, u8>;
162-
/// Write all the items of an iterator to a ws2812 strip
195+
/// Write all the items of an iterator to a SK6812W strip
163196
fn write<T, I>(&mut self, iterator: T) -> Result<(), Error<E>>
164197
where
165198
T: IntoIterator<Item = I>,
166199
I: Into<Self::Color>,
167200
{
168201
self.index = 0;
169202

203+
if cfg!(feature = "mosi_idle_high") {
204+
self.write_reset()?;
205+
}
206+
170207
for item in iterator {
171208
let item = item.into();
172209
self.write_byte(item.g)?;
173210
self.write_byte(item.r)?;
174211
self.write_byte(item.b)?;
175212
self.write_byte(item.a.0)?;
176213
}
177-
self.send_data().map_err(|e| Error::Spi(e))
214+
215+
if cfg!(feature = "reset_single_transaction") {
216+
self.write_reset()?;
217+
}
218+
219+
self.send_data().map_err(Error::Spi)?;
220+
221+
if !cfg!(feature = "reset_single_transaction") {
222+
self.send_reset()?;
223+
}
224+
Ok(())
178225
}
179226
}

0 commit comments

Comments
 (0)