SPI on Cerberus to drive WS2811 Neopixels

It’s Christmas and so flashing lights are everywhere; including connected to my GHI Spider in the form of a NeoPixel ring. They work pretty well from SPI2, and although I’m not 100% sure the timing is perfect (I see the occasional random LED) it’s not bad for less than an hours work.

However, I want to drive it from a Cerberus (well actually from a home-grown board based on the Cerberus) but I get an AurgumentException if I try and use SPI2 and no output at all (but at least it runs) if I change to SPI1. Neither an actual Cerberus or my clone will drive the NeoPixel ring using the code from here:

https://www.ghielectronics.com/community/codeshare/entry/649

But my Spider does. I’m also pretty sure that I am not using the Ethernet firmware on my Cerberus and there is nothing else declared that is using SPI.

I have read through this post (linked from the CodeShare) but can’t get anything from my Cerberus.

https://www.ghielectronics.com/community/forum/topic?id=10710&page=1

Here’s the code that generates the exception


public WS2811Led(int striplength, SPI.SPI_module SPImodule = SPI.SPI_module.SPI1, WS2811Speed speed = WS2811Speed.S800KHZ, double linearizationfactor=2.25)
{
	_StripLength = striplength;
	_StripLightNb = striplength * 3; //For later speed optimizations
	_StripBufferLen = striplength * 12; //For later speed optimizations

	// Initialize SPI
	_WS2811SPIConfig = new SPI.Configuration(STM32.Pin.GPIO_NONE, false, 0, 0, false, true, (uint)speed, SPImodule);
	try
	{
		_WS2811SPI = new SPI(_WS2811SPIConfig);
	}
		catch(System.ArgumentException ex1)
	{
		Debug.Print(ex1.Message);
		throw;
	}
	catch (Exception ex)
	{
		Debug.Print(ex.Message);
		throw;
	}
	

	// 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. By default the linearisation of human perceived luminosity is on (Weber-Fechner law)
	_WS2811Table = new byte[1024];
	BuildTable(linearizationfactor);

	//Clear all leds
	Clear();
	Transmit();
}

when I call this line


            // Initialize the strip : here using SPI2 and 800Khz model and using the linear human perceived luminosity PWM conversion factor of 2.25
            MyWS2811Strip = new WS2811Led(NumberOfLeds, SPI.SPI_module.SPI2, WS2811Led.WS2811Speed.S800KHZ, 2.25);

from my main subroutine.

Does anyone have any clues or suggestions?

@ Jason - I haven’t tried running my blinkies from a Cerberus specifically, but I have run some tests on Cerbuino Bee, which should be very similar, and I am able to run WS2812 LEDs from the Bee.

If I have some time, I’ll specifically test the NeoPixel ring I have, but in theory it should work on Cerb, I would think.

One thing to watch out for with that codeshare…there are spots where the number of LEDs is hardcoded in the codeshare code, so if you are using a different number of LEDs, the library will throw an exception…might that be what you’re running into?

1 Like

The exception seems to be when the SPI is initialised and not when it’s used; I never get that far. I’ll check the codeshare source for hard-coded pixel count too.

I’d be interested to see what you get with any testing you can do.

My Neopixel ring does “work” on a Cerberus. Just tested, and it works just the same as on my Cerbuino Bee.

I put “work” in quotes because I think there must be some timing issues, as the first pixel never lights at all, and the second pixel is full on green unless the other colors are also lit (it never goes completely out). The remaining pixels work normally, whether on Cerbuino Bee, or on Cerberus.

I haven’t tested on my Spider board, so I don’t know if the issue exists only on Cerb-family, or on all boards.

Did you modify the initialization code in the codeshare library? This is what mine looks like:

            _WS2811SPIConfig = new SPI.Configuration(Cpu.Pin.GPIO_NONE, false, 0, 0, false, true, (uint)speed, SPImodule);

where I see the first argument in your example uses STM32.Pin.GPIO_NONE. No idea whether that might cause issues, but it’s one difference I see between your code and wat I’m using.

Also, pretty sure I’m using SPI1, not SPI2.

1 Like

