/************************************************************************************************************
 SOFTWARE COPYRIGHT NOTICE:

 © [2013] Microchip Technology Inc. and its subsidiaries.
 You may use this software and any derivatives exclusively with Microchip products. 

 THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
 OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT,
 MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE, OR ITS INTERACTION WITH MICROCHIP PRODUCTS,
 COMBINATION WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION. 

 IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
 LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF
 MICROCHIP HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE.  TO THE FULLEST EXTENT
 ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT
 EXCEED THE AMOUNT OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.

 MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE OF THESE TERMS.

************************************************************************************************************/

//********************************************************************************************************************************************************************************
// File Name:       I2C_MCP4728.c
// Description:     I2C code for communicating with external DAC MCP4728
// Microcontroller: dsPIC33FJ128GP802
// Compiler:        Microchip C30 Compiler v3.30
// Author:          Zhang Feng, Medical Products Group, Microchip Technology Inc.
// Date:            1/19/2012
// Version:         1.0 Initial Release
//
// Note: 
//
//********************************************************************************************************************************************************************************

#include "I2C_MCP4728.h"
#include "p33Fxxxx.h"


/*********************************************************************
* Function:     Init_I2C()
*
* Input:		None.
*
* Output:		None.
*
* Overview:		Initialises the I2C(1) peripheral (SDA1 & SCL1 pins)
*
* Note:			Sets up Master mode, No slew rate control, 100Khz
*********************************************************************/
void Init_I2C(void)
{
	//Configre SCA/SDA pin as open-drain
//	ODCBbits.ODCB8=1;
//	ODCBbits.ODCB9=1;

//[Errata work around for I2C]: Must toggle SDA & SCL lines right before enable I2C module in order to generate proper start condition.
	LATBbits.LATB8 = 0;
	LATBbits.LATB9 = 0;
	LATBbits.LATB8 = 1;
	LATBbits.LATB9 = 1;

	//Configure the I2C for Master Mode, No Slew Rate control
	I2C1CON = 0x1200;

	//Set the I2C Baud Rate for Fscl=100KHz, Fcy=40MHz
//	I2C1BRG = 0x188;
	//Set the I2C Baud Rate for Fscl=100KHz, Fcy=10MHz
//	I2C1BRG = 0x060;
	//Set the I2C Baud Rate for Fscl=100KHz, Fcy=20MHz
	I2C1BRG = 0x0C3;

	I2C1ADD = 0;
	I2C1MSK = 0;
	I2C1RCV = 0;
	I2C1TRN = 0;

	//Enable the I2C module
	I2C1CONbits.I2CEN = 1;
}

/**********************************************************************************************
* Function:     StartI2C()
*
* Input:		None.
*
* Output:		None.
*
* Overview:		This function generates an I2C start condition.
*
* Note:			None
***********************************************************************************************/
void StartI2C(void)
{
	I2C1CONbits.SEN = 1;		//Initiate Start ondition on SDA1 & SCL1
	while (I2C1CONbits.SEN);	//Hardware clear at end of master Start sequence
}

/*********************************************************************
* Function:     RestartI2C()
*
* Input:		None.
*
* Output:		None.
*
* Overview:		This function generates an I2C Restart condition.
*
* Note:			None
********************************************************************/
void RestartI2C(void)
{
	I2C1CONbits.RSEN = 1;		//Initiate Repeated Start condition on SDA1 & SCL1
	while (I2C1CONbits.RSEN);	//Hardware clear at end of master Repeated Start sequence	
}

/*********************************************************************
* Function:     StopI2C()
*
* Input:		None.
*
* Output:		None.
*
* Overview:		This function generates an I2C stop condition.
*
* Note:			None
********************************************************************/
void StopI2C(void)
{
	I2C1CONbits.PEN = 1;		//Initiate Stop condition on SDA1 & SCL1
	while (I2C1CONbits.PEN);	//Hardware clear at end of master Stop sequence
}

/*********************************************************************
* Function:     WriteI2C()
*
* Input:		Byte to write.
*
* Output:		None.
*
* Overview:		Writes a byte out to the bus
*
* Note:			None
********************************************************************/
unsigned char WriteI2C(unsigned char byte)
{
	//This function transmits the byte passed to the function
	//while (I2C1STATbits.TRSTAT);	//Wait for bus to be idle
	I2C1TRN = byte;					//Load byte to I2C1 Transmit buffer
	while (I2C1STATbits.TBF);		//wait for data transmission complete
}

