/*******************************************************************************
* Name: main.c - Wide Vin Battery Charger Control Board Software Demo
* 
* Description: Demo software which configures the MSP430F5510 to communicate 
* with the bq20zxx fuel gauge to determine the charging voltage/current 
* requirements. Based on these parameters, the PWM output duty cycles are 
* adjusted and fed to the TPSxxx charger board to charge the battery. Use either 
* the eZ430 Spy-by-Wire Interface or the full JTAG Interface to program the 
* 'F5510 controller board. 
* 
* A. Joshi
* Texas Instruments, Inc
* 02/2011
* Built with CCS Version: 4.2.1 (MSP430 Code Generation Tools v3.3.3) and 
* IAR Embedded Workbench Version: 5.10.1
* 
* Version: 1.0
*******************************************************************************/

/*
 * Copyright (C) {2011} Texas Instruments Incorporated - http://www.ti.com/ 
 * 
 * 
 *  Redistribution and use in source and binary forms, with or without 
 *  modification, are permitted provided that the following conditions 
 *  are met:
 *
 *    Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer.
 *
 *    Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the 
 *    documentation and/or other materials provided with the   
 *    distribution.
 *
 *    Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
*/

// Definition Declarations

//Uncomment this line for Rev. D; Comment this line out for Rev. C hardware
#define REVISION_D					

#define CHARGING_VOLTAGE_HIGH_LIMIT_PERCENT		10		// Number expressed as percentage
#define CHARGING_CURRENT_HIGH_LIMIT_PERCENT		5		// Number expressed as percentage
#define LED_STATUS_CHARGING						0		// Indicate Charging in progress
#define LED_STATUS_FULLY_CHARGED				1		// Indicate battery fully charged
#define LED_STATUS_ERROR						2		// Indicate an error condition
#define LED_STATUS_BLINK_ALL					3		// Blink all status indicator LEDs

// Include Standard Library Headers
#include <intrinsics.h>
#include <stdlib.h>
#include <string.h>

// Include Project Library Headers
#include "device.h"
#include "init.h"
#include "led.h"
#include "pwm.h"
#include "smbus.h"
#include "misc.h"
#include "demo.h"

// Structure to hold the battery parameters accessed via the SMBus, ADC,
// other peripherals, etc.
typedef struct
{
	unsigned char Channel;
    // SMBus Charging Voltage Values
	unsigned int SMBusChargingVoltage[NUM_CHECKING_LOOPS];	 
    // SMBus Voltage Values
	unsigned int SMBusVoltage[NUM_CHECKING_LOOPS];	 
    // SMBus Current Values
	int SMBusCurrent[NUM_CHECKING_LOOPS];	
    // SMBus Temperature Values
	unsigned int SMBusTemperature[NUM_CHECKING_LOOPS];	
    // SMBus Charging Current Values
	unsigned int SMBusChargingCurrent[NUM_CHECKING_LOOPS];	
	// SMBus Battery Status Register
	unsigned int SMBusBatteryStatus[NUM_CHECKING_LOOPS];
	// SMBus RelativeStateOfCharge Percentage Value (0 - 100)%
	unsigned int SMBusRelativeStateOfCharge[NUM_CHECKING_LOOPS];
	// ADC Battery Voltage Conversion Value
	unsigned int ADCVoltage;
	// ADC Battery Current Conversion Value
	unsigned int ADCCurrent; 
	// Fully Charged Indicator LED
	unsigned char LEDChargedFullyIndicator;
	// Charging-In-Progress Indicator LEDs
	unsigned char LEDChargingProgressIndicator[2];
	// Alarm Failure LED Indicator
	unsigned char LEDAlarmIndicator;
} BatteryBlock;

// Declare the number of structures as to the number of batteries declared
BatteryBlock Battery[NUM_BATTERIES];

/* ****************************************************************************
 * Function Name: Timer A0 Interrupt Service Routine
 * 
 * Description: Reset the USCI I2C/SMBus lines, if there is a lockup. 
 * Keep count of TIMER_TICK and control the state of LEDs.  
 * ***************************************************************************/
#pragma vector=TIMER1_A0_VECTOR
__interrupt void TIMER1_A0_ISR(void)
{
  // Reset the SMBus communication lines, IF SMBus communication has been
  // initiated and in progress. SMBus communication can not last for more than
  // 25msec at a stretch.
  SMBus_Reset(SMBus_Start_Flag);
  SMBus_Start_Flag = SMBUS_START_FLAG_RESET;

  LEDTimerISR();							// Update LED Status
  
	// Decrement Universal Tick Counter by one when non-zero
	if(UniversalTickCounter > 0) {
		UniversalTickCounter--;
	}
	if(UniversalTickCounter == 0)
	{ 
		UniversalTickCounter = -1;
		__bic_SR_register_on_exit(LPM0_bits);             // Exit LPM0
	}	
	  
}

