#include <stdio.h>
#include <avr\io.h>
#include <avr\interrupt.h>
#include <avr\eeprom.h> 
#include <util\delay.h>
#include "lcd_h.h"

volatile int r_min,r_max;	//hodnoty minimálního a maximálního natočení rotátoru
volatile float azimut;	//hodnota natočení ve azimutpních
volatile float q;	//aditivní konstanty
volatile int ADC_hodnota;	//aktuální hodnota AD převodníku, filtrováno
volatile int ADC_pole[8];	//pole posledních hodnot převodů

volatile int novy_azimut;	//nový, požadovaný azimut
volatile char USART[16] = "xxxxxxxxxxxxxxxx"; //vyrovnávací řetězec USART kanálu
int USART_buffer_pointer = 0;	//ukazatel na vyrovnávací řetězec USART

volatile unsigned char status = 0;	//stavové slovo rotačního encoderu (7 - enter, 6, 5 - směr otáčení), bitový příazimutp

volatile char krok = 0;	//proměnná pro dynamické řízení kroku enkodéru
volatile char d_krok_zap = 0;	//spínač dynamického kroku

//---------------------------------------------------------------------
void panel_LED(void)	//obsluha LED diod pro směr otáčení na předním panelu
	{
	if (TCCR1A & 1<<COM1A1)	//vyhodnocení polarity můstku
		{
		PORTA |= 1<<6;	//rozsvícení diody 1
		}
	else PORTA &= ~(1<<6);	//zahsnutí diody 1

	if (TCCR1A & 1<<COM1B1)	//vyhodnocení polarity můstku
		{
		PORTA |= 1<<7;	//rozsvícení diody 2
		}
	else PORTA &= ~(1<<7);	//zhasnutí diody 2
	}

//---------------------------------------------------------------------
void ADC_filtrace(void)	//filtrace digitalizovaných dat z převodníku ADC0
	{
	ADC_hodnota = 0;	//filtrovaná hodnota AD převodníku 0
	for(int i = 0; i <= 3; i++)	//cyklus součtu pro průměr
		{
		ADC_hodnota += ADC_pole[i];	//suma všech hodnot
		}
	ADC_hodnota /= 4;	//filtrovaná hodnota AD převodníku 0
	}

//---------------------------------------------------------------------
void TX_10 (void)		//odvysílání aktuálního natočení po sériové lince
	{
	int a;	//pomocná proměnná
	int temp = (int)azimut;	//pomocná proměnná

	UDR = '+';	//odvysílání po USARTU
	while ( !( UCSRA & (1<<UDRE)) );	//čekání na dokončení vysílaní

	UDR = '0';	//odvysílání po USARTU
	while ( !( UCSRA & (1<<UDRE)) );	//čekání na dokončení vysílaní


	a = temp / 100;	//stovky
	temp -= a * 100;
	a |= 0b00110000;	//zakódování do ASCII
	UDR = a;	//odvysílání po USARTU
	while ( !( UCSRA & (1<<UDRE)) );	//čekání na dokončení vysílaní

	a = temp / 10;	//desítky
	temp -= a * 10;
	a |= 0b00110000;	//zakódování do ASCII
	UDR = a;	//odvysílání po USARTU
	while ( !( UCSRA & (1<<UDRE)) );	//čekání na dokončení vysílaní

	a = temp;	//jednotky
	a |= 0b00110000;
	UDR = a;	//odvysílání po USARTU
	while ( !( UCSRA & (1<<UDRE)) );	//čekání na dokončení vysílaní

	char i = 0;
	for (i = 0; i < 6; i++)
		{
		UDR = '0';	//odvysílání po USARTU
		while ( !( UCSRA & (1<<UDRE)) );	//čekání na dokončení vysílaní	
		}

	UDR = 13;	//carriage return
	while ( !( UCSRA & (1<<UDRE)) );	//čekání na dokončení vysílaní
	}

