The approach in all three cases aims to conserve clock cycles when timing the communications. This leaves the developer as many cycles as possible to perform other tasks than simply telling the lights what to do. One approach is an assembly routine that is just a shade slower but groups all 14 free cycles into one block. The next looks at using external 7400 series hardware. The final technique is good old-fashioned bit banging.

21 thoughts on “Three conceptual approaches to driving a WS2811 LED pixel”

Adafruit actually has written a neat library for driving the WS2811 Pixels. They use them in their Flora RGB Smart Pixel and in their NeoPixel strip. The library seems perfect for most users, link to it here: https://github.com/adafruit/Adafruit_NeoPixel.

The NeoPixel library is not quite in the spirit of what Alan or myself where doing. Alan had already tried FastSPI (An SPI library that some people get to work with the WS2811). His hardware did not cope with the inter-byte jitter so he wrote his own. NeoPixel will also have this problem as it shifts a byte then jumps out a subroutine and then then back in.

Since it was in reference to the interrupt, I was thinking one could write code within the interrupt vector area. As long as a person takes care not to have any other interrupt (that would jump into the middle of that code) fire off, it should work. I don’t really know if that would work as described because A) if I’m so clock cycle hungry on that particular AVR, I start looking at a different solution and B) it never really occurred to me and finally C) I don’t have an AVR spec sheet readily available atm to verify such an idea.

The AVR interrupt vectors are not the same as you would expect from from a CPU that can run code from RAM. Instead of having an address that is loaded to the PC from a vector table, the PC just gets loaded with the address of the table element. This address loaded is an RJMP to your ISR.

If you don’t mind loosing the ability to use the NEXT interrupt in the table you can make the word at the address of your interrupt be OUT IOReg, NewData then have the next word being RJMP to the code you want to run.

At the end of this code just make sure you leave the next byte in “NewData”.

Doing that “stupid AVR trick” you can easy get the next data into the IO register in under 9 clocks.

Downside is you loose the use of the next sequential interrupt in the table.

Nice. I’d previously tried using the TCO approach and failed, but his method looks totally workable. And points for Extreme Cleverness for the SPI slave thing (avoiding the not-double-buffered problem in master mode).

Adafruit_NeoPixel (thanks for the shout out, @joerautenbach) ended up using bit-banging, the primary motivation being that it’s not tied to a specific peripheral-defined pin or port, and in fact can handle multiple instances on different pins. The latter was especially important for wearable applications…you can run a strand to each extremity and ‘home run’ all the wiring, no need to double back to make a single continuous loop.

Thanks Phillip. I don’t do clever things very often but once and a while something will pop up.

The SPI as a slave is something I have done for a long time. You can even couple it with a timer interrupt to get higher clock rates and interrupt driven. (without using the stupid trick I alluded too)

It is something I also use in my high resolution laser photo plotter that I am going to get around to writing an instructable for one day.

I’m actually working on two additional approaches not seen here. One involves using an external 74HC123 monostable to generate appropriate one-shot pulses. The other involves an external SRAM, but I need to test it more before presenting it as a valid solution. My solutions (and eventual products) aren’t trying to avoid external hardware, because I want to come up with a reliable way to drive many WS2811 chains in parallel.

But if you don’t mind external hardware then a bit of VHDL and a cheap FPGA/CPLD will get you plenty of parallel WS2811 streams. Will leave you CPU plenty time to read the data over the USB/SD-Card as well.

That was approach three ;) However, I’m saving it for later as it’s not worthwhile unless you really are doing a lot of parallel streams, and I’m trying to make more of a modular system where each channel is mostly a sequential device where programmable logic is not needed. The software and programming methods for CPLDs also leave much to be desired.

For an entirely different project, I was trying to figure out a cute logic trick to rotate an 8-byte bitfield, in order to serialize it for parallel output streams. Never did figure it out…but a CPLD or 8 PISO shift registers would do the trick.

If you get particularly bored, do you think you could give the same treatment to the MHVLib AVR runtime library? I’ve designed it from the ground up to be tight and (mostly) sensible, but have not gone so far as to give it the ASM treatment.

OMG. I am using this metod with DMA and timer for a very long time. It was so obvious solution that I didn’t notice it’s even worth posting it here……. I always wondered why everyone was interfacing WS2811s with SPI – this interface has NOTHING TO DO with SPI!