/* ****************************************************************************
 * Function Name: I2C/SMBus Interrupt Vector Service Routine (USCI B1)
 * 
 * Description: This ISR configures the MSP430 I2C USCI module to RX & TX
 * SMBus commands & data in both Master and Slave configurations.   
 * ***************************************************************************/
#pragma vector = USCI_B1_VECTOR
__interrupt void USCI_B1_ISR(void)
{
  switch(__even_in_range(UCB1IV, 12))
  {
    case 0:
      break;
    case 2:                                 // ALIFG
      break;
    case 4:                                 // NACKIFG
      UCB1CTL1 |= UCTXSTP;                  // Transmit a stop
      UCB1IFG &= ~UCNACKIFG;                // Clear NACK flag
      __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
      RXFlag = FLAG_NACK;					// Data Operation Failed
      TXFlag = FLAG_NACK;					// Data Operation Failed
      break;
    case 6:                                 // STTIFG
      TA1CTL = TASSEL__ACLK + TACLR + MC__UP;
      SMBus_Start_Flag = SMBUS_START_FLAG_SET;
      break;
    case 8:                                 // STPIFG
	  __bic_SR_register_on_exit(LPM0_bits);  // Exit LPM0
	  RXFlag = FLAG_SUCCESS;				// Set Data Received RX Flag to success    
      break;
    case 10:                                // RXIFG
    
    if (RWFlag == SMBUS_MASTER_MODE_READ)
    {
      RXByteCounter--;                      // Decrement RX byte counter     
      
      if (RXByteCounter)
      {
        *pRXData++= UCB1RXBUF;              // Multiple bytes left to receive
        TA1CTL = TASSEL__ACLK + TACLR + MC__UP;
      }
      else
      {
        *pRXData= UCB1RXBUF;                // Last byte to be received
        __bic_SR_register_on_exit(LPM0_bits);  // Exit LPM0
        RXFlag = FLAG_SUCCESS;				// Set Data Received RX Flag to success
      }

	
      if(RXByteCounter == 1)               // Only one byte left? 
        UCB1CTL1 |= UCTXSTP;                // Generate I2C stop condition
                                            // If reading only 1 byte, stop condition
                                            // should have already been sent
    }
    else if (RWFlag == SMBUS_SLAVE_MODE_WRITE)
    {
        *pRXData++ = UCB1RXBUF;             // Stuff each received byte into array  	
    }
      break; 
    case 12:                                // TXIFG
                                            // Set when UCB1TXBUF is empty
      if (RWFlag == SMBUS_MASTER_MODE_READ) 
      {
	      if (TXByteCounter)                    // If there's something to transmit
	      {
	        UCB1TXBUF = *pTXData++;             // Load TX buffer
	        TXByteCounter--;                    // Decrement TX byte counter
	        RXFlag = FLAG_SUCCESS;				
	      }
	      else                                  // Nothing more to transmit 
	      {                                      
	    	UCB1CTL1 &= ~UCTR;                  // Receiver mode
	        UCB1IFG &= ~UCTXIFG;                // Clear USCI_B0 TX int flag
	        UCB1CTL1 |= UCTXSTT;                // I2C restart (SMBus protocol)
		      if(RXByteCounter == 1) {          // Receiving only one byte?
		      	// Refer to I2C Master Receiver Mode Section in the USCI - I2C Mode
		      	// chapter of the 5xx Family User's Guide (SLAU208G - pg. 607)
		      	while(UCB1CTL1 & UCTXSTT);		// Poll for Start condition sent
		        UCB1CTL1 |= UCTXSTP;            // Generate I2C stop condition
		      } 	        
	      }
      }
      else if (RWFlag == SMBUS_MASTER_MODE_WRITE)
      {
		if (TXByteCounter)						// If there's something to transmit
		{
	        UCB1TXBUF = *pTXData++;             // Load TX buffer
	        TXByteCounter--;                    // Decrement TX byte counter
	        TXFlag = FLAG_SUCCESS;
	    }
	    else
	    {
		    UCB1CTL1 |= UCTXSTP;                  // I2C stop condition
		    UCB1IFG &= ~UCTXIFG;                  // Clear USCI_B0 TX int flag
		    __bic_SR_register_on_exit(LPM0_bits); // Exit LPM0	
		    TXFlag = FLAG_SUCCESS;    	     	      	
	    }
      }
      TA1CTL = TASSEL__ACLK + TACLR + MC__UP;
      break;
    default:
      break;
  }
}

