Permalink
Browse files

Added empty constructor per neophob's request

  • Loading branch information...
1 parent b5b6a55 commit 91998a45b0eb4955242c9f54fdd2038c1349e870 @PaintYourDragon PaintYourDragon committed Jan 10, 2012
Showing with 160 additions and 76 deletions.
  1. +138 −63 LPD8806.cpp
  2. +22 −13 LPD8806.h
View
@@ -1,79 +1,144 @@
-#include "LPD8806.h"
#include "SPI.h"
-#include "pins_arduino.h"
+#include "LPD8806.h"
// Arduino library to control LPD8806-based RGB LED Strips
// (c) Adafruit industries
// MIT license
/*****************************************************************************/
-// Constructor for bitbanged (software) SPI:
+// Constructor for use with hardware SPI (specific clock/data pins):
+LPD8806::LPD8806(uint16_t n) {
+ alloc(n);
+ updatePins();
+}
+
+// Constructor for use with arbitrary clock/data pins:
LPD8806::LPD8806(uint16_t n, uint8_t dpin, uint8_t cpin) {
- // 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;
- }
+ alloc(n);
+ updatePins(dpin, cpin);
}
-// Constructor for hardware SPI use:
-LPD8806::LPD8806(uint16_t n) {
+// Allocate 3 bytes per pixel, init to RGB 'off' state:
+void LPD8806::alloc(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;
- }
+ numLEDs = n;
+ } else numLEDs = 0;
+ begun = slowmo = false;
+ pause = 3;
}
-void LPD8806::begin(void) {
+// via Michael Vogt/neophob: empty constructor is used when strip length
+// isn't known at compile-time; situations where program config might be
+// read from internal flash memory or an SD card, or arrive via serial
+// command. If using this constructor, MUST follow up with updateLength()
+// and updatePins() to establish the strip length and output pins!
+LPD8806::LPD8806(void) {
+ numLEDs = 0;
+ pixels = NULL;
+ begun = slowmo = false;
+ pause = 3;
+ updatePins(); // Must assume hardware SPI until pins are set
+}
- if (hardwareSPI) {
- SPI.begin();
- // 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);
+// Activate hard/soft SPI as appropriate:
+void LPD8806::begin(void) {
+ if(hardwareSPI == true) {
+ startSPI();
} else {
- pinMode(datapin , OUTPUT);
- pinMode(clockpin, OUTPUT);
- if(slowmo) digitalWrite(clockpin, LOW);
- else *clockport &= ~clockpinmask; // Clock = low
+ pinMode(datapin, OUTPUT);
+ pinMode(clkpin , OUTPUT);
+ writeLatch(numLEDs);
}
+ begun = true;
+}
+
+// Change pin assignments post-constructor, switching to hardware SPI:
+void LPD8806::updatePins(void) {
+ hardwareSPI = true;
+ datapin = clkpin = 0;
+ // If begin() was previously invoked, init the SPI hardware now:
+ if(begun == true) startSPI();
+ // Otherwise, SPI is NOT initted until begin() is explicitly called.
- // Issue initial latch to 'wake up' strip (latch length varies w/numLEDs)
- writezeros(3 * ((numLEDs + 63) / 64));
+ // Note: any prior clock/data pin directions are left as-is and are
+ // NOT restored as inputs!
+}
+
+// Change pin assignments post-constructor, using arbitrary pins:
+void LPD8806::updatePins(uint8_t dpin, uint8_t cpin) {
+
+ if(begun == true) { // If begin() was previously invoked...
+ // If previously using hardware SPI, turn that off:
+ if(hardwareSPI == true) SPI.end();
+ // Regardless, now enable output on 'soft' SPI pins:
+ pinMode(dpin, OUTPUT);
+ pinMode(cpin, OUTPUT);
+ writeLatch(numLEDs);
+ } // 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;
+ datapin = dpin;
+ clkpin = cpin;
+ clkport = portOutputRegister(digitalPinToPort(cpin));
+ clkpinmask = digitalPinToBitMask(cpin);
+ dataport = portOutputRegister(digitalPinToPort(dpin));
+ datapinmask = digitalPinToBitMask(dpin);
+}
+
+// Enable SPI hardware and set up protocol details:
+void LPD8806::startSPI(void) {
+ SPI.begin();
+ SPI.setBitOrder(MSBFIRST);
+ SPI.setDataMode(SPI_MODE0);
+ SPI.setClockDivider(SPI_CLOCK_DIV8); // 2 MHz
+ // SPI bus is run 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.
+
+ writeLatch(numLEDs);
}
uint16_t LPD8806::numPixels(void) {
return numLEDs;
}
-void LPD8806::writezeros(uint16_t n) {
+// 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)
+ if(NULL != (pixels = (uint8_t *)malloc(n * 3))) { // Alloc new data
+ memset(pixels, 0x80, n * 3); // Init to RGB 'off' state
+ numLEDs = n;
+ } else numLEDs = 0;
+ // 'begun' state does not change -- pins retain prior modes
+
+ if(begun == true) writeLatch(n); // Write zeros for new length
+}
+
+// Issue latch of appropriate length; pass # LEDs, *not* latch length
+void LPD8806::writeLatch(uint16_t n) {
+
+ // Latch length varies with the number of LEDs:
+ n = ((n + 63) / 64) * 3;
+
if (hardwareSPI) {
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);
+ digitalWrite(clkpin, HIGH);
+ digitalWrite(clkpin, LOW);
}
} else {
- *dataport &= ~datapinmask; // Data low
+ *dataport &= ~datapinmask; // Data is held low throughout
for(uint16_t i = 8 * n; i>0; i--) {
- *clockport |= clockpinmask;
- *clockport &= ~clockpinmask;
+ *clkport |= clkpinmask;
+ *clkport &= ~clkpinmask;
}
}
}
@@ -96,51 +161,61 @@ void LPD8806::show(void) {
for (uint8_t bit=0x80; bit; bit >>= 1) {
if(pixels[i] & bit) digitalWrite(datapin, HIGH);
else digitalWrite(datapin, LOW);
- digitalWrite(clockpin, HIGH);
- digitalWrite(clockpin, LOW);
+ digitalWrite(clkpin, HIGH);
+ digitalWrite(clkpin, LOW);
}
}
} else {
for (i=0; i<nl3; i++ ) {
for (uint8_t bit=0x80; bit; bit >>= 1) {
if(pixels[i] & bit) *dataport |= datapinmask;
else *dataport &= ~datapinmask;
- *clockport |= clockpinmask;
- *clockport &= ~clockpinmask;
+ *clkport |= clkpinmask;
+ *clkport &= ~clkpinmask;
}
}
}
- // Write latch at end of data; latch length varies with number of LEDs
- writezeros(3 * ((numLEDs + 63) / 64));
+ writeLatch(numLEDs); // Write latch at end of data
// 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(pause);
}
-// Convert R,G,B to combined 32-bit color
+// Convert separate R,G,B into combined 32-bit GRB 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
+// 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) return; // '>=' because arrays are 0-indexed
-
- pixels[n*3 ] = g | 0x80;
- pixels[n*3+1] = r | 0x80;
- pixels[n*3+2] = b | 0x80;
+ 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) 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;
+ 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 ((pixels[ofs ] << 16) |
+ (pixels[ofs + 1] << 8) |
+ pixels[ofs + 2]) & 0x7f7f7f;
+ }
+
+ return 0; // Pixel # is out of bounds
+}
View
@@ -2,42 +2,51 @@
#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);
- LPD8806(uint16_t n);
+ LPD8806(uint16_t n, uint8_t dpin, uint8_t cpin); // Configurable pins
+ LPD8806(uint16_t n); // Use SPI hardware; specific pins only
+ LPD8806(void); // Empty constructor; init pins/strip length later
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);
+ setPixelColor(uint16_t n, uint32_t c),
+ updatePins(uint8_t dpin, uint8_t cpin), // Change pins, configurable
+ updatePins(void), // Change pins, hardware SPI
+ updateLength(uint16_t n); // Change strip length
uint16_t
numPixels(void);
uint32_t
- Color(byte, byte, byte);
+ Color(byte, byte, byte),
+ getPixelColor(uint16_t n);
+ // These primarily exist for debugging and will likely come out later:
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:
+ numLEDs; // Number of RGB LEDs in strip
uint8_t
- datapin, datapinmask, clockpin, clockpinmask;
+ *pixels, // Holds LED color values (3 bytes each)
+ clkpin , datapin, // Clock & data pin numbers
+ clkpinmask, datapinmask; // Clock & data PORT bitmasks
volatile uint8_t
- *clockport, *dataport;
-
+ *clkport , *dataport; // Clock & data PORT registers
void
- writezeros(uint16_t n);
+ alloc(uint16_t n),
+ startSPI(void),
+ writeLatch(uint16_t n);
+ boolean
+ hardwareSPI, // If 'true', using hardware SPI
+ begun; // If 'true', begin() method was previously invoked
};

0 comments on commit 91998a4

Please sign in to comment.