NeoPixel LEDs on startKIT

XCore Project reviews, ideas, videos and proposals.
Post Reply
teachop
Active Member
Posts: 47
Joined: Wed Feb 05, 2014 1:25 am

NeoPixel LEDs on startKIT

Post by teachop »

Here is a simple "first light" test of an 8 element Adafruit NeoPixel strip on the startKIT:
https://github.com/teachop/xcore_neopixel_leds

The test is a pattern generator task and a driver task linked by a channel. This is a first xC attempt, so feedback is appreciated. This is just a basic start, one pin, more later...


User avatar
Lele
Active Member
Posts: 52
Joined: Mon Oct 31, 2011 4:08 pm
Contact:

Post by Lele »

Hi i'm a fan of those RGB leds with embedded rgb dimmer (WS2812).
A logarithmic look up table is necessary to have a correct brightness sensation.
That is the test code I used. It makes a nice wave effect.

Code: Select all

#include <platform.h>
#include <xs1.h>
#include <stdio.h>
#include <print.h>
#include <math.h>

// info from http://www.adafruit.com/datasheets/WS2812.pdf
#define LED_NUM 10
#define R 0
#define G 1
#define B 2
#define T0H_TICKS (350 * XS1_TIMER_MHZ / 1000) // 350ns
#define T0L_TICKS (800 * XS1_TIMER_MHZ / 1000) // 800ns
#define T1H_TICKS (700 * XS1_TIMER_MHZ / 1000) // 700ns
#define T1L_TICKS (600 * XS1_TIMER_MHZ / 1000) // 600ns
#define RES_TICKS (55 * XS1_TIMER_MHZ) // 55us
#define SEND_1    { SDO @ T <: 0xF; T += T1H_TICKS; SDO @ T <: 0x0; T += T1L_TICKS; }
#define SEND_0    { SDO @ T <: 0xF; T += T0H_TICKS; SDO @ T <: 0x0; T += T0L_TICKS; }
#define FRAMERATE 50 //ms
#define FRAME_TICKS (FRAMERATE * XS1_TIMER_KHZ)

on tile[0]: out port SDO_PORT = XS1_PORT_4E;

unsigned char DimmerLut[1000];
#define K  1.0055605803984681699491968006452 // 256^(1/1000)
#define SPH (2*3.141592654/LED_NUM) //LED_NUM wavelength

void LedDrv(out port SDO, unsigned char Data[3][LED_NUM])
{ // send GRB bytes from msb
  int Led, Bit;
  short T;
  timer t;
  int TFrame;
  double TPh;

  for(DimmerLut[0] = 0, TPh = K, T = 1; T < 1000; TPh *= K, T++) // initialize dimmer lut
    DimmerLut[T] = (unsigned char) TPh;

  t :> TFrame;
  while(1)
  {
    TFrame += FRAME_TICKS; t when timerafter(TFrame) :> void; // wait frame time
    TPh += 2*3.141592654*FRAMERATE/1000.0;
    for(Led = 0; Led < sizeof(Data)/3; Led++)
    {
      Data[R][Led] = DimmerLut[(int)(500 + 500 * sin(TPh/5.0 - SPH*Led))]; // 5sec period
      Data[G][Led] = DimmerLut[(int)(500 + 500 * sin(TPh/7.0 - SPH*Led))]; // 7sec period
      Data[B][Led] = DimmerLut[(int)(500 + 500 * sin(TPh/9.0 - SPH*Led))]; // 9sec period
    }
    // apply framebuffer
    SDO <: 0x0 @ T;
    for(Led = 0; Led < sizeof(Data)/3; Led++)
    { // led strip loop
      for( Bit = 0; Bit < 8; Bit++) // G bits loop
        if((Data[G][Led] & (0x80 >> Bit)) != 0) SEND_1 else SEND_0
      for( Bit = 0; Bit < 8; Bit++) // R bits loop
        if((Data[R][Led] & (0x80 >> Bit)) != 0) SEND_1 else SEND_0
      for( Bit = 0; Bit < 8; Bit++) // B bits loop
        if((Data[B][Led] & (0x80 >> Bit)) != 0) SEND_1 else SEND_0
    }
    T += RES_TICKS; SDO @T <: 0; // apply settings
  }
}

unsigned char Data[3][LED_NUM];

// Program entry point
int main(void)
{
  par
  {
    on tile[0]: LedDrv(SDO_PORT, Data);
  }
  return 0;
}

User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am
Contact:

Post by infiniteimprobability »

Nice fun project.
This is a first xC attempt, so feedback is appreciated.
Looks OK to me! Two tasks connect (pass data and synchronise) via a channel, timed output on a port so everything event driven (the subsequent timed output will automatically pause the core)..