/*********************************************************************
* Function:     IdleI2C()
*
* Input:		None.
*
* Output:		None.
*
* Overview:		Waits for bus to become Idle
*
* Note:			None
********************************************************************/
void IdleI2C(void)
{
	while (I2C1STATbits.TRSTAT);		//Wait for bus Idle
}

/*********************************************************************
* Function:     ACKStatus()
*
* Input:		None.
*
* Output:		Acknowledge Status.
*
* Overview:		Return the Acknowledge status on the bus
*
* Note:			None
********************************************************************/
unsigned char ACKStatus(void)
{
	return (!I2C1STATbits.ACKSTAT);		//Return Ack Status, 0=ACK, 1=NACK
}

/*********************************************************************
* Function:     NotAckI2C()
*
* Input:		None.
*
* Output:		None.
*
* Overview:		Generates a NO Acknowledge on the Bus
*
* Note:			None
********************************************************************/
void NotAckI2C(void)
{
	I2C1CONbits.ACKDT = 1;			//Set for NotACk
	I2C1CONbits.ACKEN = 1;
	while(I2C1CONbits.ACKEN);		//wait for ACK to complete
	I2C1CONbits.ACKDT = 0;			//Set for NotACk
}

/*********************************************************************
* Function:     AckI2C()
*
* Input:		None.
*
* Output:		None.
*
* Overview:		Generates an Acknowledge.
*
* Note:			None
********************************************************************/
void AckI2C(void)
{
	I2C1CONbits.ACKDT = 0;			//Set for ACk
	I2C1CONbits.ACKEN = 1;
	while(I2C1CONbits.ACKEN);		//wait for ACK to complete
}

/*********************************************************************
* Function:     MCP47x6FastModeWriteI2C()
*
* Input:		FirstByte, SecondByte_EE, ThirdByte_EE
*
* Output:		None.
*
* Overview:		Write data to DAC input register
*
* Note:			None
********************************************************************/

unsigned int MCP47x6FastModeWriteI2C(unsigned char FirstByte, unsigned char SecondByte_FastMode, unsigned char ThirdByte_FastMode)

//unsigned int MCP47x6FastModeWriteI2C(unsigned char ControlByte, unsigned char LowAdd, unsigned char data)
{
	unsigned char ErrorCode;

	IdleI2C();								//Ensure Module is Idle
	StartI2C();								//Generate Start COndition
	WriteI2C(FirstByte);					//Write Control byte
	IdleI2C();

	ErrorCode = ACKStatus();				//Return ACK Status
	
	WriteI2C(SecondByte_FastMode);			//Write 2nd byte
	IdleI2C();

	ErrorCode = ACKStatus();				//Return ACK Status

	WriteI2C(ThirdByte_FastMode);			//Write 3rd byte
	IdleI2C();
	StopI2C();								//Initiate Stop Condition
//	EEAckPolling(FirstByte);				//Perform ACK polling
//	return;
	return(ErrorCode);
}


/*********************************************************************
* Function:        MCP47x6EEPROMWriteI2C()
*
* Input:		FirstByte, SecondByte_EE, ThirdByte_EE, FourthByte_EE.
*
* Output:		None.
*
* Overview:		Writing both DAC input register and EEPROM 
*
* Note:			None
********************************************************************/

unsigned int MCP47x6EEPROMWriteI2C(unsigned char FirstByte, unsigned char SecondByte_EE, unsigned char ThirdByte_EE, unsigned char FourthByte_EE)

// unsigned int HDByteWriteI2C(unsigned char ControlByte, unsigned char HighAdd, unsigned char LowAdd, unsigned char data)
{
	unsigned char ErrorCode;

	IdleI2C();							//Ensure Module is Idle
	StartI2C();							//Generate Start COndition
	WriteI2C(FirstByte);				//Write Control byte
	IdleI2C();

	ErrorCode = ACKStatus();			//Return ACK Status
	
	WriteI2C(SecondByte_EE);
	IdleI2C();							//Write High Address
	WriteI2C(ThirdByte_EE);				//Write Low Address
	IdleI2C();

	ErrorCode = ACKStatus();			//Return ACK Status

	WriteI2C(FourthByte_EE);			//Write Data
	IdleI2C();
	StopI2C();							//Initiate Stop Condition
//	EEAckPolling(ControlByte);			//perform Ack Polling
	return(ErrorCode);
}


