
#include "lcd_h.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>

#define FOSC 4000000			// hodinovy kmitocet
#define BAUD 9600				//prenosova rychlost
#define MYUBRR FOSC/16/BAUD-1	//vypocet hodnot registru UBRR

#define FIFO_LENGTH 20			//delka fronty

typedef enum{					//vyctovy typ pro funkci Comm()
  SET_NOTHING = 0,
  SET_FREQ,
  SET_CONSTANT
}set_flag;

int ADCRES = 0;
unsigned int par = 0, dBm = 0, an_rng = 1, key = 0, freq = 0, 
				pozice = 0, status = 0, head = 0, tail = 0;
char adc_result [5];			//retezec hodnoty AD prevodu
char fifo [FIFO_LENGTH];		//fronta
char dispfreq[][8] = {"0.9 GHz",
"1.9 GHz","2.2 GHz","3.6 GHz","5.8 GHz","8.0 GHz"," 10 GHz"};
char disppar [][9] = {"FREQ    ","OFFSET10","OFFSET 1","OFFSET.1","AN OFS  ","AN RNG  "};
double power = 0, power_W = 0, 
offset = 0, an_ofs = 0, an_power = 0;
char value [10];				//retezec vypoctene hodnoty dBm
char value_W [8];				//retezec vypoctene hodnoty mW
char disp_rng[][6] = {" 3 dB","10 dB","Range"};
//float lin_tab [2][9] = {{},{}};
//promenne k vypoctu vykonu
float k;
float c;


void HW_Init (void)
{
  //def. casti portu A jako vystup spinani RtADJ.
  DDRA = (1<<5) | (1<<6) | (1<<7);
  DDRB = (1<<3);			 //def p3B jako vystup PWM.
  //pullup rezistory 32k na vstupni piny.
  PORTD = (1<<2) | (1<<3) | (1<<4) | (1<<5);

  ADMUX = (1<< REFS0);		//AVCC na AREF pin, vstup ADC0
  ADCSRA = (1<< ADEN)|(1<<ADSC)|(1<<ADPS0)|(1<<ADPS1)|(1<<ADPS2)
  |(1<<ADATE);//ADC povolen,start,deleni osc.128,AutoTrigger

  MCUCR = (1<<ISC01);		//INT0 na sestupnou hranu
  GICR = (1<<INT0);  		//povoleni preruseni INT0
  //provozni casovac					
  TCCR1B =(1<<CS11);		//TC1Normal,ClockSel8,
  TIMSK = (1<<TOIE1);		//jakakoliv zmena hrany
  TCCR0 = (0<< WGM01)|(1<<WGM00)|(1<<COM01)|(0<<COM00)|(0<<CS02)
  |(1<<CS01)|(0<<CS00);//PWM,Clr, /deleni osc. 8.
}

void USART_Init( unsigned int ubrr)
{

  UBRRH = (unsigned char)(ubrr>>8);		//nastav rychlost
  UBRRL = (unsigned char)ubrr;
  UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);//povoleni txd i rxd, rx int
  UCSRC = (1<<URSEL)|(0<<UMSEL)|(1<<UPM1)|(0<<UPM0)|(0<<USBS)
  |(0<<UCSZ2)|(1<<UCSZ1)|(1<<UCSZ0);	//UART asyn,9600,8,E,1
}

void USART_Transmit( unsigned char data )//funkce pro vyslani znaku
{
  while ( !( UCSRA & (1<<UDRE)) )   //ceka na vyprazdneni bufferu
  ;
  UDR = data;						//vyslani znaku
}

void usart_puts (char *s)	//funkce pro vyslani retezce
{
  while (*s)				//dokud neni konec retezce
  {
    USART_Transmit(*s);		//vysilej
	s++;
  }
}


void EEPROM_write(unsigned int uiAddress, unsigned char ucData)
{
  while(EECR & (1<<EEWE))	//ceka na dokonceni predch. zapisu
  ;
  EEAR = uiAddress;			//nastaveni registru
  EEDR = ucData;
  EECR |= (1<<EEMWE);
  EECR |= (1<<EEWE);		//zacne zapis
}

