#include	<avr/io.h>
#include	<avr/interrupt.h>
#define		F_CPU 16000000UL									// definiere Prozessortakt auf 16MHz
#include	<util/delay.h>

#define Baud 9600UL												// Baudrate definieren
#define R_UBRR ((F_CPU / (16L * Baud))-1)						// Baudrate umrechnen für das Baudraten-Register

volatile unsigned int 	ADC0 = 0;						// der Variable ADC0 (Mittelwert) wird der Wert 0 zugeordnet
volatile unsigned int 	ADC1 = 0;						// ADC1 (Wert des ADC1) wird der Wert 0 zugeorndet
volatile unsigned int	M1 = 0;							// Impulse M1
volatile unsigned int	M2 = 0;							// Impulse M2
volatile unsigned int 	M1_Impulse = 0;					// Impulse Motor 1
volatile unsigned int 	M2_Impulse = 0;					// Impule Motor 2

volatile unsigned char 	Umdrehungen = 0;				// Variable für Umdrehungen pro Richtungswechsel
volatile unsigned char	t_Messung = 0;
volatile unsigned char 	Freigabe = 0;					// wird auf 1 gesetzt, wenn Interrupt 0 stattgefunden hat
volatile unsigned char	OVF_Timer = 0;
volatile unsigned char	INT_0_1 = 0;
volatile unsigned char	vect_INT2 = 0;

unsigned int			ADC_10 = 0;						// Variable zur Addition der ADC-Messwerte
unsigned int			adc = 0;						// Zwischenspeicher des ADC-Wertes

double					Proz_1;							// Variable für prozentualen Drehzahl-Anteil des ADC
double					PWM_1;							// Variable für die PWM in Abhängigkeit von der eingestellten Drehzahl
double 					Quotient;						// Quotient für Umrechnung der Drehzahl (ADC1) in Prozent
double 					Multiplikator;					// Multiplikator zur Umrechnung der Drehzahl nach PWM


// ### Initialisierung der USART

	void usart_init(void) {

	UBRRH = R_UBRR >> 8;										/* R_UBRR in UBRRH schreiben &
																	um 8 Stellen nach rechts verschieben
																		in UBRRH steht: 0000 0000 */
	UBRRL = R_UBRR;												// R_UBRR in UBRRL schreiben

	UCSRC |= (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);				/* Zuweisung an UCSRC richten;		
																	Datenformat einstellen: 8 Datenbits, 1 Stopp-Bit */
	UCSRB |= (1<<TXEN);											// Sender aktivieren
	}
 



/* ###	Initialisierung des 16-Bit Timer1

		- 9-Bit PWM-Mode
		- Fast-PWM
		- Vorteiler von 1024 --> 15,625 kHz
		- schreibe 625 ins Vergleichsregister --> 25 Hz
		- Zählwerte für OCR1A & OCR1B festlegen
		- PWM-Art bestimmen
		- OC1A & OC1B sind Ausgänge
 */

	
	
	void timer_init () {

		TCCR1A |= (1<<WGM11)| (0<<WGM10);									// 9-Bit PWM-Betriebsart
		TCCR1B |= (1<<WGM13) | (1<<WGM12);									// Fast-PWM	
		ICR1 |= 625;														// zähle bis 625 --> 25Hz
		OCR1A |= 312;														// PWM an OC1A mit 50% Duty-Cycle
		OCR1B |= 312;														// PWM an OC1B mit 25% Duty-Cycle
		TCCR1A |= (1<<COM1A1) | (1<<COM1A0) | (1<<COM1B1) | (1<<COM1B0);	// invertierende PWM
		DDRD = (1<<PD5) | (1<<PD4);											// OC1A & OC1B als Ausgänge definieren
	 	TCCR1B |= (1<<CS10) | (1<<CS12);									// Vorteiler von 1024 --> 15,625kHz / Zählbeginn
 	}