/*********************************************************************
* Function:        LDByteWriteI2C()
*
* Input:		Control Byte, 8 - bit address, data.
*
* Output:		None.
*
* Overview:		Write a byte to low density device at address LowAdd
*
* Note:			None
********************************************************************/
unsigned int LDByteWriteI2C(unsigned char ControlByte, unsigned char LowAdd, unsigned char data)
{
	unsigned char ErrorCode;

	IdleI2C();						//Ensure Module is Idle
	StartI2C();						//Generate Start COndition
	WriteI2C(ControlByte);			//Write Control byte
	IdleI2C();

//	ErrorCode = ACKStatus();		//Return ACK Status
	
	WriteI2C(LowAdd);				//Write Low Address
	IdleI2C();

	ErrorCode = ACKStatus();		//Return ACK Status

	WriteI2C(data);					//Write Data
	IdleI2C();
	StopI2C();						//Initiate Stop Condition
//	EEAckPolling(ControlByte);		//Perform ACK polling
//	return;
	return(ErrorCode);
}


/*********************************************************************
* Function:        LDByteReadI2C()
*
* Input:		Control Byte, Address, *Data, Length.
*
* Output:		None.
*
* Overview:		Performs a low density read of Length bytes and stores in *Data array
*				starting at Address.
*
* Note:			None
********************************************************************/
unsigned int LDByteReadI2C(unsigned char ControlByte, unsigned char Address, unsigned char *Data, unsigned char Length)
{
	IdleI2C();					//wait for bus Idle
	StartI2C();					//Generate Start Condition
	WriteI2C(ControlByte);		//Write Control Byte
	IdleI2C();					//wait for bus Idle
	WriteI2C(Address);			//Write start address
	IdleI2C();					//wait for bus Idle

	RestartI2C();				//Generate restart condition
	WriteI2C(ControlByte | 0x01);	//Write control byte for read
	IdleI2C();					//wait for bus Idle

	getsI2C(Data, Length);		//read Length number of bytes
	NotAckI2C();				//Send Not Ack
	StopI2C();					//Generate Stop
	return(0);
}


/*********************************************************************
* Function:        HDByteWriteI2C()
*
* Input:		ControlByte, HighAddr, LowAddr, Data.
*
* Output:		None.
*
* Overview:		perform a high density byte write of data byte, Data.
*				starting at the address formed from HighAdd and LowAdd
*
* Note:			None
********************************************************************/
unsigned int HDByteWriteI2C(unsigned char ControlByte, unsigned char HighAdd, unsigned char LowAdd, unsigned char data)
{
	unsigned char ErrorCode;

	IdleI2C();						//Ensure Module is Idle
	StartI2C();						//Generate Start COndition
	WriteI2C(ControlByte);			//Write Control byte
	IdleI2C();

	ErrorCode = ACKStatus();		//Return ACK Status
	
	WriteI2C(HighAdd);
	IdleI2C();						//Write High Address
	WriteI2C(LowAdd);				//Write Low Address
	IdleI2C();

	ErrorCode = ACKStatus();		//Return ACK Status

	WriteI2C(data);					//Write Data
	IdleI2C();
	StopI2C();						//Initiate Stop Condition
//	EEAckPolling(ControlByte);		//perform Ack Polling
	return(ErrorCode);
}


/*********************************************************************
* Function:        HDByteReadI2C()
*
* Input:		Control Byte, HighAdd, LowAdd, *Data, Length.
*
* Output:		None.
*
* Overview:		Performs a low density read of Length bytes and stores in *Data array
*				starting at Address formed from HighAdd and LowAdd.
*
* Note:			None
********************************************************************/
unsigned int HDByteReadI2C(unsigned char ControlByte, unsigned char HighAdd, unsigned char LowAdd, unsigned char *Data, unsigned char Length)
{
	IdleI2C();						//Wait for bus Idle
	StartI2C();						//Generate Start condition
	WriteI2C(ControlByte);			//send control byte for write
	IdleI2C();						//Wait for bus Idle

	WriteI2C(HighAdd);				//Send High Address
	IdleI2C();						//Wait for bus Idle
	WriteI2C(LowAdd);				//Send Low Address
	IdleI2C();						//Wait for bus Idle

	RestartI2C();					//Generate Restart
	WriteI2C(ControlByte | 0x01);	//send control byte for Read
	IdleI2C();						//Wait for bus Idle

	getsI2C(Data, Length);			//Read Length number of bytes to Data
	NotAckI2C();					//send Not Ack
	StopI2C();						//Send Stop Condition
	return(0);
}


