//------------------------------------------------------------------
// FLabs frekvenciamérő 0.1										   
// 16x2 LCD-vel, háttérvilágítás ki/bekapcsolás
// két bemenettel, 1 másodperces időalappal
// 1 Hz - 50 MHz / 10 MHz-1 GHz
//------------------------------------------------------------------
#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include "simple_driver.h"		//gtk rutinja kis módosítással
#include <stdlib.h>
#include <avr/iom8.h>

#define Light_OFF()   	PORTD |=  (1<<PD0)
#define Light_ON()   	PORTD &= ~(1<<PD0)
#define Select1()   	PORTD |=  (1<<PD5); PORTB &= ~(1<<PB0);
#define Select2()   	PORTD &= ~(1<<PD5); PORTB |=  (1<<PB0);

 uint8_t  	Button1=0, Button2=0;
 char		konv[13];
 char*	 	strp;
 uint8_t 	input_select=2,input_select_elozo;
 uint32_t 	frekvencia,frekvencia_elozo,battery_voltage;
 volatile uint8_t	counter,idozites;
 uint8_t  frekvencia_T0,frekvencia_counter;
 uint16_t frekvencia_HC4020;
 //------------------------------------------------------------------
  void ADC_init(void)
  {
   ADMUX = 0x00;
   ADMUX  |= (1<<MUX1)		//ADC7 bemenet
   		  |  (1<<MUX2)
		  |  (1<<REFS1)
		  |  (1<<REFS0);	//a belső 2,56V a referencia	
   ADCSRA =  (1<<ADEN)		//AD engedélyezve
          |  (1<<ADPS2)		//ADC prescaler /128
          |  (1<<ADPS1)
          |  (1<<ADPS0);
  }
 //------------------------------------------------------------------
 uint16_t ADConvert(void) 
 {
   ADCSRA |= (1<<ADSC);			//ADC start
   while(ADCSRA & (1<<ADSC));	//ha kész, ADSC törlődik
   return (ADCL | (ADCH<<8));;
}
 //------------------------------------------------------------------
 void timer2_init(void)
 {
 		TCCR2 |= (1<<CS22)  //Timer2 belső jel számlálása 1024-gyel osztja
              |  (1<<CS21)	//túlcsordulás kb.1/15 másodpercenként
		      |  (1<<CS20);

		TIMSK |= (1<<TOIE2);  //interrupt engedélyezése 
 }
