/*****************************************************************************
 *   i2c.c:  I2C functionality test C file for NXP LPC29xx Family 
 *   Microprocessors
 *
 *   Copyright(C) 2008, NXP Semiconductor
 *   All rights reserved.
 *
 *   History
 *   2008.10.21  ver 1.00    Preliminary version
 *
 ****************************************************************************
 * Software that is described herein is for illustrative purposes only
 * which provides customers with programming information regarding the
 * products. This software is supplied "AS IS" without any warranties.
 * NXP Semiconductors assumes no responsibility or liability for the
 * use of the software, conveys no license or title under any patent,
 * copyright, or mask work right to the product. NXP Semiconductors
 * reserves the right to make changes in the software without
 * notification. NXP Semiconductors also make no representation or
 * warranty that such application will be suitable for the specified
 * use without further testing or modification.
 *****************************************************************************/

#include "LPC29xx.h"
#include "type.h"
#include "irq.h"
#include "i2c.h"

volatile BYTE I2C0state;
volatile BYTE I2C1state;

volatile BYTE I2C0Buffer[I2CBUFSIZE];
volatile BYTE I2C1Buffer[I2CBUFSIZE];
volatile BYTE I2C0cntr=0;
volatile BYTE I2C1cntr=0;
volatile BYTE I2C0txPtr=0;
volatile BYTE I2C0txBytes=0;

volatile BYTE read_write = 0;

/*****************************************************************************
** Function name:		I2CInit
**
** Descriptions:		Initialize I2C controller
**
** parameters:			I2c select is I2C0 or I2C1
** Returned value:	true or false, return false if the I2C
**				          interrupt handler was not installed correctly
** 
*****************************************************************************/
DWORD I2CInit (BYTE select, BYTE mode)
{
  
  /* I2C's clock is the PLL with autoblocking enabled and divided by 4 */
  IVNSS_CLK_CONF = CLK_SEL_PLL | AUTOBLK | DIV4;                   
  
  if (select == I2C0)
  {
    /* Setup the IO to be I2C without internal pull up */
    //SFSP1_10 = (1<<2)|(2<<0);
    //SFSP1_11 = (1<<2)|(2<<0);

	SFSP1_10 = (3<<2)|(2<<0);	 // Setup the IO to be I2C with internal pull up 
    SFSP1_11 = (3<<2)|(2<<0);


    /* Clear all flags */
    I2C0CONCLR = I2C_AA | I2C_SI | I2C_STA | I2C_I2EN;
    
    /* Setup bitrate */
    I2C0SCLH = BITRATE_CONF;
    I2C0SCLL = BITRATE_CONF;
    
    /* Enable I2C*/
    I2C0CONSET = I2C_I2EN;
    
    /* Install and enable the I2C0 interrupt handler */
    if (install_irq(I2C0_INT, (void *) I2C0Handler, 0x0F) == FALSE) return (FALSE);
    EnableIntReq_IRQ( I2C0_INT, ACTIVE_HIGH, 0x0F );
    
    /* Set the slave address if slave mode */
    if (mode == I2CSLAVE)
        I2C0ADR = I2CSLAVE_ADDR;
    
    /* Set the I2C0State to IDLE */
    I2C0state = I2C_IDLE;
  }
  else if (select == I2C1)
  {
    /* Setup the IO to be I2C */
    SFSP1_12 = (1<<2)|(2<<0);
    SFSP1_13 = (1<<2)|(2<<0);
    
    /* Clear all flags */
    I2C1CONCLR = I2C_AA | I2C_SI | I2C_STA | I2C_I2EN;
    
    /* Setup bitrate */
    I2C1SCLH = BITRATE_CONF;
    I2C1SCLL = BITRATE_CONF;
    
    /* Enable I2C1 */
    I2C1CONSET = I2C_I2EN;
    
    /* Install and enable the I2C1 interrupt handler */
    if (install_irq(I2C1_INT, (void *) I2C1Handler, 0x0F) == FALSE) return (FALSE);
    EnableIntReq_IRQ( I2C1_INT, ACTIVE_HIGH, 0x0F );
    
    /* Set the slave address if slave mode */
    if (mode == I2CSLAVE)
    {
      /* Set the slave address */  
      I2C1ADR = I2CSLAVE_ADDR;
      /* Enable the Acknowledge on slave address call */
      I2C1CONSET = I2C_AA;
      /* Clear data counter */
      I2C1cntr = 0;
    }
    
    /* Set the I2C0State to IDLE */
    I2C1state = I2C_IDLE;
  }
  else 
  {
    return (FALSE); 
  }
  
  return (TRUE);
}

