Skip to content

Commit

Permalink
driver task split out into own files
Browse files Browse the repository at this point in the history
  • Loading branch information
teachop committed Mar 15, 2014
1 parent 22cd58f commit 82323bc
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 101 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

This test/learning project controls Adafruit NeoPixel strips of fairly arbitrary length with a rolling rainbow pattern. The multiple strips are individually timed, displaying LED patterns each at their own speed. (The point is to learn about xCore moreso than make LED patterns).

**NOTE:** Putting the driver and application (driver task / generator task) into their own source files makes program memory larger. Need to research - perhaps combinable tasks are optimized when they are in the same compilation unit in a way not possible when they are linked together?

To decouple creation of pixel color data from the precise serial output timing needed, strip-sized buffers are first filled by generator tasks before then being written out by driver tasks. Eight copies of the generator task output to 8 copies of the strip driver task to control 8 strips. The 8 task pairs run concurrently without synchronization on the 8 CPU cores. Which is pretty cool.

In order to pair the generator/driver tasks up correctly on the 8 cores of the startKIT CPU, the task functions are marked as "combinable". [Combinable](https://www.xmos.com/en/published/how-define-and-use-combinable-function?secure=1) is a special XMOS xC attribute that allows multiple tasks to run on a single logical core. The par statements in main() run the tasks, combining them as needed.
Expand Down
25 changes: 25 additions & 0 deletions neopixel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//---------------------------------------------------------
// Buffered NeoPixel driver header
// by teachop
//

#ifndef __NEOPIXEL_H__
#define __NEOPIXEL_H__

#define LEDS 60

// neopixel driver interface, Adafruit library-like
interface neopixel_if {
void show(void);
void setPixelColor(uint32_t pixel, uint32_t color);
void setPixelColorRGB(uint32_t pixel, uint8_t r, uint8_t g, uint8_t b);
void setBrightness(uint8_t bright);
uint32_t getPixelColor(uint32_t pixel);
uint32_t Color(uint8_t r, uint8_t g, uint8_t b);
uint32_t numPixels(void);
};

[[combinable]] void neopixel_task(port neo, interface neopixel_if server dvr);


#endif // __NEOPIXEL_H__
85 changes: 85 additions & 0 deletions neopixel.xc
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//---------------------------------------------------------
// Buffered NeoPixel driver
// by teachop
//

#include <xs1.h>
#include <stdint.h>
#include "neopixel.h"


// ---------------------------------------------------------
// neopixel_task - output driver for one neopixel strip
//
[[combinable]]
void neopixel_task(port neo, interface neopixel_if server dvr) {
uint32_t length = LEDS;
uint8_t colors[LEDS*3];
const uint32_t delay_third = 42;
uint8_t brightness=0;

while( 1 ) {
select {
case dvr.Color(uint8_t r, uint8_t g, uint8_t b) -> uint32_t return_val:
return_val = ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
break;
case dvr.numPixels() -> uint32_t return_val:
return_val = length;
break;
case dvr.setBrightness(uint8_t bright):
brightness = bright+1;
break;
case dvr.getPixelColor(uint32_t pixel) -> uint32_t return_val:
if ( length > pixel ) {
uint32_t index = 3*pixel;
return_val = ((uint32_t)colors[index+1] << 16) | ((uint32_t)colors[index] << 8) | colors[index+2];
} else {
return_val = 0;
}
break;
case dvr.setPixelColor(uint32_t pixel, uint32_t color):
if ( length > pixel ) {
uint32_t index = 3*pixel;
colors[index++] = color>>8;//g
colors[index++] = color>>16;//r
colors[index] = color;//b
}
break;
case dvr.setPixelColorRGB(uint32_t pixel, uint8_t r, uint8_t g, uint8_t b):
if ( length > pixel ) {
uint32_t index = 3*pixel;
colors[index++] = g;
colors[index++] = r;
colors[index] = b;
}
break;
case dvr.show():
// beginning of strip, sync counter
uint32_t delay_count, bit;
neo <: 0 @ delay_count;
#pragma unsafe arrays
for (uint32_t index=0; index<sizeof(colors); ++index) {
uint32_t color_shift = colors[index];
uint32_t bit_count = 8;
while (bit_count--) {
// output low->high transition
delay_count += delay_third;
neo @ delay_count <: 1;
// output high->data transition
if ( brightness && (7==bit_count) ) {
color_shift = (brightness*color_shift)>>8;
}
bit = (color_shift & 0x80)? 1 : 0;
color_shift <<=1;
delay_count += delay_third;
neo @ delay_count <: bit;
// output data->low transition
delay_count += delay_third;
neo @ delay_count <: 0;
}
}
break;
}
}

}
128 changes: 27 additions & 101 deletions neopixel_buffered.xc
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,88 +1,12 @@
//---------------------------------------------------------
// Buffered NeoPixel driver
// Buffered NeoPixel driver test application
// by teachop
//

#include <xs1.h>
#include <timer.h>
#include <stdint.h>


// length of the strip(s)
#define LEDS 60

// microseconds it takes to write to the strip
#define LED_WRITE_TIME (30*LEDS + (LEDS>>2) + 51)

// neopixel driver interface sort of like Adafruit library
interface neopixel_if {
void show(void);
void setPixelColor(uint32_t pixel, uint32_t color);
void setPixelColorRGB(uint32_t pixel, uint8_t red, uint8_t green, uint8_t blue);
void setBrightness(uint8_t bright);
};


// ---------------------------------------------------------
// neopixel_led_task - output driver for one neopixel strip
//
[[combinable]]
void neopixel_led_task(port neo, interface neopixel_if server dvr) {
uint8_t colors[LEDS*3];
const uint32_t delay_third = 42;
uint8_t brightness=0;

while( 1 ) {
select {
case dvr.setBrightness(uint8_t bright):
brightness = bright;
break;
case dvr.setPixelColor(uint32_t pixel, uint32_t color):
if ( LEDS > pixel ) {
uint32_t index = 3*pixel;
colors[index++] = color>>8;//g
colors[index++] = color>>16;//r
colors[index] = color;//b
}
break;
case dvr.setPixelColorRGB(uint32_t pixel, uint8_t r, uint8_t g, uint8_t b):
if ( LEDS > pixel ) {
uint32_t index = 3*pixel;
colors[index++] = g;
colors[index++] = r;
colors[index] = b;
}
break;
case dvr.show():
// beginning of strip, sync counter
uint32_t delay_count, bit;
neo <: 0 @ delay_count;
#pragma unsafe arrays
for (uint32_t index=0; index<sizeof(colors); ++index) {
uint32_t color_shift = colors[index];
uint32_t bit_count = 8;
while (bit_count--) {
// output low->high transition
delay_count += delay_third;
neo @ delay_count <: 1;
// output high->data transition
if ( brightness && (7==bit_count) ) {
color_shift = (brightness*color_shift)>>8;
}
bit = (color_shift & 0x80)? 1 : 0;
color_shift <<=1;
delay_count += delay_third;
neo @ delay_count <: bit;
// output data->low transition
delay_count += delay_third;
neo @ delay_count <: 0;
}
}
break;
}
}

}
#include "neopixel.h"


// ---------------------------------------------------------
Expand All @@ -106,25 +30,27 @@ void neopixel_led_task(port neo, interface neopixel_if server dvr) {
// blinky_task - rainbow cycle pattern from adafruit strip test
//
[[combinable]]
void blinky_task(uint32_t strip, interface neopixel_if client dvr) {
void blinky_task(uint32_t taskID, interface neopixel_if client strip) {
uint8_t outer = 0;
uint8_t r,g,b;
uint32_t length = strip.numPixels();
uint32_t speed = ((30*length + (length>>2) + 51) + taskID*1000)*100;
timer tick;
uint32_t next_pass;
tick :> next_pass;

while (1) {
select {
case tick when timerafter(next_pass) :> void:
next_pass += (LED_WRITE_TIME + strip*1000)*100;
next_pass += speed;
outer++;
// cycle of all colors on wheel
for ( uint32_t pixel=0; pixel<LEDS; ++pixel) {
{r,g,b} = wheel(( (pixel*256/LEDS) + outer) & 255);
dvr.setPixelColorRGB(pixel, r,g,b);
for ( uint32_t pixel=0; pixel<length; ++pixel) {
{r,g,b} = wheel(( (pixel*256/length) + outer) & 255);
strip.setPixelColorRGB(pixel, r,g,b);
}
// write to the strip
dvr.show();
strip.show();
break;
}
}
Expand All @@ -140,41 +66,41 @@ port out_pin[8] = {
XS1_PORT_1P, XS1_PORT_1O, XS1_PORT_1I, XS1_PORT_1L
};
int main() {
interface neopixel_if neo_driver[8];
interface neopixel_if neopixel_strip[8];

par {
// 16 tasks, 8 cores, drive 8 led strips with differing patterns
[[combine]] par {
neopixel_led_task(out_pin[0], neo_driver[0]);
blinky_task(0, neo_driver[0] );
neopixel_task(out_pin[0], neopixel_strip[0]);
blinky_task(0, neopixel_strip[0] );
}
[[combine]] par {
neopixel_led_task(out_pin[1], neo_driver[1]);
blinky_task(1, neo_driver[1] );
neopixel_task(out_pin[1], neopixel_strip[1]);
blinky_task(1, neopixel_strip[1] );
}
[[combine]] par {
neopixel_led_task(out_pin[2], neo_driver[2]);
blinky_task(2, neo_driver[2] );
neopixel_task(out_pin[2], neopixel_strip[2]);
blinky_task(2, neopixel_strip[2] );
}
[[combine]] par {
neopixel_led_task(out_pin[3], neo_driver[3]);
blinky_task(3, neo_driver[3] );
neopixel_task(out_pin[3], neopixel_strip[3]);
blinky_task(3, neopixel_strip[3] );
}
[[combine]] par {
neopixel_led_task(out_pin[4], neo_driver[4]);
blinky_task(4, neo_driver[4] );
neopixel_task(out_pin[4], neopixel_strip[4]);
blinky_task(4, neopixel_strip[4] );
}
[[combine]] par {
neopixel_led_task(out_pin[5], neo_driver[5]);
blinky_task(5, neo_driver[5] );
neopixel_task(out_pin[5], neopixel_strip[5]);
blinky_task(5, neopixel_strip[5] );
}
[[combine]] par {
neopixel_led_task(out_pin[6], neo_driver[6]);
blinky_task(6, neo_driver[6] );
neopixel_task(out_pin[6], neopixel_strip[6]);
blinky_task(6, neopixel_strip[6] );
}
[[combine]] par {
neopixel_led_task(out_pin[7], neo_driver[7]);
blinky_task(7, neo_driver[7] );
neopixel_task(out_pin[7], neopixel_strip[7]);
blinky_task(7, neopixel_strip[7] );
}
}

Expand Down

0 comments on commit 82323bc

Please sign in to comment.