/* ****************************************************************************
 * Function Name: ADC10 interrupt service routine
 * 
 * Description: Clears the flag when the ADC10 conversion is done. 
 * ***************************************************************************/
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void)
{
  switch(__even_in_range(ADC10IV,12))
  {
  case  0: break;                           // Vector  0:  No interrupt
  case  2: break;                           // Vector  2:  ADC overflow
  case  4: break;                           // Vector  4:  ADC timing overflow
  case  6: break;                           // Vector  6:  ADC10HI IFG
  case  8: break;                           // Vector  8:  ADC10LO IFG
  case 10: break;                           // Vector 10:  ADC10IN IFG
  case 12:                            		// Vector 12:  ADC10MEM IFG
  	ADC10IFG &= ~(ADC10IFG0);				// Clear pending interrupt flag
    __bic_SR_register_on_exit(LPM0_bits);	// Exit LPM0
    break;
  default: break; 	
  }
  
}

/* ****************************************************************************
 * Function Name: Initialize_Battery_Definitions
 * 
 * Description: Initialize the battery structures definitions. This function
 * maps the hardware functions such as LEDs, etc to each battery.  
 * ***************************************************************************/
void Initialize_Battery_Definitions(void)
{
	unsigned int battery_index = 0;        			// Keep track of which active battery
	
    // Initialize Battery Channel definitions
	for(battery_index = 0; battery_index < NUM_BATTERIES; battery_index++)
	{
		// Header file defines BATT_1 as 0, BATT_2 as 1, etc. 
		// Modify as appropriately needed
		Battery[battery_index].Channel = battery_index;
	}
	
	// If the compiler directive for Rev. D has been defined, then
	// use the settings for Rev. D hardware.
	#ifdef REVISION_D
	
	// Assign LED Definitions for Rev. D Hardware
	// Battery 1
	Battery[BATT_1].LEDAlarmIndicator = LED_NUM_4;					// Red LED 
	Battery[BATT_1].LEDChargedFullyIndicator = LED_NUM_0;			// Green LED
	Battery[BATT_1].LEDChargingProgressIndicator[0] = LED_NUM_0;	// Green LED
	Battery[BATT_1].LEDChargingProgressIndicator[1] = LED_NUM_2;	// Orange LED
	// Battery 2
	Battery[BATT_2].LEDAlarmIndicator = LED_NUM_5;					// Red LED 
	Battery[BATT_2].LEDChargedFullyIndicator = LED_NUM_1;			// Green LED
	Battery[BATT_2].LEDChargingProgressIndicator[0] = LED_NUM_1;	// Green LED
	Battery[BATT_2].LEDChargingProgressIndicator[1] = LED_NUM_3;	// Orange LED
	
	#else
	// If the compiler directive for Rev. D has NOT been defined, then
	// use the settings for Rev. C hardware instead.

	// Assign LED Definitions for Rev. C Hardware
	// Battery 1
	Battery[BATT_1].LEDAlarmIndicator = LED_NUM_0;					// Green LED 
	Battery[BATT_1].LEDChargedFullyIndicator = LED_NUM_3;			// Green LED
	Battery[BATT_1].LEDChargingProgressIndicator[0] = LED_NUM_3;	// Green LED
	Battery[BATT_1].LEDChargingProgressIndicator[1] = LED_NUM_5;	// Orange LED
	// Battery 2
	Battery[BATT_2].LEDAlarmIndicator = LED_NUM_1;					// Green LED 
	Battery[BATT_2].LEDChargedFullyIndicator = LED_NUM_2;			// Green LED
	Battery[BATT_2].LEDChargingProgressIndicator[0] = LED_NUM_2;	// Green LED
	Battery[BATT_2].LEDChargingProgressIndicator[1] = LED_NUM_4;	// Orange LED	
	
	#endif
}

/* ****************************************************************************
 * Function Name: Get_Battery_Charger_SMBus_Parameters
 * 
 * Description: Get parameters for each battery via SMBus. If there is an 
 * error reading the value from the battery, the parameter is set to zero. 
 * The battery is interrogated multiple times (set by NUM_CHECKING_LOOPS) 
 * and the values between consecutive reads are checked for equality. If the
 * values do not match, the battery is interrogated over and over again, until
 * the values match.    
 * ***************************************************************************/