/*****************************************************************************
** Function name:		I2C0start
**
** Descriptions:		Initiate a I2C0 transmission
**
** parameters:			none
** Returned value:	TRUE = I2C0 transmission is initiated
**				          FALSE = I2C0 transmission could not be initiated
** 
*****************************************************************************/
DWORD I2C0start (BYTE r_w)  // << needs I2C selection!!
{
  DWORD timeout = 0;
  DWORD retVal = FALSE;
 
  /* Inidicate wheter read or write to slave */
  if (r_w == WRITE)
    read_write = WRITE;
  else if (r_w == READ)
    read_write = READ;

  /* Issue a start condition */
  I2C0CONSET |= I2C_STA;	/* Set Start flag */

	/* Set the Tx buffer data pointer back to 0*/  
	I2C0txPtr = 0;

  /* Wait until START transmitted */
  while(1)
  {
    if ( I2C0state == I2C_STARTED )
    {
      retVal = TRUE;
      break;	
    }
    if ( timeout >= MAX_TIMEOUT )
    {
      retVal = FALSE;
      break;
    }
    timeout++;
  }
  return( retVal );  
}

/*****************************************************************************
** Function name:		I2C0Handler
**
** Descriptions:		Handles I2C0 interrupts
**
** parameters:			none
** Returned value:	none
**				          
** 
*****************************************************************************/
void I2C0Handler (void)
{
  BYTE StatValue;
 
  StatValue = I2C0STAT;
  
  switch (StatValue)
  {
  /*--- MASTER TRANSMIT MODE ---*/
  /* A Start condition is issued, indicate STARTED, set SLA+ W or R, clear STA and SI bit */  
  case 0x08:
    I2C0state = I2C_STARTED;
    I2C0DAT = I2CSLAVE_ADDR | read_write;
    I2C0CONCLR = I2C_SI | I2C_STA;
    break;
	/* A Re-start condition is issued */
	case 0x10:
		I2C0state = I2C_STARTED;
		I2C0DAT = I2CSLAVE_ADDR | READ;
		I2C0CONCLR = I2C_SI | I2C_STA;
		break;
  /* The SLA+W has been transmitted, ACK received */
  case 0x18:
    I2C0DAT = I2C0Buffer[0];
    I2C0CONCLR = I2C_SI;
		I2C0txPtr = 1;
    break;  
  /* The SLA+W has been transmitted, NACK received */
	case 0x20:
		/* Stop transmission */
		I2C0CONSET = I2C_STO;
    I2C0state = I2C_IDLE;
    I2C0CONCLR = I2C_SI;
		break;
	/* The data byte has been transmitted, ACK received */
  case 0x28:
		if (I2C0txPtr < I2C0txBytes)
		{
			I2C0DAT = I2C0Buffer[I2C0txPtr];
			I2C0CONCLR = I2C_SI;
			I2C0txPtr++;
		}
		else 
		{
			if (I2C0Buffer[0] == 0x01)
			{
				/* Close transmission if Conf register is written */
				I2C0CONSET = I2C_STO;
				I2C0state = I2C_IDLE;
				I2C0CONCLR = I2C_SI;
			}
			else
			{
				/* Re-start transmission for reading data from device */
				I2C0state = I2C_RESTARTED;
				I2C0CONCLR = I2C_SI | I2C_STA;
			}
		}	
		break;
	/* The data byte has been transmitted, NACK received, give STOP condition */
  case 0x30:
    I2C0CONSET = I2C_STO;
    I2C0state = I2C_IDLE;
    I2C0CONCLR = I2C_SI;
		I2C0txPtr = 0;
		I2C0txBytes = 0;
    break;

  /*--- MASTER RECEIVER MODE ---*/
  /* The SLA+R has been transmitted, regardless ACK of NACK */
  case 0x40:
	  I2C0cntr = 0;
    I2C0CONCLR = I2C_SI | I2C_AA;
    I2C0state = I2C_STARTED;
		break;
  case 0x48:
	 	I2C0CONSET = I2C_STO;
    I2C0state = I2C_IDLE;
    I2C0CONCLR = I2C_SI;
    break;   
  /* The data has been received, ACK given */
  case 0x50:
    I2C0Buffer[I2C0cntr] = I2C0DAT;
    I2C0cntr++;
    I2C0CONCLR = I2C_SI;
    break;
  /* The data has been received, NACK given, give STOP condition */
  case 0x58:
    I2C0Buffer[I2C0cntr] = I2C0DAT;
		I2C0CONSET = I2C_STO;
    I2C0state = I2C_IDLE;
    I2C0CONCLR = I2C_SI;
    break;

  /*--- SLAVE RECEIVER MODE ---*/
  /* The SLA+R has been received, regardless ACK of NACK */
  case 0x60:
  case 0x68:
    I2C0state = I2C_STARTED;
    I2C0CONCLR = I2C_SI;
    break;
  /* The data has been received, ACK given */
  case 0x80:
    I2C0Buffer[I2C0cntr] = I2C0DAT;
    I2C0cntr++;
    I2C0state = DATA_ACK;
    I2C0CONCLR = I2C_SI;
    break;
  /* Stop condition received */
  case 0xA0:
    I2C0cntr = 0;
    I2C0state = I2C_IDLE;
    I2C0CONCLR = I2C_SI;
    break;
    
  /*--- SLAVE TRANSMITTER MODE ---*/
  /* The SLA+R has been received, ACK given */
  case 0xA8:
  case 0xB0:
    I2C0DAT = I2C0Buffer[I2C0cntr];
    I2C0cntr++;
    I2C0CONCLR = I2C_SI;
    break;
  /* The data transmitted and NACK or ACK received */
  case 0xC0:
  case 0xC8:
    I2C0CONCLR = I2C_SI;
    break;
  }
}

