PDA

View Full Version : PIC BASIC TLC5940 code



eletrokat
- 16th August 2009, 12:01
Hello,

I'm trying to setup a PIC using a TLC5940 chip to fade an LED array. I'm writing the code in BASIC. I've been trying to convert over some example code from an Arduino setup but haven't had much luck.

I think I'm pretty close to getting it, the PIC is shifting out the information and I can see the LEDs blinking on and off, but I don't think I'm getting the BLANK, LATCHING, CLOCKING sequence correct... not sure what the problem is, it should just be a straight conversion.

Any help would really be great. Thanks!

Chip specs:
http://www.ti.com/lit/gpn/tlc5940

Arduino example code from here:
http://pixelriot.com/pmatp/node/15

BASIC code and example Arduino code is attached.

EDIT: Also, going through the spec sheet it says "Only 3 pins are needed to input data into the device. The rising edge of the SCLK signal shifts the data from the SIN (in my code "SER") pin to the internal register. After all data is clocked in, a high-level pulse of XLAT signal latches the serial data to the internal registers.." So it seems like there should be a method of sending data using only 3 pins, however whenever I search on the internet for examples they always have a much more complicated setup to send data to the TLC5940. There are 2 basic modes Dot Correction Mode and GreyScale mode. I want to use GreyScale mode because it allows you a wider range of fading LEDs. What is the simplest operation to set the LED current for each pin??

I tried this method (below), which turns on the LEDs but it's not steady. Which leads me to believe I'm not latching correctly or in the right order..

high BLANK

for z = 0 to 15
shiftout SER, SCLK, 1, [4000]
next z

high XLAT
LOW XLAT
low blank

mackrackit
- 16th August 2009, 13:55
Just a guess.
Try


pulsout XLAT
shiftout SER, SCLK, 1, [4000]


I have not read the data sheet or tried your device. But the above works for other shift registers.

eletrokat
- 16th August 2009, 14:37
Just a guess.
Try


pulsout XLAT
shiftout SER, SCLK, 1, [4000]


I have not read the data sheet or tried your device. But the above works for other shift registers.


Isn't turning a pin HIGH and then LOW the same as PULSOUT?


I've worked a bunch with other shift registers, but mostly they were binary state (ON or OFF). This is the first time I'm working with a PWM chip so I'm a little confused on how to shift out that much information and in what order it needs to be done..

mackrackit
- 16th August 2009, 14:45
I think is has more to do with when the latch is made high. I think it needs done just before the data is sent each time.

Like I said, just a guess. Give it a try. Might get lucky.

eletrokat
- 16th August 2009, 16:54
I think is has more to do with when the latch is made high. I think it needs done just before the data is sent each time.

Like I said, just a guess. Give it a try. Might get lucky.

Thanks. Tried it out but I guess I'm not that lucky.
I think it has to do with how I'm following the Timing Chart?


Here is the Arduino I'm trying to convert to Basic.

