Jump to content
43oh

Help porting code from TLC5916 to TLC5940


Recommended Posts

Currently using an MSP430G2231 controlling two TLC5916s. I'd like to use one TLC5940 to save on space but I'm having a hard time editing the code for use with a 5940. I've studied many examples here but they all seem to use the G2553 and I don't know what parts of the code will or will not work for the G2231.

 

Here's the current code I'm using for two TLC5916s (thanks to the awesome RobG):

 

#include "msp430G2231.h"
#include "pattern.h"

#define OE BIT6
#define SDI BIT5
#define CLK BIT4
#define LE  BIT0

// 16 levels of brightness
const char lut[16] = { 0, 3, 6, 10, 20, 30, 40, 60, 80, 100, 120, 150, 180, 210, 240, 255 };

char pwmCounter = 0;
char patternIndex = 0;
char patternCounter = 0;
int patternDelay = 0;



void main(void) {

  WDTCTL = WDTPW + WDTHOLD;
  DCOCTL |= DCO0 + DCO1; // DCO = 15.25MHz
  BCSCTL1 |= RSEL0 + RSEL1 + RSEL2 + RSEL3; // as above

  P1OUT = 0;
  P1DIR |= (SDI + CLK + LE + OE);

  // setup timer
  CCR0 = 1200; //
  TACTL = TASSEL_2 + MC_1 + ID_0; // SMCLK, up mode, 1:1
  CCTL0 = CCIE; // CCR0 interrupt enabled

  _bis_SR_register(GIE);

  // patternIndex is used for pattern selection
  // patternCounter is used to count steps

  while (1) {
   patternIndex++;
   patternCounter++;
       if(patternCounter == 16) {
         patternIndex = 8;
         patternCounter = 8;
       }

                  // don't change
        _bis_SR_register(LPM0_bits); // go to sleep and wait
  };
}



#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer_A0(void) {

  patternIndex &= 0x0F; // mask patternCounter to get 0-15 range
  pwmCounter++;
  pwmCounter++; // increase again to get 128 pwm steps instead of 256

  char i;
  char pwm;
  for (i = 0; i < 16; i++) {
     pwm = lut[pattern[patternIndex][i]];
     if (pwm > pwmCounter) {
        P1OUT |= SDI;
     } else {
        P1OUT &= ~SDI;
     }
     P1OUT |= CLK;
     P1OUT &= ~CLK;
  }
  P1OUT |= LE;
  P1OUT &= ~LE;

  patternDelay++;
  if (patternDelay == 1625 
	   ) { // animation speed
     patternDelay = 0;
     _bic_SR_register_on_exit(LPM0_bits); // ready for next pattern, wake up after ISR
  }
}

 

Here's the example 5940 code I'm using:

 

#include 
#include "BinaryConst.h"

#define SIN		BIT1
#define GSCLK	BIT2
#define XLAT	BIT3
#define BLANK	BIT4
#define SCLK	BIT5

#define CLOCK_PERIOD      256

// MSB - CH1, LSB - CH15
const unsigned int RiderData[16] = {
B16(10000000, 00000000),
B16(11000000, 00000000),
B16(11100000, 00000000),
B16(01111000, 00000000),
B16(00111100, 00000000),
B16(00011110, 00000000),
B16(00000111, 00000000),
B16(00000011, 00000000),
B16(00000001, 00000000),
B16(00000011, 00000000),
B16(00000111, 00000000),
B16(00011110, 00000000),
B16(00111100, 00000000),
B16(01111000, 00000000),
B16(11100000, 00000000),
B16(11000000, 00000000),
};


int PWMCount = 0; // Counter for GSCLK pulse


void ConfigureTimer(void);
void InitializeClocks(void);

void RunKnightRiderSeq(unsigned int Brightness);
void SendData(unsigned int data, unsigned int Brightness);

void main(void)
{
int BrightnessDuty = CLOCK_PERIOD - 1; // Duty for Brightness
int BrightnessDelta = CLOCK_PERIOD / 8; // Brightness change for each round

unsigned int count;

WDTCTL = WDTPW + WDTHOLD;

P1OUT = 0;
P1DIR = 0;
P1DIR |= (SIN + SCLK + XLAT + BLANK + GSCLK);

SendData(0, 0); // Clear all output

InitializeClocks(); // Setup clock
ConfigureTimer(); // Setup Timer to generate GSCLK

while (1)
{
	_BIS_SR(GIE); // Enable Interrupt

	RunKnightRiderSeq(BrightnessDuty);

}
}

