/******************************************************************************
** Copyright 2015 MBARI - Monterey Bay Aquarium Research Institute
*******************************************************************************
*******************************************************************************
** Summary  : Routines to access INA219 Current/Power Monitor via I2C
** Filename : ina219.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    <ina219.h>



int INA219_Init(ushort Range, ushort Gain, ushort Bus_ADC, ushort Shunt_ADC, uchar Mode, ushort Calib)
{
	uchar CFG_h, CFG_l;
	uchar CAL_h, CAL_l;
	ushort config = 0;
	int ret;


	config |= (ushort)(Range << BRNG | Gain << PG0 | Bus_ADC << BADC1 | Shunt_ADC << SADC1 | Mode);

	CFG_l  = (uchar)config;
	CFG_h  = (uchar)(config >> 8);

	CAL_l  = (uchar)Calib;
	CAL_h  = (uchar)(Calib >> 8);

	I2C_Select( I2C_BUS_SYS );

	I2C_Start();                                    //I2C Start (S)
	ret =  I2C_WrByte( INA219_ADDR_WR );                    //Send Slave Addr to Wr
	ret &= I2C_WrByte( INA219_CALIB_REG );                  //Send Reg Pointer
	ret &= I2C_WrByte( CAL_h );                                 //Send High Byte
	ret &= I2C_WrByte( CAL_l );                             //Send Low  Byte
	I2C_Start();                                    //I2C Restart (Sr)
	ret &= I2C_WrByte( INA219_ADDR_WR );                    //Send Slave Addr to Wr
	ret &= I2C_WrByte( INA219_CONFIG_REG );                 //Send Reg Pointer
	ret &= I2C_WrByte( CFG_h );                             //Send High Byte
	ret &= I2C_WrByte( CFG_l );                             //Send Low  Byte
	I2C_Stop();                                     //I2C Stop (P)

	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 INA219_ReadVoltageCurrent(float *Volts, float *Amps)
{
	uchar ovf;
	ushort e_cts, s_cts, i_cts, p_cts;
	int ret;

	*Volts = *Amps = -1.0;

	INA219_SetMode( SHUNT_BUS_V_TRIG_MODE );

	ret = INA219_ReadSensor( &e_cts, &s_cts, &i_cts, &p_cts, &ovf );

	INA219_SetMode( POWER_DOWN_MODE );

	if ( ret == I2C_ACK ) {
		*Volts = (float)e_cts * 4.0e-3;      // E LSB = 4mV
		*Amps  = (float)i_cts * 25.0e-6;     // I LSB = 25uV for 0.050 Sample Resistor at 0.8A max expected current per TI INA219EVM Software Tool
		ret = (int)ovf;
	}
	else
		ret = ERROR;

	return( ret );

}


int INA219_ReadPowerMonitor(float *Volts, float *Svolts, float *Amps, float *Watts)
{
	uchar ovf;
	ushort e_cts, s_cts, i_cts, p_cts;
	int ret;

	*Volts = *Svolts = *Amps = *Watts = -1.0;

	INA219_SetMode( SHUNT_BUS_V_TRIG_MODE );

	ret = INA219_ReadSensor( &e_cts, &s_cts, &i_cts, &p_cts, &ovf );

	INA219_SetMode( POWER_DOWN_MODE );

	if ( ret == I2C_ACK ) {
		*Volts = (float)e_cts * 4.0e-3;      // E LSB = 4mV
		*Svolts= (float)s_cts * 10.0e-6;     // S LSB = 10uV
		*Amps  = (float)i_cts * 25.0e-6;     // I LSB = 25uV for 0.050 Sample Resistor at 0.8A max expected current per TI INA219EVM Software Tool
		*Watts = (float)p_cts * 500.0e-6;                // P LSB = I LSB * 20
		ret = (int)ovf;
	}
	else
		ret = ERROR;

	return( ret );

}


int INA219_ReadSensor(ushort *E_cts, ushort *S_cts, ushort *I_cts, ushort *P_cts, uchar *OVF)
{
	uchar Data_h, Data_l;
	RTCTimer TmOut;
	int ret;


	I2C_Select( I2C_BUS_SYS );

	RTCCountdownTimerSetup(&TmOut, INA219_CONV_TIMEOUT);    // set timeout to 250ms
	while( RTCCountdownTimeout(&TmOut) != true ) {

		I2C_Start();                                    //I2C Start (S)
		I2C_WrByte( INA219_ADDR_WR );                           //Send Slave Addr to Wr
		I2C_WrByte( INA219_BUS_V_REG );                         //Read Register Pointer
		I2C_Start();                                    //I2C Restart (Sr)
		I2C_WrByte( INA219_ADDR_RD );                           //Send Slave Addr to Rd
		I2C_RdByte( &Data_h );                                  //Read High Byte
		I2C_WrAck();
		I2C_RdByte( &Data_l );                                  //Read Low  Byte
		I2C_WrNak();
		I2C_Stop();                                     //I2C Stop (P)

		if( Data_l & CNVR_BIT_MASK )                                            //Check for Conversion Ready
			break;
	}

	if( RTCCountdownTimeout(&TmOut) == true     ) {
		I2C_Select( I2C_BUS_DISABLE );
		return( TIMEOUT );
	}

	if( Data_l & OVF_BIT_MASK )
		*OVF = INA219_OVF_TRUE;
	else
		*OVF = INA219_OVF_FALSE;

	*E_cts = (ushort)((Data_h << 8) + Data_l) >> 3;

	I2C_Start();                                            //I2C Start (S)
	ret =  I2C_WrByte( INA219_ADDR_WR );                            //Send Slave Addr to Wr
	ret &= I2C_WrByte( INA219_SHUNT_V_REG );                        //Read Register Pointer
	I2C_Start();                                            //I2C Restart (Sr)
	ret &= I2C_WrByte( INA219_ADDR_RD );                            //Send Slave Addr to Rd
	I2C_RdByte( &Data_h );                                          //Read High Byte
	I2C_WrAck();
	I2C_RdByte( &Data_l );                                          //Read Low  Byte
	I2C_WrNak();
	I2C_Stop();                                             //I2C Stop (P)
	*S_cts = (ushort)(Data_h << 8) + Data_l;

	I2C_Start();                                            //I2C Start (S)
	ret &= I2C_WrByte( INA219_ADDR_WR );                            //Send Slave Addr to Wr
	ret &= I2C_WrByte( INA219_CURRENT_REG );                        //Read Register Pointer
	I2C_Start();                                            //I2C Restart (Sr)
	ret &= I2C_WrByte( INA219_ADDR_RD );                            //Send Slave Addr to Rd
	I2C_RdByte( &Data_h );                                          //Read High Byte
	I2C_WrAck();
	I2C_RdByte( &Data_l );                                          //Read Low  Byte
	I2C_WrNak();
	I2C_Stop();                                             //I2C Stop (P)
	*I_cts = (ushort)(Data_h << 8) + Data_l;

	I2C_Start();                                            //I2C Start (S)
	ret &= I2C_WrByte( INA219_ADDR_WR );                            //Send Slave Addr to Wr
	ret &= I2C_WrByte( INA219_POWER_REG );                          //Read Register Pointer
	I2C_Start();                                            //I2C Restart (Sr)
	ret &= I2C_WrByte( INA219_ADDR_RD );                            //Send Slave Addr to Rd
	I2C_RdByte( &Data_h );                                          //Read High Byte
	I2C_WrAck();
	I2C_RdByte( &Data_l );                                                  //Read Low  Byte
	I2C_WrNak();
	I2C_Stop();
	*P_cts = (ushort)(Data_h << 8) + Data_l;

	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 INA219_SetMode(uchar Mode)
{
	uchar Data_h, Data_l;
	int ret;


	if( Mode > SHUNT_BUS_V_CONT_MODE )
		return( ERROR );

	I2C_Select( I2C_BUS_SYS );

	I2C_Start();                                            //I2C Start (S)
	ret =  I2C_WrByte( INA219_ADDR_WR );                            //Send Slave Addr to Wr
	ret &= I2C_WrByte( INA219_CONFIG_REG );                         //Send Reg Pointer
	I2C_Start();                                            //I2C Restart (Sr)
	ret &= I2C_WrByte( INA219_ADDR_RD );                            //Send Slave Addr to Rd
	I2C_RdByte( &Data_h );                                          //Read Config High Byte
	I2C_WrAck();
	I2C_RdByte( &Data_l );                                          //Read Config Low  Byte
	I2C_WrNak();
	I2C_Stop();                                             //I2C Stop (P)

	Data_l &= MODE_BITS_MASK;                                                       //clr mode bits
	Data_l |= Mode;                                                                         //set mode bits

	I2C_Start();                                            //I2C Start (S)
	ret &= I2C_WrByte( INA219_ADDR_WR );                            //Send Slave Addr to Wr
	ret &= I2C_WrByte( INA219_CONFIG_REG );                         //Send Reg Pointer
	ret &= I2C_WrByte( Data_h );                                    //Send Data upper
	ret &= I2C_WrByte( Data_l );                                    //Send Data lower
	I2C_Stop();                                             //I2C Stop (P)

	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 INA219_Reset()
{
	int ret;

	I2C_Select( I2C_BUS_SYS );

	I2C_Start();                                            //I2C Start (S)
	ret =  I2C_WrByte( INA219_ADDR_WR );                            //Send Slave Addr to Wr
	ret &= I2C_WrByte( INA219_CONFIG_REG );                         //Send Reg Pointer
	ret &= I2C_WrByte( INA219_RESET_VAL );                          //Send Reset Byte
	ret &= I2C_WrByte( INA219_RESET_VAL );                          //Send Reset Byte
	I2C_Stop();                                             //I2C Stop (P)

	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 );
}

