/****************************************************************************/
/* Copyright 2016 MBARI                                                     */
/****************************************************************************/
/* Summary  : I2C Support for OASIS5 on PIC32MX470F512L                     */
/* Filename : i2c.c                                                         */
/* Author   : Robert Herlien (rah)                                          */
/* Project  : OASIS Mooring Replacement (OASIS5)                            */
/* Revision: 1.0                                                            */
/* Created  : 02/25/2016                                                    */
/*                                                                          */
/* MBARI provides this documentation and code "as is", with no warranty,    */
/* express or implied, of its quality or consistency. It is provided without*/
/* support and without obligation on the part of the Monterey Bay Aquarium  */
/* Research Institute to assist in its use, correction, modification, or    */
/* enhancement. This information should not be published or distributed to  */
/* third parties without specific written permission from MBARI.            */
/*                                                                          */
/****************************************************************************/
/* Note that OASIS5 uses only I2C Bus 1 (for the RTC), so these routines    */
/* are hard-coded for that bus.  Would need to make them generic, with      */
/* parameters for bus number, if we used both busses.                       */
/****************************************************************************/
/* Modification History:                                                    */
/* 25feb2016 rah - created                                                  */
/* $Log: i2c.c,v $
/* Revision 1.1  2016/05/09 17:18:26  bobh
/* First rev of dbgTest ported to FreeRTOS
/*
/* Revision 1.1  2016/03/23 23:45:43  bobh
/* Initial revision of dbgTest.MX470
/*
 */
/****************************************************************************/

#include <xc.h>
#include <GenericTypeDefs.h>
#include <cp0defs.h>
#include "i2c.h"
#include "timer.h"

/* Following defines set up I2C clock, nominally for 500 KHz, but		*/
/* execution delays actually slow it down to about 300 KHz (measured).	*/
/* With logic analyzer, I see a min clk hi time of 1.3us, and low 2.0us	*/
/* Mins from data sheet are 0.6us and 1.3us, respectively.				*/

#define CLK_TIME		SYSCLK/2000000		/* Clk hi or low = 1us		*/
#define HALFCLK_TIME	CLK_TIME/2			/* Sample data at mid-clk = .5us*/

#define delayClkTime(stCnt)	stCnt = _CP0_GET_COUNT();	\
							while ((_CP0_GET_COUNT() - stCnt) < CLK_TIME) ; 
#define delayHalfClkTime(stCnt)	stCnt = _CP0_GET_COUNT();	\
								while ((_CP0_GET_COUNT() - stCnt) < HALFCLK_TIME) ;

/* Note that I used these macros rather than tmrBusyWaitUs(), because	*/
/* that call doesn't work accurately below 2us.							*/
/* Also note that the calling routine (rtcRead/Write in our case) should*/
/* disable interrupts around the entire I2C transaction (start to stop)	*/


/************************************************************************/
/* Function    : i2cStart                                               */
/* Purpose     : Send I2C Start bit                                     */
/* Input       : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
void i2cStart(void)
{
	setSDAOut();
	setSCL();
	setSDA();
	tmrBusyWaitUs(2);
	clrSDA();
	tmrBusyWaitUs(2);
	clrSCL();
	tmrBusyWaitUs(2);
}


/************************************************************************/
/* Function    : i2cStop                                                */
/* Purpose     : Send I2C Stop bit                                      */
/* Input       : None                                                   */
/* Outputs     : SUCCESS or I2C error code                              */
/************************************************************************/
void i2cStop(void)
{
	setSDAOut();
	clrSDA();
	tmrBusyWaitUs(2);
	setSCL();
	tmrBusyWaitUs(2);
	setSDA();
	tmrBusyWaitUs(2);
}


/************************************************************************/
/* Function    : i2cSendByte                                            */
/* Purpose     : Send Byte on I2C Bus 1                                 */
/* Input       : Byte to send                                           */
/* Outputs     : I2C_SUCCESS or I2C_NO_ACK								*/
/************************************************************************/
Errno i2cSendByte(BYTE dataByte)
{
    UINT32		stCnt = _CP0_GET_COUNT();
	UINT16		i, sndByte;
	Errno		rtn;

	for (i = 0, sndByte = dataByte; i < 8; i++)
	{
		delayHalfClkTime(stCnt);
		if (sndByte & 0x80)
			setSDA();					/* Send next bit				*/
		else
			clrSDA();

		delayHalfClkTime(stCnt);		/* Data Setup time				*/
		setSCL();						/* Clock high					*/
		delayClkTime(stCnt);			/* Data Hold time				*/
		clrSCL();						/* Clock low					*/
		sndByte <<= 1;
	}

	delayHalfClkTime(stCnt);			/* Data Hold time				*/
	setSDAIn();
	delayHalfClkTime(stCnt);	
	setSCL();							/* Clock high					*/
	delayHalfClkTime(stCnt);	
	rtn = readSDA() ? I2C_NO_ACK : I2C_SUCCESS;		/* Get ACK			*/
	delayHalfClkTime(stCnt);	
	clrSCL();							/* Clock low					*/
	clrSDA();							/* Data pin low					*/
	setSDAOut();

    return(rtn);
}


/************************************************************************/
/* Function    : i2cRcvByte                                             */
/* Purpose     : Receive a Byte from I2C Bus 1                          */
/* Input       : Ptr to where to put byte                               */
/* Outputs     : Received data byte										*/
/************************************************************************/
BYTE    i2cRcvByte(void)
{
    UINT32		stCnt = _CP0_GET_COUNT();
	UINT16		i;
	BYTE		rcvByte;

	setSDAIn();

	for (i = rcvByte = 0; i < 8; i++)
	{
		rcvByte <<= 1;					/* Shift read data byte			*/
		delayClkTime(stCnt);
		setSCL();						/* Clock high					*/
		delayHalfClkTime(stCnt);		/* Data setup time				*/
		if (readSDA())					/* Read data bit				*/
			rcvByte |= 1;
		delayHalfClkTime(stCnt);
		clrSCL();						/* Clock low					*/
	}

    return(rcvByte);
}


/************************************************************************/
/* Function    : i2cSendAck                                             */
/* Purpose     : Send I2C ACK bit                                       */
/* Input       : None                                                   */
/* Outputs     : SUCCESS or I2C error code                              */
/************************************************************************/
void i2cSendAck(void)
{
	clrSDA();
	Nop();
	setSDAOut();
	tmrBusyWaitUs(2);
	setSCL();							/* Clock high					*/
	tmrBusyWaitUs(2);
	clrSCL();							/* Clock low					*/
}


/************************************************************************/
/* Function    : i2cSendNack                                            */
/* Purpose     : Send I2C NACK bit                                      */
/* Input       : None                                                   */
/* Outputs     : SUCCESS or I2C error code                              */
/************************************************************************/
void i2cSendNack(void)
{
	setSDA();	
	Nop();
	setSDAOut();
	tmrBusyWaitUs(2);
	setSCL();							/* Clock high					*/
	tmrBusyWaitUs(2);
	clrSCL();							/* Clock low					*/
}
