Skip to content

Commit 9695c0d

Browse files
committed
drivers: led_strip: ws2812_spi: Ensure MOSI is low during reset
Some platforms, such as STM32, do not guarantee that the MOSI line is held low when the SPI peripheral is disabled. This can prevent WS2812-based LEDs from resetting correctly, as they require a low signal for a specific duration to trigger a reset. This commit introduces a Kconfig option to actively drive the MOSI line low during the reset period by sending zero-byte frames via SPI. This ensures the reset condition is reliably met across all hardware platforms. Signed-off-by: Arthur Gay <[email protected]>
1 parent 2784dfd commit 9695c0d

File tree

2 files changed

+38
-7
lines changed

2 files changed

+38
-7
lines changed

drivers/led_strip/Kconfig.ws2812

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ config WS2812_STRIP_SPI_FORCE_NOCACHE
2626
For SPI communication using DMA, place the WS2812 SPI buffer in the __nocache section to
2727
ensure it is stored in uncached memory, as DMA usually requires access to uncached regions.
2828

29+
config WS2812_STRIP_SPI_RESET_DELAY_SEND_NOP
30+
bool "Send reset delay using NOP frames"
31+
help
32+
Use SPI NOP frames to generate the reset delay before and after sending pixel data. This
33+
ensures the MOSI line is held low during the reset period, which may not be guaranteed on
34+
some platforms such as STM32.
35+
2936
endif
3037

3138
config WS2812_STRIP_UART

drivers/led_strip/ws2812_spi.c

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,19 +126,41 @@ static int ws2812_strip_update_rgb(const struct device *dev,
126126
const size_t total_bits = num_pixels * cfg->num_colors *
127127
BITS_PER_COLOR_CHANNEL * bits_per_symbol;
128128
const size_t buf_len = DIV_ROUND_UP(total_bits, SPI_FRAME_BITS);
129-
struct spi_buf buf = {
130-
.buf = cfg->px_buf,
131-
.len = buf_len,
132-
};
129+
struct spi_buf buffers[COND_CODE_0(CONFIG_WS2812_STRIP_SPI_RESET_DELAY_SEND_NOP, (1), (3))];
130+
struct spi_buf *buf = &buffers[0];
133131
const struct spi_buf_set tx = {
134-
.buffers = &buf,
135-
.count = 1
132+
.buffers = buffers,
133+
.count = ARRAY_SIZE(buffers),
136134
};
135+
uint32_t reset_delay_cycles;
137136
uint8_t *px_buf = cfg->px_buf;
138137
uint8_t bit_mask = BIT(SPI_FRAME_BITS - 1);
139138
size_t i;
140139
int rc;
141140

141+
if (IS_ENABLED(CONFIG_WS2812_STRIP_SPI_RESET_DELAY_SEND_NOP)) {
142+
/*
143+
* Convert the reset delay from micro seconds to a number of cycles of the SPI clock.
144+
* The bus frequency is a maximum and may not be the actual frequency used. In that
145+
* case, the reset delay will be longer than expected but that should not be an
146+
* issue.
147+
*/
148+
reset_delay_cycles = cfg->bus.config.frequency * cfg->reset_delay / USEC_PER_SEC;
149+
150+
/*
151+
* Define dummy buffers that will send zeroes on the MOSI line for the duration of
152+
* the reset delay.
153+
*/
154+
buffers[0].buf = NULL;
155+
buffers[0].len = reset_delay_cycles / 8;
156+
buf = &buffers[1];
157+
buffers[2].buf = NULL;
158+
buffers[2].len = reset_delay_cycles / 8;
159+
}
160+
161+
buf->buf = cfg->px_buf;
162+
buf->len = buf_len;
163+
142164
/*
143165
* Convert pixel data into an SPI bitstream. The bitstream contains
144166
* pixel data in color mapping on-wire format (e.g. GRB, GRBW, RGB,
@@ -182,7 +204,9 @@ static int ws2812_strip_update_rgb(const struct device *dev,
182204
* Display the pixel data.
183205
*/
184206
rc = spi_write_dt(&cfg->bus, &tx);
185-
ws2812_reset_delay(cfg->reset_delay);
207+
if (!IS_ENABLED(CONFIG_WS2812_STRIP_SPI_RESET_DELAY_SEND_NOP)) {
208+
ws2812_reset_delay(cfg->reset_delay);
209+
}
186210

187211
return rc;
188212
}

0 commit comments

Comments
 (0)