/*! \file i2c.c \brief I2C interface using AVR Two-Wire Interface (TWI) hardware. */
//*****************************************************************************
//
// File Name	: 'i2c.c'
// Title		: I2C interface using AVR Two-Wire Interface (TWI) hardware
// Author		: Pascal Stang - Copyright (C) 2002-2003
// Created		: 2002.06.25
// Revised		: 2003.03.02
// Version		: 0.9
// Target MCU	: Atmel AVR series
// Editor Tabs	: 4
//
// This code is distributed under the GNU Public License
//		which can be found at http://www.gnu.org/licenses/gpl.txt
//
//*****************************************************************************

#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>

#include "i2c.h"

// Standard I2C bit rates are:
// 100KHz for slow speed
// 400KHz for high speed

//#define I2C_DEBUG

#ifdef I2C_DEBUG
#include "rprintf.h"	// include printf function library
#include "uart2.h"
#endif

// I2C state and address variables
static volatile eI2cStateType I2cState;
static u08 I2cDeviceAddrRW;
// send/transmit buffer (outgoing data)
static u08 I2cSendData[I2C_SEND_DATA_BUFFER_SIZE];
static u08 I2cSendDataIndex;
static u08 I2cSendDataLength;
// receive buffer (incoming data)
static u08 I2cReceiveData[I2C_RECEIVE_DATA_BUFFER_SIZE];
static u08 I2cReceiveDataIndex;
static u08 I2cReceiveDataLength;

// function pointer to i2c receive routine
//! I2cSlaveReceive is called when this processor
// is addressed as a slave for writing
static void (*i2cSlaveReceive)(u08 receiveDataLength, u08* recieveData);
//! I2cSlaveTransmit is called when this processor
// is addressed as a slave for reading
static u08 (*i2cSlaveTransmit)(u08 transmitDataLengthMax, u08* transmitData);

// functions
void i2cInit(void)
{
	// set pull-up resistors on I2C bus pins
	// TODO: should #ifdef these
	sbi(PORTC, 0);	// i2c SCL on ATmega163,323,16,32,etc
	sbi(PORTC, 1);	// i2c SDA on ATmega163,323,16,32,etc
	sbi(PORTD, 0);	// i2c SCL on ATmega128,64
	sbi(PORTD, 1);	// i2c SDA on ATmega128,64

	// clear SlaveReceive and SlaveTransmit handler to null
	i2cSlaveReceive = 0;
	i2cSlaveTransmit = 0;
	// set i2c bit rate to 100KHz
	i2cSetBitrate(100);
	// enable TWI (two-wire interface)
	sbi(TWCR, TWEN);
	// set state
	I2cState = I2C_IDLE;
	// enable TWI interrupt and slave address ACK
	sbi(TWCR, TWIE);
	sbi(TWCR, TWEA);
	//outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
	// enable interrupts
	sei();
}

void i2cSetBitrate(u16 bitrateKHz)
{
	u08 bitrate_div;
	// set i2c bitrate
	// SCL freq = F_CPU/(16+2*TWBR))
	#ifdef TWPS0
		// for processors with additional bitrate division (mega128)
		// SCL freq = F_CPU/(16+2*TWBR*4^TWPS)
		// set TWPS to zero
		cbi(TWSR, TWPS0);
		cbi(TWSR, TWPS1);
	#endif
	// calculate bitrate division	
	bitrate_div = ((F_CPU/1000l)/bitrateKHz);
	if(bitrate_div >= 16)
		bitrate_div = (bitrate_div-16)/2;
	outb(TWBR, bitrate_div);
}

void i2cSetLocalDeviceAddr(u08 deviceAddr, u08 genCallEn)
{
	// set local device address (used in slave mode only)
	outb(TWAR, ((deviceAddr&0xFE) | (genCallEn?1:0)) );
}

void i2cSetSlaveReceiveHandler(void (*i2cSlaveRx_func)(u08 receiveDataLength, u08* recieveData))
{
	i2cSlaveReceive = i2cSlaveRx_func;
}

void i2cSetSlaveTransmitHandler(u08 (*i2cSlaveTx_func)(u08 transmitDataLengthMax, u08* transmitData))
{
	i2cSlaveTransmit = i2cSlaveTx_func;
}

inline void i2cSendStart(void)
{
	// send start condition
	outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWSTA));
}

inline void i2cSendStop(void)
{
	// transmit stop condition
	// leave with TWEA on for slave receiving
	outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA)|BV(TWSTO));
}

inline void i2cWaitForComplete(void)
{
	// wait for i2c interface to complete operation
	while( !(inb(TWCR) & BV(TWINT)) );
}

