diff --git a/LPD8806.cpp b/LPD8806.cpp index d00910d..cc8eda0 100644 --- a/LPD8806.cpp +++ b/LPD8806.cpp @@ -1,52 +1,55 @@ #include "LPD8806.h" #include "SPI.h" #include "pins_arduino.h" -#include "wiring_private.h" -//Example to control LPD8806-based RGB LED Strips +// Arduino library to control LPD8806-based RGB LED Strips // (c) Adafruit industries // MIT license /*****************************************************************************/ -// Test code for using hardware SPI -LPD8806::LPD8806(uint16_t n) { - // use hardware SPI! - dataPin = clockPin = 0; - hardwareSPI = true; - - numLEDs = n; - // malloc 3 bytes per pixel so we dont have to hardcode the length - pixels = (uint8_t *)malloc(numLEDs * 3); // 3 bytes per pixel - memset(pixels, 0x80, numLEDs * 3); // Init to RGB 'off' state -} - +// Constructor for bitbanged (software) SPI: LPD8806::LPD8806(uint16_t n, uint8_t dpin, uint8_t cpin) { - dataPin = dpin; - clockPin = cpin; - - numLEDs = n; - - clkportreg = portOutputRegister(digitalPinToPort(cpin)); - clkpin = digitalPinToBitMask(cpin); - mosiportreg = portOutputRegister(digitalPinToPort(dpin)); - mosipin = digitalPinToBitMask(dpin); + // Allocate 3 bytes per pixel: + if(NULL != (pixels = (uint8_t *)malloc(n * 3))) { + memset(pixels, 0x80, n * 3); // Init to RGB 'off' state + numLEDs = n; + hardwareSPI = false; + datapin = dpin; + datapinmask = digitalPinToBitMask(dpin); + dataport = portOutputRegister(digitalPinToPort(dpin)); + clockpin = cpin; + clockpinmask = digitalPinToBitMask(cpin); + clockport = portOutputRegister(digitalPinToPort(cpin)); + slowmo = false; + pause = 3; + } +} - // malloc 3 bytes per pixel so we dont have to hardcode the length - pixels = (uint8_t *)malloc(numLEDs * 3); // 3 bytes per pixel - memset(pixels, 0x80, numLEDs * 3); // Init to RGB 'off' state +// Constructor for hardware SPI use: +LPD8806::LPD8806(uint16_t n) { + // Allocate 3 bytes per pixel: + if(NULL != (pixels = (uint8_t *)malloc(n * 3))) { + memset(pixels, 0x80, n * 3); // Init to RGB 'off' state + numLEDs = n; + hardwareSPI = true; + pause = 3; + } } void LPD8806::begin(void) { - pinMode(dataPin, OUTPUT); - pinMode(clockPin, OUTPUT); if (hardwareSPI) { - // using hardware SPI SPI.begin(); - // run the SPI bus as fast as possible - // this has been tested on a 16MHz Arduino Uno - SPI.setClockDivider(SPI_CLOCK_DIV2); + // Run the SPI bus at 2MHz. Although the LPD8806 should, in theory, + // work up to 20MHz, the unshielded wiring from the Arduino is more + // susceptible to interference. Experiment and see what you get. + SPI.setClockDivider(SPI_CLOCK_DIV8); + } else { + pinMode(datapin , OUTPUT); + pinMode(clockpin, OUTPUT); + if(slowmo) digitalWrite(clockpin, LOW); + else *clockport &= ~clockpinmask; // Clock = low } // Issue initial latch to 'wake up' strip (latch length varies w/numLEDs) @@ -57,106 +60,78 @@ uint16_t LPD8806::numPixels(void) { return numLEDs; } -uint32_t LPD8806::Color(byte r, byte g, byte b) -{ - // Take the lowest 7 bits of each value and append them end to end - // We have the top bit set high (its a 'parity-like' bit in the protocol - // and must be set!) - - return 0x808080 | ((uint32_t)g << 16) | ((uint32_t)r << 8) | (uint32_t)b; -} - -// Basic, push SPI data out -inline void LPD8806::write8(uint8_t d) { - - // this is the 'least efficient' way (28ms per 32-LED write) - // shiftOut(dataPin, clockPin, MSBFIRST, d); - - // this is the faster pin-flexible way! the pins are precomputed once only - for (uint8_t bit=0x80; bit; bit >>= 1) { - *clkportreg &= ~clkpin; - if (d & bit) { - *mosiportreg |= mosipin; - } else { - *mosiportreg &= ~mosipin; - } - *clkportreg |= clkpin; - } - - *clkportreg &= ~clkpin; -} - -// Basic, push SPI data out void LPD8806::writezeros(uint16_t n) { - // this is the 'least efficient' way (28ms per 32-LED write) - /* - digitalWrite(dataPin, LOW); - for(uint16_t i = 8 * n; i>0; i--) { - digitalWrite(clockPin, HIGH); - digitalWrite(clockPin, LOW); - } - */ - if (hardwareSPI) { - // hardware SPI! - while(n--) - SPI.transfer(0); - return; - } - - // this is the faster pin-flexible way! 7.4ms to write 32 LEDs - *mosiportreg &= ~mosipin; - for(uint16_t i = 8 * n; i>0; i--) { - *clkportreg |= clkpin; - *clkportreg &= ~clkpin; + while(n--) SPI.transfer(0); + } else if(slowmo) { + digitalWrite(datapin, LOW); + for(uint16_t i = 8 * n; i>0; i--) { + digitalWrite(clockpin, HIGH); + digitalWrite(clockpin, LOW); + } + } else { + *dataport &= ~datapinmask; // Data low + for(uint16_t i = 8 * n; i>0; i--) { + *clockport |= clockpinmask; + *clockport &= ~clockpinmask; + } } } -// 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! +// 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, nl3 = numLEDs * 3; // 3 bytes per LED - // get the strip's attention - //writezeros(4); - // This initial latch should only be required once, - // moved to begin() method. - // write 24 bits per pixel if (hardwareSPI) { - // sped up! for (i=0; i>= 1) { + if(pixels[i] & bit) digitalWrite(datapin, HIGH); + else digitalWrite(datapin, LOW); + digitalWrite(clockpin, HIGH); + digitalWrite(clockpin, LOW); + } } } else { for (i=0; i>= 1) { + if(pixels[i] & bit) *dataport |= datapinmask; + else *dataport &= ~datapinmask; + *clockport |= clockpinmask; + *clockport &= ~clockpinmask; + } } } - // to 'latch' the data, we send just zeros - //writezeros(3*numLEDs*2); - //writezeros(4); - // 20111028 pburgess: correct latch length varies -- - // three bytes per 64 LEDs. + // Write latch at end of data; latch length varies with number of LEDs writezeros(3 * ((numLEDs + 63) / 64)); - // we need to have a delay here, a few ms seems to do the job + // We need to have a delay here, a few ms seems to do the job // shorter may be OK as well - need to experiment :( - delay(3); + delay(pause); +} + +// Convert R,G,B to combined 32-bit color +uint32_t LPD8806::Color(byte r, byte g, byte b) { + // Take the lowest 7 bits of each value and append them end to end + // We have the top bit set high (its a 'parity-like' bit in the protocol + // and must be set!) + return 0x808080 | ((uint32_t)g << 16) | ((uint32_t)r << 8) | (uint32_t)b; } // store the rgb component in our array void LPD8806::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) { - uint32_t data; - if (n >= numLEDs) return; // '>=' because arrays are 0-indexed - pixels[n*3] = g | 0x80; + pixels[n*3 ] = g | 0x80; pixels[n*3+1] = r | 0x80; pixels[n*3+2] = b | 0x80; } @@ -164,8 +139,8 @@ void LPD8806::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) { void LPD8806::setPixelColor(uint16_t n, uint32_t c) { if (n >= numLEDs) return; // '>=' because arrays are 0-indexed - pixels[n*3] = (c >> 16) | 0x80; - pixels[n*3+1] = (c >> 8) | 0x80; - pixels[n*3+2] = c | 0x80; + pixels[n*3 ] = (c >> 16) | 0x80; + pixels[n*3+1] = (c >> 8) | 0x80; + pixels[n*3+2] = c | 0x80; } diff --git a/LPD8806.h b/LPD8806.h index f544fe7..82fc0c9 100644 --- a/LPD8806.h +++ b/LPD8806.h @@ -1,29 +1,43 @@ -#if (ARDUINO <= 22) - #include -#else +#if (ARDUINO >= 100) #include +#else + #include #endif class LPD8806 { - private: - void write8(byte); - // the arrays of bytes that hold each LED's 24 bit color values - uint8_t *pixels; - uint16_t numLEDs; - uint8_t dataPin, clockPin; - volatile uint8_t *clkportreg, *mosiportreg; - uint8_t clkpin, mosipin; - boolean hardwareSPI; - - void writezeros(uint16_t n); public: + LPD8806(uint16_t n, uint8_t dpin, uint8_t cpin); LPD8806(uint16_t n); - void begin(); - void show(); - void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b); - void setPixelColor(uint16_t n, uint32_t c); - uint16_t numPixels(void); - uint32_t Color(byte, byte, byte); + 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); + uint16_t + numPixels(void); + uint32_t + Color(byte, byte, byte); + + boolean + slowmo; // If true, use digitalWrite instead of direct PORT writes + uint8_t + pause; // Delay (in milliseconds) after latch + + private: + + uint8_t + *pixels; // Holds LED color values + uint16_t + numLEDs; // Number of RGB LEDs in strand + boolean + hardwareSPI; // If true, using hardware SPI, the following are ignored: + uint8_t + datapin, datapinmask, clockpin, clockpinmask; + volatile uint8_t + *clockport, *dataport; + + void + writezeros(uint16_t n); }; diff --git a/README.md b/README.md new file mode 100644 index 0000000..9bcd44b --- /dev/null +++ b/README.md @@ -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 /libraries/ folder, + if the libraries folder does not exist - create it first! +* Restart the IDE \ No newline at end of file diff --git a/README.txt b/README.txt deleted file mode 100644 index 69cc3c7..0000000 --- a/README.txt +++ /dev/null @@ -1,7 +0,0 @@ -This is an Arduino library for LPD8806 (and probably LPD8803/LPD8809) PWM LED driver chips, strips and pixels - -Pick some up at http://www.adafruit.com/products/306 - -To download. click the DOWNLOADS button in the top right corner, rename the uncompressed folder LPD8806. Check that the LPD8806 folder contains LPD8806.cpp and LPD8806.h - -Place the LPD8806 library folder your /libraries/ folder. You may need to create the libraries subfolder if its your first library. Restart the IDE. \ No newline at end of file diff --git a/examples/longstrandtest/longstrandtest.pde b/examples/longstrandtest/longstrandtest.pde new file mode 100644 index 0000000..1716c47 --- /dev/null +++ b/examples/longstrandtest/longstrandtest.pde @@ -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. +//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