

/* I2C Status Message Codes (Master mode) */
#define	START_TXD			0x08	/* Start condition for bus transmited */
#define	REPEAT_START_TXD	0x10	/* Repeated Start condition for bus transmited */
#define	ADDRESS_TXD_ACK		0x18	/* Address plus write sent, ack received */
#define	ADDRESS_TXD_NOACK	0x20	/* Address plus write sent, NO ack received! */
#define	DATA_TXD_ACK		0x28	/* Data sent, ack received */
#define	DATA_TXD_NOACK		0x30	/* Data sent, NO ack received! */
#define	ARB_LOST			0x38	/* I2C Master Arbitration Lost */
#define	ADDRESS_RXD_ACK		0x40	/* Address plus read sent, ack received */
#define	ADDRESS_RXD_NOACK	0x48	/* Address plus read sent, NO ack received! */
#define	DATA_RXD_ACK		0x50	/* Data received, ack sent */
#define	DATA_RXD_NOACK		0x58	/* Data received, NO ack sent! */

#define I2B_I2EN 	0x40
#define I2B_STA		0x20
#define I2B_STO		0x10
#define I2B_SI		0x08
#define I2B_AA		0x04

#define I2BX_I2EN	0xBF
#define I2BX_STA	0xDF
#define I2BX_STO	0xEF
#define I2BX_SI		0xF7
#define I2BX_AA		0xFB

#define WRITEPAGE 3

bit hi2c_in(bit, uchar *);
bit hi2c_out(uchar);
bit hi2c_stop(void);
bit hi2c_start(void);

bit read_pcf8591(uchar *, uchar);
bit Seepromwrite( uint adr, uchar *s, uchar n );
bit Seepromread( uint adr, uchar *s, uchar n );

// Generates start condition on i2c bus returns 1 if error else returns 0.
bit hi2c_start(void)
{
	I2CON=0x00;							// I2C speed set using I2SCL & I2SCH
	I2CON|=I2B_I2EN;					// Enable I2C Bus.
	I2CON|=I2B_STA;						// Send Start condition.
	while (!(I2CON & I2B_SI) );			// Wait for si.

	if ( (I2STA!= START_TXD) && (I2STA!=REPEAT_START_TXD))  // If Status not as expected.
	{
		I2CON&=I2BX_STA;				// Clear start bit;
		I2CON|=I2B_STO;					// Send stop stop condition.
		I2CON&=I2BX_SI;					// Clear SI.
		while ( I2CON & I2B_STO );		// Wait till hardware clears stop bit.
		I2CON&=I2BX_I2EN;				// Disable I2C Bus.
		return 1;						// return error.
	}

	I2CON&=I2BX_STA;					// Clear start bit;
	return 0;
}

// Generates stop condition returns 1 if error else 0.
bit hi2c_stop(void)
{
	I2CON|=I2B_STO;						// Send stop stop condition.
	I2CON&=I2BX_SI;						// Clear SI.
	while ( I2CON & I2B_STO );			// Wait till hardware clears stop bit.
	I2CON&=I2BX_I2EN;					// Disable I2C Bus.
	return 0;
}

// Reads byte from i2c bus parameters ack to send pointer to uchar to store data.
// Returns 1 if error in operation.
bit hi2c_in(bit ack, uchar *i2rxdata)
{
	if ( ack )							// set auto acknowledge flag if ack is to be sent.
		I2CON|= I2B_AA;
	else
		I2CON&= I2BX_AA;

	I2CON&=I2BX_SI;						// Clear SI.
	while (!( I2CON & I2B_SI ) );		// Wait for SI.

	if ((I2STA!= DATA_RXD_ACK) && (I2STA!= DATA_RXD_NOACK)) // If status not as expected.
	{
		I2CON|=I2B_STO;					// Send stop stop condition.
		I2CON&=I2BX_SI;					// Clear SI.
		while ( I2CON & I2B_STO );		// Wait till hardware clears stop bit.
		I2CON&=I2BX_I2EN;				// Disable I2C Bus.
		return 1;						// Return error.
	}
	*i2rxdata=I2DAT;					// Store received data.
	return 0;							// Return no error.
}