inline int i2cWaitForComplete2(int retry, int* ack)
{
    int r=retry;
	// wait for i2c interface to complete operation
	while( r && (!(inb(TWCR) & BV(TWINT))) ){ r--; };
	if( inb(TWSR) == TW_MT_SLA_ACK ){ *ack=1; }else{ *ack=0; };
	*ack=1;
	return r;
}

inline void i2cSendByte(u08 data)
{
	// save data to the TWDR
	outb(TWDR, data);
	// begin send
	outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
}

inline void i2cReceiveByte(u08 ackFlag)
{
	// begin receive over i2c
	if( ackFlag )
	{
		// ackFlag = TRUE: ACK the recevied data
		outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
	}
	else
	{
		// ackFlag = FALSE: NACK the recevied data
		outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
	}
}

inline u08 i2cGetReceivedByte(void)
{
	// retieve received data byte from i2c TWDR
	return( inb(TWDR) );
}

inline u08 i2cGetStatus(void)
{
	// retieve current i2c status from i2c TWSR
	return( inb(TWSR) );
}

void i2cMasterSend(u08 deviceAddr, u08 length, u08* data)
{
	u08 i;
	// wait for interface to be ready
	while(I2cState);
	// set state
	I2cState = I2C_MASTER_TX;
	// save data
	I2cDeviceAddrRW = (deviceAddr & 0xFE);	// RW cleared: write operation
	for(i=0; i<length; i++)
		I2cSendData[i] = *data++;
	I2cSendDataIndex = 0;
	I2cSendDataLength = length;
	// send start condition
	i2cSendStart();
}

void i2cMasterReceive(u08 deviceAddr, u08 length, u08* data)
{
	u08 i;
	// wait for interface to be ready
	while(I2cState);
	// set state
	I2cState = I2C_MASTER_RX;
	// save data
	I2cDeviceAddrRW = (deviceAddr|0x01);	// RW set: read operation
	I2cReceiveDataIndex = 0;
	I2cReceiveDataLength = length;
	// send start condition
	i2cSendStart();
	// wait for data
	while(I2cState);
	// return data
	for(i=0; i<length; i++)
		*data++ = I2cReceiveData[i];
}

u08 i2cMasterSendNI(u08 deviceAddr, u08 length, u08* data)
{
	u08 retval = I2C_OK;

	// disable TWI interrupt
	cbi(TWCR, TWIE);

	// send start condition
	i2cSendStart();
	i2cWaitForComplete();

	// send device address with write
	i2cSendByte( deviceAddr & 0xFE );
	i2cWaitForComplete();

	// check if device is present and live
	if( inb(TWSR) == TW_MT_SLA_ACK)
	{
		// send data
		while(length)
		{
			i2cSendByte( *data++ );
			i2cWaitForComplete();
			length--;
		}
	}
	else
	{
		// device did not ACK it's address,
		// data will not be transferred
		// return error
		retval = I2C_ERROR_NODEV;
	}

	// transmit stop condition
	// leave with TWEA on for slave receiving
	i2cSendStop();
	while( !(inb(TWCR) & BV(TWSTO)) );

	// enable TWI interrupt
	sbi(TWCR, TWIE);

	return retval;
}

u08 i2cMasterReceiveNI(u08 deviceAddr, u08 length, u08 *data)
{
	u08 retval = I2C_OK;

	// disable TWI interrupt
	cbi(TWCR, TWIE);

	// send start condition
	i2cSendStart();
	i2cWaitForComplete();

	// send device address with read
	i2cSendByte( deviceAddr | 0x01 );
	i2cWaitForComplete();

	// check if device is present and live
	if( inb(TWSR) == TW_MR_SLA_ACK)
	{
		// accept receive data and ack it
		while(length > 1)
		{
			i2cReceiveByte(TRUE);
			i2cWaitForComplete();
			*data++ = i2cGetReceivedByte();
			// decrement length
			length--;
		}

		// accept receive data and nack it (last-byte signal)
		i2cReceiveByte(FALSE);
		i2cWaitForComplete();
		*data++ = i2cGetReceivedByte();
	}
	else
	{
		// device did not ACK it's address,
		// data will not be transferred
		// return error
		retval = I2C_ERROR_NODEV;
	}

	// transmit stop condition
	// leave with TWEA on for slave receiving
	i2cSendStop();

	// enable TWI interrupt
	sbi(TWCR, TWIE);

	return retval;
}