unsigned char EEPROM_read(unsigned int uiAddress)
{
  while(EECR & (1<<EEWE))	//ceka na dokonceni predch. zapisu
  ;
  EEAR = uiAddress;			//nastaveni registru
  EECR |= (1<<EERE);		//zacne cteni
  return EEDR;				//vrati data
}

void Set_Freq ( void )		//funkce pro zobrazeni frek a volbu RtADJ.
{
  lcd_gotoxy( 9,1 ) ;
  lcd_puts( dispfreq[freq] ) ;	//pise na displej
  switch (freq){
    case 0:
	  PORTA = (1<<7);		//pise na port - prepina rezistory
	  break;
    case 1||2||3:
	  PORTA = (1<<6);
	  break;
    case 4:
	  PORTA = (1<<5);
	  break;
    case 5:
	  PORTA = 0;
	  break;
    case 6:
	  PORTA = 0;
	  break;
  }
}

void WriteK(float num1)	//ConvF2C
{
  unsigned char *ch_arr1;
  
  ch_arr1 = (unsigned char *)(&num1);

  EEPROM_write(8*freq+0, ch_arr1[0]);
  EEPROM_write(8*freq+1, ch_arr1[1]);
  EEPROM_write(8*freq+2, ch_arr1[2]);
  EEPROM_write(8*freq+3, ch_arr1[3]);

}

float ReadK(void)		//ConvC2F
{
  unsigned char ch_arr2[4];

  ch_arr2[0] = EEPROM_read(8*freq+0);
  ch_arr2[1] = EEPROM_read(8*freq+1);
  ch_arr2[2] = EEPROM_read(8*freq+2);
  ch_arr2[3] = EEPROM_read(8*freq+3);

  return *((float *)(ch_arr2));
  
}

void WriteC(float num1)	//ConvF2C
{
  unsigned char *ch_arr1;
  
  ch_arr1 = (unsigned char *)(&num1);

  EEPROM_write(8*freq+4, ch_arr1[0]);
  EEPROM_write(8*freq+5, ch_arr1[1]);
  EEPROM_write(8*freq+6, ch_arr1[2]);
  EEPROM_write(8*freq+7, ch_arr1[3]);

}

float ReadC(void)		//ConvC2F
{
  unsigned char ch_arr2[4];

  ch_arr2[0] = EEPROM_read(8*freq+4);
  ch_arr2[1] = EEPROM_read(8*freq+5);
  ch_arr2[2] = EEPROM_read(8*freq+6);
  ch_arr2[3] = EEPROM_read(8*freq+7);

  return *((float *)(ch_arr2));
  
}

void Disp_Value(void)
{
  char *tmpptr;			//pomocne k zobrazeni
  char buffer[10];		//pomocne k zobrazeni
  int i;
  int length = 0;
  
  tmpptr = dtostrf( power,6,1,buffer);

  for (i = 0; i <10; i++)
  {
    value[i] = tmpptr[i];
    if (tmpptr[i]==0) break;
  }
  
  tmpptr = dtostrf( power_W,6,1,buffer);

  for (i = 0; i <10; i++)
  {
    value_W[i] = tmpptr[i];
    if (tmpptr[i]==0) break;
  }

  if (dBm)						//pokud se ma zobrazit dBm
  {
	while(value[length++] != '\0');//strlen
	length--;
    lcd_gotoxy( 0,0 ) ;
    lcd_puts("   ") ;
	lcd_gotoxy( 6 - length,0 ) ;//na zacatek displeje
    lcd_puts( value ) ;			//dBm
    lcd_puts( " dBm" ) ;
  }
  else							//pokud se ma zobrazit W
  {
	while(value_W[length++] != '\0');//strlen
	length--;
    lcd_gotoxy( 0,0 ) ;
    lcd_puts("") ;
    lcd_gotoxy( 6 - length,0 ) ;//na zacatek displeje
    lcd_puts( value_W ) ;		//mW
    if (power>= 0)lcd_puts( " mW " );
    else if (power>= -30)lcd_puts( " uW " );
    else lcd_puts( " nW " );
  }

  if (ADCRES<160) lcd_puts( " *" );	//nizka presnost, prah citlivosti
  else if (ADCRES>950) lcd_puts( " OVER" );//horni prah, pretizeni
  else lcd_puts( "     " );
      
}

