/******************************************************************************
 *****************LIBRERIA PARA MEMORIA ROM EXTERNA I2C************************
 *****************SACADA DE AN1488 MICROCHIP***********************************
 *****************************************************************************/


#include <xc.h>
#include <htc.h>
#include <stdio.h>
#include "I2C_EEPROM.h"

//#define _XTAL_FREQ 16000000

//.............................................................................
//              Configuracion del I2C para PIC16F1947
//.............................................................................

void Configuration_I2C (void)
{
    LCDSE2bits.SE16=0;              // RC3 (SCL1)
    LCDSE2bits.SE17=0;              // RC4 (SDA1)
    SDA_TRIS = 1;                   // SDA1 (RC4) = input
    SCL_TRIS = 0;                   // SCL1 (RC3) = output
    SCL=1;                          // SCL is high
    SSP1STAT = 0x80;
    SSP1CON1 = 0x28;
    SSP1CON3 = 0x00;
    SSP1ADD = 0x27;                 // 100khz
}


//.............................................................................
//          Writes data to the EEPROM address
//.............................................................................
void eeprom_wr_byte(unsigned int addr, unsigned char data)
{
i2c_start();                 // generate Start COndition
i2c_wr(EEPROM_WR);           // send WRITE command

// comment the next line if a Low-Density (<=2kB) device is used
i2c_wr(addr>>8);             //      address MSB
i2c_wr(addr & 0xff);         //      address LSB

i2c_wr(data);                // send data
i2c_stop();                  // initiate Stop Condition
ack_poll(EEPROM_WR);         // ACK polling
}


//.............................................................................
//          Reads a byte from the EEPROM address
//.............................................................................
unsigned char eeprom_rd_byte(unsigned int addr)
{
unsigned char data;

i2c_start();                 // generate Start COndition
i2c_wr(EEPROM_WR);           // send WRITE command

// comment the next line if a Low-Density (<=2kB) device is used
i2c_wr(addr>>8);             //      address MSB
i2c_wr(addr & 0xff);         //      address LSB

i2c_start();                 // generate Start COndition
i2c_wr(EEPROM_RD);           // send READ command

data=i2c_rd(NACK);           // read data from buffer
i2c_stop();                  // send Stop Condition

return data;                 // return data from I2C buffer
}


//.............................................................................
//         Writes a page to the EEPROM address
//.............................................................................
void eeprom_wr_page(unsigned int addr,unsigned char *data, unsigned char length)
{
unsigned char i=0, j=0, page_nr=0;

i2c_start();                 // generate Start COndition
i2c_wr(EEPROM_WR);           // send WRITE command

// comment the next line if a Low-Density (<=2kB) device is used
i2c_wr(addr>>8);             //      address MSB
i2c_wr(addr & 0xff);         //      address LSB


if (addr%PAGE_SIZE)
    {
    if (length>(PAGE_SIZE-(addr%PAGE_SIZE)))
        {
        for (i=0; i<(PAGE_SIZE-(addr%PAGE_SIZE)); i++)  // Loop through number of bytes
            {
            i2c_wr(data[j]); // write next byte from array
            j++;             // inc array index
            }
        i2c_stop();          // initiate Stop Condition
        ack_poll(EEPROM_WR); // ACK polling

        addr+=(PAGE_SIZE-(addr%PAGE_SIZE));   // go to the start of the next page
        page_nr=length/PAGE_SIZE;// calculate the number of pages
        eeprom_next_page(addr);  // write the address of the next page

        while(page_nr)           // loop until all pages are written
            {
            for (i=0; i<PAGE_SIZE; i++)// Loop through number of bytes
                {
                i2c_wr(data[j]);// write next byte from array
                j++;            // inc the array index
                }
            i2c_stop();         // send Stop condition
            ack_poll(EEPROM_WR);// ACK polling

            page_nr--;          // dec the number of pages
            addr+=PAGE_SIZE;    // inc address with 1 page
            eeprom_next_page(addr); // write the address of the next page
            }

        for (; j<length; j++)   // loop through number of bytes
             i2c_wr(data[j]);   // write next byte from array
        }

    else for (i=0; i<length; i++)// loop through number of bytes
            {
            i2c_wr(data[i]);     // write next byte from array
            }
    }


else
    {
    if (length>PAGE_SIZE)
        {
        page_nr=length/PAGE_SIZE;// calculate the number of pages
        while(page_nr)           // loop until all pages are written
            {
            for (i=0; i<PAGE_SIZE; i++)// Loop through number of bytes
                {
                i2c_wr(data[j]);// write next byte from array
                j++;            // inc the array index
                }
            i2c_stop();         // send Stop condition
            ack_poll(EEPROM_WR);// ACK polling

            page_nr--;          // dec the number of pages
            addr+=PAGE_SIZE;    // inc address with 1 page
            eeprom_next_page(addr); // write the address of the next page
            }
        for (; j<length; j++)   // loop through number of bytes
             i2c_wr(data[j]);   // write next byte from array
        }

    else for (i=0; i<length; i++)     // loop through number of bytes
            {
            i2c_wr(data[i]);    // write next byte from array
            }
    }

i2c_stop();                     // send Stop condition
ack_poll(EEPROM_WR);            // ACK polling
}


