// ##################################################################################################################

// ##### Projekt Fadensäge ##### S.P.#####

#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;							// Speicher für Impulse M1
volatile unsigned int	M2 = 0;							// Speicher für Impulse M2
volatile unsigned int 	M1_Impulse = 0;					// gezählte Impulse Motor 1
volatile unsigned int 	M2_Impulse = 0;					// gezählte Impule Motor 2

volatile unsigned char 	Umdrehungen = 0;				// Variable für Umdrehungen pro Richtungswechsel
volatile unsigned int	t_Messung = 0;					// Zeitkonstante für die Dauer der Impulsmessung
volatile unsigned char	vect_INT2 = 0;					// Variable zum Freigeben des externen Interrupts INT2
volatile unsigned char	OVF_Timer = 0;					// Variable zum Freigeben des Timer1 Overflow Interrupts
volatile unsigned char	INT_0_1 = 0;					// Variable zum Freigeben der externen Interrupts INT0 & INT1


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######



// ### 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 0 --> 16MHz
		- schreibe 1600 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 |= 1600;														// zähle bis 625 --> 10kHz
		OCR1A |= 800;														// PWM an OC1A mit 50% Duty-Cycle
		OCR1B |= 800;														// 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);												// CPU-Takt --> 16MHz / 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 Timer1 Interrupt
																	// Capture & Overflow		
	}



// ### Initialisierung der externen Interrupts ###
	
	void ext_int_init () {
		MCUCR |= (1<<ISC11) | (1<<ISC10) | (1<<ISC01) | (1<<ISC00);	// steigende Flanke löst Interrupt an INT0 & INT1 aus
		MCUCSR |= (1<<ISC2);										// steigende Flanke löst Interrupt an INT2 aus
		GICR |= (1<<INT1) | (1<<INT0) | (1<<INT2);					// INT0...2 aktivieren
		
	}




// ################################################################################################################

// ##### Funktionen / Unterprogrammme #####


// ### 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 wieder 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 (){									// Umdrehungszahl pro Richtungswechsel in Abhängigkeit von ADC0 setzen
		Umdrehungen = 0;									// Umdrehungen auf 0 setzen
			if (ADC0 <= 1023){								// Mittelwert <= 1023 ?
				Umdrehungen = 30;							// 30 Umdrehungen
			}
			
			if (ADC0 <= 950){								// Mittelwert <= 950 ?
				Umdrehungen = 25;							// 25 Umdrehungen
			}
			
			if (ADC0 <= 800){								// Mittelwert <= 800 ?
				Umdrehungen = 20;							// 20 Umdrehungen
			}
				
			if (ADC0 <= 650){								// Mittelwert <= 650 ?
				Umdrehungen = 15;							// 15 Umdrehungen
			}
			
			if (ADC0 <= 500){								// Mittelwert <= 500 ?
				Umdrehungen = 10;							// 10 Umdrehungen
			}
			
			if (ADC0 <= 350){								// Mittelwert <= 350 ?
				Umdrehungen = 5;							// 5 Umdrehungen
			}
			
			if (ADC0 <= 200){								// Mittelwert <= 200 ?
				Umdrehungen = 0;							// Leerlauf / unendlich Umdrehungen
			}
			
			if (ADC0 <= 50){								// Mittelwert <= 50 ?
				Umdrehungen = 50;							// STOPP! - Disable Motoren
			}
	}	
		


// ### 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
	}



// ### Wert des ADC1 in den PWM-Wert (Timer1) umrechnen ###

	void Drehzahl_R () {								// Maximalwert des ADC1 = 1023		
														// Maximalwert der PWM = 625
		Quotient = 10,23;								// Quotient für 1 % des ADC1-Wertes
		Multiplikator = 16;							// Multiplikator zum Errechnen des PWM-Wertes
	
		Proz_1	=	ADC1 / Quotient;					// prozentualer Wert des ADC1; 0...100%
		PWM_1	=	Proz_1 * Multiplikator;				// prozentualer Wert des ADC multipliziert mit 1% des PWM-Wertes
		OCR1A	=	PWM_1;								// Drehzahlwert in OCR1A schreiben
		OCR1B	=	PWM_1;								// Drehzahlwert in OCR1B schreiben
	}



/* ### Drezahlmessung der Impulse der Drehgeber ###
		 über einen festgelegten Zeitraum von 200ms messen */

	void t_Drehzahl () {
		vect_INT2 = 1;								/* externen Interrupt INT2 "freigeben"
				warten auf Impuls an INT2 --> 1 impuls pro Umdrehung */									
		if (t_Messung == 1000){						// 100ms um ?
			vect_INT2 = 0;							// externen Interrupt INT2 "sperren"
			M1 = M1_Impulse;						// Impulse des Motor1 in Register 'M1' schreiben
			M2 = M2_Impulse;						// Impulse des Motor2 in Register 'M2' schreiben
			M1_Impulse = 0;							// gezählte Impulse zurücksetzen
			M2_Impulse = 0;
			t_Messung = 0;							// Zeitkonstante zurücksetzen
	
	// ########## ??? wegen sehr ungenauen Messwerten evtl. mehrfach messen oder andere Lösung ??? #################
		}

	}
	
	
// 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 (INT2_vect){  
	if (vect_INT2 == 1){					// kann der Timer für die Dauer der Messung eingeschaltet werden?
		OVF_Timer = 1;						// Timer einschalten
	}
}

ISR (TIMER1_OVF_vect) {
	if (OVF_Timer = 1){						// darf die Messzeit ablaufen?
		t_Messung++;						// pro 100µs die Variable inkrementieren
		INT_0_1 = 1;						// INT0 & INT 1 aktivieren
	}
}

	
	
ISR (INT1_vect){
	if (INT_0_1 == 1){						// Zählung Motor 1 aktiviert?
	M1_Impulse++;							// Zähle Impulse am Drehencoder des Motor1
	}
}



ISR (INT0_vect){
	if (INT_0_1 == 1){						// Zählung Motor 2 aktiviert?
	M2_Impulse++;							// Zähle Impulse am Drehencoder des Motor2
	}
}



//####################################################################################################################

// ##### Mainloop #####

int main (void) {											// Mainloop
		
	usart_init ();											// USART initialisieren
	adc_init ();				 							// ADC0 initialisieren
	timer_init ();											// Initialisierung des Timers
	trigger_adc1 ();										// Timer löst Interrupt aus
	ext_int_init ();										// Initialisierung der externen Interrupts
	
	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
		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;
}
