//-------------------------------------------------------------------------
// Titel : Funkuhr V3
//-------------------------------------------------------------------------
// Funktion : Augabe der aktuellen Zeit über UART und LCD
//-------------------------------------------------------------------------
// Prozessor : ATmega8
// Takt : 3686400 Hz
// Sprache : C
// Datum : 11.9.2008
// Version : 1.0
// Programmer: mySmartUSB
//-------------------------------------------------------------------------
// created by myAVR-CodeWizard
//-------------------------------------------------------------------------
//
#define 	F_CPU 	3686400
#include <avr\io.h>
#include <avr\interrupt.h>
#include <util\delay.h>
#include <stdio.h>
#include <string.h>

void uartPutChar(char data);
void uartInit();

volatile uint8_t status = 0;
#define SYNC 		0b10000000
#define TIMEREADY	0b01000000

int firstclear=0;
/////////////////////////////////////////////////////////////////////////////
// allgemeine-Funktionen
//---------------------------------------------------------------------------
//	wait_ms(..) - Wartet einige Millisekunden
//	Die Dauer ist nicht kalibriert.
//	PE:	miliSec=Anzahl der zu wartenden Millisekunden
//---------------------------------------------------------------------------
void wait_ms(int miliSec)
{
	_delay_loop_2( 1*(F_CPU/(1000/4)) * miliSec);	// 4 Zyklen warteschleife
}
void wait_us(int mikroSec)
{
	_delay_loop_2( 1*(F_CPU/(1000000/4)) * mikroSec);	// 4 Zyklen warteschleife
}
//////////////////////////////////////////////////////////////////////////////
//	LCD-Funktionen für myAVR-Board + myAVR-LCD
//	4-BitModus an PortD Bit 4-7
//	PortD Bit 2 = RS, high=Daten, low=Kommando
//	PortD Bit 3 = E, high-Impuls für gültige Daten
//---------------------------------------------------------------------------
//	lcd_send(..) - sendet ein Byte an LCD im 4-Bit-Modus
//	RS muss vorher richtig gesetzt sein
//	PE:	data=zu sendendes Byte
//---------------------------------------------------------------------------
void lcd_send(char data)
{
	// aktuelles RS ermitteln
	char rs=PORTD;
	rs&=4;
	// High-Teil senden
	char tmp=data;
	tmp&=0xf0;
	tmp|=rs;
	PORTD=tmp;
	// Schreibsignal
	sbi(PORTD,3);
	cbi(PORTD,3);
	// Low-Teil senden
	tmp=data;
	tmp&=0x0f;
	tmp*=16;
	tmp|=rs;
	PORTD=tmp;
	// Schreibsignal
	sbi(PORTD,3);
	cbi(PORTD,3);
	// verarbeiten lassen
	wait_ms(1);
}
//---------------------------------------------------------------------------
//	lcd_cmd(..) - sendet ein Kommando an LCD
//	PE:	cmd=Kommando-Byte
//---------------------------------------------------------------------------
void lcd_cmd(char cmd)
{
	cbi(PORTD,2);		// RS löschen = Kommando
	lcd_send(cmd);		// senden
}
//---------------------------------------------------------------------------
//	lcd_write(..) - sendet ein Zeichen (Daten) an LCD
//	PE:	text=Zeichen
//---------------------------------------------------------------------------
void lcd_write(char text)
{
	sbi(PORTD,2);		// RS setzen = Daten
	lcd_send(text);		// senden
}
//---------------------------------------------------------------------------
//	lcd_write(..) - sendet eine Zeichenkette an LCD
//	Die Zeichenkette muss mit 0x00 abgeschlossen sein
//	PE:	pText=Zeiger auf Zeichenkette
//---------------------------------------------------------------------------
void lcd_write(const char* pText)
{
	while(pText[0]!=0)
	{
		lcd_write(pText[0]);
		pText++;
	}
}
//---------------------------------------------------------------------------
//	lcd_write(..) - sendet eine Zeichenkette an LCD
//	PE:	pText=Zeiger auf Zeichenkette
//		count=Anzahl der zu sendenden Zeichen
//---------------------------------------------------------------------------
void lcd_write(const char* pText, int count)
{
	while(count!=0)
	{
		lcd_write(pText[0]);
		pText++;
		count--;
	}
}
//---------------------------------------------------------------------------
//	lcd_home(..) - Cursor auf Position 1,1
//---------------------------------------------------------------------------
void lcd_home()
{
	lcd_cmd(0x02);
	wait_ms(2);			// warten
}
//---------------------------------------------------------------------------
//	lcd_clear(..) - löscht die Anzeige im LCD
//---------------------------------------------------------------------------
void lcd_clear()
{
	lcd_cmd(0x01);
	wait_ms(2);			// warten
}
//---------------------------------------------------------------------------
//	lcd_on(..) - schaltet das LCD an
//---------------------------------------------------------------------------
void lcd_on()
{
	lcd_cmd(0x0E);
}
//---------------------------------------------------------------------------
//	lcd_off(..) - schaltet das LCD aus
//---------------------------------------------------------------------------
void lcd_off()
{
	lcd_cmd(0x08);
}
//---------------------------------------------------------------------------
//	lcd_goto(..) - setzt die Cursorposition
// 	PE:	row = Zeile 1..2
//		col = Spalte 1..16
//---------------------------------------------------------------------------
void lcd_goto(int row, int col)
{
	row--;				// Null-basierend
	row&=0x01;			// sicherheitshalber
	row*=0x40;			// Zeile nach Bit 6 bringen
	col--;				// Null-basierend
	col&=0x0f;			// sicherheitshalber
	char tmp=row|col;
	tmp|=0x80;			// Cursor setzen
	lcd_cmd(tmp);		// senden
}
//---------------------------------------------------------------------------
//	lcd_init(..) - Schaltet die Ports und Initialisiert das LCD
//---------------------------------------------------------------------------
void lcd_init()
{
	// Port D = Ausgang
	DDRD=0xff;
	PORTD=0;
	// warten bist LCD-Controller gebootet
	wait_ms(50);
	// SOFT-RESET
	PORTD = 0x30;	//0b00110000;
	sbi(PORTD,3);
	cbi(PORTD,3);
	wait_ms(5);
	PORTD = 0x30;	//0b00110000;
	sbi(PORTD,3);
	cbi(PORTD,3);
	wait_us(100);
	PORTD = 0x30;	//0b00110000;
	sbi(PORTD,3);
	cbi(PORTD,3);
	wait_ms(5);

	// 4-BitModus einschalten
	PORTD=0x20;
	// Schreibsignal
	sbi(PORTD,3);
	cbi(PORTD,3);
	wait_ms(5);

	// ab hier im 4-Bit-Modus
	lcd_cmd(0x28);		// Funktions-Set: 2 Zeilen, 5x7 Matrix, 4 Bit
	//lcd_off();
	lcd_cmd(0x06);		// Entry Mode
	lcd_on();
	lcd_clear();
}
//--------------------------------------------------------------------
// TIMER1_COMPA_vect - Timer1 Interrupt bei Vergleichswert
// aktuelle Einstellung:  20 Hz  50 ms
//--------------------------------------------------------------------
volatile uint8_t anzLow=0;
volatile uint8_t anzHigh=0;
volatile uint8_t isLow=0;