//.............................................................................
//          Reads a page from the EEPROM address
//.............................................................................
void eeprom_rd_page(unsigned int addr, unsigned char *data, unsigned char length)
{
unsigned char i=0;

i2c_start();                    // generate Start COndition
i2c_wr(EEPROM_WR);              // send WRITE command

// comment the next line if a Low-Density (<=2kB) device is used
i2c_wr(addr>>8);                //      address MSB
i2c_wr(addr&0xff);              //      address LSB

i2c_start();                    // generate Start COndition
i2c_wr(EEPROM_RD);              // send READ command

for (i=0; i<length; i++)        // loop through number of bytes
    {
    
    if (i<(length-1)) data[i]=i2c_rd(ACK);  // write next byte to array & send ACK bit
    else data[i]=i2c_rd(NACK);  // send NACK bit
    }

i2c_stop();                     // send Stop Condition
}


//.............................................................................
//          Writes the start address of the nexr page
//.............................................................................
void eeprom_next_page(unsigned int address)
{
i2c_start();                    // send Start condition
i2c_wr(EEPROM_WR);              // send WRITE command
// comment the next line if a Low-Density (<=2kB) device is used
i2c_wr(address>>8);             //      address MSB
i2c_wr(address & 0xff);         //      address LSB
}


//....................................................................
// This function generates an I2C Start Condition
//....................................................................
void i2c_start(void)
{
unsigned int i;

SDA_TRIS = 1;                   // ensure SDA & SCL are high
SCL = 1;
SDA_TRIS = 0;                   // SDA = output
SDA = 0;                        // pull SDA low
for (i=0;i<2;i++) NOP();
SCL = 0;                        // pull SCL low
}


//....................................................................
// This function generates an I2C Stop Condition
//....................................................................
void i2c_stop(void)
{
unsigned int i;

SCL = 0;                        // ensure SCL is low
SDA_TRIS = 0;                   // SDA = output
SDA = 0;                        // SDA low
for (i=0;i<3;i++) NOP();
SCL = 1;                        // pull SCL high
SDA_TRIS = 1;                   // allow SDA to be pulled high
for (i=0;i<3;i++) NOP();
SCL=0;                          // ensure SCL is low
}


//....................................................................
// Outputs a bit to the I2C bus
//....................................................................
void bit_out(unsigned char data)
{
unsigned int i;

SCL = 0;                        // ensure SCL is low
SDA_TRIS=0;                     // configure SDA as an output
SDA= (data>>7);                 // output the MSB
for (i=0;i<2;i++) NOP();
SCL = 1;                        // pull SCL high to clock bit
for (i=0;i<3;i++) NOP();
SCL = 0;                        // pull SCL low for next bit
}


//....................................................................
// Inputs a bit from the I2C bus
//....................................................................
void bit_in(unsigned char *data)
{
unsigned int i;

SCL = 0;                        // ensure SCL is low
SDA_TRIS = 1;                   // configure SDA as an input
SCL = 1;                        // bring SCL high to begin transfer
for (i=0;i<3;i++) NOP();
*data |= SDA;                   // input the received bit
SCL = 0;                        // bring SCL low again.
}


//....................................................................
// Writes a byte to the I2C bus
//....................................................................
unsigned char i2c_wr(unsigned char data)
{
unsigned char i;                // loop counter
unsigned char ack;              // ACK bit

ack = 0;
for (i = 0; i < 8; i++)         // loop through each bit
    {
    bit_out(data);              // output bit
    data = data << 1;           // shift left for next bit
    }

bit_in(&ack);                   // input ACK bit
return ack;
}


//....................................................................
// Reads a byte from the I2C bus
//....................................................................
unsigned char i2c_rd(unsigned char ack)
{
unsigned char i;                // loop counter
unsigned char ret=0;            // return value

for (i = 0; i < 8; i++)         // loop through each bit
    {
    ret = ret << 1;             // shift left for next bit
    bit_in(&ret);               // input bit
    }

bit_out(ack);                   // output ACK/NAK bit
return ret;
}


//.............................................................................
//          Polls the bus for ACK from device
//.............................................................................
void ack_poll (unsigned char control)
{
unsigned char result=1;

while(result)
	{
	i2c_start();            // generate Restart condition
	result=i2c_wr(control); // send control byte (WRITE command)
        }

i2c_stop();                     // generate Stop condition
}



