/*
 * i2c.h
 *
 *  Created on: Oct 5, 2012
 *      Author: VaZso
 */
#include "msp430.h"

unsigned char rxdatacount;
unsigned char *rxptr;

//Initialize I2C and set device to 'addr' address
void i2c_init(unsigned char addr){
    P1SEL |= BIT6 + BIT7;                     // I2C pin
    P1SEL2|= BIT6 + BIT7;                     // I2C pin
    UCB0CTL1 |= UCSWRST;                      // Enable SW reset
    UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC;     // Select I2C mode, I2C Master, sync mode
    UCB0CTL1 = UCSSEL_2 + UCSWRST;            // SMCLK + SW reset
    UCB0BR0 = 10;                             // SMCLK/10 = 100kHz, I2C frequency
    UCB0BR1 = 0;
    UCB0I2CSA = addr;                         // Slave address
    UCB0CTL1 &= ~UCSWRST;                     // Disable reset
    IE2 |= UCB0RXIE;                          // Enable RX interrupt
}

//Transmit array 'data' of 'count' characters
void i2c_transmit(unsigned char *data, unsigned char count){
    UCB0CTL1 |= UCTR + UCTXSTT;            // I2C TX, start condition
    while (!(IFG2 & UCB0TXIFG));
    while(count--){
   	 UCB0TXBUF = *data++;              // Data to tx buffer, then point to next element of array
     while (!(IFG2 & UCB0TXIFG));	   // Wait until TXBUF has been sent
    }
	 UCB0CTL1 |= UCTXSTP;              // I2C stop condition
	 while (UCB0CTL1 & UCTXSTP);       // Wait, while I2C bus is not stopped
}

//Receive a maximum of 'count' characters to array 'data'
//Works in interrupt, CPU sleeps until receive finishes.
void i2c_receive(unsigned char *data, unsigned char count){
    rxdatacount=count;				// Copy RX counter to be able to use it in ISR
    rxptr=data;					// rxptr pointer to array 'data' defined in function's definition
    UCB0CTL1 &= ~UCTR;				// I2C RX
    UCB0CTL1 |= UCTXNACK + UCTXSTT;		// I2C start condition, NACK on end receive
    __bis_SR_register(CPUOFF + GIE);		// Enter LPM0
}

//------------------------------------------------------------------------------
// unsigned char i2c_slave_present(unsigned char slave_address)
//
// This function is used to look for a slave address on the I2C bus.
//
// IN:   unsigned char slave_address  =>  Slave Address
// OUT:  unsigned char                =>  0: address was not found,
//                                        1: address found
//------------------------------------------------------------------------------
unsigned char i2c_slave_present(unsigned char slave_address){
  unsigned char ie2_bak, slaveadr_bak, ucb0i2cie, returnValue;
  ucb0i2cie = UCB0I2CIE;                      // restore old UCB0I2CIE
  ie2_bak = IE2;                              // store IE2 register
  slaveadr_bak = UCB0I2CSA;                   // store old slave address
  UCB0I2CIE &= ~ UCNACKIE;                    // no NACK interrupt
  UCB0I2CSA = slave_address;                  // set slave address
  IE2 &= ~(UCB0TXIE + UCB0RXIE);              // no RX or TX interrupts
  __disable_interrupt();
  UCB0CTL1 |= UCTR + UCTXSTT + UCTXSTP;       // I2C TX, start condition
  while (UCB0CTL1 & UCTXSTP);                 // wait for STOP condition

  returnValue = !(UCB0STAT & UCNACKIFG);
  __enable_interrupt();
  IE2 = ie2_bak;                              // restore IE2
  UCB0I2CSA = slaveadr_bak;                   // restore old slave address
  UCB0I2CIE = ucb0i2cie;                      // restore old UCB0CTL1
  return returnValue;                         // return whether or not
                                              // a NACK occured
}

// -------------------------------------------------------------------------------------------------

#pragma vector = USCIAB0TX_VECTOR
__interrupt void USCIAB0TX_ISR(void){
	if (UCB0STAT & UCNACKIFG){            // send STOP if slave sends NACK and exit LPM
		rxdatacount=0;
	}

	if(rxdatacount > 0){           		// If tx data counter is greater than 0
		*rxptr++ = UCB0RXBUF;      	// Received RX data to array rxptr, then point to next element of rxptr
		rxdatacount--;             	// Decrease data counter by 1
	}
	else if(rxdatacount == 0){             		// If data counter is 0 --> stop I2C
		UCB0CTL1 |= UCTXSTP;			// I2C stop condition
		while (UCB0CTL1 & UCTXSTP);		// Wait while not stopped
		IFG2 &= ~UCB0RXIFG;			// Clear RX flag
		__bic_SR_register_on_exit(CPUOFF);	// Exit LPM
	}
}
