switch to setup for Arduino Boards Manager

This commit is contained in:
Erik Tylek Kettenburg
2015-06-23 12:42:35 -07:00
parent bc55c9bb45
commit 6ca6b114d5
3581 changed files with 93 additions and 51 deletions

View File

@@ -0,0 +1,124 @@
#include "LPD8806.h"
// Arduino library to control LPD8806-based RGB LED Strips
// (c) Adafruit industries
// MIT license
/*****************************************************************************/
// Constructor for use with arbitrary clock/data pins:
LPD8806::LPD8806(uint16_t n, uint8_t dpin, uint8_t cpin) {
pixels = NULL;
begun = false;
updateLength(n);
updatePins(dpin, cpin);
}
// Activate hard/soft SPI as appropriate:
void LPD8806::begin(void) {
startBitbang();
begun = true;
}
// Change pin assignments post-constructor, using arbitrary pins:
void LPD8806::updatePins(uint8_t dpin, uint8_t cpin) {
datapin = dpin;
clkpin = cpin;
clkport = portOutputRegister(digitalPinToPort(cpin));
clkpinmask = digitalPinToBitMask(cpin);
dataport = portOutputRegister(digitalPinToPort(dpin));
datapinmask = digitalPinToBitMask(dpin);
if(begun == true) { // If begin() was previously invoked...
startBitbang(); // Regardless, now enable 'soft' SPI outputs
} // Otherwise, pins are not set to outputs until begin() is called.
// Note: any prior clock/data pin directions are left as-is and are
// NOT restored as inputs!
hardwareSPI = false;
}
// Enable software SPI pins and issue initial latch:
void LPD8806::startBitbang() {
pinMode(datapin, OUTPUT);
pinMode(clkpin , OUTPUT);
*dataport &= ~datapinmask; // Data is held low throughout (latch = 0)
for(uint8_t i = 8; i>0; i--) {
*clkport |= clkpinmask;
*clkport &= ~clkpinmask;
}
}
// Change strip length (see notes with empty constructor, above):
void LPD8806::updateLength(uint16_t n) {
if(pixels != NULL) free(pixels); // Free existing data (if any)
numLEDs = n;
n *= 3; // 3 bytes per pixel
if(NULL != (pixels = (uint8_t *)malloc(n + 1))) { // Alloc new data
memset(pixels, 0x80, n); // Init to RGB 'off' state
pixels[n] = 0; // Last byte is always zero for latch
} else numLEDs = 0; // else malloc failed
// 'begun' state does not change -- pins retain prior modes
}
uint16_t LPD8806::numPixels(void) {
return numLEDs;
}
// This is how data is pushed to the strip. Unfortunately, the company
// that makes the chip didnt release the protocol document or you need
// to sign an NDA or something stupid like that, but we reverse engineered
// this from a strip controller and it seems to work very nicely!
void LPD8806::show(void) {
uint16_t i, n3 = numLEDs * 3 + 1; // 3 bytes per LED + 1 for latch
// write 24 bits per pixel
for (i=0; i<n3; i++ ) {
for (uint8_t bit=0x80; bit; bit >>= 1) {
if(pixels[i] & bit) *dataport |= datapinmask;
else *dataport &= ~datapinmask;
*clkport |= clkpinmask;
*clkport &= ~clkpinmask;
}
}
}
// Convert separate R,G,B into combined 32-bit GRB color:
uint32_t LPD8806::Color(byte r, byte g, byte b) {
return 0x808080 | ((uint32_t)g << 16) | ((uint32_t)r << 8) | (uint32_t)b;
}
// Set pixel color from separate 7-bit R, G, B components:
void LPD8806::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) {
if(n < numLEDs) { // Arrays are 0-indexed, thus NOT '<='
uint8_t *p = &pixels[n * 3];
*p++ = g | 0x80; // LPD8806 color order is GRB,
*p++ = r | 0x80; // not the more common RGB,
*p++ = b | 0x80; // so the order here is intentional; don't "fix"
}
}
// Set pixel color from 'packed' 32-bit RGB value:
void LPD8806::setPixelColor(uint16_t n, uint32_t c) {
if(n < numLEDs) { // Arrays are 0-indexed, thus NOT '<='
uint8_t *p = &pixels[n * 3];
*p++ = (c >> 16) | 0x80;
*p++ = (c >> 8) | 0x80;
*p++ = c | 0x80;
}
}
// Query color from previously-set pixel (returns packed 32-bit GRB value)
uint32_t LPD8806::getPixelColor(uint16_t n) {
if(n < numLEDs) {
uint16_t ofs = n * 3;
return ((uint32_t)((uint32_t)pixels[ofs ] << 16) |
(uint32_t)((uint32_t)pixels[ofs + 1] << 8) |
(uint32_t)pixels[ofs + 2]) & 0x7f7f7f;
}
return 0; // Pixel # is out of bounds
}

View File

@@ -0,0 +1,41 @@
#if (ARDUINO >= 100)
#include <Arduino.h>
#else
#include <WProgram.h>
#include <pins_arduino.h>
#endif
class LPD8806 {
public:
LPD8806(uint16_t n, uint8_t dpin, uint8_t cpin); // Configurable pins
void
begin(void),
show(void),
setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b),
setPixelColor(uint16_t n, uint32_t c),
updatePins(uint8_t dpin, uint8_t cpin), // Change pins, configurable
updateLength(uint16_t n); // Change strip length
uint16_t
numPixels(void);
uint32_t
Color(byte, byte, byte),
getPixelColor(uint16_t n);
private:
uint16_t
numLEDs; // Number of RGB LEDs in strip
uint8_t
*pixels, // Holds LED color values (3 bytes each)
clkpin , datapin, // Clock & data pin numbers
clkpinmask, datapinmask; // Clock & data PORT bitmasks
volatile uint8_t
*clkport , *dataport; // Clock & data PORT registers
void
startBitbang(void);
boolean
hardwareSPI, // If 'true', using hardware SPI
begun; // If 'true', begin() method was previously invoked
};

