/*
;************ DIFFERENZIER - INVERTER - BAUGRUPPE (DiffEasy) für Fernsteuerungen **************
						mit ATMEL ATtiny2313
 +++++++++++++++++++++++++++	FUNKTIONSUMFANG ++++++++++++++++++++++++++++++++++++++++
Für Fernsteueranlagen mit positiven Kanalimpuls.
Wert des Kanalmittelimpulses kann geändert werden
Es sind neun Differenzierungsstufen einstellbar.
Invertierung der Ausgangsrichtung konfigurierbar.
Invertierung der Differenzierrichtung einstellbar
Moduseinstellung im Konfigurationsmodus erfolgt durch Tastendruck und einer
dementsprechenden Blinkanzahl der integrierten LED.
Die Konfigurationseinstellungen beginnen erst nach einer Dauer von 3 Sekunden,
dadurch ist sichergestellt, dass ein beabsichtigter Tastendruck erfolgte.
Nach Schreiben der Konfigurationsdaten in den EEPROM erfolgt eine Bestätigung
durch die entsprechende Blinkanzahl in einer 0,5 s Blinkfrequenz. 
Einstellungen im Konfigurationsmodus bleiben im spannunglosen Zustand erhalten,
da die Daten im EEPROM abgelegt werden.
Die Watchdog - Funtionalität des Mikrocontrollers wird genutzt
*/
/*
---------------------------  ANWENDUNG ------------------------------------------------------
DIFFERENZIERFUNKTION:	Es erfolgt eine Differenzierung der Ausgangsimpulse, in eine Richtung.
						Ein Ruder soll zum Beispiel im Kurvenflug nach unten weniger ausschlagen als nach oben
						Diese Funktion ist in neun Stufen einstellbar.

INVERTIERUNG:			Die Ausgänge Output 1 und Output 2 können im Konfigurationsmodus invertiert werden
						Dies ermöglicht die Umkehrung der Servobewegung
						Die Differenzierrichtung kann ebenfalls invertiert werden.
-------------------------------------------------------------------------------------------------------
*/
/*
###################### Blinkanzal LED im Konfigurationsmodus ########################################
Taster muss erst 3 Sekunden aktiv sein, sonst keine Aktivierung der LED und des Konfigurationsmodusses
	Blinkanzahl				Wirkung
	1 x						Einlesen Mittelimpuls und Diff 0
	2 x						Diff 10%
	3 x						Diff 20%
    4 x						Diff 30%
	5 x						Diff 40%
	6 x						Diff 50%
	7 x						Diff 60%
	8 x						Diff 70%
	9 x						Diff 80%
	10 x					Diff 90%
	11 x					Invertierung Servorichtung
	12 x					Invertierung Differenzierung
	13 x und mehr			Invers zurück
;################################################################################################
*/
/*
................ BEDINGUNGEN UND KURZBESCHREIBUNG DER FUNKTIONSWEISE........................
Das Eingangssignal ist an PD2 Interrupt Int0 und an Capture-Eingang PD6 Capture-Eingang (ICP) angeschlossen
Output 1 an PB1
Output 2 an PB0
Taster an PD5
LED an PD4
Signal startet über L/H Flanke an PD2 INT0 den Timer 1
H/L Flanke des Signals steuert Capture - Eingang
Durch Auslesen der Captureregister -> Messung der Impulsbreite
Je nach Konfiguration wird der erste Kanalimpuls differenzial bearbeitet an den 
Ausgängen Output 1 und Output 2 zur Verfügung gestellt
die Ausgänge Output 1 und Output 2 können invertiert zur Verfügung gestellt werden
Differenzierrichtung kann auch invertiert werden
*/
/*=================================================================
Port-map of the MCU
++++++++++++++++++
PD2 	= Int0 Channel 1
PD6 	= ICP Capture Input from Channel 1
PB1 	= Output 1
PB0		= Output 2
PD5		= button
PD4		= LED
===================================================================
*/

// INCLUDES ========================================================

#include <stdlib.h>
#include <avr/interrupt.h>
#include <avr/wdt.h> 
#include <avr/eeprom.h>
#include <avr/pgmspace.h>

#include "MC_HF.h"				// private Include-File

// END INCLUDES ===================================================
// Forward Declaration  ================================================
void init(void);
void calculation(void);
void readEEPROM(void);
void writeEEPROM(void);
void ProgEvaluation(void);
// End Forward Declaration =============================================

// Definitions of EEPROM Place ==========================================
unsigned char  __attribute__ ((section (".eeprom"))) version[] = "Vers.0.98";
uint16_t  __attribute__ ((section (".eeprom"))) eeMiddle = 1500;
uint8_t  __attribute__ ((section (".eeprom"))) eeModus = 2; 
// End Definitions of EEPROM Place =======================================

