#include "pic18f4550_i2c.h"
#include "mcu_global.h"

inline void I2C_Init_100(void)
{
    /* SCL and SDI inputs*/
    TRISB |= 0x03;
    /* Set baud rate */
    SSPADD = 0x77;
    SSPSTAT = 0;
    SSPCON1 = 0;
    SSPCON2 = 0;
    /* Enable I2C module */
    SSPCON1bits.SSPEN = 1;
    /* I2C Master mode*/
    SSPCON1bits.SSPM = 0x08;
    /* Enable receive mode */
    SSPCON2bits.RCEN = 1;
}

inline void I2C_Idle(void)
{
    /* Wait until I2C Bus is inactive */
    while ( (SSPCON2 & 0x1F) || (SSPSTATbits.R_W) );
}

inline void I2C_Start(void)
{
    /* Send start condition */
    SSPCON2bits.SEN = 1;
    /* Wait for slave acknoledgement */
    I2C_Idle();
}

inline void I2C_ReStart(void)
{
    /* Send restart condition */
    SSPCON2bits.RSEN = 1;
    /* Wait for slave acknoledgement */
    I2C_Idle();
}

inline void I2C_Stop(void)
{
    /* Send stop condition */
    SSPCON2bits.PEN = 1;
    /* Wait for slave acknoledgement */
    I2C_Idle();
}

inline void I2C_Write( uint8_t tmp )
{
    /* load data into buffer */
    SSPBUF = tmp;
    /* Wait for buffer empty */
    while (SSPSTATbits.BF);
    /* Wait for slave acknoledgement */
    I2C_Idle();
}

inline uint8_t I2C_Read( uint8_t acknoledge )
{
    /* Wait for bus idle */
    I2C_Idle();
    /* Enable receive mode */
    SSPCON2bits.RCEN = 1;
    /* Wait for receive buffer full */
    while (!SSPSTATbits.BF);
    /* send acknoledge if needed */
    if (1 == acknoledge)
    {
        SSPCON2bits.ACKDT = 0;           // set acknowledge bit state for ACK
        SSPCON2bits.ACKEN == 1;
        /* while acknoledge is cleared */
        while (SSPCON2bits.ACKEN);
    }
    else
    {
        SSPCON2bits.ACKEN == 0;
    }
    return (SSPBUF);
}

/* *******************************
 * Write 1 byte to an I2C slave  *
 * Input parameters:             *
 * addr: slave address           *
 * data: command to send         *
 *********************************/
void I2C_Write_Byte(uint8_t addr, uint8_t data)
{
    I2C_Start();
    I2C_Write(addr << 1);
    I2C_Write(data);
    I2C_Stop();
}

/* *******************************
 * Write 2 bytes to an I2C slave *
 * Input parameters:             *
 * addr:  slave address          *
 * data1: command to send        *
 * data2: data to send
 *********************************/
void I2C_Write_Byte2(uint8_t addr, uint8_t data1, uint8_t data2)
{
    I2C_Start();
    I2C_Write(addr << 1);
    I2C_Write(data1);
    I2C_Write(data2);
    I2C_Stop();
}

/* *******************************
 * Write N bytes to an I2C slave *
 * Input parameters:             *
 * addr:  slave address          *
 * data1: command to send        *
 * data2: data to send
 *********************************/
void I2C_Write_ByteN(uint8_t addr, uint8_t cmd, uint8_t *data, uint8_t length)
{
    uint8_t index;
    I2C_Start();
    I2C_Write(addr << 1);
    I2C_Write(cmd);
    for (index = 0; index < length; index++)
    {
        I2C_Write(*data);
        data++;
    }
    I2C_Stop();
}

/* *******************************
 * Read 1 byte from an I2C slave *
 * Input parameters:             *
 * addr: slave address           *
 * data: command to send         *
 *********************************/
uint8_t I2C_Read_Byte(uint8_t addr, uint8_t cmd)
{
    uint8_t retval;
    I2C_Start();
    I2C_Write(addr << 1);
    I2C_Write(cmd);
    I2C_ReStart();
    I2C_Write(addr << 1 | 1);
    retval = I2C_Read(0);
    I2C_Stop();
    return retval;
}

/* *******************************
 * Read 2 byte from an I2C slave *
 * Input parameters:             *
 * addr: slave address           *
 * data: command to send         *
 *********************************/
void I2C_Read_Byte2(uint8_t addr, uint8_t cmd, uint8_t* data1, uint8_t* data2)
{
    I2C_Start();
    I2C_Write(addr << 1);
    I2C_Write(cmd);
    I2C_ReStart();
    I2C_Write(addr << 1 | 1);
    *data1 = I2C_Read(1);
    *data2 = I2C_Read(0);
    I2C_Stop();
}

/* *******************************
 * Read 2 byte from an I2C slave *
 * Input parameters:             *
 * addr: slave address           *
 * data: command to send         *
 *********************************/
uint16_t I2C_Read_Word(uint8_t addr, uint8_t cmd)
{
    uint16_t retval;
    I2C_Start();
    I2C_Write(addr << 1);
    I2C_Write(cmd);
    I2C_ReStart();
    I2C_Write(addr << 1 | 1);
    retval = (uint16_t)I2C_Read(1);
    retval |= ((uint16_t)I2C_Read(0) << 8);
    I2C_Stop();
    return retval;
}

/* *******************************
 * Read N byte from an I2C slave *
 * Input parameters:             *
 * addr: slave address           *
 * data: command to send         *
 *********************************/
void I2C_Read_ByteN(uint8_t addr, uint8_t cmd, uint8_t* data, uint8_t length)
{
    uint8_t index;
    I2C_Start();
    I2C_Write(addr << 1);
    I2C_Write(cmd);
    I2C_ReStart();
    I2C_Write(addr << 1 | 1);
    for (index = 0; index < length-1; index++)
    {
        *data = I2C_Read(1);
        data++;
    }
    *data = I2C_Read(0);
    I2C_Stop();
}