/*********************************************************************
* Function:        LDPageWriteI2C()
*
* Input:		ControlByte, LowAdd, *wrptr.
*
* Output:		None.
*
* Overview:		Write a page of data from array pointed to be wrptr
*				starting at LowAdd
*
* Note:			LowAdd must start on a page boundary
********************************************************************/
unsigned int LDPageWriteI2C(unsigned char ControlByte, unsigned char LowAdd, unsigned char *wrptr)
{
	IdleI2C();					//wait for bus Idle
	StartI2C();					//Generate Start condition
	WriteI2C(ControlByte);		//send controlbyte for a write
	IdleI2C();					//wait for bus Idle
	WriteI2C(LowAdd);			//send low address
	IdleI2C();					//wait for bus Idle
	putstringI2C(wrptr);		//send data
	IdleI2C();					//wait for bus Idle
	StopI2C();					//Generate Stop
	return(0);
}


/*********************************************************************
* Function:        HDPageWriteI2C()
*
* Input:		ControlByte, HighAdd, LowAdd, *wrptr.
*
* Output:		None.
*
* Overview:		Write a page of data from array pointed to be wrptr
*				starting at address from HighAdd and LowAdd
*
* Note:			Address must start on a page boundary
********************************************************************/
unsigned int HDPageWriteI2C(unsigned char ControlByte, unsigned char HighAdd, unsigned char LowAdd, unsigned char *wrptr)
{
	IdleI2C();				//wait for bus Idle
	StartI2C();				//Generate Start condition
	WriteI2C(ControlByte);	//send controlbyte for a write
	IdleI2C();				//wait for bus Idle
	WriteI2C(HighAdd);		//send High Address
	IdleI2C();				//wait for bus Idle
	WriteI2C(LowAdd);		//send Low Address
	IdleI2C();				//wait for bus Idle
	putstringI2C(wrptr);	//Send data
	IdleI2C();				//wait for bus Idle
	StopI2C();				//Generate a stop
	return(0);
}


/*********************************************************************
* Function:        LDSequentialReadI2C()
*
* Input:		ControlByte, address, *rdptr, length.
*
* Output:		None.
*
* Overview:		Performs a sequential read of length bytes starting at address
*				and places data in array pointed to by *rdptr
*
* Note:			None
********************************************************************/
unsigned int LDSequentialReadI2C(unsigned char ControlByte, unsigned char address, unsigned char *rdptr, unsigned char length)
{
	IdleI2C();						//Ensure Module is Idle
	StartI2C();						//Initiate start condition
	WriteI2C(ControlByte);			//write 1 byte
	IdleI2C();						//Ensure module is Idle
	WriteI2C(address);				//Write word address
	IdleI2C();						//Ensure module is idle
	RestartI2C();					//Generate I2C Restart Condition
	WriteI2C(ControlByte | 0x01);	//Write 1 byte - R/W bit should be 1 for read
	IdleI2C();						//Ensure bus is idle
	getsI2C(rdptr, length);			//Read in multiple bytes
	NotAckI2C();					//Send Not Ack
	StopI2C();						//Send stop condition
	return(0);
}


/*********************************************************************
* Function:        HDSequentialReadI2C()
*
* Input:		ControlByte, HighAdd, LowAdd, *rdptr, length.
*
* Output:		None.
*
* Overview:		Performs a sequential read of length bytes starting at address
*				and places data in array pointed to by *rdptr
*
* Note:			None
********************************************************************/
unsigned int HDSequentialReadI2C(unsigned char ControlByte, unsigned char HighAdd, unsigned char LowAdd, unsigned char *rdptr, unsigned char length)
{
	IdleI2C();						//Ensure Module is Idle
	StartI2C();						//Initiate start condition
	WriteI2C(ControlByte);			//write 1 byte
	IdleI2C();						//Ensure module is Idle
	WriteI2C(HighAdd);				//Write High word address
	IdleI2C();						//Ensure module is idle
	WriteI2C(LowAdd);				//Write Low word address
	IdleI2C();						//Ensure module is idle
	RestartI2C();					//Generate I2C Restart Condition
	WriteI2C(ControlByte | 0x01);	//Write 1 byte - R/W bit should be 1 for read
	IdleI2C();						//Ensure bus is idle
	getsI2C(rdptr, length);			//Read in multiple bytes
	NotAckI2C();					//Send Not Ack
	StopI2C();						//Send stop condition
	return(0);
}