/*Ti 5940 16-port LED driver
= overlapped fade across 16 LEDs at a low background level
* Peter Mackey June 2007 Pratt Digital Arts [email protected]
* 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]
}

Darrel Taylor
- 16th August 2009, 21:31
Grayscale values are 12-bits, and you need to send 16 of those values before latching the data, for a total of 192 bits.

The code in the attachment is sending 1,536 bits (16*12*8).
And the method you showed in post #1 would only send 128 (16*8).

And your variable sizes aren't right. fade is a BYTE array, and can't hold 12-bit values. data1 is also a BYTE array, but you're using it like a WORD variable.

I think something like this will work better ...
fade var WORD[16]
TempW var WORD

setGreys:
for z = 0 to 15
TempW = fade(z)
shiftout ser, sclk, 1, [TempW\12]
next z

high xlat
low xlat
return

Then you could ...

RampCount var WORD
ChanCount var BYTE

RampUP:
FOR RampCount = 0 to 4088 STEP 8
FOR ChanCount = 0 to 15
fade(ChanCount) = RampCount
NEXT ChanCount
GOSUB setGreys
PAUSE 50
NEXT RampCount
RETURN

RampDN:
FOR RampCount = 4088 to 0 STEP -8
FOR ChanCount = 0 to 15
fade(ChanCount) = RampCount
NEXT ChanCount
GOSUB setGreys
PAUSE 50
NEXT RampCount
RETURN


Dot correction values are 6-bits. 16 of those would total 96-bits.
<br>

eletrokat
- 19th August 2009, 09:19
Thanks for the help with the variable settings. This definitely helps as I'm sure I'm shifting out the right bits now.

I'm still not getting any response from the TLC5940 though. Now instead of randomly blinking LEDs it's just staying off. I think this is fine because I'm shifting out the right bits, but I'm still confused with the ordering of the LATCH, BLANK, SER.

If you look at the Timing Chart in the spec sheet they show the ordering, but there is this weird thing where they have a separate pulse GSCLK in addition to the main SCLK pulse. I'm not sure why they have this and I don't know how I'm supposed to do both at the same time..

charudatt
- 18th September 2009, 20:03
Take a look at this forum topic , maybe it can help you understand the logic of sending data to the chip.

http://www.picaxeforum.co.uk/showthread.php?t=8616&page=3

let us know when you succeed.

eletrokat
- 19th September 2009, 08:16
Hey thanks for that link. That's some really good info. I had to to put this chip on hold because I got swamped with other things, but I was actually going to go back at it this week. This info thread you posted will help.

I'll post my results (or non-results) later on this week.

Thanks.

wolwil
- 8th May 2010, 21:01
How can I get this to stop flickering? It flickers even at 16MHz. With 4 MHz it taks the GSCLK about 250 ms to complete making all the GSDATA that are 0's really stand out causing a flicker.

Is there some other way I could do the GSCLK so I am not just turning the GSCLK pin H/L 4096 times?



ANSEL = 0 'ALL DIGITAL
CMCON = 7 'COMPARATORS OFF
TRISA = %00000000
TRISB = %00000000

DPIN CON 7
SCLK CON 6
XLAT CON 5
BLANK CON 2
VPRG var PORTA.0
DCPRG var PORTA.2


C1 var byte
C2 var byte
C3 var word

GSDATA var word


GOSUB preset
'************************************************* **
'* Main Loop *
'************************************************* **
LOOP:
FOR GSDATA = 4095 TO 0 STEP -16
gosub setgs
gosub lightemup
NEXT
FOR GSDATA = 4095 TO 0 STEP 16
gosub setgs
gosub lightemup
NEXT
goto LOOP
'************************************************* **
'* Set GS *
'************************************************* **
SETGS:
for C1 = 0 to 15 ' for 16 outputs
shiftout dpin,sclk,1,[GSDATA\12] ' shift out 12 bit GS Data
next
PORTB = %00100000 ' high xlat
PORTB = %00000000 ' low xlat
return
'************************************************* **
'* LIGHT EM UP *
'************************************************* **
LIGHTEMUP:
PORTB = %00000100 ' high blank
PORTB = %00000000 ' All outputs = ON
for C3 = 0 to 4095 ' Generate 4096 GSCLK pulses
PORTA = %00001000 ' GSCLK HIGH
PORTA = %00000000 ' GSCLK LOW
next
return
'************************************************* **
'* Preset *
'************************************************* **
PRESET:
'HIGH DCPRG
HIGH VPRG ' Put into dot correction mode
HIGH blank ' All outputs OFF
low dpin ' Starts dot correction
low xlat
For C1 = 0 to 15 ' 16 Ports
shiftout dpin,sclk,1,[%111111\6] ' 6 bits of Dot Correction per port
next
high xlat ' Latch bits into dot correct register
low xlat
low VPRG ' Enter GS Mode
for C1 = 0 to 15
for C2 = 0 to 11
shiftout dpin,sclk,1,[%111111111111\12]
next
next
high xlat ' Latch bits into GS register
low xlat
high SCLK '193rd CLK pulse
LOW SCLK
low blank ' All outputs = ON
return


NOTE: This code is basically the same as the above Adruno code but I have a different fade routine. The above code used direct PORT addressing for GSCLK like I did but they were using an 8MHz Adruno for their project and it worked fine. I don't understand why this will not work in PBP with 16 MHz running the show.