View File

@@ -0,0 +1,18 @@
# Arduino library for LPD8806 #
This Library was written for the LPD8806 PWM LED driver chips, strips and pixels.
But the LPD8803/LPD8809 will probably work too.
## Where to Buy? ##
Pick some up at [Adafruit Industries](http://www.adafruit.com/products/306)
## Download ##
Click the Downloads Tab in the Tabbar above.
Or follow [this](https://github.com/adafruit/LPD8806/zipball/master) link
## Installation ##
* Uncompress the Downloaded Library
* Rename the uncompressed folder to LPD8806
* Check that the LPD8806 folder contains LPD8806.cpp and LPD8806.h
* Place the LPD8806 library folder your <arduinosketchfolder>/libraries/ folder,
if the libraries folder does not exist - create it first!
* Restart the IDE

View File

@@ -0,0 +1,255 @@
#include "LPD8806.h"
#include "SPI.h"
// Example to control LPD8806-based RGB LED Modules in a strip!
/*****************************************************************************/
#if defined(USB_SERIAL) || defined(USB_SERIAL_ADAFRUIT)
// this is for teensyduino support
int dataPin = 2;
int clockPin = 1;
#else
// these are the pins we use for the LED belt kit using
// the Leonardo pinouts
int dataPin = 16;
int clockPin = 15;
#endif
// Set the first variable to the NUMBER of pixels. 32 = 32 pixels in a row
// The LED strips are 32 LEDs per meter but you can extend/cut the strip
LPD8806 strip = LPD8806(32, dataPin, clockPin);
void setup() {
// Start up the LED strip
strip.begin();
// Update the strip, to start they are all 'off'
strip.show();
}
// function prototypes, do not remove these!
void colorChase(uint32_t c, uint8_t wait);
void colorWipe(uint32_t c, uint8_t wait);
void dither(uint32_t c, uint8_t wait);
void scanner(uint8_t r, uint8_t g, uint8_t b, uint8_t wait);
void wave(uint32_t c, int cycles, uint8_t wait);
void rainbowCycle(uint8_t wait);
uint32_t Wheel(uint16_t WheelPos);
void loop() {
// Send a simple pixel chase in...
colorChase(strip.Color(127,127,127), 20); // white
colorChase(strip.Color(127,0,0), 20); // red
colorChase(strip.Color(127,127,0), 20); // yellow
colorChase(strip.Color(0,127,0), 20); // green
colorChase(strip.Color(0,127,127), 20); // cyan
colorChase(strip.Color(0,0,127), 20); // blue
colorChase(strip.Color(127,0,127), 20); // magenta
// Fill the entire strip with...
colorWipe(strip.Color(127,0,0), 20); // red
colorWipe(strip.Color(0, 127,0), 20); // green
colorWipe(strip.Color(0,0,127), 20); // blue
colorWipe(strip.Color(0,0,0), 20); // black
// Color sparkles
dither(strip.Color(0,127,127), 50); // cyan, slow
dither(strip.Color(0,0,0), 15); // black, fast
dither(strip.Color(127,0,127), 50); // magenta, slow
dither(strip.Color(0,0,0), 15); // black, fast
dither(strip.Color(127,127,0), 50); // yellow, slow
dither(strip.Color(0,0,0), 15); // black, fast
// Back-and-forth lights
scanner(127,0,0, 30); // red, slow
scanner(0,0,127, 15); // blue, fast
// Wavy ripple effects
wave(strip.Color(127,0,0), 4, 20); // candy cane
wave(strip.Color(0,0,100), 2, 40); // icy
// make a pretty rainbow cycle!
rainbowCycle(0); // make it go through the cycle fairly fast
// Clear strip data before start of next effect
for (int i=0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, 0);
}
}
// Cycle through the color wheel, equally spaced around the belt
void rainbowCycle(uint8_t wait) {
uint16_t i, j;
for (j=0; j < 384 * 5; j++) { // 5 cycles of all 384 colors in the wheel
for (i=0; i < strip.numPixels(); i++) {
// tricky math! we use each pixel as a fraction of the full 384-color
// wheel (thats the i / strip.numPixels() part)
// Then add in j which makes the colors go around per pixel
// the % 384 is to make the wheel cycle around
strip.setPixelColor(i, Wheel(((i * 384 / strip.numPixels()) + j) % 384));
}
strip.show(); // write all the pixels out
delay(wait);
}
}
// fill the dots one after the other with said color
// good for testing purposes
void colorWipe(uint32_t c, uint8_t wait) {
int i;
for (i=0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, c);
strip.show();
delay(wait);
}
}
// Chase a dot down the strip
// good for testing purposes
void colorChase(uint32_t c, uint8_t wait) {
int i;
for (i=0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, 0); // turn all pixels off
}
for (i=0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, c); // set one pixel
strip.show(); // refresh strip display
delay(wait); // hold image for a moment
strip.setPixelColor(i, 0); // erase pixel (but don't refresh yet)
}
strip.show(); // for last erased pixel
}
// An "ordered dither" fills every pixel in a sequence that looks
// sparkly and almost random, but actually follows a specific order.
void dither(uint32_t c, uint8_t wait) {
// Determine highest bit needed to represent pixel index
int hiBit = 0;
int n = strip.numPixels() - 1;
for(int bit=1; bit < 0x8000; bit <<= 1) {
if(n & bit) hiBit = bit;
}
int bit, reverse;
for(int i=0; i<(hiBit << 1); i++) {
// Reverse the bits in i to create ordered dither:
reverse = 0;
for(bit=1; bit <= hiBit; bit <<= 1) {
reverse <<= 1;
if(i & bit) reverse |= 1;
}
strip.setPixelColor(reverse, c);
strip.show();
delay(wait);
}
delay(250); // Hold image for 1/4 sec
}
// "Larson scanner" = Cylon/KITT bouncing light effect
void scanner(uint8_t r, uint8_t g, uint8_t b, uint8_t wait) {
int i, j, pos, dir;
pos = 0;
dir = 1;
for(i=0; i<((strip.numPixels()-1) * 8); i++) {
// Draw 5 pixels centered on pos. setPixelColor() will clip
// any pixels off the ends of the strip, no worries there.
// we'll make the colors dimmer at the edges for a nice pulse
// look
strip.setPixelColor(pos - 2, strip.Color(r/4, g/4, b/4));
strip.setPixelColor(pos - 1, strip.Color(r/2, g/2, b/2));
strip.setPixelColor(pos, strip.Color(r, g, b));
strip.setPixelColor(pos + 1, strip.Color(r/2, g/2, b/2));
strip.setPixelColor(pos + 2, strip.Color(r/4, g/4, b/4));
strip.show();
delay(wait);
// If we wanted to be sneaky we could erase just the tail end
// pixel, but it's much easier just to erase the whole thing
// and draw a new one next time.
for(j=-2; j<= 2; j++)
strip.setPixelColor(pos+j, strip.Color(0,0,0));
// Bounce off ends of strip
pos += dir;
if(pos < 0) {
pos = 1;
dir = -dir;
} else if(pos >= strip.numPixels()) {
pos = strip.numPixels() - 2;
dir = -dir;
}
}
}
// Sine wave effect
#define PI 3.14159265
void wave(uint32_t c, int cycles, uint8_t wait) {
float y;
byte r, g, b, r2, g2, b2;
// Need to decompose color into its r, g, b elements
g = (c >> 16) & 0x7f;
r = (c >> 8) & 0x7f;
b = c & 0x7f;
for(int x=0; x<(strip.numPixels()*5); x++)
{
for(int i=0; i<strip.numPixels(); i++) {
y = sin(PI * (float)cycles * (float)(x + i) / (float)strip.numPixels());
if(y >= 0.0) {
// Peaks of sine wave are white
y = 1.0 - y; // Translate Y to 0.0 (top) to 1.0 (center)
r2 = 127 - (byte)((float)(127 - r) * y);
g2 = 127 - (byte)((float)(127 - g) * y);
b2 = 127 - (byte)((float)(127 - b) * y);
} else {
// Troughs of sine wave are black
y += 1.0; // Translate Y to 0.0 (bottom) to 1.0 (center)
r2 = (byte)((float)r * y);
g2 = (byte)((float)g * y);
b2 = (byte)((float)b * y);
}
strip.setPixelColor(i, r2, g2, b2);
}
strip.show();
delay(wait);
}
}
/* Helper functions */
//Input a value 0 to 384 to get a color value.
//The colours are a transition r - g - b - back to r
uint32_t Wheel(uint16_t WheelPos)
{
byte r, g, b;
switch(WheelPos / 128)
{
case 0:
r = 127 - WheelPos % 128; // red down
g = WheelPos % 128; // green up
b = 0; // blue off
break;
case 1:
g = 127 - WheelPos % 128; // green down
b = WheelPos % 128; // blue up
r = 0; // red off
break;
case 2:
b = 127 - WheelPos % 128; // blue down
r = WheelPos % 128; // red up
g = 0; // green off
break;
}
return(strip.Color(r,g,b));
}