float Linearize(float input)
{
  ;
}

void Disp_Analog(void)			//analogovy vystup PWM
{
  double int_part, decimal_part ;
  double tmp, pomocna;
  static double zmena;

  if (an_power == 0) //pokud je hodnota nulova, je rucka na nule
	  OCR0 = 0;
  else
  {
	if (an_rng == 0) //3dB
    {
	  decimal_part = an_power / 10;//jednotky dostaneme na desetiny
	  modf(decimal_part,&int_part);//ziskame celou cast
	  pomocna = decimal_part - int_part;//ziskame desetinnou cast
      tmp = pomocna*100;//ziskame cele cislo 0 - 31000
      tmp = abs(tmp);//likvidujeme znamenko
	  if(an_power < 0 )//pro zaporne hodnoty pocitame doplnek do 100
	  {
	    tmp = 30 - tmp;      
	  }
	  OCR0 = (255/30) * tmp;//prizpusobuje rozsahu registru
    }
    else if (an_rng == 1)//10dB
    {
	  decimal_part = an_power / 10;//jednotky dostaneme na desetiny
	  modf(decimal_part,&int_part);//ziskame celou cast
	  pomocna = decimal_part - int_part;//ziskame desetinnou cast
      tmp = pomocna*100;//ziskame cele cislo 0 - 100
      tmp = abs(tmp);//likvidujeme znamenko
	  if(an_power < 0 )//pro zaporne hodnoty pocitame doplnek do 100
	  {
	    tmp = 100 - tmp;
      }
	  OCR0 = 2.55 * tmp;//prizpusobuje rozsahu registru
    }
    else //plny rozsah
    {
      tmp = ADCRES/10;
      OCR0 = 2.55 * tmp;
    }
    //OCR0 = 0;
  }
}

void Measure(void)
{
  sprintf(adc_result,"%d",ADCRES);	//retezec hodnoty A/D prevodu

  power = k * (double)ADCRES + c + offset;	//vypocet vykonu
  an_power = power + an_ofs;
  if (power>= 0)
    power_W = pow(10, power/10);	//mW
  else if (power>= -30)
    power_W = pow(10, power/10+3);	//uW
  else
    power_W = pow(10, power/10+6);	//nW
  status = 0;

}

void Comm()
{
  char LF = 0x0a;			//znak LF
  char character = fifo[head];
  static float fnumber = 0;	//inicializace promennych
  static int i = 0;
  static int decimal = 0;
  static int decimal_pos = 0;
  static int minus = 0;
  static int target_flag = SET_NOTHING;
  float tmp;
  
  head = (head + 1) % FIFO_LENGTH;

  switch (character){
    case 'd':				//measure - cti hodnotu vykonu v dBm
	  usart_puts(value);	//vyslani dat
	  usart_puts(" dBm");	//vyslani dat
      USART_Transmit(LF);	//odradkuje
      break;
    case 'm':				//measure - cti hodnotu vykonu ve W
	  usart_puts(value_W);	//vyslani dat
      if (power>= 0)usart_puts( " mW " );
      else if (power>= -30)usart_puts( " uW " );
      else usart_puts( " nW " );
      USART_Transmit(LF);	//odradkuje
      break;
    case 'n':				//number - cti hodnotu A/D prevodu
	  usart_puts(adc_result);		//vyslani dat
      USART_Transmit(LF);	//odradkuje
      break;
	case 'g':				//get frequency - nacti frekvenci
	  usart_puts(dispfreq[freq]);	//vyslani dat
      USART_Transmit(LF);	//odradkuje
	  break;    
	case '.':				//zadani tecky, napr v desetinnem cislu
	  decimal = 1;
	  break;
    case 'k':				//zapisu k nebo c
	case 'c':
	  if (minus)			//znamenko zapisovane konstanty
	    fnumber *= -1;
	  if (character == 'k')	//zapis prislusne konstantydopameti
	    WriteK(fnumber);
	  else if (character == 'c')
	    WriteC(fnumber);

      fnumber = 0;
	  decimal_pos = 0;
	  decimal = 0;
	  minus = 0;
	  i=0;
	  target_flag = SET_NOTHING;
	  break;
    case '-':				//zadane minus
	  if (fnumber == 0)
	    minus = 1;
    case 'w':				//write const. - zapis konstanty
	  target_flag = SET_CONSTANT;
	  break;
    case 'f':				//set frequency - zmena frekvence
	  target_flag = SET_FREQ;
	  break;
    default:	  
	  if (isdigit(character))//kontrola na cisla
	  {
	    if (target_flag == SET_CONSTANT)
		{
  		  if (decimal)		//pripad desetinneho cisla
		  {
		    tmp = character - '0';
		    for (i = decimal_pos; i >= 0; i--)
		    {
		      tmp /= 10;
		    }
		    fnumber += tmp;
		    decimal_pos++;
		  }
		  else				//zapis dalsiho radu
		    fnumber = fnumber * 10 + character - '0';
		}
		else if (target_flag == SET_FREQ)
		{
		  freq = character - '0';//nastaveni frekvence
		  Set_Freq();			//aktualizace nastaveni
		  k = ReadK();
          c = ReadC();
		  target_flag = SET_NOTHING;
		}
	  }
	    
	  break;
  }
}

