// DG8SAQ I2C library with improvements by PE0FKO

#include <avr/io.h>
#include <util/delay.h>


#define I2C_SDA_LO			I2CDDR |= SDA
#define I2C_SDA_HI			I2CDDR &= ~SDA
#define I2C_SCL_LO			I2CDDR |= SCL
#define I2C_SCL_HI			I2CDDR &= ~SCL

#define	I2C_DELAY_uS	(1e6/I2C_BITRATE)

uint8_t	I2CErrors;

inline static  void I2CDelay(void)
{
	_delay_us(I2C_DELAY_uS);
}

//PE0FKO: The original code has no stop condition (hang on SCL low)
static  void I2CStretch(void)							// Wait until clock hi
{										// Terminate the loop @ max 2.1ms
	uint16_t i = 50;					// 2.1mS
	do {
		I2CDelay();						// Delay some time
		if (i-- == 0)
		{
			I2CErrors++;				// Error timeout
			break;
		}
	}
	while(!(PINB & SCL));				// Clock line still low
}

/*
 * Generates a start condition on the bus.
 *
 *	SDA: ..\____..
 *	       __
 *	SCL: ..  \__.. 
 */
void I2CSendStart(void)
{
	I2CErrors = 0;						// reset error counter
	I2C_SCL_HI;
	I2C_SDA_LO;  	I2CDelay(); 		// Start SDA to low
	I2C_SCL_LO;  	I2CDelay();			// and the clock low
}

/*
 * Generates a stop condition on the bus.
 *	           __
 *	SDA: ..___/  ..
 *	        _____
 *	SCL: ../     .. 
 */
void I2CSendStop(void)
{
	I2C_SDA_LO;
	I2C_SCL_HI;		I2CDelay();
	I2C_SDA_HI;		I2CDelay();
}

void I2CSend0(void)
{
	I2C_SDA_LO;							// Data low = 0
	I2C_SCL_HI;		I2CStretch();
	I2C_SCL_LO;		I2CDelay();
}

void I2CSend1(void)
{
	I2C_SDA_HI;							// Data high = 1
	I2C_SCL_HI;		I2CStretch();
	I2C_SCL_LO;		I2CDelay();
}

char I2CGetBit(void)
{
	uint8_t b;
	I2C_SDA_HI;							// Data high = input (opencollector)
	I2C_SCL_HI;		I2CStretch();		// SDA Hi Z and wait
	b = (PINB & SDA) >> BIT_SDA;		// get bit
	I2C_SCL_LO;							// clock low
	return b;
}

void I2CSendByte(char b)
{
	uint8_t i,p;
	p = 0x80;
    for (i=0; i<8; i++)
	{
		if ((p & b) == 0) I2CSend0(); else I2CSend1();
    	p = p >> 1;
	};
    I2CErrors = I2CErrors + I2CGetBit(); 		//Acknowledge
  	return; 
}

uint8_t I2CReceiveByte(void)
{
	uint8_t i;
	uint8_t b = 0;
    for (i=0; i<8; i++)
	{
		b = b << 1;
    	b = b | I2CGetBit();
  	};
	I2CSend0();									//send Acknowledge
  	return b;
}

uint8_t I2CReceiveLastByte(void)
{
	uint8_t i;
	uint8_t b = 0;
    for (i=0; i<8; i++)
	{
		b = b << 1;
    	b = b | I2CGetBit();
  	};
	I2CSend1();									//send Acknowledge
  	return b;
}

uint8_t I2CGetByte(char adr)
{
I2CSendStart();
I2CSendByte(adr | 1);								// send device address from "value"
char result=I2CReceiveLastByte();					// receive data and return
I2CSendStop();
return result;
}