// RAM Variables/Pointers/etc ============================================
volatile uint16_t  MiddelValue, uChannel, uDiffValue, Output;
volatile uint8_t direction;
volatile uint16_t capture_Inp;
uint8_t Modus, ProgCounter, WaitCounter, LEDCounter;
uint8_t Calibration_Value;
// End RAM Variables/Pointers /etc========================================

// ############## Handler by Interrupt #####################

ISR(INT0_vect)						/* signal handler for External Interrupt0 */
{
	TCNT1 = 0x00;						// 16-bit Register auf Wert (Null)bei 16-bit Timer/Counter1
	TCNT0 = 216;						// Initialisierung mit 5ms Timer0 Overflow (5ms/0,128ms=39)
										// 255-39 = 216
	TIFR |=  _BV(TOV0) | _BV(TOV1);		// clear Overflow0_Flag and Overflow1_Flag
	TIMSK |=  _BV(ICIE1) | _BV(TOIE0);	// Input Capture Interrupt Enable
									 	// Timer/Counter0 Overflow Interrupt Enable
	direction |= _BV(fl_Int0);			// Bit setzen
	
}

//--------------------------------------------------------
ISR(TIMER1_CAPT_vect )				/* signal handler for Input Capture1 Interrupt */
{
	uint16_t temp_capture;
	TIMSK &= ~_BV(ICIE1);				// Capture Interrupt Disable
	temp_capture = ICR1;				// Timer/Counter1 Input 
	if((temp_capture >= cMinCapture)&&(temp_capture <= cMaxCapture))capture_Inp = temp_capture;			
										// security risk reduce
										// Input from Capture Register
}

// -------------------------------------------------------
ISR(TIMER0_OVF_vect)					/* signal handler for Overflow0 Interrupt  */
/*###################################################################
Vorbereitung für die Berechnung von Augangswerten 
Setzt Schalter für die Berechnung von Augangswerten 
Initialisiert den Timer1 für Realisierung der Ausgangsimpulse
################################################################### */

{
	if bit_is_set(direction, fl_Int0){
			direction&=~ _BV(fl_Int0);		// Bit INT0 löschen wenn vorher Int0
			direction|= _BV(fl_calculation);// Berechnung soll durchgefürt werden
			direction |=_BV(fl_setOutp_1);
	}
// ************* Outp Channel 1**********************
	else if bit_is_set(direction, fl_setOutp_1){
		
		if bit_is_clear(Modus, fl_DiffInvers){
			if (uChannel> MiddelValue) Output = uDiffValue;
			else Output = uChannel;
		}
		else if bit_is_set (Modus, fl_DiffInvers) {
			if (uChannel < MiddelValue) Output = uDiffValue;
			else Output = uChannel;
		}
		PORTB |=	_BV(Out_1);			// PIN set
		TIMSK |= _BV(TOIE1);			// Timer/Counter1 Overflow Interrupt Enable
		TCNT1 = 0xffff - Output;		// initializes Timer1 for Output 1
		TCNT0 = 235;					// initializes for 2,5ms Timer0 Overflow(2,5ms/0,128ms=20)
	}									// 255-20 = 235

// ************* Outp Channel 2**********************	
	else if bit_is_set(direction, fl_setOutp_2){

		if bit_is_clear(Modus, fl_DiffInvers){
			if (uChannel< MiddelValue) Output = uDiffValue;
			else Output = uChannel;
		}
		else if bit_is_set (Modus, fl_DiffInvers){
			if (uChannel > MiddelValue) Output = uDiffValue;
			else Output = uChannel;
		}
		PORTB |= _BV(Out_2);			// PIN set
		TCNT1 = 0xffff - Output;		// initializes Timer1 for Output2
		TIMSK &=~_BV(TOIE0);			// Timer/Counter0 Overflow Interrupt Disable									 	
	}
}

// -------------------------------------------------------
ISR(TIMER1_OVF_vect)					/* signal handler for Overflow1 Interrupt  */
/*###################################################################
Realisiert die Ausgabe der Impulse nach jedem empfangenen Eingangsimpuls im Normalbetrieb
Im Konfigurationsmodus erfolgt in dieser Routine die Ansteuerung der LED
################################################################### */