void Get_Battery_Charger_SMBus_Parameters(unsigned char battery_index)
{
	unsigned char smbus_valid_flag = 0;				// 0 - FALSE, 1 - TRUE
	unsigned char loop_counter = 0;
    unsigned char smbus_access_status = 0;			// Return status from the SMBus functions	

	// This do-while loop will get executed at least once
	// With the MSP430 I2C USCI module configured in Master mode, interrogate 
	// the battery via SMBus for parameters 
	do {
        
		for(loop_counter=0; loop_counter < NUM_CHECKING_LOOPS; loop_counter++)
        {
          // Read Charging Voltage (mV) 
          smbus_access_status = SMBus_Access(SBS_CMD_CHARGING_VOLTAGE, SMBUS_MASTER_MODE_READ, 2);
          if(smbus_access_status == FLAG_SUCCESS)
            Battery[battery_index].SMBusChargingVoltage[loop_counter] = SMBus_Data_From_Slave[1] << 8 | SMBus_Data_From_Slave[0];
          else
            Battery[battery_index].SMBusChargingVoltage[loop_counter] = 0;                  
        	
          // Read Charging Current (mA) 
          smbus_access_status = SMBus_Access(SBS_CMD_CHARGING_CURRENT, SMBUS_MASTER_MODE_READ, 2);	
          if(smbus_access_status == FLAG_SUCCESS)		
            Battery[battery_index].SMBusChargingCurrent[loop_counter] = SMBus_Data_From_Slave[1] << 8 | SMBus_Data_From_Slave[0];
          else
            Battery[battery_index].SMBusChargingCurrent[loop_counter] = 0;

          // Read Voltage (mV)
          smbus_access_status = SMBus_Access(SBS_CMD_VOLTAGE, SMBUS_MASTER_MODE_READ, 2);
          if(smbus_access_status == FLAG_SUCCESS)		
            Battery[battery_index].SMBusVoltage[loop_counter] = SMBus_Data_From_Slave[1] << 8 | SMBus_Data_From_Slave[0];
          else
            Battery[battery_index].SMBusVoltage[loop_counter] = 0;
                  
          // Read Current (mA) 
          smbus_access_status = SMBus_Access(SBS_CMD_CURRENT, SMBUS_MASTER_MODE_READ, 2);
          if(smbus_access_status == FLAG_SUCCESS)	
            Battery[battery_index].SMBusCurrent[loop_counter] = abs(SMBus_Data_From_Slave[1] << 8 | SMBus_Data_From_Slave[0]);
          else
            Battery[battery_index].SMBusCurrent[loop_counter] = 0;
            
          // Read the Relative State of Charge
          smbus_access_status = SMBus_Access(SBS_CMD_RELATIVE_STATE_OF_CHARGE, SMBUS_MASTER_MODE_READ, 1);
          if(smbus_access_status == FLAG_SUCCESS)	
            Battery[battery_index].SMBusRelativeStateOfCharge[loop_counter] = SMBus_Data_From_Slave[0];
          else
            Battery[battery_index].SMBusRelativeStateOfCharge[loop_counter] = 0;                 
          
          // Read Temperature (0.1 K)
          smbus_access_status = SMBus_Access(SBS_CMD_TEMPERATURE, SMBUS_MASTER_MODE_READ, 2);
          if(smbus_access_status == FLAG_SUCCESS)	
            Battery[battery_index].SMBusTemperature[loop_counter] = SMBus_Data_From_Slave[1] << 8 | SMBus_Data_From_Slave[0];
          else
            Battery[battery_index].SMBusTemperature[loop_counter] = 0;

          // Read Battery Status Register
          smbus_access_status = SMBus_Access(SBS_CMD_BATTERY_STATUS, SMBUS_MASTER_MODE_READ, 2);
          if(smbus_access_status == FLAG_SUCCESS)	
            Battery[battery_index].SMBusBatteryStatus[loop_counter] = SMBus_Data_From_Slave[1] << 8 | SMBus_Data_From_Slave[0];
          else
            Battery[battery_index].SMBusBatteryStatus[loop_counter] = 0;               
            
        }                

		// Confirm charging current and charging voltage values match for 
		// all of the readings
		for(loop_counter=0; loop_counter < NUM_CHECKING_LOOPS-1; loop_counter++)
		smbus_valid_flag = (Battery[battery_index].SMBusChargingVoltage[loop_counter] == Battery[battery_index].SMBusChargingVoltage[loop_counter+1]) && \
			(Battery[battery_index].SMBusChargingCurrent[loop_counter] == Battery[battery_index].SMBusChargingCurrent[loop_counter+1]);					                
	                
	} while(!smbus_valid_flag);
		
}

/* ****************************************************************************
 * Function Name: Safety_Checks_Primary_SMBus
 * 
 * Description: Receives parametric values via SMBus protocol and compares 
 * them to high limits. If the limits are exceeded, the safety check flag 
 * is set and returned back to the calling function.   
 * ***************************************************************************/