View File

@@ -0,0 +1,523 @@
// Example to control LPD8806-based RGB LED Modules in a strip; originally
// intended for the Adafruit Digital Programmable LED Belt Kit.
// REQUIRES TIMER1 LIBRARY: http://www.arduino.cc/playground/Code/Timer1
// ALSO REQUIRES LPD8806 LIBRARY, which should be included with this code.
// I'm generally not fond of canned animation patterns. Wanting something
// more nuanced than the usual 8-bit beep-beep-boop-boop pixelly animation,
// this program smoothly cycles through a set of procedural animated effects
// and transitions -- it's like a Video Toaster for your waist! Some of the
// coding techniques may be a bit obtuse (e.g. function arrays), so novice
// programmers may have an easier time starting out with the 'strandtest'
// program also included with the LPD8806 library.
#include <avr/pgmspace.h>
#include "SPI.h"
#include "LPD8806.h"
#include "TimerOne.h"
#if defined(USB_SERIAL) || defined(USB_SERIAL_ADAFRUIT)
// this is for teensyduino support
int dataPin = 2;
int clockPin = 1;
#else
// these are the pins we use for the LED belt kit using
// the Leonardo pinouts
int dataPin = 16;
int clockPin = 15;
#endif
// Declare the number of pixels in strand; 32 = 32 pixels in a row. The
// LED strips have 32 LEDs per meter, but you can extend or cut the strip.
const int numPixels = 32;
// 'const' makes subsequent array declarations possible, otherwise there
// would be a pile of malloc() calls later.
// Instantiate LED strip; arguments are the total number of pixels in strip,
// the data pin number and clock pin number:
LPD8806 strip = LPD8806(numPixels, dataPin, clockPin);
// You can also use hardware SPI for ultra-fast writes by omitting the data
// and clock pin arguments. This is faster, but the data and clock are then
// fixed to very specific pin numbers: on Arduino 168/328, data = pin 11,
// clock = pin 13. On Mega, data = pin 51, clock = pin 52.
//LPD8806 strip = LPD8806(numPixels);
// Principle of operation: at any given time, the LEDs depict an image or
// animation effect (referred to as the "back" image throughout this code).
// Periodically, a transition to a new image or animation effect (referred
// to as the "front" image) occurs. During this transition, a third buffer
// (the "alpha channel") determines how the front and back images are
// combined; it represents the opacity of the front image. When the
// transition completes, the "front" then becomes the "back," a new front
// is chosen, and the process repeats.
byte imgData[2][numPixels * 3], // Data for 2 strips worth of imagery
alphaMask[numPixels], // Alpha channel for compositing images
backImgIdx = 0, // Index of 'back' image (always 0 or 1)
fxIdx[3]; // Effect # for back & front images + alpha
int fxVars[3][50], // Effect instance variables (explained later)
tCounter = -1, // Countdown to next transition
transitionTime; // Duration (in frames) of current transition
// function prototypes, leave these be :)
void renderEffect00(byte idx);
void renderEffect01(byte idx);
void renderEffect02(byte idx);
void renderEffect03(byte idx);
void renderAlpha00(void);
void renderAlpha01(void);
void renderAlpha02(void);
void renderAlpha03(void);
void callback();
byte gamma(byte x);
long hsv2rgb(long h, byte s, byte v);
char fixSin(int angle);
char fixCos(int angle);
// List of image effect and alpha channel rendering functions; the code for
// each of these appears later in this file. Just a few to start with...
// simply append new ones to the appropriate list here:
void (*renderEffect[])(byte) = {
renderEffect00,
renderEffect01,
renderEffect02,
renderEffect03 },
(*renderAlpha[])(void) = {
renderAlpha00,
renderAlpha01,
renderAlpha02 };
// ---------------------------------------------------------------------------
void setup() {
// Start up the LED strip. Note that strip.show() is NOT called here --
// the callback function will be invoked immediately when attached, and
// the first thing the calback does is update the strip.
strip.begin();
// Initialize random number generator from a floating analog input.
randomSeed(analogRead(0));
memset(imgData, 0, sizeof(imgData)); // Clear image data
fxVars[backImgIdx][0] = 1; // Mark back image as initialized
// Timer1 is used so the strip will update at a known fixed frame rate.
// Each effect rendering function varies in processing complexity, so
// the timer allows smooth transitions between effects (otherwise the
// effects and transitions would jump around in speed...not attractive).
Timer1.initialize();
Timer1.attachInterrupt(callback, 1000000 / 60); // 60 frames/second
}
void loop() {
// Do nothing. All the work happens in the callback() function below,
// but we still need loop() here to keep the compiler happy.
}
// Timer1 interrupt handler. Called at equal intervals; 60 Hz by default.
void callback() {
// Very first thing here is to issue the strip data generated from the
// *previous* callback. It's done this way on purpose because show() is
// roughly constant-time, so the refresh will always occur on a uniform
// beat with respect to the Timer1 interrupt. The various effects
// rendering and compositing code is not constant-time, and that
// unevenness would be apparent if show() were called at the end.
strip.show();
byte frontImgIdx = 1 - backImgIdx,
*backPtr = &imgData[backImgIdx][0],
r, g, b;
int i;
// Always render back image based on current effect index:
(*renderEffect[fxIdx[backImgIdx]])(backImgIdx);
// Front render and composite only happen during transitions...
if(tCounter > 0) {
// Transition in progress
byte *frontPtr = &imgData[frontImgIdx][0];
int alpha, inv;
// Render front image and alpha mask based on current effect indices...
(*renderEffect[fxIdx[frontImgIdx]])(frontImgIdx);
(*renderAlpha[fxIdx[2]])();
// ...then composite front over back:
for(i=0; i<numPixels; i++) {
alpha = alphaMask[i] + 1; // 1-256 (allows shift rather than divide)
inv = 257 - alpha; // 1-256 (ditto)
// r, g, b are placed in variables (rather than directly in the
// setPixelColor parameter list) because of the postincrement pointer
// operations -- C/C++ leaves parameter evaluation order up to the
// implementation; left-to-right order isn't guaranteed.
r = gamma((*frontPtr++ * alpha + *backPtr++ * inv) >> 8);
g = gamma((*frontPtr++ * alpha + *backPtr++ * inv) >> 8);
b = gamma((*frontPtr++ * alpha + *backPtr++ * inv) >> 8);
strip.setPixelColor(i, r, g, b);
}
} else {
// No transition in progress; just show back image
for(i=0; i<numPixels; i++) {
// See note above re: r, g, b vars.
r = gamma(*backPtr++);
g = gamma(*backPtr++);
b = gamma(*backPtr++);
strip.setPixelColor(i, r, g, b);
}
}
// Count up to next transition (or end of current one):
tCounter++;
if(tCounter == 0) { // Transition start
// Randomly pick next image effect and alpha effect indices:
fxIdx[frontImgIdx] = random((sizeof(renderEffect) / sizeof(renderEffect[0])));
fxIdx[2] = random((sizeof(renderAlpha) / sizeof(renderAlpha[0])));
transitionTime = random(30, 181); // 0.5 to 3 second transitions
fxVars[frontImgIdx][0] = 0; // Effect not yet initialized
fxVars[2][0] = 0; // Transition not yet initialized
} else if(tCounter >= transitionTime) { // End transition
fxIdx[backImgIdx] = fxIdx[frontImgIdx]; // Move front effect index to back
backImgIdx = 1 - backImgIdx; // Invert back index
tCounter = -120 - random(240); // Hold image 2 to 6 seconds
}
}
// ---------------------------------------------------------------------------
// Image effect rendering functions. Each effect is generated parametrically
// (that is, from a set of numbers, usually randomly seeded). Because both
// back and front images may be rendering the same effect at the same time
// (but with different parameters), a distinct block of parameter memory is
// required for each image. The 'fxVars' array is a two-dimensional array
// of integers, where the major axis is either 0 or 1 to represent the two
// images, while the minor axis holds 50 elements -- this is working scratch
// space for the effect code to preserve its "state." The meaning of each
// element is generally unique to each rendering effect, but the first element
// is most often used as a flag indicating whether the effect parameters have
// been initialized yet. When the back/front image indexes swap at the end of
// each transition, the corresponding set of fxVars, being keyed to the same
// indexes, are automatically carried with them.
// Simplest rendering effect: fill entire image with solid color
void renderEffect00(byte idx) {
// Only needs to be rendered once, when effect is initialized:
if(fxVars[idx][0] == 0) {
byte *ptr = &imgData[idx][0],
r = random(256), g = random(256), b = random(256);
for(int i=0; i<numPixels; i++) {
*ptr++ = r; *ptr++ = g; *ptr++ = b;
}
fxVars[idx][0] = 1; // Effect initialized
}
}
// Rainbow effect (1 or more full loops of color wheel at 100% saturation).
// Not a big fan of this pattern (it's way overused with LED stuff), but it's
// practically part of the Geneva Convention by now.
void renderEffect01(byte idx) {
if(fxVars[idx][0] == 0) { // Initialize effect?
// Number of repetitions (complete loops around color wheel); any
// more than 4 per meter just looks too chaotic and un-rainbow-like.
// Store as hue 'distance' around complete belt:
fxVars[idx][1] = (1 + random(4 * ((numPixels + 31) / 32))) * 1536;
// Frame-to-frame hue increment (speed) -- may be positive or negative,
// but magnitude shouldn't be so small as to be boring. It's generally
// still less than a full pixel per frame, making motion very smooth.
fxVars[idx][2] = 4 + random(fxVars[idx][1]) / numPixels;
// Reverse speed and hue shift direction half the time.
if(random(2) == 0) fxVars[idx][1] = -fxVars[idx][1];
if(random(2) == 0) fxVars[idx][2] = -fxVars[idx][2];
fxVars[idx][3] = 0; // Current position
fxVars[idx][0] = 1; // Effect initialized
}
byte *ptr = &imgData[idx][0];
long color, i;
for(i=0; i<numPixels; i++) {
color = hsv2rgb(fxVars[idx][3] + fxVars[idx][1] * i / numPixels,
255, 255);
*ptr++ = color >> 16; *ptr++ = color >> 8; *ptr++ = color;
}
fxVars[idx][3] += fxVars[idx][2];
}
// Sine wave chase effect
void renderEffect02(byte idx) {
if(fxVars[idx][0] == 0) { // Initialize effect?
fxVars[idx][1] = random(1536); // Random hue
// Number of repetitions (complete loops around color wheel);
// any more than 4 per meter just looks too chaotic.
// Store as distance around complete belt in half-degree units:
fxVars[idx][2] = (1 + random(4 * ((numPixels + 31) / 32))) * 720;
// Frame-to-frame increment (speed) -- may be positive or negative,
// but magnitude shouldn't be so small as to be boring. It's generally
// still less than a full pixel per frame, making motion very smooth.
fxVars[idx][3] = 4 + random(fxVars[idx][1]) / numPixels;
// Reverse direction half the time.
if(random(2) == 0) fxVars[idx][3] = -fxVars[idx][3];
fxVars[idx][4] = 0; // Current position
fxVars[idx][0] = 1; // Effect initialized
}
byte *ptr = &imgData[idx][0];
int foo;
long color, i;
for(long i=0; i<numPixels; i++) {
foo = fixSin(fxVars[idx][4] + fxVars[idx][2] * i / numPixels);
// Peaks of sine wave are white, troughs are black, mid-range
// values are pure hue (100% saturated).
color = (foo >= 0) ?
hsv2rgb(fxVars[idx][1], 254 - (foo * 2), 255) :
hsv2rgb(fxVars[idx][1], 255, 254 + foo * 2);
*ptr++ = color >> 16; *ptr++ = color >> 8; *ptr++ = color;
}
fxVars[idx][4] += fxVars[idx][3];
}
// Data for American-flag-like colors (20 pixels representing
// blue field, stars and stripes). This gets "stretched" as needed
// to the full LED strip length in the flag effect code, below.
// Can change this data to the colors of your own national flag,
// favorite sports team colors, etc. OK to change number of elements.
#define C_RED 160, 0, 0
#define C_WHITE 255, 255, 255
#define C_BLUE 0, 0, 100
PROGMEM prog_uchar flagTable[] = {
C_BLUE , C_WHITE, C_BLUE , C_WHITE, C_BLUE , C_WHITE, C_BLUE,
C_RED , C_WHITE, C_RED , C_WHITE, C_RED , C_WHITE, C_RED ,
C_WHITE, C_RED , C_WHITE, C_RED , C_WHITE, C_RED };
// Wavy flag effect
void renderEffect03(byte idx) {
long i, sum, s, x;
int idx1, idx2, a, b;
if(fxVars[idx][0] == 0) { // Initialize effect?
fxVars[idx][1] = 720 + random(720); // Wavyness
fxVars[idx][2] = 4 + random(10); // Wave speed
fxVars[idx][3] = 200 + random(200); // Wave 'puckeryness'
fxVars[idx][4] = 0; // Current position
fxVars[idx][0] = 1; // Effect initialized
}
for(sum=0, i=0; i<numPixels-1; i++) {
sum += fxVars[idx][3] + fixCos(fxVars[idx][4] + fxVars[idx][1] *
i / numPixels);
}
byte *ptr = &imgData[idx][0];
for(s=0, i=0; i<numPixels; i++) {
x = 256L * ((sizeof(flagTable) / 3) - 1) * s / sum;
idx1 = (x >> 8) * 3;
idx2 = ((x >> 8) + 1) * 3;
b = (x & 255) + 1;
a = 257 - b;
*ptr++ = ((pgm_read_byte(&flagTable[idx1 ]) * a) +
(pgm_read_byte(&flagTable[idx2 ]) * b)) >> 8;
*ptr++ = ((pgm_read_byte(&flagTable[idx1 + 1]) * a) +
(pgm_read_byte(&flagTable[idx2 + 1]) * b)) >> 8;
*ptr++ = ((pgm_read_byte(&flagTable[idx1 + 2]) * a) +
(pgm_read_byte(&flagTable[idx2 + 2]) * b)) >> 8;
s += fxVars[idx][3] + fixCos(fxVars[idx][4] + fxVars[idx][1] *
i / numPixels);
}
fxVars[idx][4] += fxVars[idx][2];
if(fxVars[idx][4] >= 720) fxVars[idx][4] -= 720;
}
// TO DO: Add more effects here...Larson scanner, etc.
// ---------------------------------------------------------------------------
// Alpha channel effect rendering functions. Like the image rendering
// effects, these are typically parametrically-generated...but unlike the
// images, there is only one alpha renderer "in flight" at any given time.
// So it would be okay to use local static variables for storing state
// information...but, given that there could end up being many more render
// functions here, and not wanting to use up all the RAM for static vars
// for each, a third row of fxVars is used for this information.
// Simplest alpha effect: fade entire strip over duration of transition.
void renderAlpha00(void) {
byte fade = 255L * tCounter / transitionTime;
for(int i=0; i<numPixels; i++) alphaMask[i] = fade;
}
// Straight left-to-right or right-to-left wipe
void renderAlpha01(void) {
long x, y, b;
if(fxVars[2][0] == 0) {
fxVars[2][1] = random(1, numPixels); // run, in pixels
fxVars[2][2] = (random(2) == 0) ? 255 : -255; // rise
fxVars[2][0] = 1; // Transition initialized
}
b = (fxVars[2][2] > 0) ?
(255L + (numPixels * fxVars[2][2] / fxVars[2][1])) *
tCounter / transitionTime - (numPixels * fxVars[2][2] / fxVars[2][1]) :
(255L - (numPixels * fxVars[2][2] / fxVars[2][1])) *
tCounter / transitionTime;
for(x=0; x<numPixels; x++) {
y = x * fxVars[2][2] / fxVars[2][1] + b; // y=mx+b, fixed-point style
if(y < 0) alphaMask[x] = 0;
else if(y >= 255) alphaMask[x] = 255;
else alphaMask[x] = (byte)y;
}
}
// Dither reveal between images
void renderAlpha02(void) {
long fade;
int i, bit, reverse, hiWord;
if(fxVars[2][0] == 0) {
// Determine most significant bit needed to represent pixel count.
int hiBit, n = (numPixels - 1) >> 1;
for(hiBit=1; n; n >>=1) hiBit <<= 1;
fxVars[2][1] = hiBit;
fxVars[2][0] = 1; // Transition initialized
}
for(i=0; i<numPixels; i++) {
// Reverse the bits in i for ordered dither:
for(reverse=0, bit=1; bit <= fxVars[2][1]; bit <<= 1) {
reverse <<= 1;
if(i & bit) reverse |= 1;
}
fade = 256L * numPixels * tCounter / transitionTime;
hiWord = (fade >> 8);
if(reverse == hiWord) alphaMask[i] = (fade & 255); // Remainder
else if(reverse < hiWord) alphaMask[i] = 255;
else alphaMask[i] = 0;
}
}
// TO DO: Add more transitions here...triangle wave reveal, etc.
// ---------------------------------------------------------------------------
// Assorted fixed-point utilities below this line. Not real interesting.
// Gamma correction compensates for our eyes' nonlinear perception of
// intensity. It's the LAST step before a pixel value is stored, and
// allows intermediate rendering/processing to occur in linear space.
// The table contains 256 elements (8 bit input), though the outputs are
// only 7 bits (0 to 127). This is normal and intentional by design: it
// allows all the rendering code to operate in the more familiar unsigned
// 8-bit colorspace (used in a lot of existing graphics code), and better
// preserves accuracy where repeated color blending operations occur.
// Only the final end product is converted to 7 bits, the native format
// for the LPD8806 LED driver. Gamma correction and 7-bit decimation
// thus occur in a single operation.
PROGMEM prog_uchar gammaTable[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4,
4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7,
7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11,
11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, 16, 16,
16, 17, 17, 17, 18, 18, 18, 19, 19, 20, 20, 21, 21, 21, 22, 22,
23, 23, 24, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30,
30, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36, 37, 37, 38, 38, 39,
40, 40, 41, 41, 42, 43, 43, 44, 45, 45, 46, 47, 47, 48, 49, 50,
50, 51, 52, 52, 53, 54, 55, 55, 56, 57, 58, 58, 59, 60, 61, 62,
62, 63, 64, 65, 66, 67, 67, 68, 69, 70, 71, 72, 73, 74, 74, 75,
76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
92, 93, 94, 95, 96, 97, 98, 99,100,101,102,104,105,106,107,108,
109,110,111,113,114,115,116,117,118,120,121,122,123,125,126,127
};
// This function (which actually gets 'inlined' anywhere it's called)
// exists so that gammaTable can reside out of the way down here in the
// utility code...didn't want that huge table distracting or intimidating
// folks before even getting into the real substance of the program, and
// the compiler permits forward references to functions but not data.
inline byte gamma(byte x) {
return pgm_read_byte(&gammaTable[x]);
}
// Fixed-point colorspace conversion: HSV (hue-saturation-value) to RGB.
// This is a bit like the 'Wheel' function from the original strandtest
// code on steroids. The angular units for the hue parameter may seem a
// bit odd: there are 1536 increments around the full color wheel here --
// not degrees, radians, gradians or any other conventional unit I'm
// aware of. These units make the conversion code simpler/faster, because
// the wheel can be divided into six sections of 256 values each, very
// easy to handle on an 8-bit microcontroller. Math is math, and the
// rendering code elsehwere in this file was written to be aware of these
// units. Saturation and value (brightness) range from 0 to 255.
long hsv2rgb(long h, byte s, byte v) {
byte r, g, b, lo;
int s1;
long v1;
// Hue
h %= 1536; // -1535 to +1535
if(h < 0) h += 1536; // 0 to +1535
lo = h & 255; // Low byte = primary/secondary color mix
switch(h >> 8) { // High byte = sextant of colorwheel
case 0 : r = 255 ; g = lo ; b = 0 ; break; // R to Y
case 1 : r = 255 - lo; g = 255 ; b = 0 ; break; // Y to G
case 2 : r = 0 ; g = 255 ; b = lo ; break; // G to C
case 3 : r = 0 ; g = 255 - lo; b = 255 ; break; // C to B
case 4 : r = lo ; g = 0 ; b = 255 ; break; // B to M
default: r = 255 ; g = 0 ; b = 255 - lo; break; // M to R
}
// Saturation: add 1 so range is 1 to 256, allowig a quick shift operation
// on the result rather than a costly divide, while the type upgrade to int
// avoids repeated type conversions in both directions.
s1 = s + 1;
r = 255 - (((255 - r) * s1) >> 8);
g = 255 - (((255 - g) * s1) >> 8);
b = 255 - (((255 - b) * s1) >> 8);
// Value (brightness) and 24-bit color concat merged: similar to above, add
// 1 to allow shifts, and upgrade to long makes other conversions implicit.
v1 = v + 1;
return (((r * v1) & 0xff00) << 8) |
((g * v1) & 0xff00) |
( (b * v1) >> 8);
}
// The fixed-point sine and cosine functions use marginally more
// conventional units, equal to 1/2 degree (720 units around full circle),
// chosen because this gives a reasonable resolution for the given output
// range (-127 to +127). Sine table intentionally contains 181 (not 180)
// elements: 0 to 180 *inclusive*. This is normal.
PROGMEM prog_char sineTable[181] = {
0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16, 17,
18, 19, 20, 21, 22, 23, 24, 25, 27, 28, 29, 30, 31, 32, 33, 34,
35, 36, 37, 38, 39, 40, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 77, 78, 79, 80, 81,
82, 83, 83, 84, 85, 86, 87, 88, 88, 89, 90, 91, 92, 92, 93, 94,
95, 95, 96, 97, 97, 98, 99,100,100,101,102,102,103,104,104,105,
105,106,107,107,108,108,109,110,110,111,111,112,112,113,113,114,
114,115,115,116,116,117,117,117,118,118,119,119,120,120,120,121,
121,121,122,122,122,123,123,123,123,124,124,124,124,125,125,125,
125,125,126,126,126,126,126,126,126,127,127,127,127,127,127,127,
127,127,127,127,127
};
char fixSin(int angle) {
angle %= 720; // -719 to +719
if(angle < 0) angle += 720; // 0 to +719
return (angle <= 360) ?
pgm_read_byte(&sineTable[(angle <= 180) ?
angle : // Quadrant 1
(360 - angle)]) : // Quadrant 2
-pgm_read_byte(&sineTable[(angle <= 540) ?
(angle - 360) : // Quadrant 3
(720 - angle)]) ; // Quadrant 4
}
char fixCos(int angle) {
angle %= 720; // -719 to +719
if(angle < 0) angle += 720; // 0 to +719
return (angle <= 360) ?
((angle <= 180) ? pgm_read_byte(&sineTable[180 - angle]) : // Quad 1
-pgm_read_byte(&sineTable[angle - 180])) : // Quad 2
((angle <= 540) ? -pgm_read_byte(&sineTable[540 - angle]) : // Quad 3
pgm_read_byte(&sineTable[angle - 540])) ; // Quad 4
}