/*****************************************************************************
** Function name:		I2C1Handler
**
** Descriptions:		Handles I2C1 interrupts
**
** parameters:			none
** Returned value:	none
**				          
** 
*****************************************************************************/
void I2C1Handler (void)
{
  BYTE StatValue;
 
  StatValue = I2C1STAT;
  
  switch (StatValue)
  {
  /*--- MASTER TRANSMIT MODE ---*/
  /* A Start condition is issued, indicate STARTED, set SLA+W, clear STA and SI bit */  
  case 0x08:
    I2C1state = I2C_STARTED;
    I2C1DAT = I2CSLAVE_ADDR | read_write;
    I2C1CONCLR = I2C_SI | I2C_STA;
    break;
  
  /* The SLA+W has been transmitted, regardless ACK of NACK */
  case 0x18:
  case 0x20:
    I2C1DAT = I2C1Buffer[0];
    I2C1CONCLR = I2C_SI;
    break;
    
  /* The data byte has been transmitted, regardless ACK of NACK */
  case 0x28:
  case 0x30:
    I2C1CONSET = I2C_STO;
    I2C1state = I2C_IDLE;
    I2C1CONCLR = I2C_SI;
    break;

  /*--- MASTER RECEIVER MODE ---*/
  /* The SLA+R has been transmitted, regardless ACK of NACK */
  case 0x40:
  case 0x48:
    I2C1cntr = 0;
    I2C1CONCLR = I2C_SI | I2C_AA;
    I2C1state = I2C_STARTED;
    break;   
  /* The data has been received, ACK given */
  case 0x50:
    I2C1Buffer[I2C1cntr] = I2C1DAT;
    I2C1cntr++;
    I2C1CONCLR = I2C_SI;
    break;
  /* The data has been received, NACK given, give STOP condition */
  case 0x58:
    I2C1CONSET = I2C_STO;
    I2C1state = I2C_IDLE;
    I2C1CONCLR = I2C_SI;
    break;
    
  /*--- SLAVE RECEIVER MODE ---*/
  /* The SLA+R has been received, regardless ACK of NACK */
  case 0x60:
  case 0x68:
    I2C1state = I2C_STARTED;
    I2C1CONCLR = I2C_SI;
    break;
  /* The data has been received, ACK given */
  case 0x80:
    I2C1Buffer[I2C1cntr] = I2C1DAT;
    I2C1cntr++;
    I2C1state = DATA_ACK;
    I2C1CONCLR = I2C_SI;
    break;
  /* Stop condition received */
  case 0xA0:
    I2C1cntr = 0;
    I2C1state = I2C_IDLE;
    I2C1CONCLR = I2C_SI;
    break;
    
  /*--- SLAVE TRANSMITTER MODE ---*/
  /* The SLA+R has been received, ACK given */
  case 0xA8:
  case 0xB0:
    I2C1DAT = I2C1Buffer[I2C1cntr];
    I2C1cntr++;
    I2C1CONCLR = I2C_SI;
    break;
  /* The data transmitted and NACK or ACK received */
  case 0xC0:
  case 0xC8:
    I2C1CONCLR = I2C_SI;
    break;  
  }
}

