//******************************************************************************
//	 TLC5927 Application Source Code
//
//     Description: The MSP430G2553 acts as the SPI master to three 
//	 TLC5927/TLC5926 LED drivers sinking current from 16 channels each.
//	 The drivers sink current from 16 RGB LEDs with each driver sinking 
//	 current from a particular color node (Red, Green, or Blue).  The SPI
//	 SCLK is sourced by the SMCLK = DCO ~16MHZ.  The SPI module used by the 
//	 SPI master is USCI_B0 in SPI mode.
//
//	 SPI communication with the drivers is through a SPI daisy chain configuration.
//
//	 Timer1_A3 is used to provide PWM outputs in order to dim LEDS.  This is
//	 done through the OE' pin on the drivers.  The timer is also sourced by
//	 the SMCLK.
//
// 
//                   MSP430G2553
//                 -----------------
//          LED <-|P1.0             |
//                |                 |
//          TXD <-|P1.1          RST|-> Reset Button
//                |                 |
//          RXD <-|P1.2         P1.7|-> SIMO (UCB0SIMO)
//                |                 |
//    Button S2 <-|P1.3		      |
//		      |		      |
//  		 LE <-|P1.4         P1.6|<- SOMI (UCB0SOMI)
//                |                 |
//  		OE' <-|P2.1         P1.5|-> SCLK (UCB0CLK)
//
//
//   Roberto Mancera Jr
//   Texas Instruments Inc.
//   June 2011
//   Built with CCS Version 4.2.3 
//******************************************************************************
#include "msp430g2553.h"

#define SCLK		BIT5
#define LE 			BIT4
#define OE 			BIT1

void initialize_clk();
void initialize_SPI();
void latch_out(unsigned int seq_R, unsigned int seq_G, unsigned int seq_B);
void pwm_config( unsigned int half_duty, unsigned int half_period);
void special_conf(unsigned int config_code_R, unsigned int config_code_G,
	unsigned int config_code_B);
void switch_mode(unsigned char mode);

unsigned int rDriverConfig;			    // store configuration code and error code
unsigned int gDriverConfig;			    // for each LED driver
unsigned int bDriverConfig;
unsigned int rErr, gErr, bErr;

void main()
{ 
  initialize_clk();							// Set up basic clk module
  WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog timer
  P1OUT = 0x00;
  P2SEL = 0x00;
  P2OUT = BIT1;								// Initialize OE' (LEDs off)
  P1DIR = BIT0 + BIT4;
  P2DIR = BIT1;
  
  P1IE |= BIT3;								// Initialize P1.3(button) interrupt
  __bis_SR_register(GIE);
  
  rDriverConfig = gDriverConfig = bDriverConfig = 0xFFFF; //Store default config codes
 
  __delay_cycles(75);                 		// Wait for LED driver to initialize

  latch_out(0xFFFF, 0xFFFF, 0xFFFF);		// Latch LED output (white all LEDs on)
  /*comment line below to write gain code*/
  pwm_config(0x7FFF, 0xFFFF);				// dim to half duty cycle brightness
  
  /*uncomment 4 lines below to write gain code*/
  //switch_mode(0);	
  //latch_out(0x00FF, 0x00FF , 0x00FF );		// dim (gain code 0x00 for all 3 drivers)
  //switch_mode(1);
  //P2OUT &= ~OE;
  
  while(1);

}

void initialize_clk()
{
  if (CALBC1_16MHZ == 0xFF || CALDCO_16MHZ == 0XFF) // Calibrate DCO at 16 MHz
  	while(1);										// 		DCO=MCLK=SMCLK
  BCSCTL1 = CALBC1_16MHZ;
  BCSCTL2 = DIVS_1;
  DCOCTL = CALDCO_16MHZ;  	
}

/*
 *  Function: initialize_SPI
 *  Description: Initializes module USCI_B0 in SPI 3 wire mode.  This
 * 		module is to be used to communicate with the TLC5927 LED drivers.
 * 		the module is set up to output to the SDI by send the LSB of a 16 
 * 		bit word first.
 *  Parameters: none
 *  Return: none
 */
 void initialize_SPI()
 {
  P1SEL = BIT5 + BIT6 + BIT7;				// setup SCLK = p1.5, SOMI = p1.6,  
  P1SEL2 = BIT5 + BIT6 + BIT7;				//		SIMO = P1.7
  UCB0CTL0 |= UCMST + UCSYNC + UCCKPH;  				// 3-pin, 8-bit SPI master
  UCB0CTL1 |= UCSSEL_2;                     // SMCLK
  UCB0BR0 |= 0x01;                          // SMCLK/1
  UCB0BR1 = 0;
  UCB0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
 } 

