spreng37 4 Posted September 5, 2012 Share Posted September 5, 2012 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! Quote Link to post Share on other sites
RobG 1,892 Posted September 6, 2012 Share Posted September 6, 2012 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++; } } spreng37 1 Quote Link to post Share on other sites
spreng37 4 Posted September 6, 2012 Author Share Posted September 6, 2012 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. Quote Link to post Share on other sites
RobG 1,892 Posted September 6, 2012 Share Posted September 6, 2012 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. spreng37 1 Quote Link to post Share on other sites
spreng37 4 Posted September 6, 2012 Author Share Posted September 6, 2012 Oooh, ok. I knew Xin and Xout could be used as I/O but didn't know they were considered P2. MOSI is SIN...also did not know. Cool, thanks!! Quote Link to post Share on other sites
spreng37 4 Posted September 6, 2012 Author Share Posted September 6, 2012 Works great, thanks again Rob!!! Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.