//---------------------------------------------------------------------
void USART_blank (void)		//vymazání USART
	{
	int i;
	for (i = 0; i<16; i++)
		{
		USART[i] = ' ';
		}
	}

//---------------------------------------------------------------------
void USART_prijem (void)	//příjem dat a příkatů přes sériovou linku
	{
	int i;	//ukazatel řetězce USART
	int j;	//pomocný ukazatel
	char USART_temp[16];	//kopie globálního pole USART
	int temp = 0;	//dočasná pracovní proměnná

	for (i = 0; i <= 15; i++)	//kopie USART do USART_temp
		{
		USART_temp[i] = USART[i];	//kopie prvku USART do USART_temp
		}

	for (j = 0; j <=15; j++)	//přerovnání USART_temp tak, aby případný # byl na jeho konci
		{
		if (USART_temp[0] != 'M')	//synchronizace na znak #
			{
			temp = USART_temp[0];	
			for (i = 0; i <= 14; i++)	//14+1!
				{
				USART_temp[i] = USART_temp[i+1];	//přerovnání
				}
			USART_temp[15] = temp;
			}
	
		if (USART[j] == 'C')	//požadavek na odeslání aktuálního natočení rotátoru
			{
			TX_10();	//převod proměnné azimut ASCII soustavy a odvysílání po sériové lince (aktuální natočení rotátoru)
			USART_blank();	//vymazání USART bufferu
			}
		if (USART[j] == 'R')	//doprava?
			{
			status &= ~(1<<0);	//neaplikuj proceduru "natoc" (volný mód natáčení z PC)
			TCCR1A &= 0b01011111;	//můstek deaktivován
			TCCR1A |= 1<<COM1A1;	//+12V, můstek aktivován!!	
			USART_blank();	//vymazání USART bufferu
			}
		if (USART[j] == 'L')	//doleva?
			{
			status &= ~(1<<0);	//neaplikuj proceduru "natoc" (volný mód natáčení z PC)
			TCCR1A &= 0b01011111;	//můstek deaktivován
			TCCR1A |= 1<<COM1B1;	//-12V, můstek aktivován!!
			USART_blank();	//vymazání USART bufferu
			}
	
		if (USART[j] == 'S')	//stop?
			{
			status &= ~(1<<0);	//neaplikuj proceduru "natoc" (volný mód natáčení z PC)
			TCCR1A &= 0b01011111;	//můstek deaktivován
			USART_blank();	//vymazání USART bufferu
			}
		}
		
		if (USART_temp[0] == 'M')	//nový azimut!
			{
			temp = 0;
			if ((USART_temp[3] >= 0x30) && (USART_temp[3] <= 0x39) && (USART_temp[1] != 'P'))	//čeká se na zadání posledního čísla, jednotek!
				{
				temp += 100 * (USART_temp[1] - 48);	//stovky
				temp += 10 * (USART_temp[2] - 48);	//desítky
				temp += USART_temp[3] - 48;	//jednotky
				status |= 1<<0;	//aplikuj proceduru "natoc"
				status &= ~(1<<1);	//vypisování aktuálního úhlu natočení
				novy_azimut = temp;	//hodnota nového azimutu
				USART_blank();
				}

			if (USART_temp[1] == 'P')	//PWMka
				{
				temp += 100 * (USART_temp[2] - 48);	//stovky
				temp += 10 * (USART_temp[3] - 48);	//desítky
				temp += USART_temp[4] - 48;	//jednotky
				
				OCR1A = temp;	//hodnota PWM
				OCR1B = temp;	//hodnota PWM
				USART_blank();
				}
			}

		if (USART_temp[11] == 'P')	//může jít o rychlost nebo nový azimut
			{
			OCR1A = temp;
			OCR1B = temp;
			USART_blank();
			}
	}
		