/*
 *  Function: latch_out
 *  Description: This function latches on/off sequence to the output register, but
 * 		does not modify the OE' pin on the LED drivers, it also latches configuration 
 * 		code to the configuration code register.  Function should be executed
 * 		in normal mode or special mode to write 16 bit configuration code.
 * 	Note: When latching a config code bits 15-8 are the low config code bits (0-8)
 *  Paramters: Each driver recieves a 16 bit sequence outlining the channels
 * 		that will be on/off.
 *  Return: 
 */
 void latch_out(unsigned int seq_R, unsigned int seq_G, unsigned int seq_B)
 {
 	 initialize_SPI();
 	 
 	 _delay_cycles(10);						   // turn LEDs off while writing
 	 while (!(IFG2 & UCB0TXIFG));              // USCI_B0 TX buffer ready
 	 UCB0TXBUF = (unsigned char)seq_B;				   // TX OUT15 - OUT8 intructions for
 	 while (!(IFG2 & UCB0TXIFG)); 			   // Green LED driver.  Lsb first.          
 	 UCB0TXBUF = (unsigned char)(seq_B >> 8);		   // Transmit OUT7-OUT0
 	 
 	 while (!(IFG2 & UCB0TXIFG));             
 	 UCB0TXBUF = (unsigned char)seq_G;
 	 while (!(IFG2 & UCB0TXIFG));             
 	 UCB0TXBUF = (unsigned char)(seq_G >> 8);
 	 
 	 while (!(IFG2 & UCB0TXIFG));              
 	 UCB0TXBUF = (unsigned char)seq_R;
 	 while (!(IFG2 & UCB0TXIFG));            
 	 UCB0TXBUF = (unsigned char)(seq_R >> 8);
 	 while (!(IFG2 & UCB0TXIFG)); 
 	 
 	 P1OUT |= BIT4;								// Pulse LE to store to output 
 	 _delay_cycles(1);							//	register.
 	 P1OUT &= ~BIT4;
 }
 
 /*
 *  Function: pwm_config
 *  Description: Utilizes timerA to produce a pwm output on the OE' pin
 * 		of the LED driver.  To be utilized in normal mode to choose dimm on
 * 		LEDs and to turn them on.
 *  Paramters: User provided 1/2 duty cycle and 1/2 period that the 
 * 		LEDs are to be turned off. 1/2 period must be less than or equal to 0xFFFF clock
 * 		cycles.  
 *  Return: none
 */
 void pwm_config( unsigned int half_duty, unsigned int half_period)
 {
  P2DIR |= BIT1;							//configure p1.2 for pwm out to OE'                            
  P2SEL |= BIT1;

  TA1CCR0 = half_period;        
  TA1CCTL1 = OUTMOD_6;                      // CCR1 toggle/set
  TA1CCR1 = half_period - half_duty;        // CCR1 PWM duty cycle
  TA1CTL = TASSEL_2 + MC_3;                 // SMCLK, up-down mode
 }
 
/*
 *  Function: switch_mode
 *  Description: MCU communication via LE and OE' pins of TLC5927 to command the devices
 * 		to engage in mode switching between special mode and normal mode.
 *  Parameters: mode = 0 to switch from normal to special mode, and mode = 1 to switch
 * 		from special mode to normal mode.
 *  Return: none
 */
 void switch_mode(unsigned char mode)
 {
 	/* period 1 */
 	P1OUT &= ~LE;
 	P2OUT |= OE;							 // Set OE' as high and set
 	P1SEL &= ~SCLK;						  	 // SCLK as regular output
 	P1SEL2 &= ~SCLK;						 	 // low slave clock
 	P1OUT &= ~SCLK;
 	P1DIR |= SCLK;
 	_delay_cycles(1);						 
 	
 	P1OUT |= SCLK;							 // high slave clock
 	_delay_cycles(1);
 	
 	/* period 2 */
 	P2OUT &= ~OE;
 	P1OUT &= ~SCLK;  							 // low slave clock
 	_delay_cycles(1);
 	
 	P1OUT |= SCLK;							 // high slave clock
 	_delay_cycles(1);
 	
 	/* period 3 */
 	P2OUT |= OE;
 	P1OUT &= ~SCLK;
 	_delay_cycles(1);
 	
 	P1OUT |= SCLK;
 	_delay_cycles(1);
 	
 	/* period 4 */
 	if (mode == 0)
 		P1OUT |= LE;
 	P1OUT &= ~SCLK;  						
 	_delay_cycles(1);
 	
 	P1OUT |= SCLK;							
 	_delay_cycles(1);
 	
 	/* period 5 */
 	if (mode == 0)							  // switch to special/normal
 		P1OUT &= ~LE;
 	P1OUT &= ~SCLK;
 	_delay_cycles(1);
 	
 	P1OUT |= SCLK;
 	_delay_cycles(1);
 	
 	P1OUT &= ~SCLK;							  // reset slave clk
 	_delay_cycles(4);		
 }
 /**********************************************************************************/