/****************************************************************************
 *
 * Projekt			:	Glacier Batterieboxdeckel Prüfgerät
 * Dateiname		:	drvEep.c
 * Autor			:	orginal MHoffmann, abgeändert für dieses Projekt Irene Saxer
 * Erstellungsdatum	:	17.04.2019
 * Beschreibung		:	"Treiber fuer Zugriffe auf das externe EEprom"
 *
 *
 * Versionsgeschichte
 * 	Revision 1.0	17.04.2019 SAI erste Version
 *
 ****************************************************************************/

/****************************************************************************/
/* COMPILER Direktiven */

/****************************************************************************/
/* INCLUDE Dateien */
#include "DrvEep.h"

/****************************************************************************/
/* KONSTANTEN */

/****************************************************************************/
/* Globale Variablen */
static void DrvEep_WriteHelper(uint32_t u32_Addr, uint8_t* pu8_Data, uint16_t u16_Len);
static void DrvEep_Xfer(uint8_t* pu8_Data, uint16_t u16_Len);
static void DrvEep_XferStruct(TEeXfer* pudt_Xfer);

/****************************************************************************/
/* Funktionen */

//init hw and sw structs
void DrvEep_Init(void)
{
	uint8_t u8_Tmp;

	//CS fuer extEEPROM auf HIGH setzen (inaktiv)
	DDRC   |= BM_EE_CS;

	//EEprom sich intern initialisieren lassen
	EE_CS;
	_delay_ms(1);
	EE_NCS;

	//SPI des AVR initialisieren (10MHz)
	SPSR = (1<<SPI2X);
	SPCR = (0<<SPE)|(1<<MSTR)|(0<<SPR0)|(0<<SPR1)|(0<<CPOL)|(0<<CPHA)|(0<<DORD);

	//Status- und Datareg lesen, um ggf gesetztes INT-Flag zu loeschen
	u8_Tmp = SPSR;
	u8_Tmp = SPDR;
}

//read status register
uint8_t DrvEep_GetStatus(void)
{
	uint8_t	u8_Buf[2];
	u8_Buf[0] = EE_CMD_RDSR;
	u8_Buf[1] = 0;

	DrvEep_Xfer(&u8_Buf[0], 2);

	return u8_Buf[1];
}


//enable SW write mode
void DrvEep_WrEn()
{
	uint8_t u8_Data = EE_CMD_WREN;
	DrvEep_Xfer(&u8_Data,1);
}
//disable SW write mode
void DrvEep_WrDi()
{
	uint8_t u8_Data = EE_CMD_WRDI;
	DrvEep_Xfer(&u8_Data,1);
}

//erase complete device
void DrvEep_CE()
{
	//wait for ready state
	while(DrvEep_GetStatus()&0x01);
	DrvEep_WrEn();

	uint8_t u8_Data = 0xc7;
	DrvEep_Xfer(&u8_Data,1);
}

//hw-communication (uebertraegt ohne nachzupruefen die uebergebenen Daten (~mengen))
static void DrvEep_Xfer(uint8_t* pu8_Data, uint16_t u16_Len)
{
	uint16_t u16_i;
	uint8_t* pu8_x = pu8_Data;
	
	EE_ENABLE;
	EE_CS;

	//jedes Byte einzeln lesen
	for (u16_i=0; u16_i<u16_Len; u16_i++)
	{
		SPDR = (uint8_t)*(pu8_x);	// out-Daten in das SPI-Register kopieren
		while( !(SPSR & (1<<SPIF)));		// warten, bis daten uebertragen sind
		*(pu8_x) = SPDR;			// in-Daten aus dem SPI-Register holen
		pu8_x++;
	}

	EE_NCS;
	EE_DISABLE;
}