void Disp_Par(void)
{
  lcd_gotoxy( 6,1 ) ;
  lcd_puts("          ") ;
  lcd_gotoxy( 0,1 ) ;
  lcd_puts( disppar[par] );			//pise na displej
  
  char *tmpptr;			//pomocne k zobrazeni
  char buffer[10];		//pomocne k zobrazeni
  int i;  
  
  
  char temp [8];
  unsigned int length = 0;
  switch (par){
    case 0:
	  Set_Freq();
	  break;
    case 1:
	  tmpptr = dtostrf( offset,6,1,buffer);
      for (i = 0; i <10; i++)
      {
        temp[i] = tmpptr[i];
        if (tmpptr[i]==0) break;
      }
	  while(temp[length++] != '\0');//strlen
	  length--;
	  lcd_gotoxy( 14 - length,1 ) ;
      lcd_puts(temp) ;				//pise na displej
	  lcd_puts("dB") ;
	  break;
    case 2:
	  tmpptr = dtostrf( offset,6,1,buffer);
      for (i = 0; i <10; i++)
      {
        temp[i] = tmpptr[i];
        if (tmpptr[i]==0) break;
      }
	  while(temp[length++] != '\0');//strlen
	  length--;
	  lcd_gotoxy( 14 - length,1 ) ;
      lcd_puts(temp) ;				//pise na displej
	  lcd_puts("dB") ;
	  break;
    case 3:
	  tmpptr = dtostrf( offset,6,1,buffer);
      for (i = 0; i <10; i++)
      {
        temp[i] = tmpptr[i];
        if (tmpptr[i]==0) break;
      }
	  while(temp[length++] != '\0');//strlen
	  length--;
	  lcd_gotoxy( 14 - length,1 ) ;
      lcd_puts(temp) ;				//pise na displej
	  lcd_puts("dB") ;
	  break;
    case 4:
	  sprintf(temp,"%d dB",(int)an_ofs);
	  while(temp[length++] != '\0');
	  length--;
	  lcd_gotoxy( 16 - length,1 ) ;
      lcd_puts(temp) ;				//pise na displej
	  break;
    case 5:
	  lcd_gotoxy( 10,1 ) ;
      lcd_puts(disp_rng[an_rng]) ;	//pise na displej
	  break;
  }
}

