cpldcpu / light_ws2812

Light weight library to control WS2811/WS2812 based LEDS and LED Strings for 8-Bit AVR microcontrollers.
GNU General Public License v3.0
946 stars 207 forks source link

Question about implementation - SPI? #97

Closed ole00 closed 2 years ago

ole00 commented 2 years ago

Just wondering about the comment on (I believe your) blog:

"Unfortunately, the single-line serial protocol is not supported by standard microcontroller periphery. It has to be emulated by re-purposing suitable hardware or by software timed I/O toggling, also known as bit-banging. "

I think an SPI could be possibly used - employing only SPI MOSI line and ignoring the clock line. The 1's and 0's of the WS protocol would be expressed by the numbers of 1's and 0's in the byte transferred over SPI. For example SPI 0b11111100 could be interpreted as WS 1 and SPI 0b11000000 could be interpretted as WS0. The SPI clock would have to be ~ 4x - 10x higher than the WS clock, but given the slow WS bitrate this should not be a big issue. Why only 4x increased speed? You found out that the timing of the pulses between WS bits does not play a big role as long as it is shorter than the reset period. So possibly a pattern 0x11100000 (for WS 1) and 0x100000000 (for WS 0) would also work if the SPI speed s only 4x higher. Or you could possibly improve the efficiency and send 2 WS bits by encoding them in 1 SPI bye: For example SPI 0b11101110 would be WS 0b11 and SPI 0b10001000 would be WS 0b00. This might be more prone to errors as the SPI timig would have to be quite precise in relation to WS expected timing. The MOSI line might be required to be pulled down by external resistor (5k-10k I'd imagine) in order not to float in between data bursts (to keep the WS in reset).

Just a thought - I have not tried it in practice.

Edit: just thought about which SPI frequency might work and in theory 4MHz should work fine (this should be achievable on Arduino UNO, at least when using 16MHz main crystal). For 4MHz SPI clock one SPI bit lasts 250ns, so 3 SPI bits in a row would take 750ns - that should be compatible with your findings: WS 1 pulse length must be at least 650 ns long, WS 0 pulse length must be between 64 and 500 ns long. An ideal SPI clock frequency would be ~ 3MHz (or slightly lower) where the SPI bit takes 333ns, so 2 SPI bits would express WS1 and 1 SPI bit would express WS 0. At this clock 2 WS bits could fit into 1 SPI byte while the total WS bit period would be 4 SPI bits x 333 ns, which is 1332 ns (your finding was that a minimum is 1250ns).

cpldcpu commented 2 years ago

Hi, Yes, indeed, one could use a SPI peripheral to emulate the WS2812 protocol as you describe. There are also some examples out there that do this on an AVR. On slower MCUs, and especially with out DMA, there is not a lot of benefit of using SPI, compared to bitbanging, because the CPU is basically busy all the time waiting to feed the SPI register.

On 32 bit MCUs, SPI+DMA is usually the best way to implement the WS2812 protocol. There are also many other approaches, using programme periphery. I especially like the PIO in the new RP2040.

ole00 commented 2 years ago

I should have done more investigation about WS LED's - you blog article about WS timing is now 8 years old, and - as you just wrote - other solutions were devised in the meantime. Your blog article about WS LEDS is still one of the first on the list from google search though, and rightfully so as it has plenty of interesting info. Maybe it's time to write the 3rd blog article listing other implementation approaches. One of the person on the blog's discussion mentioned PWM, which is also interesting idea. I found the blog discussion after I raised this github 'issue', one person also mentioned SPI (without further details) .

Thanks for the answer and for sharing your research.