struct DcfTime 
{
	uint8_t minute;
	uint8_t hour;
	uint8_t day;
	uint8_t weekday;
	uint8_t month;
	uint8_t year;
};
DcfTime time,tempTime;
uint8_t z=0;
uint8_t second=0;
uint8_t second1=0;
uint8_t second2=0;
uint8_t offset=0;
uint8_t offsetBit;
uint8_t currentByte=0;
uint16_t i=0;

void debugPrintTime()
{
	uartPutChar('\n');
	uartPutChar(tempTime.minute);
	uartPutChar(tempTime.hour);
	uartPutChar(tempTime.day);
	uartPutChar(tempTime.weekday);
	uartPutChar(tempTime.month);
	uartPutChar(tempTime.year);
}
void clearLCD(char* bufZ1, char* bufZ2)
{
	if (firstclear)
		return;
	for(uint8_t i=0; i<20; ++i)
		bufZ1[i] = bufZ2[i] = ' ';
	firstclear=1;
}

char bufZ1[20] = {0};	
char bufZ2[20] = {0};

char days[8][3] = {"So","Mo","Di","Mi","Do","Fr","Sa","So"};
void fillBufs(char* bufZ1, char* bufZ2, bool onlySeconds=false)
{
	// Buffer leeren = mit Leerzeichen ' ' füllen
	if(onlySeconds)
	{
		clearLCD(bufZ1,bufZ2);
		bufZ2[6] = '0' + ((offset/10)%10);
		bufZ2[7] = '0' + (offset%10);
	}
	else
	{
		
		clearLCD(bufZ1,bufZ2);
		// Zeile 1
		sprintf( bufZ1, "%s,%02x.%02x.20%02x",days[time.weekday], time.day,time.month,time.year);

		// Zeile 2
		sprintf( bufZ2, "%02x:%02x:%d",time.hour, time.minute, offset);
	}
}

