/*
Pinbelegung für ATMega-8
PD0 = RxD
PD1 = TxD
PD2 = Adressleitung (Int0)
PD3 = 
PD4 = (7) EM Takt Bestätigung (EM_ACK)
PD5 =
PD6 =
PD7 =

PB0 =
PB1 =
PB2 = SPI Select, verbunden mit PD2
PB3 = SPI (8) Daten Ausgang (RoboInt -> EM)
PB4 = SPI (9) Daten Eingang (EM -> RoboInt)
PB5 = SPI (6) Takt Daten
PB6 = (Quarz)
PB7 = (Quarz)

Beim Protokoll des RoboInt ist noch einiges unklar.
So werden vom RoboInt insgesamt 6 Bytes gesendet, aber offensichtlich nur 4 Stück verwendet.
Es ist denkbar, daß durch Senden eines anderen Headers Sonderfunktionen des EM angesprochen werden,
z.B. auslesen/beschreiben der Seriennummer. Ohne entsprechende Kenntnisse des Protokolls ist es
aber nicht empfehlenswert, mit diesen Werten zu experimentieren, weil man die Auswirkungen nicht
absehen kann.
Die Adressierung ist mit einem 74HC283 und einen 74HC08 realisiert, diese Schaltung stellt auch die 
Adressierung für die nachfolgenden Extension-Module zur Verfügung.
Mit leichten Modifikationen läßt sich auch ein Mega-8 µC verwenden.
Die Beschreibung des Protokolls und Schaltpläne sind auf http://www.ft-fanpage.de verfügbar.


Dieses Programm darf nicht für gewerbliche Zwecke genutzt werden.

*/

#define ADR_SEL 2
#define EM_ACK 4
#define DATA_IN 4

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>

// BETA V2.1
//

volatile uint8_t bytecount;					// Zähler für die empfangenen / gesendeten Bytes
volatile uint8_t flags;						// Flags zur internen Kommunikation
volatile uint8_t outputs[6];					// Array für Ausgabe (RoboInt -> EM)
volatile uint8_t inputs[6];					// Array für Inputs (EM -> RoboInt)

SIGNAL(SIG_SPI)									// Dieser Interrupt wird ausgelöst, sobald 8 Bits übertragen wurden
{
	outputs[bytecount] = SPDR;					// aktuell empfangenes Byte in das Array schreiben
	flags = 2;									// Modus an den Timer-Interrupt weitergeben
	OCR2 = 0x01;								// Verzögerung ca. 4 µs
	SFIOR = (1<<PSR2);							// Prescaler des Timers löschen (Verzögerung wird genauer)
	TCNT2 = 0x00;								// Timer mit 0 initialisieren
	TIFR |= (1<<OCF2);							// Interrupt-Flag löschen (durch setzen auf "1")
	TIMSK |= (1<<OCIE2);						// Timer-Interrupt freigeben
}

SIGNAL (SIG_OUTPUT_COMPARE2)					// Timer-Interrupt
{
	if (flags == 4)							// Modus: Bestätigungssignal ist gesendet, für nächstes Byte bereit machen
	{
		DDRD |= (1<<EM_ACK);					// Bestätigungssignal an das RoboInt auf Low legen
		TIMSK &=~(1<<OCIE2);					// Timer-Interrupt abschalten
			
		bytecount += 1;							// Zähler aktualisieren
			
		if (bytecount == 6)					// wurde das letzte Byte empfangen?
		{										// falls ja, dann
			flags |= 0x01;						// Modus setzen (kann im Hauptprogramm abgefragt werden, ob aktuelle Daten vorliegen)
			SPCR = 0x00;						// SPI-Schnittstelle abschalten
			DDRD &=~(1<<EM_ACK);				// Bestätigungssignal auf Tristate
			PORTB &=~(1<<DATA_IN);				// DATA_IN auf Tristate
		}
		else
		{										// falls nein, dann
			SPDR = inputs[bytecount];			// die SPI-Schnittstelle mit dem nächsten Input-Byte (wird an RoboInt gesendet) beschreiben
		}
	}
	if (flags == 2)							// Modus: Bestätigungssignal (kurzer Impuls) soll gesendet werden
	{
		DDRD &=~(1<<EM_ACK);					// Bestätigungssignal auf Tristate
		flags = 4;								// Modus auf 4 setzen (beim nächsten Timer-Interrupt wird ein Byte verarbeitet)
		TCNT2 = 0x00;							// Timer zurücksetzen
		OCR2 = 0x01;							// Verzögerungszeit definieren (ca. 4µs), der Vorteiler braucht in diesem Fall nicht
	}											// zurückgesetzt werden.
	
	if (flags == 8)							// Modus: Adresssignal wurde empfangen, die Übertragung beginnt nach einer Verzögerung von 700µs
	{
			TIMSK &=~(1<<OCIE2);				// Timer-Interrupt löschen
			if (bit_is_clear(PINB,ADR_SEL))	// nochmals prüfen, ob auch tatsächlich addressiert ist
			{									// falls ja, dann Übertragung initialisieren
				DDRD |= (1<<EM_ACK);			// Bestätigungssignal auf Low legen (EM sendet Bestätigung)
				bytecount = 0;					// Zähler initialisieren
				SPDR = inputs[0];				// das erste Byte in den SPI-Speicher laden
				SPCR = (1<<SPE)|(1<<CPOL)|(1<<CPHA)|(1<<SPIE); // SPI-Schnittstelle einschalten
			}
	}
}

SIGNAL (SIG_INTERRUPT0)							// Dieser Interrupt wird ausgelöst, wenn das EM adressiert wird.
{
	flags = 8;									// Modus setzen
	OCR2 = 160;									// 160 entspricht einer Verzögerung von 694µs
	SFIOR = (1<<PSR2);							// Vorteiler des Timers löschen
	TCNT2 = 0x00;								// Timer zurücksetzen
	TIFR |= (1<<OCF2);							// Evtl. vorher ausgelösten Interrupt löschen (durch beschreiben mit "1")
	TIMSK |= (1<<OCIE2);						// Timer-Interrupt freigeben. Nach Ablauf von ca. 700µs wird der Timer nun ausgelöst.
}

void init (void)
{
	DDRC = 0xFF;								// PORTA = Ausgang

	DDRB |= (1<<DATA_IN);						// Den Data-In-Port (EM -> RoboInt) auf Ausgang schalten

	TCCR2 = (1<<CS21)|(1<<CS20)|(1<<WGM21);	// Timer2 mit Prescaler 32 und CTC (Clear on Compare-Match) einschalten
	MCUCR |= (1<<ISC01);						// INT0 auf fallende Flanke einstellen
	GICR |= (1<<INT0);							// Externer Interrupt0 wird eingeschaltet
	

	sei();										// globale Interrupt-Freigabe
}

int main (void)
{
	inputs[0] = 0x00;							// Zustand der Eingänge
	inputs[1] = 0x00;							// Low-Byte des AX-Eingangs
	inputs[2] = 0x0F;							// unbekannt, evtl. weiteres Low-Byte für Analogwert
	inputs[3] = 0x31;							// unbekannt, evtl. weiteres Low-Byte für Analogwert
	inputs[4] = 0x12;							// zusammengefasste High-Bits (jeweils 2) für die Analogwerte
	inputs[5] = 0x00;							// Immer 0

	init();										// I/O und Interrupts initialisieren
	
	for (;;)									// Main-Loop
	{
		PORTC = ~outputs[1];					// aktuelles Ausgabebyte vom RoboInt auf PORTA ausgeben
	}

	return 0;
}
