//----------------------------------------------------------------------------

//Модуль поддержки внешней EEPROM

//----------------------- Используемые ресурсы: ------------------------------

//Используется внешняя микросхема EEPROM типа 24С04, которая подключена
//к пинам SCL (PB8), SDA (PB9). Интерфейс I2C эмулируется программно.
//К сожалению, аппаратный I2C1 (remap) использовать нельзя,
//так как он конфликтует с SPI1 (remap): на выводе PB5
//всегда единица (см. errata).
//Для формирования таймингов шины используется таймер TIM16.
//При записи адреса производится проверка ответа ASK.
//Если ответа нет, производятся повторные попытки записи адреса.
//Если ответ не получен в течение времени таймаута EEPROM_WRTM
//(это максимальное время записи в EEPROM), то возвращается признак ошибки.

//----------------------------------------------------------------------------

#include "main.hpp"
#include "i2csw.hpp"
#include "systimer.hpp"
#include "eeprom.hpp"
#include "eemap.hpp"

//----------------------------------------------------------------------------
//----------------------------- Класс TEEPROM: -------------------------------
//----------------------------------------------------------------------------

//---------------------- Статические переменные: -----------------------------

uint8_t TEeprom::ByteAddress;
uint8_t TEeprom::PageAddress;
bool    TEeprom::fValid;

//------------------------------ Конструктор: --------------------------------

TEeprom::TEeprom(void)
{
  TI2Csw::Init();
  fValid = 1; //требуется для считывания сигнатуры
  fValid = (Read(EE_SIGN) == EE_SIGNATURE);
}

//------------- Запись адреса с ожиданием готовности EEPROM: -----------------

//addr - адрес слова.
//Возвращает true если обнаружен ответ EEPROM

bool TEeprom::SetAddress(uint16_t addr)
{
  bool ask;
  ByteAddress = (addr << 1) & 0xFE;
  PageAddress = (addr >> 6) & 0x0E;
  TSysTimer::TimeoutStart_ms(EEPROM_WRTM);
  do
  {
    TI2Csw::Start();
    ask = TI2Csw::Write(I2C_ADDR | PageAddress);
  }
  while(!ask && !TSysTimer::TimeoutOver_ms());
  if(ask)
  {
    TI2Csw::Write(ByteAddress);
  }
  else
  {
    TI2Csw::Stop();
  }
  return(ask);
}

//----------------------------------------------------------------------------
//------------------------- Интерфейсные методы: -----------------------------
//----------------------------------------------------------------------------

//---------------------- Чтение данных из EEPROM: ----------------------------

//addr - адрес слова в EEPROM.
//def - значение по умолчанию.
//Возвращает слово данных из EEPROM.
//Если EEPROM не валидна, или при чтении произошла ошибка,
//возвращает значение def.

uint16_t TEeprom::Read(uint16_t addr, uint16_t def)
{
  if(fValid)
  {
    if(!SetAddress(addr))
      return(def);
    TI2Csw::Stop();
    TI2Csw::Start();
    TI2Csw::Write(I2C_ADDR | PageAddress | I2C_RD);
    uint8_t data_l = TI2Csw::Read(I2C_ACK);
    uint8_t data_h = TI2Csw::Read(I2C_NACK);
    return(WORD(data_h, data_l));
  }
  return(def);
}

//----------------------- Запись данных в EEPROM: ----------------------------

//addr - адрес слова в EEPROM.
//data - слово данных для записи в EEPROM.

void TEeprom::Write(uint16_t addr, uint16_t data)
{
  if(!SetAddress(addr)) return;
  TI2Csw::Write(LO(data));
  TI2Csw::Write(HI(data));
  TI2Csw::Stop();
}

//--------------------- Обновление данных в EEPROM: --------------------------

//addr - адрес слова в EEPROM.
//data - слово данных для записи в EEPROM.
//Запись производится только в том случае, если новые данные отличаются.

void TEeprom::Update(uint16_t addr, uint16_t data)
{
  if(Read(addr) != data)
    Write(addr, data);
}

//-------------------------- Запись сигнатуры: -------------------------------

void TEeprom::Validate(void)
{
  TEeprom::Update(EE_SIGN, EE_SIGNATURE);
  fValid = (Read(EE_SIGN) == EE_SIGNATURE);
}

//----------------------- Сброс флага валидности: ----------------------------

void TEeprom::Invalidate(void)
{
  fValid = 0;
}

//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
