/*_____ I N C L U D E S _________________________________*/
#include "i2c.h"

/*_____ F U N C T I O N S _______________________________*/

/*_____ D E F I N I T I O N S ___________________________*/
typedef struct{
	uint8_t*	bptrTx_StartAddress;
	uint8_t*	bptrTx_EndAddress;	
	uint8_t*  bptrTx_Buf;	
  uint8_t 	fTx_Done:1;	
  uint8_t 	fTx_Stop:1;	
}I2C_TxHardware_st;

typedef struct{
	uint8_t* 	bptrRx_StartAddress;
	uint8_t* 	bptrRx_EndAddress;	
	uint8_t*	bptrRx_Buf;	
  uint8_t 	fRx_Done:1;	
  uint8_t 	fRx_Stop:1;	
}I2C_RxHardware_st;

typedef struct{
	uint8_t	  bMateAddress;	
  uint8_t 	fTxRxSel:1;	
}I2C_MasterHardware_st;

/*_____ D E C L A R A T I O N S _________________________*/
I2C_MasterHardware_st stI2C_MasterHardware;
I2C_TxHardware_st stI2C_TxHardware;
I2C_RxHardware_st stI2C_RxHardware;

/*_____ M A C R O S _____________________________________*/


/**
 *	@Brief		Initialize GPIO status
 *	@param		None
 *	@retval		None
 */
void GPIO_Initialization(void)
{
	/*	Set whole IOs to output low	*/
	IO_PORT0_OUTPUT_LOW
	IO_PORT1_OUTPUT_LOW
	IO_PORT2_OUTPUT_LOW
	IO_PORT3_OUTPUT_LOW
}

/**
 *	@Brief		Initializes and configures the I2C function	
              including pin assignment, clock, address, intterupt 
 *	@param		uint8_t bMuxNum, uint8_t bClockDiv, uint8_t bSelfAddress, uint8_t bMateAddress
 *	@retval		None
 */
void I2C_ConfigureRegister(uint8_t bMuxNum, uint8_t bClockDiv, uint8_t bSelfAddress, uint8_t bMateAddress)
{
  /*	Configure the I2C's IO status and register */
  if(bMuxNum==0)
  {
    I2C_PIN_SET_INPUT_0
  }
  else
  {
    I2C_PIN_SET_INPUT_1
  }
	I2CCON = mskI2CCON_I2C_ENABLE | bClockDiv;
  
  /* Cofigure the master address and slave address */
	I2CADR = (bSelfAddress<<1); 
  stI2C_MasterHardware.bMateAddress = bMateAddress;
  
  /*	Enable I2C interrupt  */
  IEN1 = mskIEN1_INT_I2C;
}

/**
 *	@Brief		Initialize the master parameter and assign data buffer
 *	@param		bit fTxRxSel, uint8_t *bptrDataBuf, uint16_t dbLength
 *	@retval		None
 */
void I2C_InitMaster(bit fTxRxSel, uint8_t *bptrDataBuf, uint16_t dbLength)
{ 
  if(fTxRxSel == I2C_TX_SEL)
  {
    stI2C_TxHardware.bptrTx_StartAddress = bptrDataBuf;
    stI2C_TxHardware.bptrTx_EndAddress = bptrDataBuf + (dbLength-1);
    stI2C_TxHardware.bptrTx_Buf = stI2C_TxHardware.bptrTx_StartAddress;
    stI2C_MasterHardware.fTxRxSel = 0; 
  }
  else
  {
    stI2C_RxHardware.bptrRx_StartAddress = bptrDataBuf;
    stI2C_RxHardware.bptrRx_EndAddress = bptrDataBuf + (dbLength-1);
    stI2C_RxHardware.bptrRx_Buf = stI2C_RxHardware.bptrRx_StartAddress;
    stI2C_MasterHardware.fTxRxSel = 1; 
  }
  
  I2CCON |= mskI2CCON_I2C_STA;
  I2CCON &= ~mskI2CCON_I2C_AA; 
}

/**
 *	@Brief		Initialize the slave parameter and assign data buffer
 *	@param		bit fTxRxSel, uint8_t *bptrDataBuf, uint16_t dbLength
 *	@retval		None
 */