ISR(TIMER1_COMPA_vect)
{
	cbi(PORTB,0);			// Traffic LED aus

	//	uartPutChar((PINC & 0x02)*32);
	// high und war high
	if(PINC & 0x02 && !isLow)
		anzHigh++;
	// low und war low
	else if(!(PINC & 0x02) && isLow)
		anzLow++;
	// Änderung nach high -> test auf SYNC
	else if(isLow)
	{
		if(anzLow>40)
		{
			status|=SYNC;
			offset=0;
			offsetBit=1;
			currentByte=0;
		}
		isLow=false;
		anzLow=0;
	}
	// Änderung nach low -> Bitwert ermitteln
else
	{
		sbi(PORTB,0);	// Traffic LED an
		uint8_t bit=0;
		//uartPutChar(anzHigh);
		if(status & SYNC)
		{
			if(anzHigh>5)
			{
				bit=offsetBit;
				z++;			//1-en zählen
			}
		}
		offsetBit*=2;
		bool ok=true;
		///////// Bitwert speichern ////////////////////
		if(offset<21)
		{
			if(offset==20)
			{
				currentByte=0;
				offsetBit=1;
			}
		}
	else if(offset<29)	// Minute
		{
			currentByte|=bit;
			if(offset==27)
			{
				tempTime.minute=currentByte & 0x7F;
			}
		else if(offset==28)	// Prüfbit
			{
				currentByte=0;
				offsetBit=1;
			}
		}
	else if(offset<36)	// Stunde
		{
			currentByte|=bit;
			if(offset==34)
			{
				tempTime.hour=currentByte & 0x3F;
			}
		else if(offset==35)	// Prüfbit
			{
				currentByte=0;
				offsetBit=1;

			}
		}
	else if(offset<42)	// Tag
		{
			currentByte|=bit;
			if(offset==41)
			{
				tempTime.day=currentByte &0x3F;
				currentByte=0;
				offsetBit=1;
			}
		}
	else if(offset<45)	// Wochentag
		{
			currentByte|=bit;
			if(offset==44)
			{
				tempTime.weekday=currentByte & 0x07;
				currentByte=0;
				offsetBit=1;
			}
		}
	else if(offset<50)	// Monat
		{
			currentByte|=bit;
			if(offset==49)
			{
				tempTime.month=currentByte & 0x1F;
				currentByte=0;
				offsetBit=1;
			}
		}
	else if(offset<58)	// Year
		{
			currentByte|=bit;
			if(offset==57)
			{
				tempTime.year=currentByte;
				currentByte=0;
				offsetBit=1;
			}
		}
	else if(offset==58)	// Prüfbit für Day...Year
		{
			/*TODO -> onError DeSync*/
			// wenn ok
			time=tempTime;
			status|=TIMEREADY;
		}

		offset++;
		// next
		isLow=true;
		anzHigh=0;
		//debugPrintTime();
	}
}

// Initialisierungen
//------------------------------------------------------------------------
void init()
{
	

	time.year=0;
	// Ports initialisieren
	cbi(DDRC,1); 		
	DDRB=0xFF; 		// PORTB auf Ausgang

	//--- Timer 1 initialisieren ---
	TCCR1B=0x02; 	// Teiler 1/8
	TCCR1B|=0x08; 	// Modus: Zählen bis Vergleichswert
	OCR1A=23040/2; 	// Vergleichswert speichern
	TIMSK|=0x10; 	// Interrupt bei Vergleichswert

	uartInit();

	// DCF-Init	
	sbi(PORTB,5);
	for(uint16_t i=0; i<10000; i++)
	{}
	cbi(PORTB,5);

	//--- Interrupts erlauben ---
	sei();
}
void uartPutString(const char* data, int size=-1)
{
	if(size == -1)
		size = strlen(data);
	for(int i=0; i<size; ++i)
		uartPutChar(data[i]);
}

/////////////////////////////////////////////////////////////////////////////
// Main-Funktion
/////////////////////////////////////////////////////////////////////////////
main()
{
	init(); 	// Initialisierungen
	lcd_init();

	while (true)  	// Mainloop-Begin
	{

		if(status & SYNC)
		{
			cbi(PORTB,1);
			fillBufs(bufZ1,bufZ2,true);		
		}
		else
		{
			sbi(PORTB,1);
			sprintf(bufZ1,"Synchronisation     ");
			sprintf(bufZ2,"Bitte warten        ");
		}

		if(status & TIMEREADY)
		{
			status &= ~TIMEREADY;
			fillBufs(bufZ1,bufZ2);
			offset=0;
		}

		lcd_goto(1,1);
		lcd_write(bufZ1);		
		lcd_goto(2,1);
		lcd_write(bufZ2);

		uartPutString(bufZ1);	
		uartPutString(bufZ2);

	}
	// Mainloop-Ende
}

void uartInit()
{
	UBRRL  = 23;         	//9600Baud siehe Baudratentabelle
	UCSRB = 0x08;           //Sender enable UCSRB / UCR bei z.B.: 2313
}

void uartPutChar(char data)
{
	//warte bis UDR leer ist UCSRA / USR bei z.B.: 2313
	while (!(UCSRA&32));
	//sende
	UDR=data;
}
