LED effects code with the 5940

  • warning: Illegal string offset 'data' in /home/psmackman/pixelriot.com/pmatp/includes/tablesort.inc on line 110.
  • warning: Illegal string offset 'data' in /home/psmackman/pixelriot.com/pmatp/includes/tablesort.inc on line 110.
  • : preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in /home/psmackman/pixelriot.com/pmatp/includes/unicode.inc on line 311.
  • : preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in /home/psmackman/pixelriot.com/pmatp/includes/unicode.inc on line 311.
  • : preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in /home/psmackman/pixelriot.com/pmatp/includes/unicode.inc on line 311.
  • : preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in /home/psmackman/pixelriot.com/pmatp/includes/unicode.inc on line 311.
  • : preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in /home/psmackman/pixelriot.com/pmatp/includes/unicode.inc on line 311.
  • : preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in /home/psmackman/pixelriot.com/pmatp/includes/unicode.inc on line 311.
  • : preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in /home/psmackman/pixelriot.com/pmatp/includes/unicode.inc on line 311.
  • : preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in /home/psmackman/pixelriot.com/pmatp/includes/unicode.inc on line 311.
Closeup of 5940 connections to Arduino

This description includes both very basic descriptions and some fairly advanced concepts, neither of which are very detailed, so my apologies to both newbies and developers ;-)

To vary the brightness of an LED, which is essentially a binary device (it is either on or off...), requires some coding gymnastics, with the help of a little hardware.

The Arduino http://www.arduino.cc/ board (and others like it) has PWM ports ("pulse width modulation") which can be used to vary whether a device like an LED gets voltage or not, on a millisecond or faster basis.

That's ok for small projects, but what if you have many many LEDs? Then you'll need a separate LED driver chip. I've tried several, including the 4051 (which is a multiplexer that allows addressing to in my case 8 ports).

4051 driver for 8 LEDS4051 driver for 8 LEDS

But this chip only allows one overall "brightness" setting, and I needed to vary a lot of LED's independently. For that, the Texas Instruments 5940 chip is a great solution.

Arduino with 16-port 5940 driver: Driving a 4x4 test matrix of LEDs using the Texas Instruments 5940 with Arduino.Arduino with 16-port 5940 driver: Driving a 4x4 test matrix of LEDs using the Texas Instruments 5940 with Arduino.

This shot represents an experimental hookup to 16 leds arranged in a 4x4 matrix (that's the green foam to the right), which I eventually hooked up to an accelerometer to control which parts of the array would brighten based on the tilt position of the array.

For purposes of further experimentation, I set up a simpler arrangement of 16 leds in a row, just to create a fading effect across the row, which repeats.

Detail of connections from 5940 to ArduinoDetail of connections from 5940 to Arduino

The code for this was developed thanks to insights gained from jimS and Amp and others in the Arduino forum, as well as consulting with Matt Mackey, who's Burning Man project a couple of years ago included a bunch of cycling LEDs (and Matt's generally a super-geek when it comes to electronics).

So as a sample, here is Arduino code to address the TLC5490, in this case just for fading across a row of LEDs smoothly.

To fully grok what's going on here, get the data sheet for the chip:
http://focus.ti.com/docs/prod/folders/print/tlc5940.html

The biggest challenge was avoiding flicker, since the usual Arduino digitalWrite commands are a little too slow. Amp's "4MHz output from ports" post helped reveal to me the PORTB call, which is easy to use and much faster. Note I only needed to do this when pulsing the 5940's greyscale clock.

Here is the .pde file content. Sorry the formatting is gone. I have attached a zip of the pde below.

/*Ti 5940 16-port LED driver
= overlapped fade across 16 LEDs at a low background level
* Peter Mackey June 2007 Pratt Digital Arts pmackey@pratt.edu
* direct adressing to PORTB, smooth flickerless fading (thanks to Amp on the Arduino forum)
* additional logic from David Cuartielles's & Marcus Hannerstig's LEDdriver demo
= see the TLC5940 data sheet for the logic behind these pulse sequences
*/

//using the pin codes on the TLC5940NT to name the Arduino ports
#define VPRG 2 //"chip pin 27 to Arduino pin 2"
#define SIN 3
#define SCLK 7
#define XLAT 4
#define BLANK 5
#define DCPRG 6
#define GSCLK 8 //note: but using PORTB method

#define MSINTRVL 0 //could be used to delay updating of incrementFades()
#define FADEMIN 100 //lowest fade level LEDs will reach (min would be 0, max 4065)
#define FADEINCR 64 //determines how many steps it takes to run the desired range (lower=longer)

