#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#define F_CPU 16000000UL
#include <util/delay.h>     



#include "hd44780.h"
#include "ft245bm.h"
#include "spi.h"
// brauche ich wahrscheinlich nicht beim STK500 protokoll
//#include "devices.h"
#include "main.h"
#include "stk500v2.h"


 
   




int main(void)

{	
	USB_init ();
	lcd_init();
	lcd_light_on();
	lcd_display_on ();
	lcd_display_clear();
	lcd_text_scroll_up ("#### Systemstart ###");
	//spi_init();
	
	

	for (;;)

		{
		unsigned char datum = 0;
		unsigned char i = 0;
		datum =  receive_message ();
		if (datum == 0)
			{
			lcd_scroll_up ();
			lcd_gotoxy(0,3);
			lcd_write_text ("Emfange Mitteilung :");
			lcd_scroll_up ();
			lcd_gotoxy(0,3);
			for (i=0;i<10;i++)
				{
				if (message_array [i] < 0xf) lcd_write_text ("0");
				lcd_write_variable(message_array [i],16);
				}

			} 
		if (datum == 2)lcd_text_scroll_up ("Fehler: Array Größe");
		if (datum == 3)lcd_text_scroll_up ("Fehler: Token"	);
		if (datum == 4)lcd_text_scroll_up ("Fehler: Prüfsumme"	);	 
 

		// Auswertung der gesendeten Daten
		switch (message_array [5])
			{

			case CMD_SIGN_ON:
					{
					lcd_text_scroll_up ("E: CMD_SIGN_ON");
					//Antwort erstellen
					message_array[2] = 0;
					message_array[3] = 11;
					message_array[5] = CMD_SIGN_ON;
					message_array[6] = STATUS_CMD_OK;
					message_array[7] = 8;
					message_array[8] = 'A';
					message_array[9] = 'V';
					message_array[10] = 'R';
					message_array[11] = 'I';
					message_array[12] = 'S';
					message_array[13] = 'P';
					message_array[14] = '_';
					message_array[15] = '2';
					transmit_message ();
					break;
					}
			// Abfrage von Parametern
			case CMD_GET_PARAMETER:
					{
					// Welcher Parameter wird abgefragt ?
					switch (message_array [6])
						{
						// Abfrage der Hardwareversion
						case PARAM_HW_VER:
								{
								message_array[2] = 0;
								message_array[3] = 3;
								message_array[6] = STATUS_CMD_OK;
								// Hardwareversion
								message_array[7] = 1;
								transmit_message ();
								break;
								}
						//Abfrage der Softwareversion (Hauptversion)
						case PARAM_SW_MAJOR:
								{
								message_array[2] = 0;
								message_array[3] = 3;
								message_array[6] = STATUS_CMD_OK;
								// Softwareversion - Hauptversion 
								message_array[7] = 0;
								transmit_message ();
								break;
								}
						//Abfrage der Softwareversion (Nebenversion)
						case PARAM_SW_MINOR:
								{
								message_array[2] = 0;
								message_array[3] = 3;
								message_array[6] = STATUS_CMD_OK;
								// Softwareversion - Nebenversion 
								message_array[7] = 1;
								transmit_message ();
								break;
								}
						// Abfrage der verwendeten Betriebsspannung
						case PARAM_VTARGET:
								{
								message_array[2] = 0;
								message_array[3] = 3;
								message_array[6] = STATUS_CMD_OK;
								// Betriebsspannung Wert * 10 
								message_array[7] = 50;
								transmit_message ();
								break;
								}
						// Parameter noch sehr nebulös
						case PARAM_SCK_DURATION:
								{
								message_array[2] = 0;
								message_array[3] = 3;
								message_array[6] = STATUS_CMD_OK;
								// ???? 
								message_array[7] = 4;
								transmit_message ();
								break;
								}
						}
					}
			// in den Programmiermodus umschalten
			case CMD_ENTER_PROGMODE_ISP:
					{
					// Wartezeit am Anfang des Init Prozesses
					_delay_ms(message_array[2]);
					//_delay_ms(1);
					}

			}			
		}
}	

	