void i2cMasterTransferNI(u08 deviceAddr, u08 sendlength, u08* senddata, u08 receivelength, u08* receivedata)
{
	// disable TWI interrupt
	cbi(TWCR, TWIE);

	// send start condition
	i2cSendStart();
	i2cWaitForComplete();

	// if there's data to be sent, do it
	if(sendlength)
	{
		// send device address with write
		i2cSendByte( deviceAddr & 0xFE );
		i2cWaitForComplete();
		
		// send data
		while(sendlength)
		{
			i2cSendByte( *senddata++ );
			i2cWaitForComplete();
			sendlength--;
		}
	}

	// if there's data to be received, do it
	if(receivelength)
	{
		// send repeated start condition
		i2cSendStart();
		i2cWaitForComplete();

		// send device address with read
		i2cSendByte( deviceAddr | 0x01 );
		i2cWaitForComplete();

		// accept receive data and ack it
		while(receivelength > 1)
		{
			i2cReceiveByte(TRUE);
			i2cWaitForComplete();
			*receivedata++ = i2cGetReceivedByte();
			// decrement length
			receivelength--;
		}

		// accept receive data and nack it (last-byte signal)
		i2cReceiveByte(TRUE);
		i2cWaitForComplete();
		*receivedata++ = i2cGetReceivedByte();
	}
	
	// transmit stop condition
	// leave with TWEA on for slave receiving
	i2cSendStop();
	while( !(inb(TWCR) & BV(TWSTO)) );

	// enable TWI interrupt
	sbi(TWCR, TWIE);
}

int i2cMasterTransferNI2(u08 deviceAddr, u08 sendlength, u08* senddata, u08 receivelength, u08* receivedata, int retry)
{
	// disable TWI interrupt
	cbi(TWCR, TWIE);

	int ack;

	// send start condition
	i2cSendStart();
	if( !i2cWaitForComplete2(retry,&ack) ){ return 0; };
	
	int totalbytes=sendlength+receivelength;

	// if there's data to be sent, do it
	if(sendlength)
	{
		// send device address with write
		i2cSendByte( deviceAddr & 0xFE );
		if( !i2cWaitForComplete2(retry,&ack) ){ return 0; };
		if( ack==0 ){ return 0; };
		
		// send data
		while(sendlength)
		{
			i2cSendByte( *senddata++ );
			if( !i2cWaitForComplete2(retry,&ack) ){ return 0; };
			totalbytes--;
			//if( ack==0 && totalbytes>0 ){ return 0; };
			sendlength--;
		}

		if(receivelength){
			// send repeated start condition
			i2cSendStart();
			if( !i2cWaitForComplete2(retry,&ack) ){ return 0; };
		};
	}

	// if there's data to be received, do it
	if(receivelength)
	{
		// send device address with read
		i2cSendByte( deviceAddr | 0x01 );
		if( !i2cWaitForComplete2(retry,&ack) ){ return 0; };
		if( ack==0 ){ return 0; };

		// accept receive data and ack it
		while(receivelength > 1)
		{
			i2cReceiveByte(TRUE);
			if( !i2cWaitForComplete2(retry,&ack) ){ return 0; };
			//if( ack==0 ){ return 0; };
			*receivedata++ = i2cGetReceivedByte();
			// decrement length
			receivelength--;
		}

		// accept receive data and nack it (last-byte signal)
		//i2cReceiveByte(TRUE);
		//if( !i2cWaitForComplete2(retry,&ack) ){ return 0; };
		i2cReceiveByte(FALSE);
		if( !i2cWaitForComplete2(retry,&ack) ){ return 0; };
		*receivedata++ = i2cGetReceivedByte();
	}
	
	// transmit stop condition
	// leave with TWEA on for slave receiving
	i2cSendStop();
	while( !(inb(TWCR) & BV(TWSTO)) );

	// enable TWI interrupt
	sbi(TWCR, TWIE);

	return 1;
}