void RunKnightRiderSeq(unsigned int Brightness)
{
unsigned int count;
unsigned char CurStep;

for (CurStep = 0; CurStep < 16; CurStep++)
{
	SendData(RiderData[CurStep], Brightness); // Send current Step data to TLC5940
	__delay_cycles(10000); // Delay for each step
}
}

void SendData(unsigned int data, unsigned int Brightness)
{
char i, j;
int k;

TACTL = TASSEL_2 | MC_0;  // Disable Timer

P1OUT &= ~(SCLK + XLAT + BLANK);

for (i = 0; i < 16; i++)
{
	// Send data for each channel, CH15 first
	if (data & 0x1) // Channel is ON
	{
		k = Brightness;
		// Send duty bits, 12-bit, MSB first
		for (j = 0; j < 12; j++)
		{
			if (k & 0x800)
				P1OUT |= SIN;
			else
				P1OUT &= ~SIN;

			// pulse SCLK
			P1OUT |= SCLK;
			P1OUT &= ~SCLK;

			k <<= 1; // next bit for duty
		}
	}
	else // Channel is OFF, send 0 for Duty
	{
		P1OUT &= ~SIN; // 0 for all duty bits

		for (j = 0; j < 12; j++)
		{
			// pulse SCLK
			P1OUT |= SCLK;
			P1OUT &= ~SCLK;
		}
	}

	data >>= 1; // next Channel
}

// Pulse BLANK, XLAT, SCLK to latch data to TLC5940, reset PWMcount
P1OUT |= BLANK;
PWMCount = 0;
P1OUT |= XLAT;
P1OUT &= ~XLAT;
P1OUT &= ~BLANK;
P1OUT |= SCLK;
P1OUT &= ~SCLK;

TACTL = TASSEL_2 | MC_1;  // Enable Timer
}

void InitializeClocks(void)
{
 BCSCTL1 = CALBC1_1MHZ;
 DCOCTL = CALDCO_1MHZ;
}

void ConfigureTimer(void)
{
 TACTL = TASSEL_2 | MC_0 | TACLR;  // TACLK = SMCLK, Up mode, reset count
 TACCR0 = 40;						// Timer Frequency
 TACCTL0 = CCIE;					// Trigger Interrupt whenever Timer reach TACCR0
 TACTL = TASSEL_2 | MC_1;  		// TACLK = SMCLK, Up mode
}

#pragma vector=TIMERA0_VECTOR
__interrupt void ta0_isr(void)
{
// Pulse GSCLK
P1OUT |= GSCLK;
P1OUT &= ~GSCLK;

// pulse BLANK to restart PWM cycle when GSCLK pulse reach max count
if (++PWMCount >= CLOCK_PERIOD)
{
	P1OUT |= BLANK;
	P1OUT &= ~BLANK;
	PWMCount = 0;
}

TACCTL1 &= ~CCIFG;
}

 

I'm hoping it's just a matter of changing the pin pulses but I really have no clue.

 

Thanks so much in advance!

Link to post
Share on other sites

Here's one example, 12bit GS.

I am using bit banging instead of USI module to make it work with my TLC5940 board, which was designed to be used with 2553/USCI.

 

#include 

#define SCLK_PIN	BIT5
#define MOSI_PIN	BIT7

#define GSCLK_PIN	BIT4
#define BLANK_PIN	BIT2
#define XLAT_PIN	BIT1
#define DCPRG_PIN	BIT7 //P2
#define VPRG_PIN	BIT0

typedef unsigned char u_char;
typedef unsigned int u_int;

#define NUMBER_OF_LEDS 16

u_int leds[NUMBER_OF_LEDS] = { 0, };
u_char timerCounter = 0;

void updateTLC();
void sendData(u_int data);