// empfängt ein Datentelegramm des STK500 Protokolls
unsigned char receive_message (void)

	{
	// Indexzeiger zum Zugriff auf das Array
	unsigned int i_message_array = 0;
	//Größe des MESSAGE_BODY
	unsigned int message_body_size = 0;
	// Hilfsvariable zur Prüfsummenberechnung
	unsigned char checksum = 0;
	// empfange Message Start
	message_array [i_message_array] = USB_read_block();
	// falls erwartetes Byte nicht empfangen wird -> Funktionsende 
	if (message_array [i_message_array] != MESSAGE_START) return 1;
	// Prüfsumme berechnen
	checksum ^= message_array [i_message_array];
	// Indexzeiger erhöhen	
	i_message_array ++;
	// empfange Sequenznummer 
	message_array [i_message_array] = USB_read_block();
	checksum ^= message_array [i_message_array];
	// Indexzeiger erhöhen	
	i_message_array ++;
	// empfange message Größe - MSB
	message_array [i_message_array] = USB_read_block();
	checksum ^= message_array [i_message_array];
	message_body_size = message_array [i_message_array] * 8;
	// Indexzeiger erhöhen	
	i_message_array ++;
	// empfange message Größe - LSB
	message_array [i_message_array] = USB_read_block();
	checksum ^= message_array [i_message_array];
	message_body_size += message_array [i_message_array];
	// falls Größe zu groß für unser Array -> Funktionsende
	if (message_body_size > (MESSAGEARRAYSIZE - 6)) return 2;
	// Indexzeiger erhöhen	
	i_message_array ++;
	// empfange Message Token
	message_array [i_message_array] = USB_read_block();
	// falls erwartetes Byte nicht empfangen wird -> Funktionsende 
	if (message_array [i_message_array] != TOKEN) return 3;
	// Prüfsumme berechnen
	checksum ^= message_array [i_message_array];
	// Indexzeiger erhöhen	
	i_message_array ++;
	// empfange alle Daten des Message Body laut 
	// übermittelte Anzahl
	while (message_body_size > 0)
		{
		message_array [i_message_array] = USB_read_block();
		checksum ^= message_array [i_message_array];
		// Indexzeiger erhöhen	
		i_message_array ++;
		// nächstes Zeichen	
		message_body_size --;
		}
	// empfange Prüfsumme
	message_array [i_message_array] = USB_read_block();
	// falls Prüfsumme nicht übereinstimmt -> Funktionsende 
	if (message_array [i_message_array] != checksum) return 4;
	return 0;
	}


unsigned char transmit_message (void)
	{
	// Indexzeiger zum Zugriff auf das Array
	unsigned int i_message_array = 0;
	//Größe des MESSAGE_BODY
	unsigned char checksum = 0;
	// Größe der gesammtem Message
	unsigned int message_size = 0;
	// Gesamtlänge der Message
	// Message Body Länge + Message_Start + Sequenznummer + Message Size + Token
	// Die Prüfsumme wird im Anschluß extra angehängt
	message_size = message_array [2] * 8 + message_array [3] + 5;
	while (i_message_array <  message_size)
		{
		USB_write_block (message_array [i_message_array]);
		checksum ^= message_array [i_message_array];
		// Indexzeiger erhöhen	
		i_message_array ++;
		}
	// zum Schluß noch die Prüfsumme übertragen
	USB_write_block (checksum);
	return 0;
	}



// Alter Kram


