/****************************************************************************/
/* 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 "i2c.h"
#include "timer.h"


/************************************************************************/
/* Function    : i2cInit                                                */
/* Purpose     : Initialize I2C module for Bus 1                        */
/* Input       : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
void i2cInit(void)
{
    I2C1CON = 0x0200;
    I2C1STAT = 0;
    I2C1MSK = 0;
    I2C1BRG = 26;               /* 400KHz I2C clk given 24MHz PBCLK     */
                                /* Recalculate this if anything changes */
    I2C1CONSET = _I2C1CON_ON_MASK; /* Turn on module                    */
}


/************************************************************************/
/* Function    : i2cWaitBusIdle											*/
/* Purpose     : Wait for I2C bus 1 to become idle                      */
/* Input       : None                                                   */
/* Outputs     : SUCCESS or I2C error code                              */
/* Comment     : External version, for starting the protocol            */
/************************************************************************/
Errno i2cWaitBusIdle(void)
{
    int         i;

    i = I2C1RCV;						/* Clear any received byte		*/

    for (i = 0; i < 100; i++)
        if (i2c1BusIsBusy())
		{
			if (rtosIsRunning())
				tmrDelayMs(1);			/* Wait 1ms						*/
			else
				tmrBusyWaitUs(1000);
		}
        else
            return(SUCCESS);

    return(I2C_BUS_BUSY);
}


/************************************************************************/
/* Function    : waitBusIdle                                            */
/* Purpose     : Wait for I2C bus 1 to become idle                      */
/* Input       : None                                                   */
/* Outputs     : None                                                   */
/* Comment     : Internal (static) version, for between bus bytes       */
/************************************************************************/
static void waitBusIdle(void)
{
    int         i;

    for (i = 0; i2c1BusIsBusy() && (i < 10); i++)
        tmrBusyWaitUs(20);
}


/************************************************************************/
/* Function    : i2cStart                                               */
/* Purpose     : Send I2C Start bit                                     */
/* Input       : None                                                   */
/* Outputs     : SUCCESS or I2C error code                              */
/************************************************************************/
Errno i2cStart(void)
{
    if (i2cWaitBusIdle() != SUCCESS)
        return(I2C_BUS_BUSY);

    I2C1CONSET = _I2C1CON_SEN_MASK;

    if (I2C1STATbits.BCL)
        return(I2C_MASTER_BUS_COLLISION); 
    else
        return(I2C_SUCCESS);
}


/************************************************************************/
/* Function    : i2cRestart                                             */
/* Purpose     : Send I2C Restart bit                                   */
/* Input       : None                                                   */
/* Outputs     : SUCCESS or I2C error code                              */
/************************************************************************/
Errno i2cRestart(void)
{
    waitBusIdle();

    I2C1CONSET = _I2C1CON_RSEN_MASK;

    if (I2C1STAT & (_I2C1STAT_BCL_MASK | _I2C1STAT_IWCOL_MASK))
        return(I2C_MASTER_BUS_COLLISION); 

    return(I2C_SUCCESS);
}


/************************************************************************/
/* Function    : i2cStop                                                */
/* Purpose     : Send I2C Stop bit                                      */
/* Input       : None                                                   */
/* Outputs     : SUCCESS or I2C error code                              */
/************************************************************************/
void i2cStop(void)
{
    waitBusIdle();

    I2C1CONSET = _I2C1CON_PEN_MASK;
}


/************************************************************************/
/* Function    : i2cSendByte                                            */
/* Purpose     : Send Byte on I2C Bus 1                                 */
/* Input       : Byte to send                                           */
/* Outputs     : SUCCESS or I2C error code                              */
/************************************************************************/
Errno i2cSendByte(BYTE dataByte)
{
    int         i;

    waitBusIdle();

    I2C1TRN = dataByte;

    if (I2C1STAT & (_I2C1STAT_BCL_MASK | _I2C1STAT_IWCOL_MASK))
        return(I2C_MASTER_BUS_COLLISION); 

    for (i = 0; I2C1STATbits.TRSTAT; i++)
    {
        if (i >= 20)
            return(I2C_BUS_BUSY);
        tmrBusyWaitUs(10);
    }

    return(I2C1STATbits.ACKSTAT ? I2C_NO_ACK : I2C_SUCCESS);
}


/************************************************************************/
/* Function    : i2cRcvByte                                             */
/* Purpose     : Receive a Byte from I2C Bus 1                          */
/* Input       : Ptr to where to put byte                               */
/* Outputs     : SUCCESS or I2C error code                              */
/************************************************************************/
void i2cClrRcv(void)
{
    BYTE        i;

    I2C1STATCLR = _I2C1STAT_I2COV_MASK;
    i = I2C1RCV;
}


/************************************************************************/
/* Function    : i2cRcvByte                                             */
/* Purpose     : Receive a Byte from I2C Bus 1                          */
/* Input       : Ptr to where to put byte                               */
/* Outputs     : SUCCESS or I2C error code                              */
/************************************************************************/
Errno i2cRcvByte(BYTE *dataByte)
{
    int         i;

    waitBusIdle();

    I2C1CONSET = _I2C1CON_RCEN_MASK;

    for (i = 0; i < 20; i++)
    {
        if (I2C1STATbits.I2COV)
            return(I2C_RECEIVE_OVERFLOW);
        if (I2C1STATbits.RBF)
        {
            *dataByte = I2C1RCV;
            return(I2C_SUCCESS);
        }
        tmrBusyWaitUs(20);
    }

    return(I2C_BUS_BUSY);
}


/************************************************************************/
/* Function    : i2cSendAck                                             */
/* Purpose     : Send I2C ACK bit                                       */
/* Input       : None                                                   */
/* Outputs     : SUCCESS or I2C error code                              */
/************************************************************************/
Errno i2cSendAck(void)
{
    int         i;

    waitBusIdle();

    I2C1CONCLR = _I2C1CON_ACKDT_MASK;
    I2C1CONSET = _I2C1CON_ACKEN_MASK;

    for (i = 0; i < 10; i++)
    {
        if (I2C1CONbits.ACKEN)
            return(I2C_SUCCESS);
        tmrBusyWaitUs(10);
    }

    return(I2C_BUS_BUSY);
}


/************************************************************************/
/* Function    : i2cSendNack                                            */
/* Purpose     : Send I2C NACK bit                                      */
/* Input       : None                                                   */
/* Outputs     : SUCCESS or I2C error code                              */
/************************************************************************/
Errno i2cSendNack(void)
{
    int         i;

    waitBusIdle();

    I2C1CONSET = (_I2C1CON_ACKDT_MASK | _I2C1CON_ACKEN_MASK);

    for (i = 0; i < 10; i++)
    {
        if (I2C1CONbits.ACKEN)
            return(I2C_SUCCESS);
        tmrBusyWaitUs(10);
    }

    return(I2C_BUS_BUSY);
}