// ### Initialisierung des Analog-Digital Wandlers mit Referenzspannung AVCC 5V
	
	void adc_init () {
		ADMUX |= (1<<REFS0);										/* AVCC als Referenzspannung
																		5V				*/													
		ADCSRA |= (1<<ADPS0) | (1<<ADPS1) | (1<<ADPS2);				/* Wandelrate des ADC festlegen,
																		Wandelrate = Takt/128 --> 125kHz */
		ADCSRA |= (1<<ADEN);										// Enable ADC
	}


// Trigger-Quelle für ADC1 einstellen
	void trigger_adc1 (){
		TIMSK |= (1<<TICIE1) | (1<<TOIE1);										// Enable Timer Interrupt
		
	}

// Initialisierung der externen Interrupts
	void ext_int_init () {
		MCUCR |= (1<<ISC11) | (1<<ISC10) | (1<<ISC01) | (1<<ISC00);
		MCUCSR |= (1<<ISC2);
		GICR |= (1<<INT1) | (1<<INT0) | (1<<INT2);
		
	}


// Zehn Messwerte des ADC aufnehmen	
	void ADC_10_Messung () {								// zehnmal mit dem ADC messen
		ADC0 = 0;											// Variablen zurück auf 0 setzen
		ADC_10 = 0;
		adc = 0;
		cli();												// Interrupt des Timer1 deaktivieren
		for (unsigned char i = 10; i > 0; i--){				// for-Schleife: beginne mit i = 10;
															// betreten solange i > 0;
															// wenn einmal durch: i - 1
			ADCSRA |= (1<<ADSC);							// AD-Wandlung starten
			while (ADCSRA & (1<<ADSC)); 					// wenn Wandlung abgeschlossen
				adc = ADC;									// ADC-Wert in adc schreiben
				ADC_10 = ADC_10 + adc;						// Wert des ADC (adc) mit ADC_10 addieren & in ADC_10 schreiben
		}
		sei();												// Interrupts aktivieren
	}  
	
// ADC Mittelwert berechnen
	void ADC_Mittelwert () {
		ADC0 = ADC_10 / 10;									// Summe der 10 Messwerte durch 10 teilen & in ADC0 schreiben
	}

//Umdrehungszahl setzen
	void Umdrehungszahl (){
		Umdrehungen = 0;
			if (ADC0 <= 1023){
				Umdrehungen = 30;
			}
			
			if (ADC0 <= 950){
				Umdrehungen = 25;
			}
			
			if (ADC0 <= 800){
				Umdrehungen = 20;
			}
				
			if (ADC0 <= 650){
				Umdrehungen = 15;
			}
			
			if (ADC0 <= 500){
				Umdrehungen = 10;
			}
			
			if (ADC0 <= 350){
				Umdrehungen = 5;
			}
			
			if (ADC0 <= 200){
				Umdrehungen = 0;							// Leerlauf
			}
			
			if (ADC0 <= 50){
				Umdrehungen = 50;							// STOPP!
			}
	}	
		
// Umdrehungen an USART ausgeben
void UDZ_zu_USART () {
			// Umdrehungen senden
			while(!(UCSRA & (1<<UDRE)));				// Warten bis Sendepuffer leer / bereit zum Senden?
			UDR = Umdrehungen;							// 'Umdrehungen' ins Sendedatenregister ('UDR') schreiben
			//  'carriage return' in ASCII
			while(!(UCSRA & (1<<UDRE)));				// Warten bis Sendepuffer leer / bereit zum Senden?				
			UDR = 0x0D;									// ASCII-Code für Zeilenanfang
			// 'line feed' in ASCII
			while(!(UCSRA & (1<<UDRE)));				// Warten bis Sendepuffer leer / bereit zum Senden?
			UDR = 0x0A;									// ASCII-Code für Zeilenumbruch
	}

void Drehzahl_R () {
	Quotient = 10,23;
	Multiplikator = 6,25;
	
	Proz_1	=	ADC1 / Quotient;
	PWM_1	=	Proz_1 * Multiplikator;
	}