// Transmits a byte on i2c bus parameter byte to send.
// Returns 1 if error occured.
bit hi2c_out(uchar i2txdata)
{
	I2DAT=i2txdata;						// Put data to transmit in register.
	I2CON&=I2BX_SI;						// Clear SI.
	while ( ! ( I2CON & I2B_SI ) );		// Wait for SI.

	if ((I2STA!=DATA_TXD_ACK) && (I2STA!=ADDRESS_TXD_ACK) && (I2STA!=ADDRESS_RXD_ACK)) // If status not as expected.
	{

		I2CON|=I2B_STO;					// Send stop stop condition.
		I2CON&=I2BX_SI;					// Clear SI.
		while ( I2CON & I2B_STO );		// Wait till hardware clears stop bit.
		I2CON&=I2BX_I2EN;				// Disable I2C Bus.
		return 1;						// Return error.
	}
	return 0;							// Return no error.
}

// Reads a value from ADC PCF8591 Parameters pointer to uchar to store result and channel no to read.
// Returns 1 if error occured in operation.
bit read_pcf8591( uchar *adrdbuff, uchar adch)
{
	if(hi2c_start())					// Send start condition.
		return 1;
	if(hi2c_out(0x90))					// Send device address + write.
		return 1;
    if(hi2c_out(adch))					// send channel number to read.
		return 1;
	hi2c_stop();						// Send stop condition.

	if(hi2c_start())					// send start condition.
		return 1;
	if(hi2c_out(0x91))					// Send device address + Read.
		return 1;
	if(hi2c_in(1, adrdbuff))			// Dummy read data from ADC send ack.
		return 1;			
	if(hi2c_in(1, adrdbuff))			// Dummy read data from ADC send ack.
		return 1;			
	if(hi2c_in(1, adrdbuff))			// Dummy read data from ADC send ack.
		return 1;			
	if(hi2c_in(1, adrdbuff))			// Dummy read data from ADC send ack.
		return 1;			
	if(hi2c_in(0, adrdbuff))			// Final Read data from ADC send nack.
		return 1;				
	hi2c_stop();						// Send stop condition.

	return 0;							// Return no error.
}

/////////////// Modified to support AT24C64 ////////////////////
bit Seepromadr( uchar adrh, uchar adrl, bit rd )
{
  	uchar devadr=0xa2;				//I2C Bus Address 0.
	devadr |= rd;					//Or the RD/WR bit.

	hi2c_start();					//Send a start codition.
	if( hi2c_out( devadr ))			//Send the device address.
		return 1;
	if(!rd)							//If Write Operation.
	{
		if( hi2c_out( adrh ))		//Send the address high byte.
    		return 1;				
  	  	return hi2c_out( adrl );	//Send the address low byte.
	}
	return 0;						// Return no error.
}

bit Seepromwrite( uint adr, uchar *s, uchar n )
{
	uchar i;
	do
	{
		// Acknowledge pooling for eeprom.
	    for( i = 255;; )
		{
      		if( 0 == Seepromadr( adr>>8, adr, 0) )
				break;				// receive ACK = write finished
      		hi2c_stop();
		    if( 0 == --i )			// try it 25.5msec
				return 1;			// Timeout
    	}

    	if( 0 == n )
      		return hi2c_stop();		// all bytes written

	    i = adr & ((1 << WRITEPAGE) - 1);   // prepare write boundary check
    	do
		{
      		if( hi2c_out( *s ))
			{
				hi2c_stop();
				return 1;			// Write error
		    }
		    s++;
		    adr++;
	    }while( --n && ++i < (1 << WRITEPAGE));
    	hi2c_stop();
	}while(1);
}

bit Seepromread( uint adr, uchar *s, uchar n )
{
  	if( Seepromadr( adr>>8, adr, 0))
    	return 1;							// EEPROM not connected
  	do
	{
    	if( Seepromadr( adr>>8, 0, 1))		// repeat start for read
      		return 1;

    	for(;;)
		{
	     	bit ack = --n && ++adr & 0xff;	// last byte or last in page
		  	if( hi2c_in(ack, s) )
				return 1;
		  

		    s++;
    		if( !ack )
				break;
	    }
	}while( n );
	hi2c_stop();							// send stop condition.
  	return 0;								// return no error.
}
//*****************************************************************


