/****************************************************************************/
/* 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"

#define CLK_TIME	SYSCLK/1000000			/* Clk hi or low = 2us		*/
#define MIDCLK_TIME	CLK_TIME/2				/* Sample data at mid-clk = 1us*/

#define delayClkTime(stCnt)		while ((_CP0_GET_COUNT() - stCnt) < CLK_TIME) ; \
								stCnt += CLK_TIME
#define delayMidClkTime(stCnt)	while ((_CP0_GET_COUNT() - stCnt) < MIDCLK_TIME) ; \
								stCnt += MIDCLK_TIME


/************************************************************************/
/* 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++)
	{
		delayMidClkTime(stCnt);
//		tmrBusyWaitUs(1);				/* Data setup time				*/
		if (sndByte & 0x80)
			setSDA();					/* Send next bit				*/
		else
			clrSDA();

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

	delayMidClkTime(stCnt);				/* Data Hold time				*/
	setSDAIn();
	delayMidClkTime(stCnt);	
	setSCL();							/* Clock high					*/
	delayMidClkTime(stCnt);	
	rtn = readSDA() ? I2C_NO_ACK : I2C_SUCCESS;	/* Get ACK			*/
	delayMidClkTime(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)
{
	UINT16		i;
	BYTE		rcvByte;

	setSDAIn();

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

    return(rcvByte);
}


/************************************************************************/
/* Function    : i2cSendAck                                             */
/* Purpose     : Send I2C ACK bit                                       */
/* Input       : None                                                   */
/* Outputs     : SUCCESS or I2C error code                              */
/************************************************************************/
void i2cSendAck(void)
{
	clrSDA();
	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();
	setSDAOut();
	tmrBusyWaitUs(2);
	setSCL();							/* Clock high					*/
	tmrBusyWaitUs(2);
	clrSCL();							/* Clock low					*/
}