void I2C_InitSlave(bit fTxRxSel, uint8_t *bptrDataBuf, uint16_t dbLength)
{  
  if(fTxRxSel == I2C_TX_SEL)
  {
    stI2C_TxHardware.bptrTx_StartAddress = bptrDataBuf;
    stI2C_TxHardware.bptrTx_EndAddress = bptrDataBuf + (dbLength-1);
    stI2C_TxHardware.bptrTx_Buf = stI2C_TxHardware.bptrTx_StartAddress;
    stI2C_MasterHardware.fTxRxSel = 0; 
  }
  else
  {  
    stI2C_RxHardware.bptrRx_StartAddress = bptrDataBuf;
    stI2C_RxHardware.bptrRx_EndAddress = bptrDataBuf + (dbLength-1);
    stI2C_RxHardware.bptrRx_Buf = stI2C_RxHardware.bptrRx_StartAddress;
    stI2C_MasterHardware.fTxRxSel = 1; 
  }
  
  I2CCON &= ~mskI2CCON_I2C_STA;
  I2CCON |= mskI2CCON_I2C_AA;
}

/**
 *	@Brief		Repeat start condition for data transmission
 *	@param		None
 *	@retval		None
 */
void I2C_RepeatMaster(void)
{ 
  if(stI2C_MasterHardware.fTxRxSel == I2C_TX_SEL)
  {
    stI2C_TxHardware.bptrTx_Buf = stI2C_TxHardware.bptrTx_StartAddress;
  }
  else
  {
    stI2C_RxHardware.bptrRx_Buf = stI2C_RxHardware.bptrRx_StartAddress;
  }
  
  I2CCON |= mskI2CCON_I2C_STA;
  I2CCON &= ~mskI2CCON_I2C_AA; 
}

/**
 *	@Brief		Repeat 
 *	@param		None
 *	@retval		None
 */
void I2C_RepeatSlave(void)
{ 
  if(stI2C_MasterHardware.fTxRxSel == I2C_TX_SEL)
  {
    stI2C_TxHardware.bptrTx_Buf = stI2C_TxHardware.bptrTx_StartAddress;
  }
  else
  {
    stI2C_RxHardware.bptrRx_Buf = stI2C_RxHardware.bptrRx_StartAddress;
  }
 
  I2CCON &= ~mskI2CCON_I2C_STA;
  I2CCON |= mskI2CCON_I2C_AA;
}

/**
 *	@Brief		Return flag for user check transmission finish
 *	@param		None
 *	@retval		fDoneFlag
 */
bit I2C_CheckTransDataDone(void)
{
  bit fDoneFlag=0;
  if(stI2C_TxHardware.fTx_Done)
  { 
    fDoneFlag = stI2C_TxHardware.fTx_Done;
    stI2C_TxHardware.fTx_Done = 0;
  }
  return fDoneFlag;
}

/**
 *	@Brief		Return flag for user check data NACK as transmitter
 *	@param		None
 *	@retval		fNACKFlag
 */
bit I2C_CheckTransDataStop(void)
{
  bit fNACKFlag=0;
  if(stI2C_TxHardware.fTx_Stop)
  { 
    fNACKFlag = stI2C_TxHardware.fTx_Stop;
    stI2C_TxHardware.fTx_Stop = 0;
  }
  return fNACKFlag;
}

/**
 *	@Brief		Return flag for user check recive finish
 *	@param		None
 *	@retval		fDoneFlag
 */
bit I2C_CheckReciDataDone(void)
{
  bit fDoneFlag=0;
  if(stI2C_RxHardware.fRx_Done)
  { 
    fDoneFlag = stI2C_RxHardware.fRx_Done;
    stI2C_RxHardware.fRx_Done = 0;
  }
  return fDoneFlag;
}

/**
 *	@Brief		Return flag for user check data NACK as reciver
 *	@param		None
 *	@retval		fNACKFlag
 */
bit I2C_CheckReciDataStop(void)
{
  bit fNACKFlag=0;
  if(stI2C_RxHardware.fRx_Stop)
  { 
    fNACKFlag = stI2C_RxHardware.fRx_Stop;
    stI2C_RxHardware.fRx_Stop = 0;
  }
  return fNACKFlag;
}