{
// Output 1
	if bit_is_set(direction, fl_setOutp_1){
	 	PORTB &= ~_BV(Out_1);				// PIN clear
	 	direction &=~_BV(fl_setOutp_1);		// clear Bit Flag 
		direction |=_BV(fl_setOutp_2);
	}										
// Output 2
	else if bit_is_set(direction, fl_setOutp_2) {
		PORTB &= ~_BV(Out_2);				// PIN clear
		direction &=~_BV(fl_setOutp_2);		// clear Bit Flag 
		TIMSK &= ~_BV(TOIE1);				// Timer/Counter1 Overflow Interrupt disable
	}
// *****************Function by press Button down and LED aktiv********************
	else if bit_is_set(direction,fl_ButtonDown) {// is Flag set
		if bit_is_clear(PIND,button){			// is Button down
			if (WaitCounter++ >=6){
				if bit_is_clear(PIND, LED){		// is LED off
					PORTD |= _BV(LED);			// then LED on
					ProgCounter++;				// and increment ProgCounter
					LEDCounter = ProgCounter *2;
				}
				else PORTD &=~_BV(LED);			// otherwise LED off
			}
		}
		else {
			direction &=~_BV(fl_ButtonDown);	// clear Flag
			PORTD &=~_BV(LED);					// LED off
			WaitCounter= 0;
		}
		TCNT1 = cTimer1_button;					// value to 0.5 sec.
	}
	else if bit_is_set(direction, fl_LED_activ) {
		if (LEDCounter-- >0) {
			if bit_is_clear(PIND, LED){			// is LED off
				PORTD |= _BV(LED);				// then LED on
			}
			else PORTD &=~_BV(LED);				// otherwise LED off
			TCNT1 = cTimer1_LED;				// value to 0.2 sec.
		}
		else {
			direction &=~_BV(fl_LED_activ);		// clear Flag
			PORTD &=~_BV(LED);
			ProgCounter=LEDCounter = 0;

		}
	}
}
// ############## Ende Handler by Interrupt #####################
void init(void)
/*###################################################################
Initialisierung mit allen erforderlichen Werten
################################################################### */

{
//  ----------------------------- Timer ----------------------
	TCCR0B = _BV(CS02)|_BV(CS00);	// Timer0 = CK/1024 bei 8MHz Crystal alle 0,128ms pro Takt
	TCCR1B= _BV(CS11);				// Timer1 = CK/8 bei 8MHz Crystal alle 1µs pro Takt
									// Capture ist auf fallende Flanke eingestellt bei Pin (ICP)
// ------------------------ extern Interrupt ----------------
	MCUCR =   _BV(ISC01) | _BV(ISC00);	// Steigende Flanke INT0  generiert einen Interrupt
	GIMSK |= _BV(INT0);					// INT0: Externer Interrupt Enable
	sei();								// alle Interrupts enable

// ------------------------- Port Definition ------------------
	DDRB = 0x03;						// data-direction for PortB ('1' = Output)
	DDRD = 0x10;						// data-direction for PortD ('1' = Output)
	PORTD |= _BV(PD5);					// PullUp Resistor set (button)
// --------------------------------------------
// Wait integrieren -> Zeit zum Einlesen MittenImpuls
// Setzen Mittenimpuls -> auch in EEPROM
}


void readEEPROM(void)
/*###################################################################
Einlesen aller erforderlichen Daten
beim Start der Baugruppe
################################################################### */
{
	uint16_t* ptr_eeprom_16;
	uint8_t* ptr_eeprom_8;
	ptr_eeprom_16 = &eeMiddle;
	MiddelValue = eeprom_read_word(ptr_eeprom_16) ;		// MiddelValue
	ptr_eeprom_8 = &eeModus;
	Modus = eeprom_read_byte(ptr_eeprom_8) ;			// what is the modus
/* -------AUSLESEN UND SETZEN DES KALIBRIERUNGSBYTES ------------------------------*/ 
	_EEGET(Calibration_Value,E2END); 					// Kalibrationsbyte auslesen
	OSCCAL = Calibration_Value;							// Kalibrationsbyte setzen
/* -------------------------------------------------------------------------------- */
}

void writeEEPROM(void)
/*###################################################################
nach Beenden der Konfiguration werden Daten in EEPROM geschrieben
################################################################### */
{
	uint16_t* ptr_eeprom_16;
	uint8_t* ptr_eeprom_8;
	ptr_eeprom_16 = &eeMiddle;
	while (!eeprom_is_ready());						// write the MiddelValue in EEPROM
		eeprom_write_word(ptr_eeprom_16, MiddelValue) ;
	ptr_eeprom_8 = &eeModus;
	while (!eeprom_is_ready());
		eeprom_write_byte(ptr_eeprom_8, Modus ) ;	// write the Modus in EEPROM
}

void ProgEvaluation(void)
/*###################################################################
wird während des Konfigurationsmodusses aufgerufen
################################################################### */
{
	switch(ProgCounter)
		{
		case 1: 	MiddelValue = capture_Inp;	
					Modus = Diff_0;
						break;
		case 2: 	Modus = Diff_1;
						break;
		case 3: 	Modus = Diff_2;
						break;
		case 4: 	Modus = Diff_3;
						break;
		case 5: 	Modus = Diff_4;
						break;
		case 6: 	Modus = Diff_5;
						break;
		case 7: 	Modus = Diff_6;
						break;
		case 8: 	Modus = Diff_7;
						break;
		case 9: 	Modus = Diff_8;
						break;
		case 10: 	Modus = Diff_9;
						break;
		case 11:	Modus |= _BV(fl_Invers);
						break;				
		case 12:	Modus |=_BV(fl_DiffInvers);
						break;			
		default: 	Modus &= 0x0f; 		// Invers zurück
						break;
		}
}