unsigned char Safety_Checks_Primary_SMBus(unsigned char battery_index)
{
	unsigned char safety_check_flag = FLAG_SUCCESS;	// Safety check flag
	unsigned char loop_counter = 0;
	
	// Run these checks for each set of values read from the battery
	// via the SMBus protocol 
	for(loop_counter = 0; loop_counter < NUM_CHECKING_LOOPS; loop_counter++)
	{
		// If any of the values reported from the battery happen to be set to zero,
		// then there is no battery! And safety status is a failure. 
		if((Battery[battery_index].SMBusVoltage[loop_counter] == 0) && \
			(Battery[battery_index].SMBusChargingCurrent[loop_counter] == 0) && \
			(Battery[battery_index].SMBusChargingVoltage[loop_counter] == 0)) {
			safety_check_flag = FLAG_FAIL; 
		}
		
		// Is Battery Voltage > Desired Charging Voltage * High Limit Percentage 
		{
			// Calculate the charging voltage high limit (percentage calculation)
			unsigned long ChargingVoltageHighLimit = 0;
	
			// Multiply by the percentage number
			ChargingVoltageHighLimit = (unsigned long) Battery[battery_index].SMBusChargingVoltage[loop_counter] * \
									(100 + CHARGING_VOLTAGE_HIGH_LIMIT_PERCENT);
			// Divide by 100
			ChargingVoltageHighLimit /= (unsigned long) 100;	
			
			// Do the comparison 
			if(Battery[battery_index].SMBusVoltage[loop_counter] > ChargingVoltageHighLimit) 
			{
				safety_check_flag = FLAG_FAIL; 
			}
		}
		
		// Is Current > Desired Charging Current * High Limit Percentage
		{
			// Calculate the charging current high limit (percentage calculation)
			unsigned long ChargingCurrentHighLimit = 0;
	
			// Multiply by the percentage number
			ChargingCurrentHighLimit = (unsigned long) Battery[battery_index].SMBusChargingCurrent[loop_counter] * \
									(100 + CHARGING_CURRENT_HIGH_LIMIT_PERCENT);
			// Divide by 100
			ChargingCurrentHighLimit /= (unsigned long) 100;	
			
			// Do the comparison 
			if(Battery[battery_index].SMBusCurrent[loop_counter] > ChargingCurrentHighLimit) {
				// Note SMBus current check is disabled for testing
//				safety_check_flag = FLAG_FAIL;
			}
		}		

		
		// In Battery Status register, are any of the following bits set?
		// OCA, TCA, OTA, FC? Refer to the bq Technical Reference for 
		// detailed explanation on the bits.  
		if((Battery[battery_index].SMBusBatteryStatus[loop_counter] & SBS_REG_BATTERYSTATUS_OCA) || \
			(Battery[battery_index].SMBusBatteryStatus[loop_counter] & SBS_REG_BATTERYSTATUS_TCA) || \
			(Battery[battery_index].SMBusBatteryStatus[loop_counter] & SBS_REG_BATTERYSTATUS_OTA) || \
			(Battery[battery_index].SMBusBatteryStatus[loop_counter] & SBS_REG_BATTERYSTATUS_FC))
		{
			safety_check_flag = FLAG_FAIL; 
		}
		
		// Check Battery Temperature via SMBus
		{
			// Local Variables				
			int Temperature = 0;
			
			// SMBus temperature value reported in 0.1 K
			// Subtract 2730 first to convert to 0.1 C
			Temperature = Battery[battery_index].SMBusTemperature[loop_counter] - 2730;
			// Divide the value by 10 to get value in C
			Temperature /= 10; 					
			
			// Is Temperature > 45 oC?
			if(Temperature > 45) {
				safety_check_flag = FLAG_FAIL; 
			}
		}
		
		// Check the relative state of charge
		// If 100%, then battery is charged
		if(Battery[battery_index].SMBusRelativeStateOfCharge[loop_counter] == 100) {
			safety_check_flag = FLAG_FAIL; 
		}
		
	}	
	
	// Return safety checks status flag back 
	return safety_check_flag;
	
}

/* ****************************************************************************
 * Function Name: Safety_Checks_Secondary_ADC
 * 
 * Description: Uses the on-chip ADC to convert battery voltage and current
 * values and compares them with the desired charging voltage/current. If the 
 * high limit is exceeded, the safety check flag is set and returned back to
 * the calling function. 
 * ***************************************************************************/