//-------------------------------------------------------------------
void counter_init(void) 
{
 	TCCR0 |=  (1<<CS02)  //Timer0 külső jel számlálása lefutó élre
           |  (1<<CS01);
      //   |  (1<<CS00);
 	
    TIMSK  |= (1<<TOIE0) ;  //enable interrupt
    sei();
}
//--------------------------------------------------------------------
uint8_t int2str(uint32_t szam, char* szoveg) //int típust konvertál sztringgé a kiíráshoz
{
 char*  ptr = szoveg;
 char	tmp;
 uint8_t  hossz,i;
 for (i=0;i<11;i++)		//a sztringet "kinullázzuk", fix 10 karakteres kijelzés
	{					// hogy a helyiértékek ne ugráljanak
    *(szoveg+i)=' ';  
  	}
	
    do {							// átalakítjuk a számot,
        *(ptr) ='0'+(szam%10);		// de felcserélődik a számjegyek sorrendje
		ptr++;
    	} while ((szam/=10));		//a legnagyobb értékes számjegy előtti 
									// nullák helyett szóközök szerepelnek
 hossz=(uint8_t)(ptr-szoveg);				//ennyi jegyű a szám
 ptr--;
 for (uint8_t i=0;i<5;i++) 			//megfordítjuk a számjegyek sorrendjét
 	{
    tmp=*(szoveg+i);*(szoveg+i)=*(szoveg+9-i);*(szoveg+9-i)=tmp;  
 	}
 /*  for (i=0;i<hossz/2;i++) 
 	{
    tmp=*(szoveg+i);*(szoveg+i)=*(ptr-i);*(ptr-i)=tmp;  
 	}
 */
return hossz;				//ennyi számjegyű a szám
}
//--------------------------------------------------------------------
uint16_t frekvencia_4020(void)			//a külső számláló kiolvasása
{
 uint16_t szamlalo=0;
 uint8_t  CP=0;
 uint16_t j;

 TCCR1B&=~((1<<CS11)|(1<<CS10));			//PWM leáll, nehogy a kiolvasással 
 if (PIND&(1<<PD4)) CP=1;			//		beleszaladjunk a következő számlálási ciklusba
 PORTD|=(1<<PD7); DDRD|=(1<<PD7); 	//PD7 kimenet és PD7=1-> Kiolvasás indul
 if (CP==1)									//ha PD4=1, vagyis a számláló>8192
 	{
 	for(j=16384;j>8191;j--)				
 		{
 		PORTD|= (1<<PD7);		//PD7=1
 		PORTD&=~(1<<PD7);		//PD7=0
		PORTD|= (1<<PD7);		//PD7=1
		if ((~PIND)&(1<<PD4)) {szamlalo=j; j=1;}	//ha PD4=0 lett, megkaptuk a HC4020-ban 
 		}											//tárolt számot
	}
 else										//ha PD4=0, vagyis a számláló<8192
 	{
 	for(j=8191;j>0;j--)			
 		{
 		PORTD|= (1<<PD7);		//PD7=1
 		PORTD&=~(1<<PD7);		//PD7=0
		PORTD|= (1<<PD7);		//PD7=1
		if (PIND&(1<<PD4)) {szamlalo=j; j=1;}		//ha PD4=1 lett, megkaptuk...
 		}
 	}

DDRD  &= ~(1<<PD7);PORTD &=~(1<<PD7); //PD7 bemenet és felhúzó kikapcsolva:kiolvasás befejeződött
PORTD |=  (1<<PD6);						//Reset=1
PORTD &= ~(1<<PD6);
TCCR1B|=(1<<CS11)|(1<<CS10);			//PWM tovább indul
return szamlalo;
}
//--------------------------------------------------------------------
void test(uint8_t adat)	//ez csak egy bájtot jelenít meg bitenként a kijelzőn, a fejlesztés során használtam
{
 lcd_set_DDRAM_addr(0);
 if(adat&0b10000000) lcd_putch ('1'); else lcd_putch('0');
 if(adat&0b01000000) lcd_putch ('1'); else lcd_putch('0');
 if(adat&0b00100000) lcd_putch ('1'); else lcd_putch('0');
 if(adat&0b00010000) lcd_putch ('1'); else lcd_putch('0');
 if(adat&0b00001000) lcd_putch ('1'); else lcd_putch('0');
 if(adat&0b00000100) lcd_putch ('1'); else lcd_putch('0');
 if(adat&0b00000010) lcd_putch ('1'); else lcd_putch('0');
 if(adat&0b00000001) lcd_putch ('1'); else lcd_putch('0');
}
//--------------------------------------------------------------------
void button(void)	//T2 megszakítása hívja meg 1/15 másodpercenként, a 2 nyomógombot kérdezi le
{
Button1=Button1<<1;	//az előzmények shiftelése
Button2=Button2<<1;
if (~PIND &(1<<PD1)) Button1|=0b00000001; else Button1&=0b11111110;	//Button1=1 ha meg van nyomva
if (~PIND &(1<<PD2)) Button2|=0b00000001; else Button2&=0b11111110; //Button2=1 ha meg van nyomva

if ((Button2&0b00000111)==1)		//háttérvilágitás ki-/bekapcsolása pergésmentesitéssel
  	{
 	if (~PORTD&(1<<PD0)) Light_OFF(); else Light_ON();
  	}
if ((Button1&0b00000111)==1)		//Select1-2 kapcsolása
  	{
 	if (input_select==1) input_select=2;
	else                 input_select=1;
  	}
}
//--------------------------------------------------------------------
void PWM_init()				//Timer1 beállítása a kapujel generálására
{							//jelenleg csak az 1 másodperces kapuidő működik

  //TCCR1A |= (1<<WGM10);     
  TCCR1A |= (1<<WGM11);		//fast mode PWM (datasheet 14. mode)
  TCCR1B |= (1<<WGM12);     
  TCCR1B |= (1<<WGM13);
  TCCR1B |= (1<<CS11);		//presc: 64; F_CPU = 4 000 000Hz;
  TCCR1B |= (1<<CS10);
  //TCCR1A |= (1<<COM1A0);	// invertalt PWM, A csatorna
  TCCR1A |= (1<<COM1A1);    // fast PWM
  DDRB   |= (1<<PB1);     	// PORTB PB1 lab kimenet
  OCR1A = 62499;         	// PWM kitoltesi tenyezo:4MHz/64/62500 = 1s
  ICR1  = 65500;			// TOP ertek megadasa: nem kritikus az értéke, csak 
  							// ne legyen túl közel az előbbihez.
}							//ha ez a szám is letelt, akkor indul újra az 1s kapuidő

//------------------------------------------------------------------
SIGNAL(SIG_OVERFLOW0)		//Timer0 túlcsordulása által kiváltott megszakítás
{
   counter++;				//a HC4020 Q14-as jel számlálása		
}
 //------------------------------------------------------------------