@ devhammer - Thanks for checking so quickly. I tried so many things. The STM32 reference is for my clone, but I tried the GPIO.None reference too. I could get either to work on my clone and I can’t remember if I tried SPI1 on my Cerberus. The hardware is in the office so I’ll check again tomorrow.

Have you made any further mods to the source from codeshare? I had the same random LEDs (usually green) and put it down to timing.

What are you using (FW/HW) to drive your “Star” project?

The only mod I made to the codeshare was replacing the hard-coded value for the number of LEDs with the variable name from the top of the class.

I just tested the same code on one of my Spider boards (on NETMF 4.2, because I can’t get the darn bootloader to work…GRR!), and it works fine. In fact, I’m not seeing the anomalies on the Spider, so it must be a timing issue on the Cerb, which is a bummer.

I’ve also got a FEZ Hydra and a FEZ Cobra II, so perhaps I’ll give those a go at some point, but probably not today.

For the initial testing, I’m actually using an A* microcontroller from Pololu, since they’ve got an easy-to-use Arduino library for the LED strips. Ideally, I’d like to switch it over to use one of my Gadgeteer boards, so I can add a Tunes module, but don’t know if I’ll get that done this year…running out of time rapidly. :slight_smile:

But the bottom line is that, notwithstanding some weirdness, the WS2811/12 should work on Cerberus.

Hope this helps!

@ Jason - FYI, I tested the code running on Spider FW 4.2.11, and it works well with my star. It’s actually so fast that much of the demo is almost too fast to see. But should work well if I decide to go NETMF/Gadgeteer for the control.

@ devhammer - The example, not the library, includes a reference to 240 and not NumberOfLeds. I have updated this in any case, but I am not using the example code, just the library at present. I have a Spider and a Cobra II so I’ll test it more thoroughly on them too, as well as further testing on different SPI ports.

I’ll post more on this tomorrow.

Sorry…must have misremembered where the error was.

I made some tweaks to my code on the Spider to better suit the Star project, and here’s the result:

[video=vimeo]115205926

A good start, though I’d like to add some additional patterns and perhaps some fades.

As I noted in my project thread, I’m finding glitching behavior when I try to run the board off of an external power supply (12V 3A) connected to the USB DP. If I run on USB alone, or on both USB and external power, no glitching. Weird.

2 Likes

Doh. :wall:

The STM32F405 datasheet has pin A7 and B5 both marked as SPI1_MOSI. I found A7 first and so stopped looking - mistake #1. With the NeoPixel connected to A7 (on my Cerberus clone) nothing worked, however, using the SPI1 on my Cerberus and Cerbuino Bee everything was golden.

Closer inspection of the Cerb* schematic (which is difficult to read where the pin names overlap) shows that the Gadgeteer socket uses PB5, not PA7.

I moved the connection to PB7 and my Cerb clone works like a charm too.

So, firstly thanks @ devhammer for your help and testing, it’s much appreciated. And secondly, why does the MCU have two pins for SPI1_MOSI, why does only one of them work and do I need to check the PK to know which one I should be using?

I’ll post a video of them working soon.

Here is it.

Library used: https://www.ghielectronics.com/community/codeshare/entry/649

1 Like

@ Jason - Nicely done.

Are the two rings wired in sequence? That is, is the Data Out from one wired to Data In on the other? I ask because I can see the first green pixel that I usually see on mine when running on Cerb, but only on one of the rings.

Would be great to figure out why that happens.

So what are you planning with your Neopixel rings?

@ devhammer, ye they are wired in series with output -> input and connected to my STM32F405 (same as the Cerb*) boards. When I get a spare hour or two I might take a look at why they behave the way they do on the Cerb. I occasionally see other random leds but at the moment I only have a scope to debug the hardware with and the SPI is a little fast and I don’t always see the trigger. I’m thinking of investing in a Bus Pirate so I can take a look at more than a couple of lines of IO at a time.

I have no current plans but they might get incorporated into an FTTh (fibre to the home) installation we are looking at for our demo area in my offices. They would be great way to visualise the route the fibres (and their signals) take from the central office to a home.