/*
unsigned char spi_programm_init (void)

	{
	unsigned char datum = 0;
	// Power_up Sequenz
	PORTB |= (1<<RESET_PIN);
	_delay_ms(5);
	//systimer_pause (5);
	PORTB &= ~(1<<RESET_PIN);
	_delay_ms(20);
	//systimer_pause (20);

	// Programmierung einschalten
	// erstes Byte zur Initialisierung senden
	spi_write_block (0xac);
	// zweites Byte zur Initialisierung senden
	spi_write_block (0x53);
	// drittes Byte zur Initialisierung senden
	spi_write_block (0x00);
	// jetzt muß der AVR das zweite Init Byte zurücksenden.
	// dann Initialisierung erfolgreich
	datum = spi_read_block ();
	// viertes Byte zur Initialisierung senden
	spi_write_block (0x00);	
	// Initialisierung erfolgreich ?
	if ( datum == 0x53) return 0;
	else return 1;
	}





datum=USB_read_block();
		
		switch (datum)
			{
				
			// Name des Programmers senden
			case 'S':
				{
				USB_write_block ('U');
				USB_write_block ('S');
				USB_write_block ('B');
				USB_write_block ('-');
				USB_write_block ('I');
				USB_write_block ('S');
				USB_write_block ('P');
				break;
				}
			// Softwareversion des Programmers senden
			case 'V':
				{
				USB_write_block ('0');
				USB_write_block ('1');
				break;
				}
			// Hardwareversion des Programmers senden
			case 'v':
				{
				USB_write_block ('1');
				USB_write_block ('0');
				break;
				}
			// Typ des Programmers senden
			// "U" wird noch nicht benutzt, ich nehme es für USB 
			case 'p':
				{
				USB_write_block ('U');
				break;
				}
			// Autoinkrement Modus unterstützt
			case 'a':
				{
				USB_write_block ('Y');
				break;
				}

			// Überprüft Blockmodus Unterstützung
			// und sendet die Größe des Pufferspeichers zurück
			case 'b':
				{
				USB_write_block ('Y');
				USB_write_block (0x01);
				USB_write_block (0x00);
				// da wir "Blockmodus möglich" senden,
				// gehen wir dafon aus, auch diesen zu benutzen > AVR109
				// bei diesem Modus ist nicht 'L' der letzte Befehl, sondern 'E'
				// daher blockieren wir mit dieser variablen den Rücksprung
				// ins Haupprogramm bei 'L'
				//bootloader_109 = 1;
				break;
				}


			// Abfrage der unterstützten Prozessoren
			// diese werden in der device.h festgelegt
			case 't':
				{
				unsigned char anzahl = 0;
				while ( anzahl < D_NUMBER)
					{
					// Jeweils 1. Byte aus Programmspeicher lesen und ausgeben
					USB_write_block ( pgm_read_byte (&devices [anzahl] [0]));
					// nächste Zeile anwählen
					anzahl ++;
					}
				
				USB_write_block (0);
				break;
				}
			
			// verwendeter Devicetype empfangen
			case 'T':
				{
				unsigned char anzahl = 0;
				device_type = USB_read_block ();
				lcd_scroll_up ();
				lcd_gotoxy(0,3);
				lcd_write_text ("Devicetyp = 0x");
				lcd_write_variable(device_type,16);
				for (anzahl=0;anzahl < D_NUMBER;anzahl++)
					{
					if ((pgm_read_byte (&devices [anzahl] [0])) == device_type)
					device_entry_table = anzahl;
					} 
				lcd_gotoxy(18,3);
				lcd_write_variable(device_entry_table,10);
				// 'OK' an avrdude
				USB_write_block ('\r');
				break;
				}

			// in Programmiermodus wechseln
			case 'P':
				{
				unsigned char temp = 0;
				temp = spi_programm_init ();
				if (temp == 0)  lcd_text_scroll_up("SPI Init OK");
				else 		lcd_text_scroll_up("SPI Init Fehler");
				// 'OK' an avrdude
				USB_write_block (13);
				break;
				}
			// Signaturbytes senden
			case 's':
				{
				unsigned char datum = 0;
				unsigned char i = 0;
				lcd_scroll_up ();
				lcd_gotoxy(0,3);
				lcd_write_text ("Sig");
				// 3 mal Durchlauf für alle Signaturbyte
				// avrdude erwartet das höchste Byte zuerst
				for (i=3;i>0;i--)
					{
					// Befehlssequenz senden
					spi_write_block (read_signatur_0);
					spi_write_block (read_signatur_1);
					spi_write_block (i-1);
					spi_write_block (0x00);
					// Signatur abholen
					datum = spi_read_block ();
					// Signatur per USB übertragen
					USB_write_block (datum);
					lcd_write_text ("  ");
					lcd_write_variable(datum,16);
					}
				break;
				}


			}			

*/


