Adafruit Neopixel Library [PORTED]

@BDub here is a short vine of the wire situation: https://vine.co/v/h3tAvF5h1bg

The strip has 2 grounds, 2 5v, and a data pin (green wire). I have a 5v going from the strip to the power supply and a ground going from the strip to the power supply. I also have a 5v going to the VIN on the spark core and a ground going to the ground on spark core. The data wire is plugged into D0. I hope that’s not confusing… lol.

@BDub - more demo videos here:

https://vine.co/v/h3t1JUnEVFO
https://vine.co/v/h3tnlXMFZKA
https://vine.co/v/h3tJF9eYinU
https://vine.co/v/h3t5BUjMdWF
https://vine.co/v/h3tiJE0EZtp
https://vine.co/v/h3tJEggMqVP

I tried running one pixel, but defining 120 pixels in the code… and it looks like (#1 I’m blind from staring at the damn neopixel) and #2 noInterrupts(); and interrupts(); don’t do as much as I would have liked…

Switched those out for __disable_irq(); and __enable_irq(); and it’s glitch free. Thanks @jagor for mentioning those! I need to readjust the timing a little now, but just making those changes works well.

I powered the pixel from Vin and 3.3 and 3.3* and all are flicker free now.

1 Like

Excellent! Can't wait for these to come through! I'm on gchat if you're around.

I'm powering the strip from the VIN and ground on the VIN side. Will this work?

I have them hooked up on my desk now =P https://vine.co/v/h3tw9HFrUJ7

From the datasheet, the Din on the pixels should be 3.5V if powering from 5V, but apparently YMMV and it you might get lucky! Your desk looks insanely glowie! So is it not glitching just with those changes?

Regarding the flashing blue… that can happen if you block the main loop too much. If using rainbow(); with 120 pixels, it takes about 3.6ms to send all pixel data, so you can’t set rainbow to anything less than rainbow(5); I’d say which is 5 milliseconds. If you set rainbow to anything higher than rainbow(50) I’ve noticed it block the main loop.

It is! I need to extend this so we can turn on/off rainbow mode, turn the entire led strip a color, blink a section of the lights while continuing normal operations on the other lights, etc. I think we should start at making a few spark functions for various things, like: toggleRainbow( 'true' || 'false' ) and so forth.

It's WAYY smoother with __disable_irq(); and __enabled_irq();

I'll be sure to take note of this!

1 Like

I actually just added this for debugging, heh heh

colorAll(strip.Color(0, 0, 255), 50);


// Set all pixels in the strip to a solid color, then wait (ms)
void colorAll(uint32_t c, uint8_t wait) {
  uint16_t i;
  
  for(i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
  }
  strip.show();
  delay(wait);
}

Fill free to use my library for LED Strip https://github.com/Jahor/LEDStrip

It was designed to make christmas lights, which are 10 groups of 4 LEDs (circles) connected in series.
Also it is not in usual Arduino library style of C++ class, as I was doing as much as possible to shrink it to fit Atmega8 8K of flash (for 25-pixel rabbit toy). Now I’m too lazy to make it back a set of classes. Hope example helps.

It has some patterns built-in.

You will have one “real” strip (whole in example), which does all the transmission work.

You can have partial strip (circles, half arrays in example), which is just strip mapped to the part of another strip, so it is easier to apply different patterns to different parts of the strip.

It also allows to have Meta-strips (cl - consists of whole circles, clh - half-circles, cl2 - odd/even circles, cl3 - each 3rd circle in example ). Meta-strip consists of several strips, each of which becomes it’s pixel and so can be controlled as a single LED. Fun starts when colour you set is not plain RGB colour, but animated colour. Like you can have rainbow animation on meta-strip, but instead of setting sub-strip to a single colour from rainbow it will set it to running pixel animation of that colour (rainbowRun in example application).

Check out example application.cpp

Have fun and let me know if you have any questions.

@jagor Do you have a video of what this baby can do? Looks pretty slick.

No, unfortunately I don’t. It’s installed in another place right now. Will try to make one tomorrow.

Timing is really nice now for larger strips. I just tried driving the arduino’s max number of 500 pixels (arduino runs out of ram at that point), and the Spark Core :spark: is banging them all out in ~15ms without deviating on the last pulse by more than 5ns! Of course I have the color fixed for this test, but if you change the bit pattern, the timing changes by design. All timing of the high and low portions of the HIGH/LOW bit patterns are measuring within 8ns and under of spec, and the spec has a +/-150ns tolerance so I’d say that’s pretty damn good for C code and a little ASM for delays.
https://github.com/technobly/SparkCore-NeoPixel

3 Likes

Hello all,

I tried and succeeded to use the SPI to create the WS2812 data stream. When SPI clock divider is set to 8, the SPI frequency is 9MHz, so a bit period is 111nS. So sending 3 bits set to 1 in a row creates a 0.33µS pulse, and sending 6 bits set to 1 creates a 0.66µS pulse; this is well within the tolerance for the WS2812. This works well and with very little code, see below.

However, the approach suffers from the same problem as all others: spark system IRQs interrupt the execution of show() for more than 50uS now and then, which resets the WS2812 chips in mid-transmission and leads to ugly glitches in the color pattern. This can be fixed using __disable_irq(), but in turn this blocks other tasks within the spark core firmware for quite long chunks of time.

So what’s the point of using SPI then? The spark chip can use DMA to send SPI data streams without any CPU load, at full speed. So if we can manage to set up DMA, and an IRQ to re-fill the output buffer from RGB data, this would be the most elegant solution for the WS2812 driving problem. So take the following code as a proof-of-concept that SPI can be used to generate WS2812 timing easily, and a starting point for a DMA based, non IRQ blocking WS2812 driver.

Anyone out there who already got experience with the spark core’s DMA?

Here’s the minimal code based on SPI (complete working WS2812 driver, connect LED data to pin A5):

#define NUMLEDS 240
#define NUMDATABYTES (NUMLEDS*3)
byte msg[NUMDATABYTES];

void begin()
{
    SPI.begin();
    SPI.setClockDivider(SPI_CLOCK_DIV8); // System clock is 72MHz, we need 9MHz for SPI
    SPI.setBitOrder(MSBFIRST); // MSB first for easier scope reading :-)
    SPI.transfer(0); // make sure SPI line starts low (Note: SPI line remains at level of last sent bit, fortunately)
    // initialize the buffer
    for (int i=0; i<NUMDATABYTES; i++) {
        msg[i] = 0;
    }
}

void show()
{
    // Note: on the spark core, system IRQs might happen which exceed 50uS
    // causing WS2812 chips to reset in midst of data stream.
    // Thus, until we can send via DMA, we need to disable IRQs while sending
    __disable_irq();
    for (int i=0; i<NUMDATABYTES; i++) {
        byte b = msg[i];
        for (int j=0; j<8; j++) {
           SPI.transfer(b & 0x80 ? 0x7E : 0x70); // 0x7E = 6 bits high = WS2812 HI, 0x70 = 3 bits high = WS2812 LO
           b = b << 1;
        }
    }
    __enable_irq();
}

void setRGB(int aLEDNumber, byte aRed, byte aGreen, byte aBlue)
{
    int i = aLEDNumber*3;
    // order in message is G-R-B for each LED
    msg[i] = aGreen;
    msg[i+1] = aRed;
    msg[i+2] = aBlue;
}
1 Like

I just cleaned up the code for the SPI based WS2812 and pushed it to github: https://github.com/plan44/ws2812_spi_sparkcore

1 Like

I’ve been experimenting with different kinds of ways to output stable waveform (not on Spark, but on AVR Xmega mostly).
The problem with SPI is that if it is done with pure DMA it will require to pre-process all the data and it will take 8 times more (byte per bit). You can do this after DMA has finished each transaction, but this will take CPU during transmission then.

First thing I used to deal with this is to add attiny13, which will convert SPI to WS2811 waveform just by knowing delays required. This works fine if you have stable 800KHz SPI output, but adds more HW and FW (just ~50 lines of asm, but still thing to flash IC with).
Another option was DMA+SPI + 1G97 + capacitor for delay, which is lower cost and no additional FW. Adds a little of analog circuitry though, so I decided to go digital-only way.

It was DMA + PWM + AWeX and some preprocessing (but it does not add more memory requirements - just bits rearrangement) + OR IC on each channel to be able to output to 16 LED strips in parallel. Though unlike Cortex-M3 it does not have separate memory bus for each DMA and CPU, so it was not possible to do something while DMA is working or you can brake things, but DMA allows to get exact timings on lots of parallel channels.

For Spark it should be possible to port OctoWS2811, as it was written for Teensy. It uses Cortex-M4, not M3, so I’m not sure how big differences will be. From what I see it does not have separate bus for DMA, so may have the same problems I had on Xmega. But it can be worth to try it anyway.

What is SPI exactly? Trying to grasp all of that. Also, the DMA acronym you used, @jagor?

SPI - Serial Peripheral Interface. Synchronous data transfer interface with clock, 2 data lines and chip select per slave device.
DMA - Direct Memory Access (in this case - the way to transfer data between memory and peripherals without using CPU)
AWeX - Advanced Waveform Extension (basically extended PWM on Xmega)

2 Likes

Very cool @luz! I just checked the timing on my scope and it’s about 330ns high and 660ns high as you say, but the off time is about 2us and that makes the period out of spec from 1.2us +/- 600ns. That said, it seems to work ok for me with some WS2812’s. Nice color cycle too :wink:

Have you been finding the __disable_irq(); to cause issues with your bg tasks? So far I haven’t seen any, but I might not be testing the right thing.

Some good general information on WS23xx timing here http://hypnocube.com/2013/

I know that the off time is much longer than the specs say, but from my understanding of a bit stream decoder as the one in the WS2812 I’d guess the low time specs are just minimums. You can’t go faster, but you can go muuuch slower. The circuit probably just triggers on the rising edge, then samples the state about 0.5µS later. How long the signal remains low does not matter, except that it might not be low for longer than 50µS, because then the chip resets.

So far, I haven’t found __disable_irq() causing issues to bg tasks. In particular, the cloud API works fine. However, my spark core crashes about once every 2 hours (as others have reported in other threads) - maybe the IRQ blocking contributes to this, hard to know.