void main(void) {

WDTCTL = WDTPW + WDTHOLD; // disable WDT
DCOCTL |= DCO0 + DCO1; // DCO = 15.25MHz
BCSCTL1 |= RSEL0 + RSEL1 + RSEL2 + RSEL3; // as above
BCSCTL2 |= DIVS_3; // divide clock by 8

P1OUT &= ~(VPRG_PIN + BLANK_PIN + XLAT_PIN + SCLK_PIN + MOSI_PIN);
P1DIR |= VPRG_PIN + BLANK_PIN + XLAT_PIN + SCLK_PIN + MOSI_PIN;

P1DIR |= GSCLK_PIN; // port 1.4 configured as SMCLK out
P1SEL |= GSCLK_PIN;

P2SEL &= ~(BIT6 | BIT7);
P2OUT &= ~DCPRG_PIN;
P2DIR |= DCPRG_PIN;

// setup timer
CCR0 = 0xFFF;
TACTL = TASSEL_2 + MC_1 + ID_0; // SMCLK, up mode, 1:1
CCTL0 = CCIE; // CCR0 interrupt enabled

updateTLC();
P1OUT |= XLAT_PIN;
P1OUT &= ~XLAT_PIN;

_bis_SR_register(GIE);

leds[0] = 0x0F;
u_char counter = 0;
u_char direction = 0;
u_char first = 0;
u_char firstDirection = 0;
u_int brightness = 0xFFF;

while (1) {
	//
	if (direction) {
		counter--;
		leds[counter] = brightness;
		leds[counter + 1] = 0;
		if (counter == first) {
			direction = 0;
			// first
			if (firstDirection) {
				first--;
				if (first == 0)
					firstDirection = 0;
			} else {
				leds[first - 1] = brightness;
				first++;
				if (first == (NUMBER_OF_LEDS - 2))
					firstDirection = 1;
			}
		}
	} else {
		counter++;
		leds[counter] = brightness;
		leds[counter - 1] = 0;
		if (counter == (NUMBER_OF_LEDS - 1)) {
			direction = 1;
		}
	}

	//update all leds
	u_char c = 0;
	while(c < NUMBER_OF_LEDS) {
		if(leds[c] > 0) leds[c] = brightness;
		c++;
	}
	//update brightness
	brightness -= 0x02;

	// sleep
	_bis_SR_register(LPM0_bits);
}
}

void updateTLC() {

u_char ledCounter = NUMBER_OF_LEDS >> 1;

while (ledCounter-- > 0) {
	u_char i = ledCounter << 1;
	sendData(leds[i + 1]);
	sendData(leds[i]);
}
}

#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer_A0(void) {
P1OUT |= BLANK_PIN;
P1OUT |= XLAT_PIN;
P1OUT &= ~XLAT_PIN;
P1OUT &= ~BLANK_PIN;

timerCounter++;
if (timerCounter == 0x08) { // 0x08 - 2ms * 8 = 16.384ms, ~61Hz
	updateTLC();
	timerCounter = 0;
	_bic_SR_register_on_exit(LPM0_bits);
}
}

void sendData(u_int data) {
u_char c = 0;
while (c < 12) {
	(data & 0x0800) ? (P1OUT |= MOSI_PIN) : (P1OUT &= ~MOSI_PIN);
	P1OUT |= SCLK_PIN;
	P1OUT &= ~SCLK_PIN;
	data <<= 1;
	c++;
}
}

 

 

And here's the code that works just like the TLC5916 one (12bit GS instead of 8bit.)

 

#include 
#include "pattern.h"

#define SCLK_PIN	BIT5
#define MOSI_PIN	BIT7

#define GSCLK_PIN	BIT4
#define BLANK_PIN	BIT2
#define XLAT_PIN	BIT1
#define DCPRG_PIN	BIT7 //P2
#define VPRG_PIN	BIT0

typedef unsigned char u_char;
typedef unsigned int u_int;

#define NUMBER_OF_LEDS 16

u_char timerCounter = 0;

// 16 levels of brightness
const u_int lut[16] = { 0, 30, 60, 100, 200, 300, 400, 600, 800, 1000, 1200, 1500, 1800, 2100,
	3600, 4095 }; // range is 0-4095 (12bit)

