zephyrproject-rtos / zephyr

Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
https://docs.zephyrproject.org
Apache License 2.0
10.98k stars 6.68k forks source link

WS2812 SPI first LED misbehaving #75965

Open matt-wood-ct opened 4 months ago

matt-wood-ct commented 4 months ago

Describe the bug

When driving a strip of WS2812B LEDs from the IMXRT1040 SOC using the WS2812_SPI driver the first LED is not controlled

To Reproduce Setup a board based on an IMXRT series process with an overlay which configures the ws2812-spi driver, example configuration:

&lpspi1 {
    led_strip: ws2812@0 {
        compatible = "worldsemi,ws2812-spi";

        /* SPI */
        reg = <0>; /* ignored, but necessary for SPI bindings */
        spi-max-frequency = <6000000>;
        reset-delay = <70>;

        /* WS2812 */
        chain-length = <6>; /* arbitrary; change at will */
        spi-one-frame = <0xF8>;
        spi-zero-frame = <0x60>;
        color-mapping = <LED_COLOR_ID_GREEN
                         LED_COLOR_ID_RED
                         LED_COLOR_ID_BLUE>;
    };
};

Run the LED strip driver sample.

See LEDs sequencing through colours except the first LED remains off.

Expected behavior All LEDs including the first LED should go through the intended colour sequence.

Impact In tree patch created, can bring the driver out of tree until fix is implemented/adopted

Logs and console output N/A

Environment (please complete the following information):

Additional context

I have investigated the problem and create a patch which can resolve the problem in my case, but I am unclear why a similar feature is not already in tree, unless I am missing something... which I probably am 😄

What I found was that the SPI MOSI had LED data shifted out as expected but at the start of the burst the transition was not graceful so the LEDs appear to ignore the first LED worth of data. Below is a logic analyser capture of the line activity:
image

The fix I discovered was to add some bus low time at the start (80uS) to trigger a reset on the LED. After this reset the burst of data can be sent and all LEDs behave well. I achived this by adding a head an tail of zeros onto the SPI data which is sent on ws2812_strip_update_rgb, in my case 64 byte of zero which translated to around 85uS of low on paper and ~82uS in practice on my hardware. This yields a bus behaviour which looks like this:
image

Patch: WS2812.patch This patch is not tidy enough to merge but it is functional, I imagine any production ready implementation would expose the head and tail delay parameters as device tree parameters for per SOC tweaking similar to the spi-one-frame and spi-zero-frame values.

One thing I did not understand when reading through the driver was this line https://github.com/zephyrproject-rtos/zephyr/blob/596ef0a519eec584e2c403927543b130a7166a45/drivers/led_strip/ws2812_spi.c#L132 It says reset delay, but the bus is not being driven at this time so they delay does not actually cause the intended 50uS of bus low time the datasheet calls for: image The end effect of that line is the driver just waits 50uS while doing nothing with the bus, in most cases the bus will be driving the MOSI line high during this time nullifying any useful effect.

simonguinot commented 4 months ago

Related with #66664

simonguinot commented 4 months ago

Hi @matt-wood-ct,

The short answer is that the ws2812_spi driver relies on the fact that the MOSI signal is idle low.

Unfortunately, adding a start/stop frame is not a great strategy. See #66664 where it has already been discussed. Note that some WS2812-compatible controllers such as the Everlight B1414 have a 250us reset delay. At a 6MHz frequency, this would waste 188 bytes of memory...

Si in order to use the ws2812_spi driver you need to configure your SPI controller to get the MOSI low when idle.

And if it is not possible then the ws2812_i2s, ws2812_gpio, and (hopefully soon) ws2812_uart drivers are good alternatives.

matt-wood-ct commented 4 months ago

Thanks @simonguinot for the quick response, I'll take a look at the NXP reference manual and zephyr SPI driver and see if I can adjust the SPI to behave in that manner, I see a few NXP forum threads wanting similar things with mixed results so it may be possible 😄.

Side note, perhaps if this a somewhat common issue as MOSI is not necessarily defined when the bus is idle, would it be worth having some kind of mitigation in the driver toggleable on a 'as needed' basis e.g. kconfig default'd false. It seems that we all agree manually inserting start/stop frames does in fact work, I agree with your comments about RAM waste, but there are more optimal ways to implement the same behaviour without growing the buffer.

matt-wood-ct commented 4 months ago

For the moment I have drafted a pull request just to float the idea of a default disabled preamble system which would allow users in a position where the bus behaviour is problem to apply a simple device level tree fix. How do people feel about this?

https://github.com/zephyrproject-rtos/zephyr/pull/75971

github-actions[bot] commented 2 months ago

This issue has been marked as stale because it has been open (more than) 60 days with no activity. Remove the stale label or add a comment saying that you would like to have the label removed otherwise this issue will automatically be closed in 14 days. Note, that you can always re-open a closed issue at any time.

matt-wood-ct commented 2 months ago

FYI I am going to resume working on this and the associated PR