Here’s your answer:


        public WS2811Led(int striplength, SPI.SPI_module SPImodule = SPI.SPI_module.SPI1, WS2811Speed speed = WS2811Speed.S800KHZ, double linearizationfactor = 2.25)
{
    _StripLength = striplength;
    _StripLightNb = striplength * 3; //For later speed optimizations
    _StripBufferLen = striplength * 12; //For later speed optimizations

    // Initialize SPI
    // Set clock edge to false to remove the failing initial LED
    _WS2811SPIConfig = new SPI.Configuration(Cpu.Pin.GPIO_NONE, false, 0, 0, false, false, (uint)speed, SPImodule);
    try
    {
        _WS2811SPI = new SPI(_WS2811SPIConfig);
    }
    catch (Exception ex)
    {
        Debug.Print(ex.Message);
        throw;
    }
...

Change the Clock_Edge to false in the SPI.Configuration method call.

The try…catch…finally is optional.

We have our friendly AWOL Kiwi to thank for it.

Trying to use this library with my Bee, and no blinky. I’m quite sure I’m missing something obvious.

My set up is simple - have a breakout on socket #1. Pin 7 (PB5 SPI1 MOSI) is connected to the NeoPixel In, as is GND and +5v.

Am using Nicolas’ v3 code in the codeshare. Currently I only have 1 pixel connected to test.

In debugging, I tried different speeds and adjusting the SPI config falling edge setting mentioned above.

            
var ws = new WS2811Led(1, Microsoft.SPOT.Hardware.SPI.SPI_module.SPI1);
            byte r = 0, g = 75, b = 150;
            GT.Timer t = new GT.Timer(100);           
            t.Tick += (o) =>
                {
                    if (r < 245)
                        r+= 5;
                    else
                        r = 0;

                    if (g < 245)
                        g+= 5;
                    else
                        g = 0;

                    if (b < 245)
                        b+=5;
                    else
                        b = 0;

                    ws.Set(0, r, g, b);
                    ws.Transmit();
                };
            t.Start();

@ mhectorgato - Do you happen to have another SPI-based module you could test with?

I’ve got 3 different Cerbuino Bee boards, and on two of them, I’ve had trouble getting WS2811 blinkies working. The third works like a charm, as do my Spider and Cobra II.

Testing with a different SPI module would at least confirm whether or not SPI is working correctly.

Unfortunately I don’t. I plugged the same code on my Spider and it worked ok.

My next Bee failure was trying to use the 'Dunio header and SPI2, but it’s blowing up on the SPI constructor.

An unhandled exception of type ‘System.ArgumentException’ occurred in Microsoft.SPOT.Hardware.dll

This fails:
var ws = new WS2811Led(1, Microsoft.SPOT.Hardware.SPI.SPI_module.SPI2);


_WS2811SPIConfig = new SPI.Configuration(Cpu.Pin.GPIO_NONE, false, 0, 0, false, false, (uint)speed, SPImodule);
// exception happens below
_WS2811SPI = new SPI(_WS2811SPIConfig);

This doesn’t throw an exception, but also doesn’t work.
var ws = new WS2811Led(1, Microsoft.SPOT.Hardware.SPI.SPI_module.SPI1);

My board is 4.2; so maybe I need to upgrade to 4.3 and see what happens then.

@ mhectorgato - Cerberus firmware only supports SPI1…

That would explain the exception!

Upgraded to 4.3 - much better. However am getting similar issues with the first pixel.

The code should presumably make all 4 have the same color, but the first one is different (left most)


            var ws = new WS2811Led(4, Microsoft.SPOT.Hardware.SPI.SPI_module.SPI1, WS2811Led.WS2811Speed.S800KHZ);            
            ws.Clear();

            byte r = 0, g = 75, b = 150;
            GT.Timer t = new GT.Timer(50);           
            t.Tick += (o) =>
                {
                    if (r < 245) r+= 5; else r = 0;
                    if (g < 245) g+= 5; else g = 0;
                    if (b < 245) b+=5; else b = 0;

                    ws.Set(0, r, g, b);
                    ws.Set(1, r, g, b);
                    ws.Set(2, r, g, b);
                    ws.Set(3, r, g, b);
                    ws.Transmit();
                };
            t.Start();

As per comments on this thread, changed the SPI constructor:


_WS2811SPIConfig = new SPI.Configuration(Cpu.Pin.GPIO_NONE, false, 0, 0, false, false, (uint)speed, SPImodule);