Main Site Documentation

WS2811 control using SPI or RLP?


#1

Hi everyone,
I’m working on using my Panda 2 to control a couple strings of the RGB leds that have the ws2811 built in. The ws2811 does not use a clock signal and therefor requires bits to be fed to it using a specific timing standards. A zero is sent as a .25us high followed by a 1.0us low and a one is sent as a 1.0us high followed by a .25us low. Bits must be within 50us of each other.

Here’s a datasheet and a link with more information and corrected timing info.


http://bleaklow.com/2012/12/02/driving_the_ws2811_at_800khz_with_a_16mhz_avr.html

A number of people , including myself, have managed to use SPI to send out bytes that have roughly the correct timing to pass as a one or zero to the ws2811. For example 128 (1000000) works as zero and 240(11110000) works as one. So, in order to send out the three bytes to set the gray-scale values for one led (G,R,B values) you end up sending out a total of 24 bytes of data through SPI. So say I have four strings of 150 leds and I want to set all leds, I’d have to convert and send out 600 x24 bytes = 14400 bytes.

To me, it seems that the SPI method is quite wasteful and I have a feeling the memory requirements will become an issue. What would you guys suggest, keeping in mind the timing restraints?

Thanks


#2

Have you looked at using OutputCompare? I do not have a Panda 2 so I am not sure this is/was included in the firmware.
http://wiki.tinyclr.com/index.php?title=Output_Compare


#3

From what I have read about it its a no go with .net


#4

Far easier to use ws801 or ws2803
For instance 5m of 2801 with 32 LEDs per m is no problem with spi


#5

Yes, the Panda-II includes OutputCompare.


#6

Hi everybody !

Has anyone been able to make something work with the WS2811 yet ? If not I am going to make up something. Sounds like outputcompare is not possible here (too short pulses - about 0,25uS or only 0,5uS with the 400Khz mode) so I think I’ll address it using SPI at 1,6Mb/s and 4 bits to produce a single output state.

Thanks in advance,
Nicolas


#7

Hi Nicolas,

I looked into and after the initial excitement gave it up as a bad idea.
If you get it working l’d happy shake your hand and say well done :slight_smile:
They are a great idea, just a shame they didn’t use the 2801 instead.

Cheers
Justin


#8

It was pretty easy getting the ws2811 to operate using SPI. The kicker, for me at least, was that the SPI transfer seemed to suck up too much cpu time to allow for a decent update rate. To be fair, I’m a programming newb so it could have a lot to do with my coding.

I’ve since switched to programming C on a different chip that has SPI and DMA peripherals. Using that setup, the cpu basically tells the SPI bus to start transferring and from that point on the SPI bus and DMA handle most of the transfer functions. The cpu is then left to perform other tasks like getting the next transfer ready.

I haven’t looked to see if the Panda2 or other .net devices have a similar setup where you’re able to hand off the SPI duties to the peripherals but if they do I would think that would be the way to go.


#9

@ Stinky - how many where you able to run?


#10

I think I tried as many as 150 on the Panda 2. With the new chip I’ve done up to 600.


#11

@ Stinky - just to confirm you used the ws2811 and not ws2801?
if you used 2811 was it inbuilt into the led?


#12

Yes on both questions. 150 led RGB strips with the ws2811 built in to the led with a power supply voltage of 5v.


#13

Cool, I stand corrected.
Any chance you can share some code?


#14

Some quick dirt code in 2 hours to demonstrate using SPI :wink: Works well. The following code moves a purple light arround a 4 meters 240 RGB light string I got from Adafruit. Nothing fancy here, but I’ll clean up the code, add some comments and put it to the CodeShare later. G31VL6hlbgE


using System;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

using GHIElectronics.NETMF.FEZ;

namespace WS2811_test
{

    public class Program
    {
        public static void Main()
        {
            WS2811 MyWS2811Strip = new WS2811(240,SPI.SPI_module.SPI2);

            Thread.Sleep(2000);

            while (true)
            {
                for (int i = 0; i < 240; i++)
                {
                    if (i > 0) MyWS2811Strip.Set(i - 1, 0, 0, 0, false);
                    MyWS2811Strip.Set(i, 255, 0, 255);
                }
                for (int i = 238; i >= 0; i--)
                {
                    MyWS2811Strip.Set(i + 1, 0, 0, 0, false);
                    MyWS2811Strip.Set(i, 255, 0, 255);
                }
            }

        }

    }


