66% or better

Three conceptual approaches to driving a WS2811 LED pixel

driving-a-ws2811

[Cunning_Fellow] published a post with three proof-of-concept approaches to driving a WS2811 LED pixel. We looked at a project early in December that used an AVR microcontroller to drive the RGB package. [Cunning_Fellow] saw this, and even though he doesn’t have any of these parts on hand he still spent the time hammering out ways to overcome the timing issues involved with address the device. His motto is “put up or shut up” when it comes to criticizing projects featured on Hackaday. We love seeing someone pick up an idea and run with it.

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.

[Photo Credit]

Comments

  1. 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.

  2. SavannahLion says:

    What “stupid AVR trick” is he referring to? I can only think of one and I’m not certain it would work anyways.

  3. CunningFellow says:

    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.

    Savannah – What trick where you thinking of ?

    • SavannahLion says:

      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.

      • cunning_fellow says:

        Yep – that is the trick.

        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.

  4. 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.

  5. CunningFellow says:

    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.

  6. macegr says:

    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.

    • CunningFellow says:

      macegr – I have one word to say to you “programmable logic”.

      OK it’s two words.

      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.

      • macegr says:

        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.

        • cunning_fellow says:

          VHDL is not so bad. And for $2 or $3 you can get a chip that could offload all the fast/time-critical work.

          That said – you could bit bang 8x WS2811 streams out a single AVR port in software if you didn’t need to shuffle the data order.

          Bit banging 8 parallel streams would take close to 100% of your free time. So you could not serialise AND get new data at the same time. This would mean you would have to

          1, Get the data from UART/USB/SD-Card to RAM
          2, Serialise the data

          SO you are straight away limited to how much RAM you have on board.

          • macegr says:

            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.

  7. CunningFellow: Nicely done, you obviously know your stuff :)

    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.

    http://www.makehackvoid.com/project/MHVLib

  8. cunning_fellow says:

    Alastair,

    Always willing to help out fellow Aussies and I have enjoyed reading some of the Make Hack Void stuff.

    HOWEVER – from a very early age all my teachers have said “cunning does not play well with others”

    If you have specific routines you would like looked at then give me a task that I can go off and do in ASM by myself.

  9. NeedAWire says:

    Here are a few more code snippets in assembly for the MSP430 Launchpad.

  10. jerry9871 says:

    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!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s