#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "dm_lcd.h"

#define PWM_out(value)        OCR1A=value


volatile uint16_t freq;							// zmienna okreslajaca czestotliwosc
volatile uint16_t adcvalue;						// przerworzona wartosc z ADC
uint16_t params[10];							// tablica parametrow programu

SIGNAL(SIG_ADC) {								// przerwanie z przetwornika ADC
	adcvalue = ADCW;							// czytaj wartosc z przetwornika ADC
}

void lcd_puti(uint8_t value) {					// wyswietla liczbe 8 bitowa na LCD
	char string[3];
	if ( value < 10 ) { lcd_puts(" "); }
	if ( value < 100 ) { lcd_puts(" "); }
	itoa(value, string, 10);
	lcd_puts(string);
}

void lcd_putd(uint16_t value) {					// wyswietla liczbe 16 bitowa na LCD
	char string[5];
	if ( value < 10 ) { lcd_puts(" "); }
	if ( value < 100 ) { lcd_puts(" "); }
	if ( value < 1000 ) { lcd_puts(" "); }
	//if ( value < 10000 ) { lcd_puts(" "); }	// i tak wykorzystujemy liczby max 10b
	itoa(value, string, 10);
	lcd_puts(string);
}

int kbb_read() {								// obsluga klawiatury
		
	uint8_t read;								// inicjalizacja zmiennej tymczasowej
	read = ( PINC & DDRC );						// odczyt portu C, maskowanie rejesterm kirunkowym
	
	if (read ==  4) { return 1; }				// zwracanie odczytu
	if (read ==  8) { return 2; }
	if (read == 16) { return 3; }
	if (read == 32) { return 4; }
	
	return 0;
}

void pwm_init(void)	{							// inicjalizacja PWM
	// Ustaw licznik 1 w trybie 10 bitowego wyjscia PWM
	//TCCR1A = _BV(COM1A1)|_BV(COM1B1)|_BV(WGM10)|_BV(WGM11);		// 10bit pwm
	TCCR1A = _BV(COM1A1)|_BV(COM1B1)|_BV(WGM11);		// 10bit fast pwm
	
	// Ustaw czestotliwosc licznika zgodna z czestotliwoscia taktownia zegara
	//TCCR1B = _BV(CS10);
	TCCR1B = _BV(CS10)|_BV(WGM12)|_BV(WGM13);
		
	ICR1 = 1;									// prescaler
}

void adc_init(void) {							// inicjalizacja ADC
	// wlacz przetwornik adc i uruchom generowanie przerwan
	// czestotilwosc taktowania F_ADC=F_CPU/64(125 kHz)
	ADCSRA = _BV(ADEN)|_BV(ADIE)|_BV(ADPS2)|_BV(ADPS1);
	
	//	Bit | Nazwa | Opis
	//	 0  | ADPS0 | preskaler
	//	 1  | ADPS1 | preskaler
	//	 2  | ADPS2 | preskaler
	//	 3  | ADIE  | generowanie przerwania przez przetwornik
	//	 4  | ADIF  | znacznik przerwania z przetwornika
	//	 5  | ADFR  | przetwarzanie samodzielne (Free Run)
	//	 6  | ADSC  | start konwersji - jesli jest ustawiony ten bit oraz ADEN
	//	 7  | ADEN  | wlaczenie przetwornika (AD Enable)
	//
	// Preskaler:
	// ADPS2  ADPS1  ADPS0  podzial
	// ----------------------------
	//	 0      0      0       1
	//	 0      0      1       2
	//	 0      1      0       4
	//	 0      1      1       8
	//	 1      0      0      16
	//	 1      0      1      32
	//	 1      1      0      64
	//	 1      1      1     128

	// kanal przetwornika
	ADMUX = 0;
}

void menu(int m) {								//obsluga menu programu
	lcd_clrscr();
	switch (m) {
		case 0 :
			lcd_gotoxy(0,0);
			lcd_puts("wyp PWM: ");
			lcd_gotoxy(12,0);
			lcd_putd(params[m]);
			return;
		case 1 :
			lcd_gotoxy(0,0);
			lcd_puts("adcvalue: ");
			lcd_gotoxy(12,0);
			lcd_putd(adcvalue);
			return;
		case 2 :
			lcd_gotoxy(0,0);
			lcd_puts("wejscie: ");
			lcd_gotoxy(12,0);
			lcd_puti(TCCR1A);
			return;
	}
}

int main(void) {
	// inicjalizacja peryferiow
	lcd_init(LCD_DISP_ON);						// wyswietlacza
	adc_init();									// przetwornika A/C
	pwm_init();									// wyjscia PWM
	
	// konfiguracja portow
	DDRC = _BV(2)|_BV(3)|_BV(4)|_BV(5);
	PORTC = 0x00;
	DDRD = 0xff;
	PORTD = 0x00;
	
	// deklaracja i inicjalizacja zmiennych
	char key, key_;
	params[0]=512;
	uint8_t refresh = 0, menuitem = 0;
	uint16_t value = 0, keycount = 0, softdelay = 0, adcvalue_ = 0;
	
	sei();										// wlacz obsluge przerwan
	menu(menuitem);								// wyswietl menu
	
	while(1) {
		
		key_ = key;
		key = kbb_read();
		
		if (key == key_ && key != 0) {
			keycount++;
			
			if (keycount >= 1500) {
				refresh = 1;
				keycount = 0;
				value = 16;
			}
		}
		else {
			keycount = 0;
			value = 1;
		}
		
		
		if ( (key && key != key_) || (refresh) ) {
			
			if (key == 1) { menuitem--;}
			if (key == 2) { menuitem++;}
			
			if (key == 3) { params[menuitem]-=value;}
			if (key == 4) { params[menuitem]+=value;}
			
			menuitem = menuitem % 3;
			menu(menuitem);
			refresh = 0;
		}


		if (99 == softdelay%100 ) {	
			ADCSRA |= _BV(ADSC);
		}
		
		if (softdelay == 2000) {	
			//refresh = 1;
			softdelay = 0;
		}
		
		PWM_out(params[0]);
		OCR1B=adcvalue;
		
		softdelay++;
	}
}
