/**************************************************************************
*Multiswitchmodul auf Basis eines Tiny44
*Ausgabe ueber einen Pin mit 33K, sowie eine zweiten Pin fuer das Sync-Signal
*
*Sync entweder über ein einzelnes Sync-Signal, oder ueber PPM-Summe
*
*Eingang des PPM-Signals an PB2 (INT0), Ausgabe an PB0 (normal) und PB1 (Sync)
*Alle Schalter sind per AD-Eingang angebunden
*
*
*Int0 triggert auf neg. Flanke und startet Timer0. Der laeuft max. 2ms, falls in
*der Zeit kein weiterer Int reinkommt, ist das Frame beendet, und der nächste Zustand 
*wird an den Ausgang gegeben. Falls zur Laufzeit ein Int kommt, wird der Timer resetet
*
*
***************************************************************************/
#include <avr/io.h>
#include <avr/interrupt.h>


/*************************************************************
*MAKROS und #defines
**************************************************************/
#define FLAG_VARIABLE GPIOR0
//FLAGS
#define  	NEXT_VAL		0



//Defines der Ausgangszustaende
#define SW_SYNC		0
#define SW_HIGH		1
#define SW_LOW		2
#define SW_MID		3

//Schalterzuordnung
#define AD_S1	0
#define	AD_S2	1
#define	AD_S3	3	
#define	AD_S4	2
#define	AD_S5	4
#define	AD_S6	5
#define	AD_S7	6
#define	AD_S8	7


//MAKROS
#define CHECK_FLAG(A) (FLAG_VARIABLE & (1<<A))
#define DEL_FLAG(A)	(FLAG_VARIABLE &= ~(1<<A))
#define SET_FLAG(A)	(FLAG_VARIABLE |= (1<<A))
//Schaltereingaenge


#define OBERE_SCHRANKE		158

#define UNTERE_SCHRANKE		78

#define OUT_PORT	PORTB
#define OUT_DDR		DDRB
#define	OUT_PIN		PB0
#define SYNC_PIN	PB1


/*************************************************************
*ISR
**************************************************************/
ISR(INT0_vect)
	{	
	//Timer starten oder zuruecksetzen
	//Timer-Int-Flag loeschen
	TIFR0 |= (1<<TOV0);
	//Zaehlerregister loeschen
//alte Version
//	TCNT0 = 208;		//Preset, um Periode von 3ms zu erreichen
//	TCCR0B |= (1<<CS01) | (1<<CS00);	//Prescaler 64, Timer startet
//neuere Version -> kleinere Pause

	//Timer reset
	TCNT0 = 0;
	TCCR0B |= (1<<CS01);	//Prescaler neu setzen
	
	}


ISR(TIM0_OVF_vect)
	//Timer anhalten, und neuen Zustand an den Ausgang geben, bzw. das Flag setzen
	{
	//Timer anhalten -> Prescaler loeschen
	TCCR0B &= 0xF8;

	SET_FLAG(NEXT_VAL);
	}




/*************************************************************
*funktionen
**************************************************************/

void init_ports(void)
	{
	//Schalterport als Eingang;
	DDRA = 0x00;
	
	DDRB  |= (1<<PB0) | (1<<PB1);		//die beiden Ausgangspins als Ausgang setzen
	}

void set_Ausgang(uint8_t zustand)
	{
	switch(zustand)
		{
		case SW_SYNC:
			OUT_PORT &= ~(1<<SYNC_PIN);
			OUT_DDR |= (1<<SYNC_PIN);
			break;
		case SW_LOW:
			//Sync abschalten
			OUT_DDR &= ~(1<<SYNC_PIN);
			OUT_PORT &= ~(1<<SYNC_PIN);
			//Pin auf Ausgang und low						
			OUT_DDR |= (1<<OUT_PIN);
			OUT_PORT &= ~(1<<OUT_PIN);
			break;
		case SW_MID:
			//Sync abschalten
			OUT_DDR &= ~(1<<SYNC_PIN);
			OUT_PORT &= ~(1<<SYNC_PIN);	
			//Pin auf Eingang und Pullup aus
			OUT_DDR &= ~(1<<OUT_PIN);
			OUT_PORT &= ~(1<<OUT_PIN);		
			break;
		case SW_HIGH:
			//Sync abschalten
			OUT_DDR &= ~(1<<SYNC_PIN);
			OUT_PORT &= ~(1<<SYNC_PIN);
			//Pin auf Ausgang und high						
			OUT_DDR |= (1<<OUT_PIN);
			OUT_PORT |= (1<<OUT_PIN);
			break;		
		default:
			//Sync abschalten
			OUT_DDR &= ~(1<<SYNC_PIN);
			OUT_PORT &= ~(1<<SYNC_PIN);
			//Pin auf Eingang und Pullup aus
			OUT_DDR &= ~(1<<OUT_PIN);
			OUT_PORT &= ~(1<<OUT_PIN);	
			break;
		}
	}