SIGNAL(SIG_OVERFLOW2)		//Timer2 túlcsordulása által kiváltott megszakítás
{
   button();idozites++;		// kb. 1/15 másodpercenként meghívódik a nyomógombok lekérdezéséhez
}
//-------------------------------------------------------------------
void kijelzes(void)	//ha valami változott a kijelzendő értékek közül (pl. frekvencia naná!)
{					//akkor ez a fv. írja ki a HD44780 komp. kijelzőre
 
if (input_select!=input_select_elozo)		//a bemenetválasztás kijelzése
 	{
	PORTD |=  (1<<PD6);						//Reset=1
	PORTD &= ~(1<<PD6);
	frekvencia_counter=0;counter=0;
	frekvencia_T0=0;TCNT0=0;	//a számlálók törlése, hogy 											
	frekvencia_HC4020=0;		//a bemenet változásakor hülye értékek ne jelenjenek meg
	if (input_select==2) 
		{Select2();input_select_elozo=input_select;lcd_putstr("2",0xF);}
	if (input_select==1) 
		{Select1();input_select_elozo=input_select;lcd_putstr("1",0xF);}
	}
 frekvencia=(uint32_t)frekvencia_counter<<22;
 frekvencia+=(uint32_t)frekvencia_T0<<14;
 frekvencia+=frekvencia_HC4020;

if (input_select==1) 
		{
		if ((frekvencia_counter==0) && (frekvencia_T0<9)) 
										frekvencia=0;//a Be1 10MHz-es  min. bemenő frekije miatt
		}		//az ez alattiakat töröljük, azért, hogy ne ugráljon a számláló, 
				//ha nincs rajta korrekt bemeneti jel
 if (frekvencia!=frekvencia_elozo) 			//frekvencia kijelzése
 	{
 	if (input_select==1) frekvencia<<=6;		//a 64-es előosztó miatt
	int2str(frekvencia,strp);
	konv[10]=konv[9];konv[9]=konv[8];konv[8]=konv[7];konv[7]=' ';
	if (frekvencia>999) {konv[7]=',';lcd_putstr ("k",0x4D);}	//tizedesvessző kiírása	
		else {lcd_putstr (" ",0x4D);}							//Hz kiírása, vagyis letöröljük a "k"-t a "kHz"-ből
 	lcd_putstr(strp,0x41);
	frekvencia_elozo=frekvencia;
  	}
 if (idozites<3) lcd_putstr ("G",0x0); else lcd_putstr (" ",0x0);		//A Gate jelzés kiírása, 1 másodpercenként felvillan

 if (battery_voltage<220)
 	{
 	if ((battery_voltage>210)&&(idozites<4)) lcd_putstr ("Low battery",2); //"Low battery" villog 2,2V alatt
 		else lcd_putstr ("Frekvencia ",2);
	if (battery_voltage<210) lcd_putstr ("Low battery  ",0x0);	//"Low battery" állandó 2,1V alatt
	}		 
}
//-------------------------------------------------------------------
int main ()
{
  DDRD = 0b01100001;		//PD0,PD5,PD6: kimenet, a tobbi bemenet
  PORTD= 0b00001111;		//felhuzo-ellenallasok bekapcsolasa, kivéve PD4 es PD7
  DDRB = 0b00000001;		//PB0:kimenet, a többi bemenet
  PORTB= 0xff;				//felhuzo-ellenallasok bekapcsolasa
  strp = &konv[0];

  if (input_select==2) {Select2();} else {Select1();}	//bemenet kiválasztása, induláskor Be2 aktív
  input_select_elozo=input_select;
  
  lcd_init ();
  /*display on, cursor & blink off */
  lcd_control (1, 0, 0);
  lcd_cls ();
  frekvencia=0;frekvencia_elozo=1;
  Light_ON();			//háttérvilágítás be
  lcd_putstr ("FLabs       0.1",0x40); lcd_putstr ("Frekvenciamero",0);
  _delay_ms(2000);lcd_cls ();

  lcd_putstr ("Frekvencia",2);

  if (input_select==1)  {lcd_putstr ("Be1",0x0D);} 
  else {lcd_putstr ("Be2",0x0D);}
  lcd_putstr (" Hz",0x4D);
  

  timer2_init();					//timerek indítása
  PWM_init();						//a kapujel előállításához
  counter_init();					// a belső 8 bites (T0) számláló beállítása
  ADC_init();						//az AD csak az elem lemerülésének jelzéséhez kell,
									//hogy ne vigye mélykisütésbe az akkukat


  while(1)							//itt indul a végtelen ciklus méréssel, kijelzéssel, mindennel
  	{
  	while(!(TIFR&(1<<OCF1A))) {kijelzes();}		//vár a kapujel felfutására, a számlálás befejeződött, jöhet		
  	idozites=0;									// a kiértékelés
  	TIFR|=(1<<OCF1A);							//töröljük a PWM flag-et, hogy ez az ág minden kapuzásnál csak egyszer fusson le
	frekvencia_T0=TCNT0; frekvencia_counter=counter;		//számlálók kiolvasása
	frekvencia_HC4020=frekvencia_4020();

	TCNT0=0;										//és nullázása
	counter=0;
	battery_voltage=((uint32_t)ADConvert()*267)>>10;	//2,67V a belső referencia feszültség a
   	}													//mérésem szerint (adatlapban 2,56V) módosítandó másik kontroller példánynál
  return 0;
}