//! I2C (TWI) interrupt service routine
SIGNAL(SIG_2WIRE_SERIAL)
{
	// read status bits
	u08 status = inb(TWSR) & TWSR_STATUS_MASK;

	switch(status)
	{
	// Master General
	case TW_START:						// 0x08: Sent start condition
	case TW_REP_START:					// 0x10: Sent repeated start condition
		#ifdef I2C_DEBUG
		rprintfInit(uart1AddToTxBuffer);
		rprintf("I2C: M->START\r\n");
		rprintfInit(uart1SendByte);
		#endif
		// send device address
		i2cSendByte(I2cDeviceAddrRW);
		break;
	
	// Master Transmitter & Receiver status codes
	case TW_MT_SLA_ACK:					// 0x18: Slave address acknowledged
	case TW_MT_DATA_ACK:				// 0x28: Data acknowledged
		#ifdef I2C_DEBUG
		rprintfInit(uart1AddToTxBuffer);
		rprintf("I2C: MT->SLA_ACK or DATA_ACK\r\n");
		rprintfInit(uart1SendByte);
		#endif
		if(I2cSendDataIndex < I2cSendDataLength)
		{
			// send data
			i2cSendByte( I2cSendData[I2cSendDataIndex++] );
		}
		else
		{
			// transmit stop condition, enable SLA ACK
			i2cSendStop();
			// set state
			I2cState = I2C_IDLE;
		}
		break;
	case TW_MR_DATA_NACK:				// 0x58: Data received, NACK reply issued
		#ifdef I2C_DEBUG
		rprintfInit(uart1AddToTxBuffer);
		rprintf("I2C: MR->DATA_NACK\r\n");
		rprintfInit(uart1SendByte);
		#endif
		// store final received data byte
		I2cReceiveData[I2cReceiveDataIndex++] = inb(TWDR);
		// continue to transmit STOP condition
	case TW_MR_SLA_NACK:				// 0x48: Slave address not acknowledged
	case TW_MT_SLA_NACK:				// 0x20: Slave address not acknowledged
	case TW_MT_DATA_NACK:				// 0x30: Data not acknowledged
		#ifdef I2C_DEBUG
		rprintfInit(uart1AddToTxBuffer);
		rprintf("I2C: MTR->SLA_NACK or MT->DATA_NACK\r\n");
		rprintfInit(uart1SendByte);
		#endif
		// transmit stop condition, enable SLA ACK
		i2cSendStop();
		// set state
		I2cState = I2C_IDLE;
		break;
	case TW_MT_ARB_LOST:				// 0x38: Bus arbitration lost
	//case TW_MR_ARB_LOST:				// 0x38: Bus arbitration lost
		#ifdef I2C_DEBUG
		rprintfInit(uart1AddToTxBuffer);
		rprintf("I2C: MT->ARB_LOST\r\n");
		rprintfInit(uart1SendByte);
		#endif
		// release bus
		outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
		// set state
		I2cState = I2C_IDLE;
		// release bus and transmit start when bus is free
		//outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWSTA));
		break;
	case TW_MR_DATA_ACK:				// 0x50: Data acknowledged
		#ifdef I2C_DEBUG
		rprintfInit(uart1AddToTxBuffer);
		rprintf("I2C: MR->DATA_ACK\r\n");
		rprintfInit(uart1SendByte);
		#endif
		// store received data byte
		I2cReceiveData[I2cReceiveDataIndex++] = inb(TWDR);
		// fall-through to see if more bytes will be received
	case TW_MR_SLA_ACK:					// 0x40: Slave address acknowledged
		#ifdef I2C_DEBUG
		rprintfInit(uart1AddToTxBuffer);
		rprintf("I2C: MR->SLA_ACK\r\n");
		rprintfInit(uart1SendByte);
		#endif
		if(I2cReceiveDataIndex < (I2cReceiveDataLength-1))
			// data byte will be received, reply with ACK (more bytes in transfer)
			i2cReceiveByte(TRUE);
		else
			// data byte will be received, reply with NACK (final byte in transfer)
			i2cReceiveByte(FALSE);
		break;

	// Slave Receiver status codes
	case TW_SR_SLA_ACK:					// 0x60: own SLA+W has been received, ACK has been returned
	case TW_SR_ARB_LOST_SLA_ACK:		// 0x68: own SLA+W has been received, ACK has been returned
	case TW_SR_GCALL_ACK:				// 0x70:     GCA+W has been received, ACK has been returned
	case TW_SR_ARB_LOST_GCALL_ACK:		// 0x78:     GCA+W has been received, ACK has been returned
		#ifdef I2C_DEBUG
		rprintfInit(uart1AddToTxBuffer);
		rprintf("I2C: SR->SLA_ACK\r\n");
		rprintfInit(uart1SendByte);
		#endif
		// we are being addressed as slave for writing (data will be received from master)
		// set state
		I2cState = I2C_SLAVE_RX;
		// prepare buffer
		I2cReceiveDataIndex = 0;
		// receive data byte and return ACK
		outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
		break;
	case TW_SR_DATA_ACK:				// 0x80: data byte has been received, ACK has been returned
	case TW_SR_GCALL_DATA_ACK:			// 0x90: data byte has been received, ACK has been returned
		#ifdef I2C_DEBUG
		rprintfInit(uart1AddToTxBuffer);
		rprintf("I2C: SR->DATA_ACK\r\n");
		rprintfInit(uart1SendByte);
		#endif
		// get previously received data byte
		I2cReceiveData[I2cReceiveDataIndex++] = inb(TWDR);
		// check receive buffer status
		if(I2cReceiveDataIndex < I2C_RECEIVE_DATA_BUFFER_SIZE)
		{
			// receive data byte and return ACK
			i2cReceiveByte(TRUE);
			//outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
		}
		else
		{
			// receive data byte and return NACK
			i2cReceiveByte(FALSE);
			//outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
		}
		break;
	case TW_SR_DATA_NACK:				// 0x88: data byte has been received, NACK has been returned
	case TW_SR_GCALL_DATA_NACK:			// 0x98: data byte has been received, NACK has been returned
		#ifdef I2C_DEBUG
		rprintfInit(uart1AddToTxBuffer);
		rprintf("I2C: SR->DATA_NACK\r\n");
		rprintfInit(uart1SendByte);
		#endif
		// receive data byte and return NACK
		i2cReceiveByte(FALSE);
		//outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
		break;
	case TW_SR_STOP:					// 0xA0: STOP or REPEATED START has been received while addressed as slave
		#ifdef I2C_DEBUG
		rprintfInit(uart1AddToTxBuffer);
		rprintf("I2C: SR->SR_STOP\r\n");
		rprintfInit(uart1SendByte);
		#endif
		// switch to SR mode with SLA ACK
		outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
		// i2c receive is complete, call i2cSlaveReceive
		if(i2cSlaveReceive) i2cSlaveReceive(I2cReceiveDataIndex, I2cReceiveData);
		// set state
		I2cState = I2C_IDLE;
		break;

	// Slave Transmitter
	case TW_ST_SLA_ACK:					// 0xA8: own SLA+R has been received, ACK has been returned
	case TW_ST_ARB_LOST_SLA_ACK:		// 0xB0:     GCA+R has been received, ACK has been returned
		#ifdef I2C_DEBUG
		rprintfInit(uart1AddToTxBuffer);
		rprintf("I2C: ST->SLA_ACK\r\n");
		rprintfInit(uart1SendByte);
		#endif
		// we are being addressed as slave for reading (data must be transmitted back to master)
		// set state
		I2cState = I2C_SLAVE_TX;
		// request data from application
		if(i2cSlaveTransmit) I2cSendDataLength = i2cSlaveTransmit(I2C_SEND_DATA_BUFFER_SIZE, I2cSendData);
		// reset data index
		I2cSendDataIndex = 0;
		// fall-through to transmit first data byte
	case TW_ST_DATA_ACK:				// 0xB8: data byte has been transmitted, ACK has been received
		#ifdef I2C_DEBUG
		rprintfInit(uart1AddToTxBuffer);
		rprintf("I2C: ST->DATA_ACK\r\n");
		rprintfInit(uart1SendByte);
		#endif
		// transmit data byte
		outb(TWDR, I2cSendData[I2cSendDataIndex++]);
		if(I2cSendDataIndex < I2cSendDataLength)
			// expect ACK to data byte
			outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
		else
			// expect NACK to data byte
			outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
		break;
	case TW_ST_DATA_NACK:				// 0xC0: data byte has been transmitted, NACK has been received
	case TW_ST_LAST_DATA:				// 0xC8:
		#ifdef I2C_DEBUG
		rprintfInit(uart1AddToTxBuffer);
		rprintf("I2C: ST->DATA_NACK or LAST_DATA\r\n");
		rprintfInit(uart1SendByte);
		#endif
		// all done
		// switch to open slave
		outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
		// set state
		I2cState = I2C_IDLE;
		break;

	// Misc
	case TW_NO_INFO:					// 0xF8: No relevant state information
		// do nothing
		#ifdef I2C_DEBUG
		rprintfInit(uart1AddToTxBuffer);
		rprintf("I2C: NO_INFO\r\n");
		rprintfInit(uart1SendByte);
		#endif
		break;
	case TW_BUS_ERROR:					// 0x00: Bus error due to illegal start or stop condition
		#ifdef I2C_DEBUG
		rprintfInit(uart1AddToTxBuffer);
		rprintf("I2C: BUS_ERROR\r\n");
		rprintfInit(uart1SendByte);
		#endif
		// reset internal hardware and release bus
		outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWSTO)|BV(TWEA));
		// set state
		I2cState = I2C_IDLE;
		break;
	}
}

eI2cStateType i2cGetState(void)
{
	return I2cState;
}
