/******************************************************************************
** Copyright 2015 MBARI - Monterey Bay Aquarium Research Institute
*******************************************************************************
*******************************************************************************
** Summary  : Routines to control TCA6424 24 Bit I/O Expander
** Filename : tca6424.c
** Author   : Luke Coletti
** Project  :
** Version  : 1.0
** Compiler : MetroWerks Code Warrior v8.3
** Created  : 12/15/14
** Archived :
*******************************************************************************
** Modification History:
** 01/02/15 : CF2 Port, LJC
*******************************************************************************/

#include        <cfxpico.h>             // Persistor BIOS and I/O Definitions
#include        <tca6424.h>

extern ushort WrRegVal;


int TCA6424_Init(void)
{
	int ret;

	I2C_Select( I2C_BUS_DURA );

	I2C_Start();
	ret  = I2C_WrByte( TCA6424_ADDR_WR );
	ret &= I2C_WrByte( TCA6424_OUTPUT_AUTOI );
	ret &= I2C_WrByte( TCA6424_OUTPUT_VAL0 );
	ret &= I2C_WrByte( TCA6424_OUTPUT_VAL1 );
	ret &= I2C_WrByte( TCA6424_OUTPUT_VAL2 );
	I2C_Start(); //Restart, I2C Sr Condition
	ret &= I2C_WrByte( TCA6424_ADDR_WR );
	ret &= I2C_WrByte( TCA6424_CONFIG_AUTOI );
	ret &= I2C_WrByte( TCA6424_CONFIG_VAL0 );
	ret &= I2C_WrByte( TCA6424_CONFIG_VAL1 );
	ret &= I2C_WrByte( TCA6424_CONFIG_VAL2 );
	I2C_Start(); //Restart, I2C Sr Condition
	ret &= I2C_WrByte( TCA6424_ADDR_WR );
	ret &= I2C_WrByte( TCA6424_INVERT_AUTOI );
	ret &= I2C_WrByte( TCA6424_INVERT_VAL0 );
	ret &= I2C_WrByte( TCA6424_INVERT_VAL1 );
	ret &= I2C_WrByte( TCA6424_INVERT_VAL2 );
	I2C_Stop();

	I2C_Select( I2C_BUS_DISABLE );

	//ret &= TCA6424_WritePortDefaults();

	if( ret != I2C_ACK )                                                    //If device ACK'd every transmitted byte, then we're in good shape
		ret = ERROR;

	return( ret );                                                          //Return I2C Transfer Status

}


int TCA6424_WritePortDefaults(void)
{
	int ret;

	I2C_Select( I2C_BUS_DURA );

	I2C_Start();
	ret  = I2C_WrByte( TCA6424_ADDR_WR );
	ret &= I2C_WrByte( TCA6424_OUTPUT_AUTOI );
	ret &= I2C_WrByte( TCA6424_OUTPUT_VAL0 );
	ret &= I2C_WrByte( TCA6424_OUTPUT_VAL1 );
	ret &= I2C_WrByte( TCA6424_OUTPUT_VAL2 );
	I2C_Stop();

	I2C_Select( I2C_BUS_DISABLE );

	if( ret != I2C_ACK )                                                            //If device ACK'd every transmitted byte, then we're in good shape
		ret = ERROR;

	return( ret );

}


int TCA6424_ReadPort(uchar Port, uchar *Data)
{
	int ret;

	I2C_Select( I2C_BUS_DURA );

	I2C_Start();
	ret =  I2C_WrByte( TCA6424_ADDR_WR );
	ret &= I2C_WrByte( Port );
	I2C_Start();
	ret &= I2C_WrByte( TCA6424_ADDR_RD );
	I2C_RdByte( Data );
	I2C_WrNak();
	I2C_Stop();

	I2C_Select( I2C_BUS_DISABLE );

	if( ret != I2C_ACK )                                                            //If device ACK'd every transmitted byte, then we're in good shape
		ret = ERROR;

	return( ret );

}


int TCA6424_ReadPorts(uchar *Data0, uchar *Data1, uchar *Data2)
{
	int ret;

	I2C_Select( I2C_BUS_DURA );

	I2C_Start();
	ret =  I2C_WrByte( TCA6424_ADDR_WR );
	ret &= I2C_WrByte( TCA6424_INPUT_AUTOI );
	I2C_Start();
	ret &= I2C_WrByte( TCA6424_ADDR_RD );
	I2C_RdByte( Data0 );
	I2C_WrAck();
	I2C_RdByte( Data1 );
	I2C_WrAck();
	I2C_RdByte( Data2 );
	I2C_WrNak();
	I2C_Stop();

	I2C_Select( I2C_BUS_DISABLE );

	if( ret != I2C_ACK )                                                            //If device ACK'd every transmitted byte, then we're in good shape
		ret = ERROR;

	return( ret );

}


int TCA6424_WritePort(uchar Port, uchar Data)
{
	int ret;

	if( (Port < 0x04) && (Port > 0x06) )
		return( ERROR );

	I2C_Select( I2C_BUS_DURA );

	I2C_Start();
	ret  = I2C_WrByte( TCA6424_ADDR_WR );
	ret &= I2C_WrByte( Port );
	ret &= I2C_WrByte( Data );
	I2C_Stop();

	I2C_Select( I2C_BUS_DISABLE );

	if( ret != I2C_ACK )                                                            //If device ACK'd every transmitted byte, then we're in good shape
		ret = ERROR;

	return( ret );

}


int TCA6424_WritePortBit(uchar Port, uchar Bitval, uchar State)
{
	int ret;
	uchar Data;

	if( (State != HIGH) && (State != LOW) )
		return( ERROR );

	if( (Port < 0x04) && (Port > 0x06) )
		return( ERROR );

	I2C_Select( I2C_BUS_DURA );

	I2C_Start();
	ret =  I2C_WrByte( TCA6424_ADDR_WR );
	ret &= I2C_WrByte( Port-TCA6424_RD_OFFSET );          //read port offset from passed write port
	I2C_Start();
	ret &= I2C_WrByte( TCA6424_ADDR_RD );
	I2C_RdByte( &Data );
	I2C_WrNak();
	I2C_Stop();

	if( State == HIGH )
		Data |= Bitval;
	else
		Data &= ~Bitval;

	I2C_Start();
	ret &= I2C_WrByte( TCA6424_ADDR_WR );
	ret &= I2C_WrByte( Port );
	ret &= I2C_WrByte( Data );
	I2C_Stop();

	I2C_Select( I2C_BUS_DISABLE );

	if( ret != I2C_ACK )                                                            //If device ACK'd every transmitted byte, then we're in good shape
		ret = ERROR;

	return( ret );

}


void TCA6424_ArmINT(void)
{
	//add extra I2C clock pulse to "arm" TCA6424 INT generation
	//clk is added after the I2C Stop Condition

	I2C_Select( I2C_BUS_DURA );

	WrRegVal &= ~SYS_I2C_SCL;                                       // set I2C_CLK low
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	Delay5us();

	WrRegVal |= SYS_I2C_SCL;                                        // set I2C_CLK high
	*((vushort *)CS8_BASE_ADDR) = WrRegVal;

	I2C_Select( I2C_BUS_DISABLE );

}


int TCA6424_MonINT(void)
{

	//checks the status of RdReg bit5, i.e., DURA_I2C_INT
	//TCA6424_ArmINT() must be called PRIOR to calling this monitoring function

	if( !(RegIO_RdWord() & DURA_I2C_INT) )
		return( TRUE );
	else
		return( FALSE );

}