unsigned char Safety_Checks_Secondary_ADC(unsigned char battery_index)
{
	unsigned char safety_check_flag = FLAG_SUCCESS;	// Safety check flag
	unsigned char loop_counter = 0;
	
	// Run Voltage ADC conversion
	Battery[battery_index].ADCVoltage = VI_ADC_Read(Battery[battery_index].Channel, \
		CHANNEL_VOLTAGE);
		
	// Run Current ADC conversion
	Battery[battery_index].ADCCurrent = VI_ADC_Read(Battery[battery_index].Channel, \
		CHANNEL_CURRENT);		
	
	// Run these checks for each set of values read from the battery
	// via the SMBus protocol 
	for(loop_counter = 0; loop_counter < NUM_CHECKING_LOOPS; loop_counter++)
	{
		unsigned long SMBusChargingVoltage = 0;
		unsigned long SMBusChargingCurrent = 0;
		
		/* --------------------- VOLTAGE CALCULATIONS ---------------------------------------------------- */
		// SMBus Charging Voltage * High Limit Percentage (Units of mV)
		SMBusChargingVoltage = (unsigned long) Battery[battery_index].SMBusChargingVoltage[loop_counter] * \
								(100 + CHARGING_VOLTAGE_HIGH_LIMIT_PERCENT);
		// Divide by 100
		SMBusChargingVoltage /= (unsigned long) 100;							
	
		// The value is multiplied by 1023 as that is the range for a 10-bit ADC
		SMBusChargingVoltage *= (unsigned long) 1023;			// 10-bit ADC10_A
		
		// The Voltage is multiplied by the resistor divider ratio to bring
		// the voltage down to the 3.3V range. 		
		SMBusChargingVoltage *= (unsigned long) ADC_VOLTAGE_RESISTOR_DIVIDER_NUM;
		SMBusChargingVoltage /= (unsigned long) ADC_VOLTAGE_RESISTOR_DIVIDER_DEN;
		
		// This value is then further divided down by 3.3V to give us the digitized 
		// value that the ADC would give. This allows easier comparison. 
		SMBusChargingVoltage /= (unsigned long) 3.3;			// 3.3V input range
		
		// Divide the SMBus Charging Voltage by 1000 to convert from units of mVolt to Volt
		SMBusChargingVoltage /= (unsigned long) 1000;	
		
		// Is the ADC Conversion Battery Voltage value > Charging Voltage * High Limit Percentage?	
		if(Battery[battery_index].ADCVoltage > SMBusChargingVoltage) {
		// Note ADC voltage check disabled for testing purposes
//			safety_check_flag = FLAG_FAIL;
		}
		/* --------------------- END VOLTAGE CALCULATIONS ------------------------------------------------ */
	
		/* --------------------- CURRENT CALCULATIONS ---------------------------------------------------- */
		// SMBus Charging Current * High Limit Percentage (Units of mA)
		SMBusChargingCurrent = (unsigned long) Battery[battery_index].SMBusChargingCurrent[loop_counter] * \
								(100 + CHARGING_CURRENT_HIGH_LIMIT_PERCENT);
		// Divide by 100
		SMBusChargingCurrent /= (unsigned long) 100;								
	
		// This value is then further divided down by 3.3V to give us the digitized 
		// value that the ADC would give. This allows easier comparison. 
		SMBusChargingCurrent *= (unsigned long) 1023;			// 10-bit ADC10_A
		
		/* In the following two lines of code, the values on right hand side are 
		 * multiplied by 10 to prevent truncation due to integer types
		 */ 
		SMBusChargingCurrent *= (unsigned long) (3.3 * 10);		// Multiply by 3.3 as that is the reference voltage
		SMBusChargingCurrent /= (unsigned long) (3.3 * 10);		// 3.3V input range
		SMBusChargingCurrent /= (unsigned long) 10;				// Divide by 10 as 10 Amps is the full range
		
		// Divide the SMBus Charging Current by 1000 to convert from units of mAmp to Amp
		SMBusChargingCurrent /= 1000;			
		
		// Is the ADC Conversion Battery Current value > Charging Current * High Limit Percentage?	
		if(Battery[battery_index].ADCCurrent > SMBusChargingCurrent) {
		// Note Current ADC check is disabled for testing purposes
//				safety_check_flag = FLAG_FAIL;
		}	
		/* --------------------- END CURRENT CALCULATIONS ------------------------------------------------ */	
	
	}
	
	// Return safety checks status flag back 
	return safety_check_flag;
	
}

/* ****************************************************************************
 * Function Name: Set_LED_Indicator_Status
 * 
 * Description: Set the hardware LEDs to indicate status such as charging,
 * fully charged or error condition. 
 * ***************************************************************************/