View File

@@ -0,0 +1,60 @@
#include "LPD8806.h"
#include "SPI.h"
// Simple test for 160 (5 meters) of LPD8806-based RGB LED strip
/*****************************************************************************/
// Number of RGB LEDs in strand:
int nLEDs = 160;
// Chose 2 pins for output; can be any valid output pins:
int dataPin = 2;
int clockPin = 3;
// First parameter is the number of LEDs in the strand. The LED strips
// are 32 LEDs per meter but you can extend or cut the strip. Next two
// parameters are SPI data and clock pins:
LPD8806 strip = LPD8806(nLEDs, dataPin, clockPin);
// You can optionally use hardware SPI for faster writes, just leave out
// the data and clock pin parameters. But this does limit use to very
// specific pins on the Arduino. For "classic" Arduinos (Uno, Duemilanove,
// etc.), data = pin 11, clock = pin 13. For Arduino Mega, data = pin 51,
// clock = pin 52. For 32u4 Breakout Board+ and Teensy, data = pin B2,
// clock = pin B1. For Leonardo, this can ONLY be done on the ICSP pins.
//LPD8806 strip = LPD8806(nLEDs);
void setup() {
// Start up the LED strip
strip.begin();
// Update the strip, to start they are all 'off'
strip.show();
}
void loop() {
colorChase(strip.Color(127, 0, 0), 100); // Red
colorChase(strip.Color( 0,127, 0), 100); // Green
colorChase(strip.Color( 0, 0,127), 100); // Blue
colorChase(strip.Color(127,127,127), 100); // White
}
// Chase one dot down the full strip. Good for testing purposes.
void colorChase(uint32_t c, uint8_t wait) {
int i;
// Start by turning all pixels off:
for(i=0; i<strip.numPixels(); i++) strip.setPixelColor(i, 0);
// Then display one pixel at a time:
for(i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, c); // Set new pixel 'on'
strip.show(); // Refresh LED states
strip.setPixelColor(i, 0); // Erase pixel, but don't refresh!
delay(wait);
}
strip.show(); // Refresh to turn off last pixel
}