//---------------------------------------------------------------------
void natoc (int n_azimut)	//procedura natočení rotátoru na zadaný novy_azimut x
	{
	static float l,r;
	
	if (status & 1<<0)	//příkaz zadán z PC nebo enkoderu
		{
		if ((azimut - n_azimut > l) || (n_azimut - azimut > r))	//aktivace můstku, pokud se požadovaná hodnota liší od aktuální o více jak jeden azimutpeň
			{
			if ((azimut < n_azimut) && (azimut <= 360))	//rozhodování na kterou stranu začít točit
				{
				TCCR1A &= 0b01011111;	//můstek deaktivován
				TCCR1A |= 1<<COM1A1;	//+12V
				l = 5;
				r = 0;
				}
			
			if ((azimut > n_azimut) && (azimut >= 0))
				{
				TCCR1A &= 0b01011111;	//můstek deaktivován
				TCCR1A |= 1<<COM1B1;	//-12V
				r = 5;
				l = 0.5;
				}	
			}
		else
			{
			TCCR1A &= 0b01011111;	//můstek deaktivován
			l = 2;
			r = 2;
			}
		}
	}

//---------------------------------------------------------------------
void keyboard (void)	//obsluha tlačítek směru na panelu
	{
	if (!(PINB & 1<<4))	//tlačítko 1
		{
		while (!(PINB & 1<<4));	//zalomená sirka
		status &= ~(1<<0);	//neaplikuj proceduru "natoc" (volný mód natáčení klávesami)
		status &= ~(1<<1);	//vypisování aktuálního úhlu natočení

		if (TCCR1A & 1<<COM1B1)	//točí se na opačnou stranu?
			{
			TCCR1A &= 0b01011111;	//můstek deaktivován
			}
		else
			{
			if (TCCR1A & 1<<COM1A1)	//nejedná se o druhý stisk tlačítka?
				{
				TCCR1A &= 0b01011111;	//2. stiskem je můstek deaktivován
				}
			else
				{
				if ((azimut <= 360) || (status & 1<<3))	//mimo kalibraci
					{
					TCCR1A &= 0b01011111;	//můstek deaktivován
					TCCR1A |= 1<<COM1A1;	//+12V
					}
				}	
			}
		}

	if (!(PINB & 1<<3))	//tlačítko 2
		{
		while (!(PINB & 1<<3));	//zalomená sirka
		status &= ~(1<<0);	//neaplikuj proceduru "natoc" (volný mód natáčení klávesami)
		status &= ~(1<<1);	//vypisování aktuálního úhlu natočení

		if (TCCR1A & 1<<COM1A1)	//točí se na opačnou stranu?
			{
			TCCR1A &= 0b01011111;	//můstek deaktivován
			}
		else
			{
			if (TCCR1A & 1<<COM1B1)	//nejedná se o 2. stisk tlačítka?
				{
				TCCR1A &= 0b01011111;	//2. stiskem je můstek deaktivován
				}
			else
				{
				if ((azimut >= 0) || (status & 1<<3))	//mimo status
					{
					TCCR1A &= 0b01011111;	//můstek deaktivován
					TCCR1A |= 1<<COM1B1;	//-12V
					}
				}
			}
		}
	}