You don't need a streaming channel end for this level of performance, but no harm in using it when you have loads of resources available, like in this case.

You could put a

Code: Select all

select {
   case comm :> grb[loop]:
   ...
   break;
   default: //so core doesn't block on channel input
   break;
}
..on the get new colours bit so that it will continue to display the last colour until an update is received, which would take the requirement to always be ready from blinky_task.. But it depends how you want to do it really..

Good stuff!
teachop
Active Member
Posts: 47
Joined: Wed Feb 05, 2014 1:25 am

Post by teachop »

Thanks for the feedback! I removed the streaming modifier. The driver blocking on channel reads is acceptable for this test (unless it is burning resources?). NeoPixels don't need to be refreshed if nothing changes.

Next the goal is to output on a wide port for multiple strips in parallel.
teachop
Active Member
Posts: 47
Joined: Wed Feb 05, 2014 1:25 am

Post by teachop »

Could I ask more help about channels and hard timing performance for this application?

I updated the github repo to remove buffered strip images within the tasks. In prior versions an entire image of the strip is built, fully transferred and re-buffered at the driver task, and finally spooled out to the hardware as 800KHz serial data. With channel communication not in the output loop, channel blocking could never cause an underflow. It is important that underflow is prevented as NeoPixels use a time gap as punctuation ending serial data transfers.

The updated version removes pre/post buffering, putting the chan in the loop. (For performance I switched to streaming.) This works correctly on a 60 pixel length strip. Is it possible based on the deterministic nature of xCore to prove it will always work - that data will never underflow causing an unexpected gap in the serial output?
User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am
Contact:

Post by infiniteimprobability »

Is it possible based on the deterministic nature of xCore to prove it will always work - that data will never underflow causing an unexpected gap in the serial output?
Definitely. You need to work out the rate at which the xcore has to respond to I/O. That will give you a maximum time between I/O statements and (assuming 8 active cores at 500MHz) you divide that number by 16ns to work out how many logical core cycles you have. That is usually close to the number of instructions you can execute (mostly it's one per clock).

You then either simulate using xsim (cycle accurate) simulator to see how it behaves https://www.xmos.com/en/published/xsimtut?secure=1 with respect to time. This is the same as any simulation because it is data dependent - although your case is pretty straightforward so unlikely to be much variance in timing....

Or you can annotate the code with timing assertions https://www.xmos.com/en/published/xde-t ... l?secure=1 and the tools to check timing at compile time. This has 100% coverage (it doesn't depend on data). Alternatively you can use XTA within the GUI to browse the code block and work out timings from there.

Or you could use some timers in the code to log how much time is spent in certain sections..

Or you could use the "gprof collection" option in the GUI to do statistical analysis of the PC (program counter) to see how long it waits on I/O..

There are dozens of other ways to check for timing (toggle I/Os, slowing down clock until it breaks etc..), but XTA is the most accurate because it's a formal tool.
teachop
Active Member
Posts: 47
Joined: Wed Feb 05, 2014 1:25 am

Post by teachop »

This example NeoPixel code https://github.com/teachop/xcore_neopixel_leds is updated slightly to drive 4 LED strips. There is no 4-strip-handling code, it is just running 4 sets of the single strip tasks (which is actually pretty cool). I am sure writing to a 4 bit wide port is better so that is next...
User avatar
matt79
Newbie
Posts: 1
Joined: Tue Mar 11, 2014 2:33 pm

Post by matt79 »

I made a board with up to eight WS2812B RGB LEDswhich can be hooked up to a USB port to control it.
It uses an ATtiny85 with V-USB (using the internal oscillator at 16,5 Mhz).

I control the LEDs with the light_ws2812 lib (the first version): https://github.com/cpldcpu/light_ws2812

My prototype with only tree LEDs worked fine, but i used the older WS2812 LEDs.
Now I got the PCB and I have problems controlling the LEDs:

- When I use the light_ws2812 lib in the newest version itdoesn't work at all. All LEDs are flashing wildly or don't change the color at all.
- When I use the older version of the light_ws2812 lib the LEDs are sometimes working fine, but sometimes especially the first and the last ones have problems.
User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am
Contact:

Post by infiniteimprobability »

It's an old thread, but had a quick look at Teachop's neopixel repo for a recent project and, have to say, it looks great. Well written and it compiled and ran (with simple board and port modifications for my XCORE-200-EXPLORER kit) first time with no dramas.

https://github.com/teachop/xcore_neopixel_buffered

Good work!
Post Reply