//*********************	Accessing external I2C-EEPROM *********************
#include "main.h"
#include "xeeprom.h"


#define PAGE_SIZE        128

#define EEPROM_ADDR     0xA0			// A2,A1,A0 = GND

#define i2c_delay()	_delay_us( 2.5 / 2 )	// 2.5us = 400kHz


#define ssda_lo()	SSDA_DDR = 1
#define ssda_hi()	SSDA_DDR = 0
#define	sscl_lo()	SSCL = 0
#define sscl_hi()	SSCL = 1


static void si2c_start( void )			// input: SDA = 1, SCL = x
{
  SSCL_DDR = 1;
  SSDA = 0;
  SSDA_DDR = 0;
  sscl_hi();
  i2c_delay();
  ssda_lo();
  i2c_delay();
  sscl_lo();
}


static void si2c_stop( void )			// input: SDA = 1, SCL = 0
{
  ssda_lo();
  i2c_delay();
  sscl_hi();
  i2c_delay();
  ssda_hi();
}


static uint16_t si2c_rw( uint16_t val )		// input: SDA = x, SCL = 0
{
  for( uint8_t i = 9; i; i-- ){				// data + ACK
    if( val & 0x8000 )
      ssda_hi();
    else
      ssda_lo();
    i2c_delay();
    sscl_hi();
    i2c_delay();
    val <<= 1;
    if( SSDA_PIN )
      val |= 0x80;
    sscl_lo();
  }
  return val;
}


static uint8_t si2c_r( uint8_t nack )			// input NACK
{
  if( nack )
    nack = 0x80;					// set only bit 7 !
  return si2c_rw( 0xFF00 | nack ) >> 8;			// return data
}


static uint8_t si2c_w( uint8_t val )			// input data
{
  return si2c_rw( 0x00FF | (val<<8)) & 0xFF;		// return NACK
}


static uint8_t set_addr( uint16_t addr )
{
  uint8_t i;

  for( i = 255; i; i-- ){
    si2c_start();
    si2c_w( EEPROM_ADDR );
    si2c_w( addr >> 8 );				// address high byte
    if( !si2c_w( addr ))				// address low byte
      break;						// 0 = ACK received
    _delay_us( 40 );					// timeout: 10.2ms
    si2c_stop();
  }
  return i;						// 0 = timeout
}


uint8_t eeprom_wr( uint16_t eeaddr, uint8_t *sram, uint16_t len )
{
  do{
    if( !set_addr( eeaddr ))
      return 0;                  			// 0 = failed
    do
      si2c_w( *sram++ );
    while( --len && (++eeaddr & (PAGE_SIZE-1)));	// end or next page
    si2c_stop();
  }while( len );					// end
  return 1;						// 1 = success
}


uint8_t eeprom_rd( uint16_t eeaddr, uint8_t *sram, uint16_t len )
{
  if( !set_addr( eeaddr ))
    return 0;						// 0 = failed
  si2c_start();						// repeat start
  si2c_w( EEPROM_ADDR + 1 );				// read mode
  do
    *sram++ = si2c_r( !--len );				// NACK on last byte
  while( len );
  si2c_stop();
  return 1;						// 1 = success
}