//hw-communication (uebertraegt eine XferStruktur)
static void DrvEep_XferStruct(TEeXfer* pudt_Xfer)
{
	uint16_t u16_i;
	uint8_t* pu8_x;
	uint8_t u8_Tmp=0;
	uint8_t	u8_Header[4];

	//Header zusamenbauen
	u8_Header[0] = pudt_Xfer->u8_Cmd;
	u8_Header[1] = (uint8_t)(pudt_Xfer->u32_Addr >> 16);
	u8_Header[2] = (uint8_t)(pudt_Xfer->u32_Addr >> 8);
	u8_Header[3] = (uint8_t)(pudt_Xfer->u32_Addr);

	EE_ENABLE;
	EE_CS;
	
	//erst header uebertragen
	for (u16_i=0; u16_i<4; u16_i++)
	{
		SPDR = u8_Header[u16_i];		//out-Daten in das SPI-Register kopieren
		while( !(SPSR & (1<<SPIF)));	//warten, bis daten uebertragen sind
		u8_Tmp = SPDR;					//in-Daten aus dem SPI-Register holen
	}

	//Datenzeiger initialisieren
	pu8_x = pudt_Xfer->pu8_Data;

	//jedes Byte einzeln lesen
	for (u16_i=0; u16_i<pudt_Xfer->u16_Len; u16_i++)
	{
		SPDR = (uint8_t)*(pu8_x);		//out-Daten in das SPI-Register kopieren
		while( !(SPSR & (1<<SPIF)));	//warten, bis daten uebertragen sind
		*(pu8_x) = SPDR;				//in-Daten aus dem SPI-Register holen
		pu8_x++;
	}

	EE_NCS;
	EE_DISABLE;
}

//read from eeprom memory
void DrvEep_Read(uint32_t u32_Addr, uint8_t* pu8_Data, uint16_t u16_Len)
{
	TEeXfer udt_EeXfer;

	//init XferStruct
	udt_EeXfer.u8_Cmd   = EE_CMD_READ;
	udt_EeXfer.u32_Addr = u32_Addr & 0xffffff;	//nur 24 bit unterstuetzt
	udt_EeXfer.pu8_Data = pu8_Data;
	udt_EeXfer.u16_Len  = u16_Len;

	//Datentransfer starten
	DrvEep_XferStruct(&udt_EeXfer);
}

//write into eeprom memory
void DrvEep_Write(uint32_t u32_Addr, uint8_t* pu8_Data, uint16_t u16_Len)
{
	uint32_t u32_Addr1=u32_Addr;
	uint16_t u16_Len1=EE_PAGE_SIZE - (u32_Addr1%EE_PAGE_SIZE);
	uint32_t u32_Addr2=0;
	uint16_t u16_Len2=0;

	if (u16_Len1 < u16_Len)
	{
		u32_Addr2 = u32_Addr1 + u16_Len1;
		u16_Len2 = u16_Len - u16_Len1;
	}
	else
		u16_Len1 = u16_Len;

	//Ersten Teil uebertragen
	DrvEep_WriteHelper(u32_Addr1, pu8_Data, u16_Len1);

	//ggf. zweiten Teil uebertragen
	if (u16_Len2 != 0)
	{
		DrvEep_WriteHelper(u32_Addr2, pu8_Data+u16_Len1, u16_Len2);
	}
}

static void DrvEep_WriteHelper(uint32_t u32_Addr, uint8_t* pu8_Data, uint16_t u16_Len)
{
	TEeXfer udt_EeXfer;

	//wait for ready state
	while(DrvEep_GetStatus()&0x01);

	//enable write mode
	DrvEep_WrEn();

	//init XferStruct
	udt_EeXfer.u8_Cmd   = EE_CMD_WRITE;
	udt_EeXfer.u32_Addr = u32_Addr & 0xffffff;	//nur 24 Bit verwendbar
	udt_EeXfer.pu8_Data = pu8_Data;
	udt_EeXfer.u16_Len  = u16_Len;

	//Datentransfer starten
	DrvEep_XferStruct(&udt_EeXfer);
}

//berechnet einen vergleichbaren Sekundenwert eines Datums
uint64_t DrvEep_ToSec(tDateTime DT)
{
	uint64_t rv = 0;
	
	rv += DT.sek;
	rv += DT.min*60;
	rv += DT.std*3600;
	rv += DT.tag*86400;	
	rv += DT.mon*2678400;		//approx: Annahme: jeder Monat hat 31 Tage
	rv += DT.jhr*2539123200;	//approx (siehe mon)
	
	return rv;
}


