/* zombee i2c - 2016 */

#ifndef __I2C_H__
#define __I2C_H__

#ifndef byte
#define byte uint8_t
#endif

#ifndef word
#define word uint16_t
#endif

#include<util/twi.h>
#include<util/delay.h>

//I2C állandók:
#define ACK			1
#define NACK		0
#define I2C_READ	1
#define I2C_WRITE	0
#define I2C_TIMEOUT	200

//Ha a sebesség nem definiált...
#ifndef F_I2C
#define F_I2C 400000
#endif

//hibajelző és hibakódok:
#define I2C_TRANSFER_OK		0
#define I2C_START_ERROR		1
#define I2C_DEVICE_MISSING	2
#define I2C_WRITE_ERROR		3
byte i2c_error  = 0;


void i2c_init()
{
	TWCR = (1<<TWEA) | (1<<TWEN);	//0b01000100;
	TWSR = 0b00000000;				//prescaler=1
	TWBR = ((F_CPU/F_I2C)-16)/2;	//F_I2C=F_CPU/(16+2*TWBR*(4^TWPS)) ;TWPS=0
}

byte i2c_start()
{
	//megvárja az előző "STOP condition" végrehajtását!
	for(byte i=0; (TWCR & (1<<TWSTO))&& (i<I2C_TIMEOUT); i++) _delay_us(1);
	//adatátvitel indul!
	TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
	//TWINT-re vár; Ez jelzi hogy a START el lett küldve.
	for(byte i=0; (!(TWCR & (1<<TWINT)))&& (i<I2C_TIMEOUT); i++) _delay_us(1);
	//visszatérési érték: a művelet eredménye
	return TWSR & 0xF8;		//'TW_START'
}

//cím és adat átadása is itt történik!
byte i2c_write(byte data)
{
	TWDR = data;
	TWCR = (1<<TWINT) | (1<<TWEN);
	for(byte i=0; (!(TWCR & (1<<TWINT)))&& (i<I2C_TIMEOUT); i++) _delay_us(1);
	//visszatérési érték: a művelet eredménye
	return TWSR & 0xF8;		//'TW_MT_SLA_ACK' vagy 'TW_MT_DATA_ACK' vagy 'TW_MR_SLA_ACK'
}

//csak olvasásra. "ack": több-bájtos kiolvasásra!
byte i2c_read(byte ack)
{
	if(ack)	TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
	else	TWCR = (1<<TWINT) | (1<<TWEN);
	for(byte i=0; (!(TWCR & (1<<TWINT)))&& (i<I2C_TIMEOUT); i++) _delay_us(1);
	//visszatérési érték: a művelet eredménye
	return TWSR & 0xF8;		//???
}


void i2c_stop()
{
	TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
}


//fejlett I2C műveletek (nagybetűvel):

//regiszter írása az I2C eszközön; sikertelen átvitel esetén 0 a visszaadott érték:
byte I2C_WRITE_REGISTERS(byte addr, byte* reg, byte regsize, byte* data, byte datasize)
{
	//az adatmező nem lehet üres (a regiszter lehet)
	if(!datasize) { i2c_error=4; return 0; }
	i2c_error = 0;
	//START:
	if(i2c_start() != TW_START)						{ i2c_stop(); i2c_error=1; return 0; }
	//CONTROL:
	if(i2c_write(addr+I2C_WRITE) != TW_MT_SLA_ACK)	{ i2c_stop(); i2c_error=2; return 0; }
	//REGISZTER (ha van)
	for(byte i=0; i<regsize; i++)
	{
		if(i2c_write(reg[i]) != TW_MT_DATA_ACK)		{ i2c_stop(); i2c_error=3; return 0; }
	}
	//ADAT:
	for(byte i=0; i<datasize; i++)
	{
		byte result = i2c_write(data[i]);
		//utolsó bájt esetén megengedjük a NACK-ot is!!!
		if(result!=TW_MT_DATA_ACK && !((result==TW_MT_DATA_NACK)&&(i+1==datasize)))
		{	
			i2c_stop(); i2c_error=3; return 0;
		}
	}
	//STOP:
	i2c_stop();
	//sikeres átvitel jelzése:
	return 1;
}

//regiszter olvasása az I2C eszközön; sikertelen átvitel esetén 0 a visszaadott érték:
byte I2C_READ_REGISTERS(byte addr, byte* reg, byte regsize, byte* data, byte datasize)
{
	//az adatmező nem lehet üres (a regiszter lehet)
	if(!datasize) { i2c_error=4; return 0; }
	i2c_error = 0;
	//START:
	if(i2c_start() != TW_START)						{ i2c_stop(); i2c_error=1; return 0; }
	//Ha az olvasni kívánt regisztert is címezni akarjuk, akkor először írási művelet kell:
	if(regsize)
	{
		//CONTROL(W):
		if(i2c_write(addr+I2C_WRITE) != TW_MT_SLA_ACK)	{ i2c_stop(); i2c_error=2; return 0; }
		//REGISZTER:
		for(byte i=0; i<regsize; i++)
		{
			if(i2c_write(reg[i]) != TW_MT_DATA_ACK)		{ i2c_stop(); i2c_error=3; return 0; }
		}
		//REP-START:
		if(i2c_start() != TW_REP_START)					{ i2c_stop(); i2c_error=1; return 0; }
	}
	//CONTROL(R):
	if(i2c_write(addr+I2C_READ) != TW_MR_SLA_ACK)		{ i2c_stop(); i2c_error=2; return 0; }
	//ADAT:
	for(byte i=0; i<datasize; i++)
	{
		//utolsó bájt esetén NACK kell!!!
		if(i<datasize-1)
		{
			if(i2c_read(ACK) !=  TW_MR_DATA_ACK)		{ i2c_stop(); i2c_error=2; return 0; }
			data[i] = TWDR;
		}
		else 
		{
			if(i2c_read(NACK) !=  TW_MR_DATA_NACK)		{ i2c_stop(); i2c_error=2; return 0; }
			data[i] = TWDR;
		}
	}
	//STOP:
	i2c_stop();
	//sikeres vétel jelzése:
	return 1;
}


/*
void i2c_expander(byte data)
{
	if(i2c_start() != TW_START)								{ i2c_stop(); i2c_error=1; return; }
	if(i2c_write(PCF8574_addr+I2C_WRITE) != TW_MT_SLA_ACK)	{ i2c_stop(); i2c_error=2; return; }
	if(i2c_write(data) != TW_MT_DATA_ACK)					{ i2c_stop(); i2c_error=3; return; }
	i2c_stop();
	i2c_error = 0;
}
*/
/*
word i2c_adc()
{
	byte _adcH, _adcL;
	i2c_start();
	i2c_write(MCP3221_addr+I2C_READ);
	_delay_us(CTIME);	//T(conv) ~ 8.96us
	i2c_read(ACK);
	_adcH = TWDR;
	i2c_read(NACK);
	_adcL = TWDR;
	i2c_stop();
	return (_adcH<<8)+_adcL;
}
*/
#endif