    public class WS2811 : IDisposable
    {

        private byte[] _WS2811Table;
        private byte[] _WS2811Buffer;
        private SPI.Configuration _WS2811SPIConfig;
        private SPI _WS2811SPI;
        private int _StripLength;

        public WS2811(int striplength, SPI.SPI_module SPImodule)
        {
            _StripLength = striplength;

            // Initialize SPI
            _WS2811SPIConfig = new SPI.Configuration(Cpu.Pin.GPIO_NONE, false, 0, 0, false, true, 3200, SPImodule);
            _WS2811SPI = new SPI(_WS2811SPIConfig);

            // SPI Transmit buffer = 4 output byte per color, 3 colors per light 
            _WS2811Buffer = new byte[4 * 3 * striplength]; 

            // Compute fast byte to SPI lookup table
            _WS2811Table = new byte[1024];     
            for (int i = 0, ptr = 0; i <= 255; i++)
            {
                for (int j = 6; j >= 0; j -= 2)
                {
                    switch (i >> j & 3)
                    {
                        case 0: _WS2811Table[ptr++] = 0x88; break;
                        case 1: _WS2811Table[ptr++] = 0x8C; break;
                        case 2: _WS2811Table[ptr++] = 0xC8; break;
                        case 3: _WS2811Table[ptr++] = 0xCC; break;
                    }
                }
            }

            //Clear all leds
            Clear();
        }


        public void Dispose()
        {
            _WS2811Buffer = null;
            _WS2811Table = null;
            _WS2811SPI.Dispose();
            Debug.GC(true);
        }



        public void SetAll(byte[] BGRBuffer, bool transmit=true)
        {
            // Transcode
            for (int i = 0; i < _StripLength * 3; i++) Array.Copy(_WS2811Table, BGRBuffer[i] << 2, _WS2811Buffer,i << 2, 4);

            // Send
            if (transmit) _WS2811SPI.Write(_WS2811Buffer);
        }


        public void Clear (bool transmit=true)
        {
            // Transcode
            for (int i = 0; i < _StripLength * 3; i++) Array.Copy(_WS2811Table, 0, _WS2811Buffer, i << 2, 4);
            
            // Send
            if (transmit) _WS2811SPI.Write(_WS2811Buffer);
        }


        public void Set(int index, byte r, byte g, byte b, bool transmit=true)
        {
            // Transcode
            Array.Copy(_WS2811Table, g << 2, _WS2811Buffer, index * 12, 4);
            Array.Copy(_WS2811Table, r << 2, _WS2811Buffer, index * 12 + 4, 4);
            Array.Copy(_WS2811Table, b << 2, _WS2811Buffer, index * 12 + 8, 4);

            // Send
            if (transmit) _WS2811SPI.Write(_WS2811Buffer);
        }


        public void Transmit()
        {
            _WS2811SPI.Write(_WS2811Buffer);
        }

    }
}


#15

Very good. Will you post this on codeshare?


#16

Sure; But it’ll need more work before, optimization, comments, benchmarking :wink:

As you can see, the old Rhino you gave me is still working fine !


#17

@ Nicolas3 - sweet


#18

That is fast! Where are you guys getting the 2811s from?


#19

Hi Eric ! I got mine from adafruit : http://adafruit.com/products/1138
They are very bright and with a high pixel density (60 RGB leds per meter). And they are waterproof !
It takes only 2 wires (gnd and SPI), however it is sucking up quite a bit of power: Needs 5v 10A for lighing up the entire strip… but Adafruit is selling a great DC power to go with it.

My old Rhino board can sweep the entire 240 RGB leds in 3s, however to refresh all the leds at once, I barely get 10Hz : am Still trying to improve that. Right now we have a memory buffer for the 720 lights, which takes a little less than 3kB. Currently, I am pre-calculating a 1024bytes look up table to speed up the process. This is all a compromise between memory usage and speed (you might get much higher refresh rates on newer GHIs boards !!! especially if you have plenty of RAM available…). I will try to set up a separate thread for the SPI transfer to free up CPU time for the art :wink:

Have FUN !


#20

it’s same work with this http://www.adafruit.com/products/322 ?