//---------------------------------------------------------------------
void kalibrace (void)	//procedura kalibrace, chybí dodělat uložení hodnot kalibrace do EEPROM
	{
	status |= 1<<3;	//režim kalibrace
	int azimut_change;	//pomocná proměnná pro prohození krajních hodnot

	lcd_gotoxy(0,0);
	lcd_puts("Kalibrace, ENTER");	//úvodní obrazovka kalibrace

	while ( ! (status & 1<<7) );	//čeká se na stisknutí klávesy ENTER
	_delay_ms(300);
	status &= ~(1<<7);	//vymazání příznaku stisknutí klávesy ENTER

	lcd_gotoxy(0,0);
	lcd_puts("nastav k. p. 1  ");	//výzva k natočení rotátoru do jedné z krajních poloh
	_delay_ms(1000);
	
	while ( ! (status & 1<<7))	//čekání na potvrzení krajní polohy ENTERem
		{
		keyboard();	//příkaz ke spuštění natáčení se očekává z klávesnice
		
		ADC_filtrace();	//filtrace dat z AD převodníku
		panel_LED();	//signalizace LED diodami směru na hlavním panelu
		char d_temp[16];
		sprintf(d_temp, "k. poloha 1 %4.0d", (int)ADC_hodnota);	//výpis aktuální hodnoty AD převodníku
		lcd_puts(d_temp);
		r_min = (int)ADC_hodnota;	//průběžné ukládání krajní polohy
		}
	_delay_ms(300);
	status &= ~(1<<7);	//vymazání příznaku stisknutí klávesy ENTER
	
	lcd_gotoxy(0,0);
	lcd_puts("nastav k. p. 2  ");	//výzva k natočení rotátoru do druhé z krajních poloh
	_delay_ms(1000);
	
	while ( ! (status & 1<<7))	//čekání na potvrzení krajní polohy ENTERem
		{
		keyboard();	//příkaz ke spuštění natáčení se očekává z klávesnice
		
		ADC_filtrace();	//filtrace dat z AD převodníku
		panel_LED();	//signalizace LED diodami směru na hlavním panelu
		char d_temp[16];
		sprintf(d_temp, "k. poloha 2 %4.0d", (int)ADC_hodnota);	//výpis aktuální hodnoty AD převodníku
		lcd_puts(d_temp);
		r_max = (int)ADC_hodnota;	//průběžné ukládání krajní polohy
		}
	_delay_ms(300);
	status &= ~(1<<7);	//vymazání příznaku stisknutí klávesy ENTER

	if (r_max <= r_min)	//prohození horní a dolní meze, pokud je třeba
		{
		azimut_change = r_max;	//azimut change
		r_max = r_min;
		r_min = azimut_change;
		}

	eeprom_write_word(0x00, r_min);	//zápis do eeprom paměti, adresa, 16b data
	eeprom_write_word(0x02, r_max);	//zápis do eeprom paměti, adresa, 16b data

	lcd_gotoxy(0,0);
	lcd_puts("Data ulozena!   ");	//potvrzení kalibrace
	_delay_ms(1000);
	
	status |= 1<<0;	//aktivace procedury natoc
	novy_azimut = 180;	//natočení na 180°
	lcd_gotoxy(0,0);
	lcd_puts("natacim 180...  ");	//potvrzení kalibrace
			
	while (!((azimut <= 360) && (azimut >= 0)));	//čekej na návrat do intervalu

	status &= ~(1<<3);	//režim kalibrace ukončen
	}

//---------------------------------------------------------------------
void azimut_vypis (void)	//výpis aktuálního natočení na display
	{
	char d_temp[16];
	sprintf(d_temp, "azimut:      %3.0d", (int)azimut);	//explicitní konverze float na int proměnné azimut
	lcd_puts(d_temp);	//výpis na display
	}
	
//---------------------------------------------------------------------
void novy_azimut_vypis (void)
	{
	char d_temp[16];
	sprintf(d_temp, "novy azimut: %3.0d", novy_azimut);
	lcd_puts(d_temp);	//výpis na display
	}	

//---------------------------------------------------------------------
ISR (ADC_vect)	//přerušení od AD převodníků
	{
	static int i = 0;	//ukazatel na pole hodnot AD převodů převodníku 0
	
	ADC_pole[i] = ADCW;	//načtení aktuální hodnota ADC0
	ADC_filtrace();	//ADC dolní propust
	i++;
	
	if (!(i<=3))	//kruhový ukazatel
		{
		i = 0; 
		}
	}