int fadeLevel[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //stores a level for each of 16 ports
int faderNdx = 0; //counter used in this fading sequence

int fadeState[] = {
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //stores the direction of fading for each port 1,0,-1
//start with first port

int next; //used for limit checking in fading function
float prevMillis; //used for a timing delay

int word[] = {
0,0,0,0,0,0,0,0,0,0,0,0}; //temp storage for reversing bits in a word (for greyscale setting)

void setup() {
pinMode(VPRG, OUTPUT);
pinMode(SIN, OUTPUT);
pinMode(SCLK, OUTPUT);
pinMode(XLAT, OUTPUT);
pinMode(BLANK, OUTPUT);
pinMode(DCPRG, OUTPUT);
pinMode(GSCLK, OUTPUT); //could also set DDRB directly

beginSerial(9600); //in case of debugging
Serial.println("Ready...");

preset(); //input 'Dot Correction' data
}

void loop () {
setGreys();
feedPorts();
if (millis() > (prevMillis+MSINTRVL)){
incrementFades();
prevMillis=millis();
}
}

void incrementFades() {
//a very particular sequence: fade up from LED 0 to 15 then fade down in same direction
//overlaps incoming&outgoing adjacent ports' fade level

for (faderNdx=0; faderNdx<=15; faderNdx++) {

if (fadeState[faderNdx]!=0) { //if the state for this LED is not 0...
if (fadeState[faderNdx]==1) { //then fade up...
next = fadeLevel[faderNdx]+FADEINCR;
if (next<4095) {
fadeLevel[faderNdx] = next; //...by incrementing the value in the fadeLevel array
}
else { //set the state for this LED to fade down
fadeLevel[faderNdx] = 4095; //be sure is at "max level"
fadeState[faderNdx] = -1; //flip my sign
//AND...make next neighbor begin a fade up
if (faderNdx<15) {
fadeState[faderNdx+1] = 1;
}
else {
fadeState[0] = 1;
}
}//------------------------------------------------
} //end fading up

else { //fade down instead...........................
next = fadeLevel[faderNdx]-FADEINCR;
if (next>=FADEMIN) { //-----------------
fadeLevel[faderNdx] = next;
}
else {
//set me to fade down
fadeLevel[faderNdx] = FADEMIN; //be sure at minimum level
fadeState[faderNdx] = 0; //stop fading me until neighbor sets me to fade up
}//------------------------------------------------
}//end fading down

}//end check for state not 0
}// end of cycle thru each port
}

//=======5940 control======================================

void setGreys() {
//data for each port (12 bit word * 16 ports =192 bits in this loop)...
//read the fadeLevel array
for (int i=15; i>=0; i--) { // ports, count DOWN
int datb = fadeLevel[i];

//load fade level bits into the temp array BACKWARDS
for (int j=11; j>=0; j--) {
word[j]=(datb & 1); //& bitwise AND
datb >>= 1; //shift right and assign
// (maybe there's a slicker way to do this!? but this works...)
}
//send the data to the 5940
for (int j=0; j<12; j++) {
digitalWrite(SIN,word[j]);
pulseSCLK();
}
}
digitalWrite(XLAT, HIGH);
digitalWrite(XLAT, LOW);
}

void feedPorts() {
//The actual sequencing of the PWM data into the LEDs, must do constantly...
digitalWrite(BLANK, HIGH);
digitalWrite(BLANK, LOW); //=all outputs ON, start PWM cycle

for (int i=0; i<4096; i++) {
pulseGSCLK();
}
}

//DOT CORRECTION...do once
void preset() {
//Input 'DotCorrex' Data
//16 outputs, 64 posssible levels of adjustment, 6 bits/chan = 96 bits total
//[use if any LEDs in array are physically too bright]

digitalWrite(DCPRG, HIGH); //leaving it H is my arbitrary choice (="write to register not EEPROM")
digitalWrite(VPRG, HIGH); //=inputting data into dot correx register

digitalWrite(BLANK, HIGH); //=all outputs off, when this goes high it resets the greyscale counter
digitalWrite(SIN, LOW); //to start dot correction
digitalWrite(XLAT, LOW);

//begin loading in the dot correx data, most significant bit first...
//but here we are not correcting anything, so LSB is going first!
for (int i=0; i<16; i++) { //16 ports
for (int j=0; j<6; j++) { //6 bits of data for each port
digitalWrite(SIN, HIGH); //for now, 111111 for everybody
pulseSCLK();
digitalWrite(SIN, LOW);
}
}

//----doing the FIRST GREYSCALE SETTING here becuz of the unique 193rd clock pulse

digitalWrite(XLAT, HIGH); //latch the dot data into the dot correx register
digitalWrite(XLAT, LOW);
digitalWrite(VPRG, LOW); //entering greyscale mode

for (int i=0; i<16; i++) { //16 ports
int datb = 4095; //using same fade level for all ports this first time

for (int j=0; j<12; j++) { //data for each port, all the same value to start
digitalWrite(SIN, datb & 01);
pulseSCLK();
datb>>=1;
}
}
digitalWrite(XLAT, HIGH); //latch the greyscale data
digitalWrite(XLAT, LOW);
pulseSCLK(); //193rd clock pulse only need to do the FIRST time after dot correx

digitalWrite(BLANK, LOW); //=all outputs ON, start PWM cycle... moved here
}

//SCLK used in dot correx and greyscale setting
void pulseSCLK() {
digitalWrite(SCLK, HIGH);
digitalWrite(SCLK, LOW);
}

void pulseGSCLK() {
//ultra fast pulse trick, using digitalWrite caused flickering
PORTB=0x01; //bring PORTB0 high (pin 8), other ports go low [0x01 does only pin 8, 0x21 also lifts pin 13]
//16nanosecs is the min pulse width for the 5940, but no pause seems needed here
PORTB=0x20; //keep pin13 high [0x00 would be all low]
}


AttachmentSize
leds5940_mackey.pde.zip2.67 KB

Back to top