/**
 *	@Brief		Run I2C Statemachine to keep transmission
 *	@param		None
 *	@retval		None
 */
void I2C_ExecuteTransmission(void)
{
  switch(I2CSTA) 
  {
    /*  Master TX  */
    /*	A I2C Start condition has been transmitted	*/
		case 0x08:  
			I2CCON &= ~mskI2CCON_I2C_STO;
      I2CDAT = (stI2C_MasterHardware.bMateAddress<<1) | stI2C_MasterHardware.fTxRxSel;
    break;
    case 0x10:    
      I2CCON &= ~mskI2CCON_I2C_STO;
      I2CDAT = (stI2C_MasterHardware.bMateAddress<<1) | stI2C_MasterHardware.fTxRxSel;
    break;  
		
		/*	Master TX mode status 
				Step 1: Slave address and write bit has been transmitted
				Step 2: Data byte in I2CDAT	has been transmitted 
				Step 3: Stop data transmittion after last data	*/	
    case 0x18:			/*	after addr_ACK, write I2CDAT to transmitte first data	*/      
      I2CCON &= ~mskI2CCON_I2C_STA;
      I2CDAT = *stI2C_TxHardware.bptrTx_Buf++;    
      if(stI2C_TxHardware.bptrTx_Buf > (stI2C_TxHardware.bptrTx_EndAddress+1))
      {
        I2CCON |= mskI2CCON_I2C_STO;
        stI2C_TxHardware.fTx_Done = 1;
      }
    break;
    case 0x20:			/*	after addr_NACK, write STO=1 to stop transmittion sequence	*/ 
      I2CCON &= ~mskI2CCON_I2C_STA;
      I2CCON |= mskI2CCON_I2C_STO;
      stI2C_TxHardware.fTx_Stop = 1;
    break; 
    case 0x28:			/*	after data_ACK, write I2CDAT to transmitte new data	*/  
      I2CDAT = *stI2C_TxHardware.bptrTx_Buf++;
      if(stI2C_TxHardware.bptrTx_Buf > (stI2C_TxHardware.bptrTx_EndAddress+1))
      {
        I2CCON |= mskI2CCON_I2C_STO;
        stI2C_TxHardware.fTx_Done = 1;
      }
    break; 
    case 0x30:			/*	after data_NACK, when data NACK has been recevie, write STO=1 to stop transmittion sequence	*/ 
			I2CCON |= mskI2CCON_I2C_STO;
      stI2C_TxHardware.fTx_Stop = 1;
    break;
    
		/*	Master RX mode status 
				Step 1: Slave address and read bit has been transmitted
				Step 2: Data byte has been recived, Read data from I2CDAT
				Step 3: Stop to recive data after last data	*/	  
    case 0x40:			/*	after addr_ACK, ready to recive data  */ 
      I2CCON &= ~mskI2CCON_I2C_STA;
      I2CCON |= mskI2CCON_I2C_AA;      
    break; 
    case 0x48:      /*  after addr_NACK, repeat start condition */
      I2CCON |= mskI2CCON_I2C_STA;
      I2CCON |= mskI2CCON_I2C_STO; 
    break; 
    case 0x50:      /*  after data_ACK,  get data from I2CDAT */
      *stI2C_RxHardware.bptrRx_Buf++ = I2CDAT;
      if(stI2C_RxHardware.bptrRx_Buf >= stI2C_RxHardware.bptrRx_EndAddress)
      {
        I2CCON &= ~mskI2CCON_I2C_AA;
      }
    break; 
    case 0x58:      /*  after data_NACK, stop transmission */
      *stI2C_RxHardware.bptrRx_Buf++ = I2CDAT;
      I2CCON &= ~mskI2CCON_I2C_AA;
      I2CCON |= mskI2CCON_I2C_STO;
      stI2C_RxHardware.fRx_Done = 1;
    break; 
    
    /*	Slave RX mode status 
				Step 1: Match address
				Step 2: Data byte has been recived, Read data from I2CDAT
				Step 3: Stop to recive data	after last data */	  
    case 0x60:			/*	after addr_ACK, ready to recive data	*/ 
      if(stI2C_RxHardware.bptrRx_Buf >= stI2C_RxHardware.bptrRx_EndAddress)
      {
        I2CCON &= ~mskI2CCON_I2C_AA;
      }
      else
      {
        I2CCON |= mskI2CCON_I2C_AA;
      }
    break; 
    case 0x80:      /*  after data_ACK,  get data from I2CDAT  */
      *stI2C_RxHardware.bptrRx_Buf++ = I2CDAT;
      if(stI2C_RxHardware.bptrRx_Buf >= stI2C_RxHardware.bptrRx_EndAddress)
      {
        I2CCON &= ~mskI2CCON_I2C_AA;
      }
      else
      {
        I2CCON |= mskI2CCON_I2C_AA;
      }
    break; 
    case 0x88:       /*  after data_NACK,  get data from I2CDAT  */
      *stI2C_RxHardware.bptrRx_Buf++ = I2CDAT;
      I2CCON &= ~mskI2CCON_I2C_STA;
      I2CCON &= ~mskI2CCON_I2C_AA;
      stI2C_RxHardware.fRx_Done = 1;
    break; 
     case 0xA0:      /*  after stop/re-start condition, swtich to not addressed slave mode */
      I2CCON &= ~mskI2CCON_I2C_STA;
      I2CCON &= ~mskI2CCON_I2C_AA;
      stI2C_RxHardware.fRx_Stop = 1;     
    break;         
        
		/*	Slave TX mode status 
				Step 1: Match address
				Step 2: Data byte in I2CDAT	has been transmitted 
				Step 3: Stop data transmittion after last data	*/		
    case 0xA8:			/*	after addr_ACK, write I2CDAT to transmitte first data	*/         
      I2CDAT = *stI2C_TxHardware.bptrTx_Buf++;
      if(stI2C_TxHardware.bptrTx_Buf > stI2C_TxHardware.bptrTx_EndAddress)
      {
        I2CCON &= ~mskI2CCON_I2C_AA;
      }
      else
      {
        I2CCON |= mskI2CCON_I2C_AA;
      }   
    break;
    case 0xB0:			/*	after arbitration lost and addr ACK, write I2CDAT to transmitte first data	*/ 
      I2CDAT = *stI2C_TxHardware.bptrTx_Buf++;
      if(stI2C_TxHardware.bptrTx_Buf > stI2C_TxHardware.bptrTx_EndAddress)
      {
        I2CCON &= ~mskI2CCON_I2C_AA;
      }
      else
      {
        I2CCON |= mskI2CCON_I2C_AA;
      }
    break; 
    case 0xB8:			/*	after data_ACK, write I2CDAT to transmitte new data	*/  
      I2CDAT = *stI2C_TxHardware.bptrTx_Buf++;
      if(stI2C_TxHardware.bptrTx_Buf > stI2C_TxHardware.bptrTx_EndAddress)
      {
        I2CCON &= ~mskI2CCON_I2C_AA;
      }
      else
      {
        I2CCON |= mskI2CCON_I2C_AA;
      }
    break; 
    case 0xC0:			/*	after data_NACK, swtich to not addressed slave mode	*/ 
      I2CCON &= ~mskI2CCON_I2C_AA;
      stI2C_TxHardware.fTx_Stop = 1;
    break;   
    case 0xC8:		  /*	after last data_ACK, swtich to not addressed slave mode	*/   
      I2CCON &= ~mskI2CCON_I2C_AA;
      stI2C_TxHardware.fTx_Done = 1;
    break;       
		
    default:				
			I2CCON &= ~mskI2CCON_I2C_STA;
      I2CCON |= mskI2CCON_I2C_STO;
		break;
  }
  
	/*	Clear interrupt flag by software is necessary for transmittion	*/
  I2CCON &= ~mskI2CCON_I2C_FLAG;
}

/**
 *	@Brief		ISR_Adc
 *						ADC interrupt service routine
 */
void ISR_I2c(void) interrupt ISRI2c
{		
	I2C_ExecuteTransmission();
}