//---------------------------------------------------------------------
ISR (TIMER0_OVF_vect)	//přerušení od přetečení časovače 0
	{
	int static i = 0;	//zpomalení debug led 128x
	ADMUX &= ~(1<<MUX0);	//úhel
	
	i++;
	
	ADCSRA |= 1<<ADSC;	//vzorkování a nový převod
	natoc(novy_azimut);		//natáčení rotátoru
	
	azimut = 379 * (float)ADC_hodnota / ((float)r_max - (float)r_min) + q - 10;	//přepočet na úhel,  azimut = k * ADC_hodnota + q
	
	if (((azimut <= 0) && (TCCR1A & 1<<COM1B1))  && !(status & 1<<3))	//překročení intervalu mimo kalibraci
		TCCR1A &= 0b01011111;	//můstek deaktivován

	if (((azimut >= 360) && (TCCR1A & 1<<COM1A1))  && !(status & 1<<3))	//překročení intervalu mimo kalibraci
		TCCR1A &= 0b01011111;	//můstek deaktivován

	if (i == 128)
		{
		if (PINB & 1<<0)	//debug led
			{
			PORTB &= ~(1<<0);	//zhasni
			}
		else	PORTB |= 1<<0;	//rožni
		i = 0;
		}
	}

//---------------------------------------------------------------------
ISR (TIMER2_OVF_vect)	//přerušení od přetečení časovače 2
	{
	d_krok_zap = 0;	//vypnutí dynamického řízení kroku - otáčení enkodérem je pomalé
	krok = 0;	//normální krok
	TIMSK &= ~(1<<TOIE2);	//zakázání přerušení od časovače 2 -vyhodnocení dynamického kroku pouze při otáčení enkodérem
	
	//GIFR &= ~(1<<INT0);	//smazání parazitních příznaků přerušení INT0
	//GICR |= 1<<INT0;	//povolení synchronizace enkoderu po odeznění mechanických zákmitů
	}

//---------------------------------------------------------------------
ISR (USART_TXC_vect)	//sériové vysílání ukončeno
	{
	
	}

//---------------------------------------------------------------------
ISR (USART_RXC_vect)	//sériový příjem dokončen
	{
	USART[USART_buffer_pointer] = UDR;	//čtení z UDR

	if (USART_buffer_pointer >= 15)	//reset pointeru na USART
		{
		USART_buffer_pointer = -1;	//i = 0
		}
	USART_buffer_pointer++;	//další pozice vyrovnávací paměti, i = 0;
	}

//---------------------------------------------------------------------
ISR (INT0_vect)		//externí přerušení 1 - synchro pin enkodéru
	{
	PORTD |= 1<<7;		//rožni	DEBUG LED
	status &= ~(1<<0);	//neaplikuj procesuru "natoc" (až po stisknutí ENTERu)
	status |= 1<<1;		//vypisování nového azimutu z enkoderu až do stisknutí ENTERu, přijmu z PC, nebo příkazu klávesnice

	TIMSK |= 1<<TOIE2;	//povolení přerušení od časovače 2
	//GICR &= ~(1<<INT0);	//zakázání přerušení 0 kvůli mechanickým záknitům

	if (d_krok_zap == 1)	//dynamické řízení kroku?
		{
		if (TCNT2 < 200)	//rychlé otáčení?
			krok = 10;	//navýšení kroku
		else	krok = 0;	//normální krok
		}

	if (PIND & 1<<6)	//vyhodnocení log 1 enkoderu
		{
		if (novy_azimut < 360)
			novy_azimut+= 1 + krok;
		}
	if (!(PIND & 1<<6))	//vyhodnocení log 0 enkoderu
		{
		if (novy_azimut > 0)
			novy_azimut-= 1 + krok;
		}

	if (novy_azimut > 360)	//úprava kvůli dynamickému kroku
		novy_azimut = 360;
	if (novy_azimut < 0)
		novy_azimut = 0;
	
	d_krok_zap = 1;		//zapnutí dynamického řízení kroku

	TCNT2 = 0;		//nastavení časovače 2 - reset
	GIFR &= ~(1<<INT0);	//smazání parazitních příznaků přerušení INT0
	}