/*********************************************************************
* Function:       getsI2C()
*
* Input:		array pointer, Length.
*
* Output:		None.
*
* Overview:		read Length number of Bytes into array
*
* Note:			None
********************************************************************/
unsigned int getsI2C(unsigned char *rdptr, unsigned char Length)
{
	while (Length --)
	{
		*rdptr++ = getI2C();		//get a single byte
		
		if(I2C1STATbits.BCL)		//Test for Bus collision
		{
			return(-1);
		}

		if(Length)
		{
			AckI2C();				//Acknowledge until all read
		}
	}
	return(0);
}


/*********************************************************************
* Function:        getI2C()
*
* Input:		None.
*
* Output:		contents of I2C1 receive buffer.
*
* Overview:		Read a single byte from Bus
*
* Note:			None
********************************************************************/
unsigned int getI2C(void)
{
	I2C1CONbits.RCEN = 1;			//Enable Master receive
	Nop();
	while(!I2C1STATbits.RBF);		//Wait for receive bufer to be full
	return(I2C1RCV);				//Return data in buffer
}


/*********************************************************************
* Function:        EEAckPolling()
*
* Input:		Control byte.
*
* Output:		error state.
*
* Overview:		polls the bus for an Acknowledge from device
*
* Note:			None
********************************************************************/
unsigned int EEAckPolling(unsigned char control)
{
	IdleI2C();				//wait for bus Idle
	StartI2C();				//Generate Start condition
	
	if(I2C1STATbits.BCL)
	{
		return(-1);			//Bus collision, return
	}

	else
	{
		if(WriteI2C(control))
		{
			return(-3);		//error return
		}

		IdleI2C();			//wait for bus idle
		if(I2C1STATbits.BCL)
		{
			return(-1);		//error return
		}

		while(ACKStatus())
		{
			RestartI2C();	//generate restart
			if(I2C1STATbits.BCL)
			{
				return(-1);	//error return
			}

			if(WriteI2C(control))
			{
				return(-3);
			}

			IdleI2C();
		}
	}
	StopI2C();				//send stop condition
	if(I2C1STATbits.BCL)
	{
		return(-1);
	}
	return(0);
}


/*********************************************************************
* Function:        putstringI2C()
*
* Input:		pointer to array.
*
* Output:		None.
*
* Overview:		writes a string of data upto PAGESIZE from array
*
* Note:			None
********************************************************************/
unsigned int putstringI2C(unsigned char *wrptr)
{
	unsigned char x;

	for(x = 0; x < PAGESIZE; x++)		//Transmit Data Until Pagesize
	{	
		if(WriteI2C(*wrptr))			//Write 1 byte
		{
			return(-3);				//Return with Write Collision
		}
		IdleI2C();					//Wait for Idle bus
		if(I2C1STATbits.ACKSTAT)
		{
			return(-2);				//Bus responded with Not ACK
		}
		wrptr++;
	}
	return(0);
}


/******************************************************************************
* Function:     I2C_MCP4728_Write()
*
* Input:		FirstByte, SecondByte, ThirdByte, FourthByte
*
* Output:		None.
*
* Overview:		Multi or Single Write data to DAC input register (or EEPORM)
*
* Note:			Z.F. 1/19/2012
******************************************************************************/
void I2C_MCP4728_Write (unsigned char _1stByte, unsigned char _2ndByte, unsigned char _3rdByte, unsigned char _4thByte)
{
	unsigned char ErrorCode;

	IdleI2C();								//Ensure Module is Idle
	StartI2C();								//Generate Start Condition

	WriteI2C(_1stByte);						//Write Control byte
	IdleI2C();

	ErrorCode = ACKStatus();				//Return ACK Status
	
	WriteI2C(_2ndByte);						//Write 2nd byte
	IdleI2C();

	ErrorCode = ACKStatus();				//Return ACK Status

	WriteI2C(_3rdByte);						//Write 3rd byte
	IdleI2C();

	ErrorCode = ACKStatus();				//Return ACK Status

	WriteI2C(_4thByte);						//Write 4th byte
	IdleI2C();

	ErrorCode = ACKStatus();				//Return ACK Status

	StopI2C();								//Initiate Stop Condition
}