void PWM_OCR1 () {
	OCR1A	=	PWM_1;								// Drehzahlwert in OCR1A schreiben
	OCR1B	=	PWM_1;								// Drehzahlwert in OCR1B schreiben
}	


void t_Drehzahl () {
	vect_INT2 = 1;
	if (t_Messung == 6){
		vect_INT2 = 0;
		M1 = M1_Impulse;
		M2 = M2_Impulse;
		M1_Impulse = 0;
		M2_Impulse = 0;
		t_Messung = 0;
		}											// evtl. mehrfach messen
}
	
	
// gemessene Impulse von M1 an USART ausgeben
	void M1_zu_USART () {
			// High-Byte senden
			while(!(UCSRA & (1<<UDRE)));				// Warten bis Sendepuffer leer / bereit zum Senden?
			UDR = M1/256;								// das gleiche wie: UDR = ADC0>>8; ADC0 um 8 Stellen nach rechts schieben &
														// in UDR (Sendedatenregister) schreiben*/
			// Low-Byte senden
			while(!(UCSRA & (1<<UDRE)));				// Warten bis Sendepuffer leer / bereit zum Senden?				
			UDR = M1;									// ADC0 mit '0000 0000 | 1111 1111' &-vergleichen und in UDR schreiben
														// andere Möglichkeit: UDR = ADC0;
		//  'carriage return' in ASCII
			while(!(UCSRA & (1<<UDRE)));				// Warten bis Sendepuffer leer / bereit zum Senden?				
			UDR = 0x0D;									// ASCII-Code für Zeilenanfang
			
			// 'line feed' in ASCII
			while(!(UCSRA & (1<<UDRE)));				// Warten bis Sendepuffer leer / bereit zum Senden?
			UDR = 0x0A;									// ASCII-Code für Zeilenumbruch
	}




// #### Interrupt Service Routines ####

ISR (TIMER1_CAPT_vect) { 
		ADMUX |= (1<<MUX0);											// auf ADC1 umschalten
		ADCSRA |= (1<<ADSC);										// Wandlung starten
		while (ADCSRA & (1<<ADSC));									// Wandlung abgeschlossen?
		ADC1 = ADC;													// Wert des ADC in ADC1 schreiben
		ADMUX &=~ (1<<MUX0);										// auf ADC0 umschalten
}



ISR (TIMER1_OVF_vect) {
	if (OVF_Timer = 1){
		t_Messung++;
		INT_0_1 = 1;
	}
}



ISR (INT2_vect){
	if (vect_INT2 == 1){
		OVF_Timer = 1;
	}
}

	
	
ISR (INT1_vect){
	if (INT_0_1 == 1){
	M1_Impulse++;
	}
}



ISR (INT0_vect){
	if (INT_0_1 == 1){
	M2_Impulse++;
	}
}



// ##### Mainloop #####

int main (void) {											// Mainloop
		
	usart_init ();											// USART initialisieren
	adc_init ();				 							// ADC0 initialisieren
	timer_init ();											// Initialisierung des Timers
	trigger_adc1 ();
	ext_int_init ();
	
	sei ();													// Interrupts aktivieren

	while (1){
		
		ADC_10_Messung ();								// zehnmal mit dem ADC messen
		ADC_Mittelwert ();								// Mittelwert des ADC berechnen
		Umdrehungszahl ();								// Zahl der Umdrehungen pro Richtungswechsel
		//UDZ_zu_USART ();								// Umdrehungszahl an USART ausgeben*/
		Drehzahl_R ();									// Umrechnung ADC1 --> Drehzahl
		PWM_OCR1 ();									// eingestelltes Impuls-Pausenverhältnis übertragen
		t_Drehzahl ();									// Drehzahl für eine bestimmte Zeit messen
		M1_zu_USART ();									// Ausgabe der gemessenen Impulse von M1 an USART
		// M2_zu_USART ();								// Ausgabe der gemessenen Impulse von M2 an USART
		//_delay_ms(3000);								// 3 Sekunden warten
	}
		
	return 0;
}