void init_int(void)
	{
	DDRB &= ~(1<<PB2);
//	PORTB |= (1<<PB2);						//Pullup an ->nicht noetig, da extern vorhanden
	MCUCR |= (1<<ISC01);					//triggern auf fallende Flanke
	GIMSK |= (1<<INT0);						//Int freigeben
	}


void init_timer0(void)
	{
	//Normal-Modus, Prescaler 8, OFV-Int an
//	TCCR0B |= (1<<CS01);			//Prescaler 8, wird erst durch den Int gestartet
	TIMSK0 |= (1<<TOIE0);			//OFV-Int angeschaltet
	}


void init_ADC(void)
	{
	//Digital-Puffer abschalten
	DIDR0 = 0xFF;

	ADCSRB |= (1<<ADLAR);				//AD-Ergebnisse linksbuendig ->fuer ein 8-Bit-Ergebniss nur ADCH noetig
	ADCSRA |= (1<<ADPS1);				//prescaler 4 ->250kHz		// | (1<<ADPS0);	//Prescaler auf 8 setzen
	ADCSRA |= (1<<ADEN);				//AD-Wandler anwerfen

	//Referenzspannung: VCC

	ADCSRA |= (1<<ADSC);              		// eine ADC-Wandlung, um den Wandler warmlaufen zu lassen
	while ( ADCSRA & (1<<ADSC) ) {}  		// auf Abschluss der Konvertierung warten 
	
	}

/**/

uint8_t ADCRead(uint8_t mux)		//liefert nur 8 Bit zurueck
	//AD-Wandler-Funktion, bekommt den Kanal übergeben, liefert das Ergebnis zurück
	{
	uint8_t result;
	result = 0;         			
	ADMUX = mux;           // Kanal waehlen (falls andere Referenz als VCC -> aufpassen, liegt im gleichen register)
	//Eigentliche Messung
	ADCSRA |= (1<<ADSC);            	// eine Wandlung "single conversion"
	while ( ADCSRA & (1<<ADSC) ) {}		// auf Abschluss der Konvertierung warten

	result = ADCH;		   				//die obersten 8 Bit interessant -> nur das High-Byte lesen
 	return  result;
	}

uint8_t get_schalter_richtung(uint8_t schalter)
	//Passt die Richtung der Schalter an, da sie durchs routen unterschiedlich an VCC ung GND hängen
	//-> Schalter nach oben ergibt in jedem Fall "low", Schalter nach unten "high"
	//-> wie beim Robbe-Orginal
	{
	if ((schalter == 1) || (schalter == 2) || (schalter == 3) || (schalter == 4))
		{return 1;}
	else
		{return 0;}
	}


/*************************************************************
*Main
**************************************************************/
int main(void)
	{
	uint8_t frame_counter;
	uint8_t ad_ergebnis;	

	frame_counter = 0;
	ad_ergebnis	= 0;

	FLAG_VARIABLE = 0;

	init_ports();
	init_ADC();
	init_int();
	init_timer0();
	set_Ausgang(0xff);

	sei();

	for(;;)
		{

/**/
		if(CHECK_FLAG(NEXT_VAL))	//Timer abgelaufen, neues Ergebniss anlegen
			{
			DEL_FLAG(NEXT_VAL);
			frame_counter++;
			switch (frame_counter)
				{
				case 1:
					ad_ergebnis = ADCRead(AD_S1);
					break;
				case 2:
					ad_ergebnis = ADCRead(AD_S2);
					break;
				case 3:
					ad_ergebnis = ADCRead(AD_S3);
					break;
				case 4:
					ad_ergebnis = ADCRead(AD_S4);
					break;
				case 5:
					ad_ergebnis = ADCRead(AD_S5);
					break;
				case 6:
					ad_ergebnis = ADCRead(AD_S6);
					break;
				case 7:
					ad_ergebnis = ADCRead(AD_S7);
					break;
				case 8:
					ad_ergebnis = ADCRead(AD_S8);
					break;
				default:
					frame_counter = 0;	//->Sync ausloesen
					break;
				}		
			if(frame_counter == 0)		//->Sync
				{set_Ausgang(SW_SYNC);}

			else	//kein sync, entsprechend Ausgang setzen
				{
				if (ad_ergebnis > OBERE_SCHRANKE)
					{
					if (get_schalter_richtung(frame_counter))
						{set_Ausgang(SW_HIGH);}
					else
						{set_Ausgang(SW_LOW);}
					}
				else
					{
					if (ad_ergebnis < UNTERE_SCHRANKE)
						if (get_schalter_richtung(frame_counter))
							{set_Ausgang(SW_LOW);}
						else
							{set_Ausgang(SW_HIGH);}
					else
						{set_Ausgang(SW_MID);}
					
					}
				}
			}	//if(CHECK_FLAG(NEXT_VAL))
		}	//for(;;)
	
	return 0;
	}