View File

@@ -0,0 +1,143 @@
#include "LPD8806.h"
#include "SPI.h"
// Example to control LPD8806-based RGB LED Modules in a strip
/*****************************************************************************/
// Number of RGB LEDs in strand:
int nLEDs = 32;
// Chose 2 pins for output; can be any valid output pins:
int dataPin = 2;
int clockPin = 3;
// First parameter is the number of LEDs in the strand. The LED strips
// are 32 LEDs per meter but you can extend or cut the strip. Next two
// parameters are SPI data and clock pins:
LPD8806 strip = LPD8806(32, dataPin, clockPin);
// You can optionally use hardware SPI for faster writes, just leave out
// the data and clock pin parameters. But this does limit use to very
// specific pins on the Arduino. For "classic" Arduinos (Uno, Duemilanove,
// etc.), data = pin 11, clock = pin 13. For Arduino Mega, data = pin 51,
// clock = pin 52. For 32u4 Breakout Board+ and Teensy, data = pin B2,
// clock = pin B1. For Leonardo, this can ONLY be done on the ICSP pins.
//LPD8806 strip = LPD8806(nLEDs);
void setup() {
// Start up the LED strip
strip.begin();
// Update the strip, to start they are all 'off'
strip.show();
}
void loop() {
// Send a simple pixel chase in...
colorChase(strip.Color(127, 127, 127), 50); // White
colorChase(strip.Color(127, 0, 0), 50); // Red
colorChase(strip.Color(127, 127, 0), 50); // Yellow
colorChase(strip.Color( 0, 127, 0), 50); // Green
colorChase(strip.Color( 0, 127, 127), 50); // Cyan
colorChase(strip.Color( 0, 0, 127), 50); // Blue
colorChase(strip.Color(127, 0, 127), 50); // Violet
// Fill the entire strip with...
colorWipe(strip.Color(127, 0, 0), 50); // Red
colorWipe(strip.Color( 0, 127, 0), 50); // Green
colorWipe(strip.Color( 0, 0, 127), 50); // Blue
rainbow(10);
rainbowCycle(0); // make it go through the cycle fairly fast
}
void rainbow(uint8_t wait) {
int i, j;
for (j=0; j < 384; j++) { // 3 cycles of all 384 colors in the wheel
for (i=0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel( (i + j) % 384));
}
strip.show(); // write all the pixels out
delay(wait);
}
}
// Slightly different, this one makes the rainbow wheel equally distributed
// along the chain
void rainbowCycle(uint8_t wait) {
uint16_t i, j;
for (j=0; j < 384 * 5; j++) { // 5 cycles of all 384 colors in the wheel
for (i=0; i < strip.numPixels(); i++) {
// tricky math! we use each pixel as a fraction of the full 384-color wheel
// (thats the i / strip.numPixels() part)
// Then add in j which makes the colors go around per pixel
// the % 384 is to make the wheel cycle around
strip.setPixelColor(i, Wheel( ((i * 384 / strip.numPixels()) + j) % 384) );
}
strip.show(); // write all the pixels out
delay(wait);
}
}
// Fill the dots progressively along the strip.
void colorWipe(uint32_t c, uint8_t wait) {
int i;
for (i=0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, c);
strip.show();
delay(wait);
}
}
// Chase one dot down the full strip.
void colorChase(uint32_t c, uint8_t wait) {
int i;
// Start by turning all pixels off:
for(i=0; i<strip.numPixels(); i++) strip.setPixelColor(i, 0);
// Then display one pixel at a time:
for(i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, c); // Set new pixel 'on'
strip.show(); // Refresh LED states
strip.setPixelColor(i, 0); // Erase pixel, but don't refresh!
delay(wait);
}
strip.show(); // Refresh to turn off last pixel
}
/* Helper functions */
//Input a value 0 to 384 to get a color value.
//The colours are a transition r - g -b - back to r
uint32_t Wheel(uint16_t WheelPos)
{
byte r, g, b;
switch(WheelPos / 128)
{
case 0:
r = 127 - WheelPos % 128; //Red down
g = WheelPos % 128; // Green up
b = 0; //blue off
break;
case 1:
g = 127 - WheelPos % 128; //green down
b = WheelPos % 128; //blue up
r = 0; //red off
break;
case 2:
b = 127 - WheelPos % 128; //blue down
r = WheelPos % 128; //red up
g = 0; //green off
break;
}
return(strip.Color(r,g,b));
}