Skip to content

Commit

Permalink
Merge branch 'master' of github.com:adafruit/LPD8806
Browse files Browse the repository at this point in the history
  • Loading branch information
ladyada committed Jan 9, 2012
2 parents c5d9b8b + d0d6b53 commit 8a4c09b
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 135 deletions.
191 changes: 83 additions & 108 deletions LPD8806.cpp
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -57,115 +60,87 @@ 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<nl3; i++ ) {
SPDR = pixels[i];
while (!(SPSR & (1<<SPIF))) {};
while(!(SPSR & (1<<SPIF)));
}
} else if(slowmo) {
for (i=0; i<nl3; i++ ) {
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);
}
}
} else {
for (i=0; i<nl3; i++ ) {
write8(pixels[i]);
for (uint8_t bit=0x80; bit; bit >>= 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;
}

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;
}

54 changes: 34 additions & 20 deletions LPD8806.h
Original file line number Diff line number Diff line change
@@ -1,29 +1,43 @@
#if (ARDUINO <= 22)
#include <WProgram.h>
#else
#if (ARDUINO >= 100)
#include <Arduino.h>
#else
#include <WProgram.h>
#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);
};
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
7 changes: 0 additions & 7 deletions README.txt

This file was deleted.

Loading

0 comments on commit 8a4c09b

Please sign in to comment.