void calculation(void)
/*###################################################################
Aufruf erfolgt nach Setzen eines Schalters in der Timer0 Routine
################################################################### */
{
	int16_t channel1Value, OutDiff;
	uint8_t tmp_Modus;
	channel1Value = capture_Inp - MiddelValue;			// calculation value to MiddleIValue
	if (channel1Value < 0) direction |= _BV(fl_Negativ);// if value <  MiddleIValue
		else direction &= ~_BV(fl_Negativ);				// or clear then positive
	if bit_is_set(Modus, fl_Invers){					// change Flag by Invers Modus
		if bit_is_set(direction, fl_Negativ) direction &= ~_BV(fl_Negativ);
		else	direction |= _BV(fl_Negativ);
	}
	uChannel = abs(channel1Value);						// result is absolute value
// *************************************************************************************************
	tmp_Modus = Modus & 0x0f;
	switch (tmp_Modus)
	{
		case Diff_0: 	uDiffValue = uChannel;	// keine Diff.
								break;						
				
		case Diff_1:	uDiffValue = (uChannel *100)/110;
								break;
		
		case Diff_2:	uDiffValue = (uChannel *100)/120;
								break;
	
		case Diff_3:	uDiffValue = (uChannel*100)/130;
								break;

		case Diff_4:	uDiffValue = (uChannel*100)/140;
								break;
		
		case Diff_5:	uDiffValue = (uChannel*100)/150;
								break;
	
		case Diff_6:	uDiffValue = (uChannel*100)/160;
								break;
		
		case Diff_7:	uDiffValue = (uChannel*100)/170;
								break;
	
		case Diff_8:	uDiffValue = (uChannel*100)/180;
								break;

		case Diff_9:	uDiffValue = (uChannel*100)/190;
								break;

	    default:				break;
	}
	if bit_is_set(direction, fl_Negativ) OutDiff = uChannel- (uChannel*2);
	else	OutDiff = uChannel;
	uChannel = OutDiff + MiddelValue;			// result is uChannel 
	if bit_is_set(direction, fl_Negativ) OutDiff = uDiffValue - (uDiffValue *2);
	else	OutDiff = uDiffValue ;
	uDiffValue  = OutDiff + MiddelValue;		// result is uDiffValue
	TCNT0 = 255;								// Timer0 Overflow gleich aufrufen										
}
// *************************************************************************************************
int main (void)
{
beginn:	
	wdt_enable(WDTO_120MS);
	readEEPROM();										// first step by start
	init();	
			
	for (;;) {
		wdt_reset();
		if(bit_is_clear(PIND,button)){					// button question
			PORTB &= ~_BV(Out_1) & ~_BV(Out_2);			// Output's clear
			wdt_disable();   
			GIMSK &=  ~_BV(INT0);						// INT0: External Interrupt Request disable
			TIMSK &= ~_BV(TOIE0) & ~_BV(ICIE1);			// Timer/Counter0  Overflow and Capture Interrupt disable
			TCCR1B= _BV(CS11) | _BV(CS10) ;				// Timer1 = CK/64 by 8MHz Crystal all 8µs one step
			TIFR  |=  _BV(TOV0) | _BV(TOV1);			// clear Overflow0_Flag and Overflow1_Flag
			TCNT1 = cTimer1_button;						// value to 0.5 sec.
			TIMSK |= _BV(TOIE1);						// Timer/Counter1 Overflow enable
			direction =_BV(fl_ButtonDown); 
			loop_until_bit_is_clear(direction,fl_ButtonDown);// Wait
			TIMSK &= ~_BV(TOIE1);						// Timer/Counter1 Overflow Interrupt disable
				if (ProgCounter >=1){
					ProgEvaluation();					// evaluate Prog Modus
					writeEEPROM();						// write values to EEPROM
					TIFR  |=  _BV(TOV0) | _BV(TOV1);	// clear Overflow0_Flag and Overflow1_Flag
					TCNT1 = cTimer1_LED;				// value to 0.25 sec.
					direction |=_BV(fl_LED_activ);		// set bit Flag
					TIMSK |= _BV(TOIE1);				// Timer/Counter1 Overflow enable
					loop_until_bit_is_clear(direction, fl_LED_activ);// Wait
				}
			goto beginn;
		}
		if bit_is_set(direction, fl_calculation){
		direction&=~_BV(fl_calculation);		
		calculation();									// die Berechnung wird aufgerufen
		}
	}	
	return (0);
}

