mirror of
https://github.com/digistump/DigistumpArduino.git
synced 2025-09-17 09:22:28 -07:00
switch to setup for Arduino Boards Manager
This commit is contained in:
124
digistump-avr/libraries/LPD8806/LPD8806.cpp
Normal file
124
digistump-avr/libraries/LPD8806/LPD8806.cpp
Normal 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
|
||||
}
|
41
digistump-avr/libraries/LPD8806/LPD8806.h
Normal file
41
digistump-avr/libraries/LPD8806/LPD8806.h
Normal 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
|
||||
};
|
18
digistump-avr/libraries/LPD8806/README.md
Normal file
18
digistump-avr/libraries/LPD8806/README.md
Normal 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
|
@@ -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));
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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));
|
||||
}
|
Reference in New Issue
Block a user