u_char patternIndex = 0;
u_char patternCounter = 0;

void updateTLC();
void sendData(u_int data);

void main(void) {

WDTCTL = WDTPW + WDTHOLD; // disable WDT
DCOCTL |= DCO0 + DCO1; // DCO = 15.25MHz
BCSCTL1 |= RSEL0 + RSEL1 + RSEL2 + RSEL3; // as above
BCSCTL2 |= DIVS_3; // divide clock by 8

P1OUT &= ~(VPRG_PIN + BLANK_PIN + XLAT_PIN + SCLK_PIN + MOSI_PIN);
P1DIR |= VPRG_PIN + BLANK_PIN + XLAT_PIN + SCLK_PIN + MOSI_PIN;

P1DIR |= GSCLK_PIN; // port 1.4 configured as SMCLK out
P1SEL |= GSCLK_PIN;

P2SEL &= ~(BIT6 | BIT7);
P2OUT &= ~DCPRG_PIN;
P2DIR |= DCPRG_PIN;

// setup timer
CCR0 = 0xFFF;
TACTL = TASSEL_2 + MC_1 + ID_0; // SMCLK, up mode, 1:1
CCTL0 = CCIE; // CCR0 interrupt enabled

updateTLC();
P1OUT |= XLAT_PIN;
P1OUT &= ~XLAT_PIN;

_bis_SR_register(GIE);

while (1) {
	//
	// logic goes here
	// patternIndex is used for pattern selection
	// patternCounter is used for timeline
	if(patternCounter < 16) {
		patternIndex++;
		patternCounter++;
	} else {
		patternIndex--;
		patternCounter++;
	}

	// sleep
	_bis_SR_register(LPM0_bits);
}
}

void updateTLC() {

u_char ledCounter = NUMBER_OF_LEDS >> 1;

while (ledCounter-- > 0) {
	u_char i = ledCounter << 1;
	sendData(lut[pattern[patternIndex][i + 1]]);
	sendData(lut[pattern[patternIndex][i]]);
}
}

#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer_A0(void) {
P1OUT |= BLANK_PIN;
P1OUT |= XLAT_PIN;
P1OUT &= ~XLAT_PIN;
P1OUT &= ~BLANK_PIN;

patternIndex &= 0x0F; // mask patternCounter to get 0-15 range

timerCounter++;
if (timerCounter == 0x08) { // 0x08 - 2ms * 8 = 16.384ms, ~61Hz
	updateTLC();
	timerCounter = 0;
	_bic_SR_register_on_exit(LPM0_bits);
}
}

void sendData(u_int data) {
u_char c = 0;
while (c < 12) {
	(data & 0x0800) ? (P1OUT |= MOSI_PIN) : (P1OUT &= ~MOSI_PIN);
	P1OUT |= SCLK_PIN;
	P1OUT &= ~SCLK_PIN;
	data <<= 1;
	c++;
}
}

Link to post
Share on other sites

Awesome! Couple questions... You have MOSI and DCPRG both defined to BIT7, commenting that DCPRG is P2. Since the 2231 doesn't have a Port 2 and the 5940 doesn't have a pin labelled MOSI I assume BIT7 just goes to DCPRG and MOSI is simply for the programming.

 

I have it all on the breadboard but first attempt wouldn't produce any output - I'm sure there's something I'm missing. Should SIN, SOUT and/or XERR be brought low? High?

 

Just to be clear, P1.0 to VPRG, P1.1 to XLAT, P1.2 to BLANK, P1.4 to GSCLK, P1.5 to SCLK and P1.7 to DCPRG.

Link to post
Share on other sites

SCLK_PIN P1.5

MOSI_PIN P1.7 (SIN)

GSCLK_PIN P1.4

BLANK_PIN P1.2

XLAT_PIN P1.1

DCPRG_PIN P2.7

VPRG_PIN P1.0

 

2231 has two P2 pins, P2.6 and P2.7 (pins 13 & 12.)

SOUT and XERR should be left floating, those are outputs.

 

You can re-arrange pins as you like, just make sure when you set/reset them, you use appropriate port number

P1OUT |= XLAT_PIN, etc.

The only pin that has to stay as it is is GSCLK P1.4.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...