void execute_btn1(void) {
  switch(par)
    {
	case 0://FREQ
      freq = (freq + 1) % 7;//rotuje udaj nahoru
      k = ReadK();			//aktualizace prevodnich konstant
      c = ReadC();
	  break;
    case 1://OFFSET 10
	  if (offset < 100)		//omezeni hodnoty offset
	    offset = offset + 10;
	  break;
    case 2://OFFSET 1
	  if (offset < 100)		//omezeni hodnoty offset
	    offset = offset + 1;
	  break;
    case 3://OFFSET 0.1
	  if (offset < 100)		//omezeni hodnoty offset
	    offset = offset + 0.1;
	  break;
    case 4://AN_OFS
	  if (an_ofs < 100)		//omezeni hodnoty an_ofs
	    an_ofs = an_ofs + 1;
	  break;
    case 5://AN_RNG
	  if (an_rng < 2)
		an_rng = an_rng + 1;
	  break;
  }
  
  Disp_Par();				//vola funkci pro zobrazeni
}

void execute_btn2(void) {
  switch(par)
    {
	case 0://FREQ
	  freq = (freq + 6) % 7;//rotuje udaj dolu
      k = ReadK();			//aktualizace prevodnich konstant
      c = ReadC();
	  break;
    case 1://OFFSET 10
	  if (offset > -100)	//omezeni hodnoty offset
	    offset = offset - 10;
	  break;
    case 2://OFFSET 1
	  if (offset > -100)	//omezeni hodnoty offset
	    offset = offset - 1;
	  break;
    case 3://OFFSET 0.1
	  if (offset > -100)	//omezeni hodnoty offset
	    offset = offset - 0.1;
	  break;
    case 4://AN_OFS
	  if (an_ofs > -100)	//omezeni hodnoty an_ofs
	    an_ofs = an_ofs - 1;
	  break;
    case 5://AN_RNG
	  if (an_rng > 0)
	    an_rng = an_rng - 1;
	  break;
  }

  Disp_Par();				//vola funkci pro zobrazeni
}

/*----------------------------------MAIN------------------------------------------------*/
/*----------------------------------MAIN------------------------------------------------*/

int main (void)
{
  HW_Init ();				//inicializace portu a periferii
  USART_Init(MYUBRR);		//inicializace USART
  lcd_init();				//inicializace displeje
  lcd_clrscr();				//smazani displeje
  
  head = 0;					//inicializace fronty
  tail = 0;
  dBm = 1;					//inicializace zobrazeni jednotek
  k = ReadK();				//inicializace prevodnich konstant
  c = ReadC();
  
  sei() ;					//globalni povoleni preruseni

  Disp_Par();				//vypsani vychoziho nastaveni na displej
  
  while( 1 )				//hlavni programova smycka
  {
    if (status)
	{
	  Measure();			//mereni a vypocet hodnot
	  Disp_Value();			//zobrazeni hodnot
      Disp_Analog();
	}

    if (head != tail)		//pokud je neco ve fronte
	  Comm();				//zpracuj pozadavek, komunikuj
  }
}
/*-----------------------------END OF MAIN----------------------------------------------*/
/*-----------------------------END OF MAIN----------------------------------------------*/

ISR(USART_RXC_vect)			//preruseni na prijem dat
{
  fifo[tail] = UDR;			//prijeti dat do fronty
  tail = (tail + 1) % FIFO_LENGTH;
}

ISR( TIMER1_OVF_vect )		//preruseni od casovace 1
{
  int temp = 0, i;
  for (i = 0;i <8; i++)		//cislicova filtrace
  temp += ADCL+(ADCH<<8);	//prumer z osmi vzorku
  ADCRES = temp>>3;			//ulozeni prumerovane hodnoty z A/D prevodniku
  status = 1;				//provede se Measure()
}

ISR( INT0_vect )			//preruseni od tlacitek
{
  int portinput = (PIND & 0x3c);//maskuji port a ctu stav
  
  switch(portinput){
    case 56:				//tlacitko down
	  execute_btn1();		//vola zpracovani akce od tlacitka down
	  break;
    case 48:				//tlacitko up
	  execute_btn2();		//vola zpracovani akce od tlacitka up
	  break;
    case 40:				//tlacitko set
	  par = (par + 1) % 6;	//rotuje parametr
	  Disp_Par();			//zobrazuje aktualni stav na displeji
	  break;
    case 24:				//tlacitko dBm/W
	  dBm = !dBm;			//volba jednotek
	  break;
  }
}