//---------------------------------------------------------------------
ISR (INT1_vect)		//externí přerušení 0 - ENTER
	{
	status |= 1<<7;	//příznak ENTERu
	PORTD |= 1<<7;	//rožni	DEBUG
	
	if (!(status & 1<<3))	//režim kalibrace
		{
		status |= (1<<0);	//aplikuj procesuru "natoc"
		}

	status &= ~(1<<1);	//vypisování úhlu natočení
	}

//---------------------------------------------------------------------
int main (void)
	{
	DDRA = 0b11000000;	//panel led
	DDRB = 0b00000001;	//debug led
	DDRD = 0b00110000;	//výstup pro můstek

	TCCR0 = 0b00000011;	//vstup čítače 0 je fosc / 64
	TIMSK |= 1<<TOIE0;	//povolení přerušení od časovače 0
	
	SFIOR = 0b10000000;	//ADC spouštěn od přeteční čítače 0
	ADCSRA = 0b11001111;	//zapnutí ADC, start převodu, povolení přerušení od ADC, nastavení (předděličky) taktování

	UCSRA = 0b00000010;	//zdvojnásobení přenosové rychlosti (část nastavení baud rate) 
	UCSRB = 0b11011000;	//povolení přerušení TX, TX dokončeno, zapnutí přijímače a vysílače

	UCSRC = 0b10000110;	//URSEL = 1 - zápis do UCSRC -> parita vypnuta, 1 stop-bit, 8 datových bitů
	
	UBRRL = 103;		//(UBRRL = 12 pro 1MHz)baud rate = 9600bps

	GICR |= 1<<INT0 | 1<<INT1;	//povolení externího přerušení 0 - ENTER, a externího přerušení 1 - synchro pin enkodéru
	MCUCR |= 1<<ISC01 | 1<<ISC11;	//sestupná pro INT0, sestupná pro INT1
	
	TCCR1A |= 1<<WGM10;		//8-bit rychlá PWM
	TCCR1B |= 1<<WGM12 | 1<<CS12;	//8-bit rychlá PWM, předdělička na 256 (asi 120Hz)

	TCCR2 |= 1<<CS22 | 1<<CS21 | 1<<CS20;	//vstup čítače 2 je fosc / 1024


	lcd_init();		//lcd inicializace (4 bitová komunikace, PORTB, ...)
	lcd_clrscr();		//počáteční smazání displaye

	OCR1A = 255;
	OCR1B = 255;

	r_min = eeprom_read_word(0x00);	//načtení kalibračních hodnot (16b) z paměti
	r_max = eeprom_read_word(0x02);	//načtení kalibračních hodnot (16b) z paměti

//------------

	for (int i = 0 ; i < 3; i++)	//inicialiční obrazovka
		{
		lcd_gotoxy(0,0);
		lcd_puts("Antenni rotator!");
		_delay_ms(1000);

		lcd_gotoxy(0,0);
		lcd_puts("  Craete RC- 5  ");
		_delay_ms(800);

		}

	lcd_clrscr();
	sei();	//povolenípřerušení, globálně, normální běh programu


	if (!((PINB & 1<<5) && (PINB & 1<<4)))	//stisknutá obě tlačítka pro pojezd doleva a doprava
		{
		kalibrace();	//kalibrační procedura
		}

	q = - 379 * (float)r_min / ((float)r_max - (float)r_min);	//výpočet aditivní konstanty, azimut = k * ADC_hodnota + q
	
  	while(1)
  		{
		USART_prijem();

		panel_LED();		//diody směru
   		keyboard();		//obsluha tlačítek
		
		if (status & 1<<1)	//vypisování aktuálního úhlu natočení
			{
			novy_azimut_vypis();	//výpis nového novy_azimutu
			}
		else
			{
			azimut_vypis();		//vypis aktuálního natočení na LCD
			}
		}
}
//---------------------------------------------------------------------