void Set_LED_Indicator_Status(unsigned char battery_index, unsigned char status_condition)
{
	switch (status_condition)
	{
		// Indicate Battery Charging in Progress
		case LED_STATUS_CHARGING:
			LED_Control(Battery[battery_index].LEDChargedFullyIndicator, LED_MODE_OFF, LED_BLINK_RATE_SLOW);
			LED_Control(Battery[battery_index].LEDAlarmIndicator, LED_MODE_OFF, LED_BLINK_RATE_SLOW);
			LED_Control(Battery[battery_index].LEDChargingProgressIndicator[0], LED_MODE_ON, LED_BLINK_RATE_SLOW);
			LED_Control(Battery[battery_index].LEDChargingProgressIndicator[1], LED_MODE_ON, LED_BLINK_RATE_SLOW);			
			break;
		
		// Indicate Battery Fully Charged 
		case LED_STATUS_FULLY_CHARGED:
			LED_Control(Battery[battery_index].LEDAlarmIndicator, LED_MODE_OFF, LED_BLINK_RATE_SLOW);
			LED_Control(Battery[battery_index].LEDChargingProgressIndicator[0], LED_MODE_OFF, LED_BLINK_RATE_SLOW);
			LED_Control(Battery[battery_index].LEDChargingProgressIndicator[1], LED_MODE_OFF, LED_BLINK_RATE_SLOW);
			LED_Control(Battery[battery_index].LEDChargedFullyIndicator, LED_MODE_ON, LED_BLINK_RATE_SLOW);		
			break;
		
		// Indicate Battery Charging Error Condition
		case LED_STATUS_ERROR:
			LED_Control(Battery[battery_index].LEDChargedFullyIndicator, LED_MODE_OFF, LED_BLINK_RATE_SLOW);
			LED_Control(Battery[battery_index].LEDAlarmIndicator, LED_MODE_ON, LED_BLINK_RATE_FAST);
			LED_Control(Battery[battery_index].LEDChargingProgressIndicator[0], LED_MODE_OFF, LED_BLINK_RATE_SLOW);
			LED_Control(Battery[battery_index].LEDChargingProgressIndicator[1], LED_MODE_OFF, LED_BLINK_RATE_SLOW);	
			break;
		
		// Blink all Status Indicator LEDs
		case LED_STATUS_BLINK_ALL:
		{
			unsigned int index = 0;
	
			// Program each LED to blink 
			for(index = 0; index < MAX_NUM_LEDS; index++)
			{
				LED_Control(index, LED_MODE_BLINK, LED_BLINK_RATE_MEDIUM);
			}
			break;
		}
		
		// Default Case: Turn off all LEDs			
		default:
		{
			unsigned int index = 0;
	
			// Program each LED to blink 
			for(index = 0; index < MAX_NUM_LEDS; index++)
			{
				LED_Control(index, LED_MODE_OFF, LED_BLINK_RATE_SLOW);
			}
			break;
		}		
	}
}

/* ****************************************************************************
 * Function Name: Battery_Charger_Controller
 * 
 * Description: Battery Charger Controller Function
 * 
 * o Select SMBus Channel
 * o Initialize MSP430 I2C USCI in Master Mode
 * o Initialize Peripherals for the controller board
 * o BCAST = 0; no broadcast allowed from battery
 * o Interrogate Battery via SMBus for Charging Voltage, Current, Temperature
 * and other relevant parameters. 
 * o Perform primary safety checks by checking if the parameters received via
 * SMBus exceed a certain high limit. 
 * o Perform secondary safety checks by checking if the current/voltage values
 * converted from the ADC exceed a certain high limit.
 * o If the limits are not exceeded and the battery is not fully charged, the
 * duty cycle values for Current and Voltage PWMs are set.
 * o If the limits are exceeded or the battery is fully charged or if the 
 * battery does not exist, the PWM outputs are shut off to LOW. 
 * o Status Indicator LEDs are updated to indicate the relevant status of the 
 * software state machine.  
 * ***************************************************************************/
void Battery_Charger_Controller(void)
{
    unsigned char battery_index = 0;        			// Keep track of which active battery		
	unsigned char safety_check_flag = FLAG_SUCCESS;		// Safety check flag  
	
	// Variables for PWM Duty Cycle Fine Tune Adjustment       
	unsigned long CurrentPWMAdjustFactor[NUM_BATTERIES];	
	unsigned int PreviousChargingCurrent[NUM_BATTERIES];
    
    // Initialize the Battery Structures with definitions
	Initialize_Battery_Definitions();
	
	// Initialize each element of the arrays to zero
	for(battery_index = 0; battery_index < NUM_BATTERIES; battery_index++)
	{
		CurrentPWMAdjustFactor[battery_index] = 0;
		PreviousChargingCurrent[battery_index] = 0;	
	}

	// Run forever
	for(;;) {

	// Address each battery one-by-one
	for(battery_index = 0; battery_index < NUM_BATTERIES; battery_index++)
	{
		
		// Select the active SMBus battery
		// This will toggle a GPIO which will route the SMBus lines via a 1-to-2
		// demux to the selected battery
		SMBus_Select(battery_index);
		
        // Initialize the MSP430 in SMBus MASTER Mode 
        SMBus_Initialize(SMBUS_MASTER_MODE);		
		
		// Interrogate the battery via SMBus for parameters such as Voltage,
		// Current, Charging Voltage & Current, Temperature, etc. 
		Get_Battery_Charger_SMBus_Parameters(battery_index);
			
		/*---------------------- SAFETY CHECKS -------------------------------*/
		// Run Primary Safety Checks via SMBus protocol
		safety_check_flag = Safety_Checks_Primary_SMBus(battery_index);
		
		// Run Secondary Safety Checks via the ADC conversions
		safety_check_flag |= Safety_Checks_Secondary_ADC(battery_index);
		
		/*---- Make decisions based on the status of the safety flags -------*/
		// If the safety check has failed...
		if(safety_check_flag == FLAG_FAIL)
		{
			// Turn off PWM outputs for Voltage and Current 
			PWM_Control(Battery[battery_index].Channel, CHANNEL_VOLTAGE, PWM_OFF, PWM_DUTY_0);
			PWM_Control(Battery[battery_index].Channel, CHANNEL_CURRENT, PWM_OFF, PWM_DUTY_0);
			
			// Reset the PWM Duty Cycle Fine Tune Adjust Variables
			CurrentPWMAdjustFactor[battery_index] = 0;
			PreviousChargingCurrent[battery_index] = 0;
			
			// Has the battery been fully charged?
			// Check the FC bit (if the FC toggle mode has been set) or the Relative State
			// of Charge (if it is 100%)
			if((Battery[battery_index].SMBusBatteryStatus[0] & SBS_REG_BATTERYSTATUS_FC) || \
			(Battery[battery_index].SMBusRelativeStateOfCharge[0] == 100))
			{
				// Yes, the battery has been fully charged
				Set_LED_Indicator_Status(battery_index, LED_STATUS_FULLY_CHARGED);
			}
			else {
				// No, Houston we have a problem...
				Set_LED_Indicator_Status(battery_index, LED_STATUS_ERROR);
			}
		}
		else {
			// If the safety checks passed, then set the V/I PWM outputs based on Charging Voltage/Current
			unsigned long VoltagePWM = 0;
			unsigned long CurrentPWM = 0;			
						
			// Multiply the Charging Voltage value by the maximum PWM range value
			VoltagePWM = (unsigned long) Battery[battery_index].SMBusChargingVoltage[0] * PWM_DUTY_100;
			// Divide the Charging Voltage by the Maximum Input Voltage Range 
			VoltagePWM /= (unsigned long) MAX_VOLTAGE_HIGH;
			// Divide by 1000 to convert the value from mVolt to Volt
			VoltagePWM /= (unsigned long) 1000;		
			
			// Set the Voltage PWM output	
			PWM_Control(Battery[battery_index].Channel, CHANNEL_VOLTAGE, PWM_ON, VoltagePWM);
			
			// Multiply the Charging Current value by the maximum PWM range value
			CurrentPWM = (unsigned long) Battery[battery_index].SMBusChargingCurrent[0] * PWM_DUTY_100;
			// Divide the Charging Current by the Maximum Current Range 
			CurrentPWM /= (unsigned long) MAX_CURRENT_HIGH;
			// Divide by 1000 to convert the value from mAmp to Amp
			CurrentPWM /= (unsigned long) 1000;	
			
			// If the Charging Current reported by the battery via SMBus changes, then reset the
			// PWM adjustment factor to zero. 
			if(PreviousChargingCurrent[battery_index] != Battery[battery_index].SMBusChargingCurrent[0])
			{
				CurrentPWMAdjustFactor[battery_index] = 0;
				PreviousChargingCurrent[battery_index] = Battery[battery_index].SMBusChargingCurrent[0];
			}			
			
			// If the current on the battery terminals exceeds what is desired, 
			// then turn down the PWMs a notch				
			if(Battery[battery_index].SMBusCurrent[0] > Battery[battery_index].SMBusChargingCurrent[0])
			{
				CurrentPWMAdjustFactor[battery_index] += 150;
			}
			
			// Fine tune PWM by a certain adjust factor	
			CurrentPWM *= (unsigned long) 100;
			CurrentPWM -= (unsigned long) CurrentPWMAdjustFactor[battery_index];
			CurrentPWM /= (unsigned long) 100;						
				
			// Set the Current PWM output		
			PWM_Control(Battery[battery_index].Channel, CHANNEL_CURRENT, PWM_ON, CurrentPWM);
			
			// Flash the LEDs to indicate battery charging
			Set_LED_Indicator_Status(battery_index, LED_STATUS_CHARGING);
			
		}
		
		// Place the MSP430 in sleep mode for SLEEP_MODE_DURATION
		Delay_Timer(SLEEP_MODE_DURATION);
	
	}		
	
	}
}

/* ****************************************************************************
 * Function Name: main
 * 
 * Description: Initializes the peripherals and then executes the charger 
 * software routine forever. If for some reason, it exits the charger routine
 * and then blinks all LEDs to indicate an error. 
 * ***************************************************************************/
void main(void)
{
	
	WDTCTL = WDTPW + WDTHOLD;		        // Halt watchdog timer
	
	UCS_Init();								// Initialize the clock module
	LED_Init();								// Setup the LEDs to OFF
	Timer_Init();							// Setup the two timers for SMBus timeout + PWM control 
	PWM_Init();								// Setup the PWM outputs to 0% duty cycle
	ADC_Init();								// Initialize the ADC for single-channel, single-conversion
	
	Battery_Charger_Controller();			// Function that will control the battery charger
	
	// If the program executes this function, there's a serious problem
	Set_LED_Indicator_Status(0, LED_STATUS_BLINK_ALL);							
	
	__bis_SR_register(LPM3_bits + GIE);     // Enter LPM3, enable interrupts  					
	
}
