#include <16F876A.h>
#device adc=10

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES NOLVP,NOPROTECT,XT,NOBROWNOUT
#use delay(clock=4000000)
//#define EEPROM_SCL   PIN_C3
//#define EEPROM_SDA   PIN_C4
#use rs232(baud=9600,xmit=PIN_C6,disable_ints)
//#use i2c(Master,Fast,sda=PIN_C4,scl=PIN_C3)


#define LCD_RS          PIN_C7      // rizeni registru LCD displeje <lcd.c>
#define LCD_E           PIN_B7      // enable LCD displeje <lcd.c>
#define LCD_D0          PIN_B3      // data LCD  <lcd.c>
#define LCD_D1          PIN_B2
#define LCD_D2          PIN_B1
#define LCD_D3          PIN_B0
#define PWM_OUT         PIN_C2      // kontrast LCD - menic
#define LCD_ENABLE      PIN_A1

#define GM_PIN          PIN_A4      // vstup od GMT
#define HV_ENABLE       PIN_A3      // start 400V
#define CHARGE          PIN_A2      // nabijeni baterie

#define KBD_K4          0           //tlacitko na PIN_B7 neexistuje <kbd.c>
#define TL3             0x10        //tlacitko PIN_B4 -ostatni default, viz <kbd.c>
#define TL2             0x20        //tlacitko PIN_B5 <kbd.c>
#define TL1             0x40        //tlacitko PIN_B6 <kbd.c>

#define KBD_CALMTIME     1          //x*40ms - doba na zakmity tlacitek <kbd.c>
#define KEYDELAY        30          //x*40ms - doba podrzeni tlacitka do opakovani
#define KEYREPEAT        6          //x*40ms - doba opakovani stiskleho tlacitka
#define LOG_PERIOD      30          //perioda logovani v min
#define TIME_TO_SLEEP   19          //x*16s - doba do uspani, pokud zadny stisk a HV nebezi

#define MAX_EEPROM      255         //max velikost vnitrni ee pameti
#define EE_LOG_SWITCH   1           //misto v ee, kam se uklada zpusob logovani
#define EE_LOG_OFFSET   0           //misto v ee, kde je ulozen offset dalsiho volneho mista

#define ECHO_MAX_CYCLE  700         //ryhlost hlavni smyèky za 40ms /cca 20kHz/ (700)
#define ECHO_PIN1       PIN_C4      //piezo
#define ECHO_PIN2       PIN_C5      //piezo

#define TEST_V_BATT     0x0f        //maska minut pro test baterie
#define BATT_ENABLE     525         //1024/ADC*1.5+0.18 ADC=525 .. Ubat=3.1V; jeli Ubat nizsi pak sleep

#include <lcd.c>
#include <kbd.c>

int8 ms7RTC,sRTC,mRTC,hRTC,dRTC,mdRTC,yRTC;  //promenne RTC
int1 vypocet,refresh,counters,sleep,log,piezo; //povoleni vypoctu,zobrazeni,citani,uspani,logovani,cvak pieza
int16 avg,bkg,min,max,dif; //hlavni zobrazované promìnne
int32 gmcount,mincount;    //citac trubice GM,citac minut od startu
int8  timer_n,timer_s;     //citace pulzù do 40ms, v celych 40ms, v predchozi 40ms
int32 gmcount_p;           //pocet pulzu od zapnuti v predchozi cele minute
int32 gmcount_sm;          //pocet pulzu od zapnuti precteno v cele minute
int8  s25tik,sTIK;              //tiky pro casovani trubice 25tiku do sekundy, 60s do min
int8  key_timer,save_key,keyp;  //pomocny citac pro tlacitka,ulozeni stiskleho tlacitka,provedeni akce stiskleho tl.
int8 menu_A,menu_B;        //vektor pro pohyb v menu na LCD
int8 log_interval;         //minutovy citac do dalsiho logu
int8 log_switch;           //prepinac zpusobu logovani
int8 ee_offset;            //kam v ee ulozit dalsi zaznam
int8 echo_switch,echo_tik; //povolení echa,poèet tiku za 40ms
int16 echo_timer,echo_cycle; //èitaè rychlosti smyèky pro echo,cvaknuti v cyklu
int8 pwm;                     //kontrast
int8 tik_to_sleep;            //casovac pro uspani
float analog;              //napeti baterie
int1 test_batt;             //test napeti baterie
int1 test;                 //pokus
int8 timertest;            //pokus

#priority timer2,timer1,rb

#int_TIMER2      //nastaven presne na 40ms,cte casovac timer0 GMT
TIMER2_isr()
{
   kbd_ticktimer();
   if (key_timer) key_timer--;
   if (!counters) {set_timer0(0);s25TIK=0;sTIK=0;return;}
   if (echo_switch) echo_switch=2;
   timer_n=get_timer0();
   s25TIK++;
   if (timer_n!=timer_s) //nastala zmena v citaci,pak proved pricteni puslu
   {
      if (timer_n < timer_s) {timer_s=256-timer_s+timer_n;gmcount=gmcount+timer_s;} //test preteceni casovace
      else {timer_s=timer_n-timer_s;gmcount=gmcount+timer_s;}
      echo_tik=timer_s;
      timer_s=timer_n;
    if (menu_A==1 && menu_B==0) refresh=true;
   }
   else echo_tik=0;
   if (s25TIK==25)    //cela sekunda
   {
      s25TIK=0;
      sTIK++;
      if (menu_A==1 && menu_B==5) refresh=true;
      if (sTIK==60)  //cela minuta
      {
         sTIK=0;
         mincount++;
         vypocet=true;
         gmcount_sm=gmcount;
      }
   }
}

#int_RB
RB_isr()
{
 kbd_pullkbd();
 tik_to_sleep = TIME_TO_SLEEP;
}

int8 modulo(int8 h,int8 m) //pomocna fce pro modulo x
{
   if (h<m) return (h);
   return(h-m);
}

void clear_lcd()
{
   printf(lcd_putc,"\f");
}

void set_date()         //citac datumu
{
   dRTC++;
   switch (mdRTC)
   {
      case 1:
      case 3:
      case 5:
      case 7:
      case 8:
      case 10:
      case 12: if(dRTC>=32) {dRTC=1;mdRTC++;if(mdRTC==13) {mdRTC =1;yRTC=modulo(yRTC++,100);}} break;
      case 4:
      case 6:
      case 9:
      case 11: if(dRTC>=31) {dRTC=1;mdRTC++;} break;
      case 2:  if (dRTC >= 30) {dRTC=1;mdRTC++;break;}
               if (dRTC ==29) {if (!(yRTC & 0x03)) break;dRTC=1;mdRTC++;}
   }

}



#int_TIMER1          // RTC
TIMER1_isr()
{
      sRTC=sRTC+16;
      if (sRTC >= 60)
      {
         mRTC++;sRTC=modulo(sRTC,60);          //1min
         if (mRTC>=60) {hRTC++;mRTC=0;}        //1hod
         if ((mRTC & TEST_V_BATT) == TEST_V_BATT) test_batt=true;
         if (hRTC>=24) {hRTC=0;set_date();}    //1den
      }
      refresh=true;
      if (tik_to_sleep) tik_to_sleep--;
}

void ee_head()  //ulozeni hlavicky do ee cas,datum,zpusob logovani
{               //v ee 100hhhhh /hodiny/,l0mmmmmm /log + minuty/,000ddddd /den/,yyyymmmm /rok-7,mesic/
   int8 data;
   log=true;
   if (!log_switch) return;
   if (ee_offset>5) {
      data=read_eeprom(ee_offset-4);
      if (bit_test(data,7)) ee_offset=ee_offset-4;
   }
   if (ee_offset>=(MAX_EEPROM-5)) {log=false;return;}
   log_interval=0;
   data=hRTC;
   bit_set(data,7);
   write_eeprom(ee_offset++,data);
   data=mRTC;
   if (bit_test(log_switch,0)) bit_set(data,7);
   write_eeprom(ee_offset++,data);
   write_eeprom(ee_offset++,dRTC);
   data=((yRTC-7) << 4)|mdRTC;
   write_eeprom(ee_offset++,data);
}



void menu_proces(int8 key)       //menu zobrazene na LCD pri citani
{
   switch (key)
   {
      case TL1:   menu_A=2;refresh=true;clear_lcd();return;
      case TL2:   if (log) log=false;
                  else ee_head();
                  break;
      case TL3:   menu_B=modulo(++menu_B,6);break;
   }
   printf(lcd_putc,"\rdif=%lu   ",dif);
   lcd_gotoxy(11,1);
   if (log) printf(lcd_putc,"L");  //pokud se loguje, zobraz L
   else  printf(lcd_putc," ");
   if (bkg) printf(lcd_putc,"B");  //pokud jiz je BKG,zobraz B
   lcd_gotoxy(14,1);
   printf(lcd_putc,"%3u\n",(int8)(MAX_EEPROM-ee_offset)/2); //zbivajici misto v ee

   switch (menu_B)
   {
      case 0: printf(lcd_putc,"n=%lu",gmcount);break;
      case 1: printf(lcd_putc,"min=%Lu",min);break;
      case 2: printf(lcd_putc,"max=%lu",max);break;
      case 3: printf(lcd_putc,"avg=%lu",avg);break;
      case 4: printf(lcd_putc,"bkg=%lu",bkg);break;
      case 5: printf(lcd_putc,"ontime=%lu:%02d ",mincount,sTIK);break;
   }
}

void send_data()  //posle data z ee na rs232 9600 8N1
{
   int8 i;
   int16 data;
   int8 *adr;
   for (i=2;i<ee_offset;i)
   {
      adr=&data;
      *adr=read_eeprom(i++);
      if (bit_test(*adr,7)) //test, zda se nejedna o hlavicku v ee
      {
         bit_clear(*adr,7);
         printf("\r\n%d:",*adr);
         *adr=read_eeprom(i++);
         printf("%02d ",(0x3f&*adr));
         adr++;*adr=read_eeprom(i++);
         printf("%d/",*adr);
         *adr=read_eeprom(i++);
         printf("%02d/",*adr&0x0f);
         *adr=(*adr>>4)+7;
         printf("%02d ",*adr);
         adr--;
         if (bit_test(*adr,7)) printf("1min");
         else  {*adr=LOG_PERIOD;printf("%umin",*adr);}

      }
      else{
      data=data<<8;
      *adr=read_eeprom(i++);
      printf("\r\n%lu",data);
      }

   }
}

void display(int8 key)                //zobrazeni na LCD - zakladni menu
{
   int8 mon,year;                    //pomocne promenne
   if (key) clear_lcd();
   keyp=0;
   refresh=false;
   if (menu_A>1) {                    //zmena pri stisku tl spolecna pro vetsinu menu_A

      if (key==TL3) {menu_A++;if (menu_A>11) menu_A=2;} //TL3 pak rolluj v menu_A
      if ( ( menu_A != 10 ) && ( menu_A != 6 ) && (menu_A != 11)  )
      {
         if (key==TL1)
         {
            if (counters) menu_A=1;
            else menu_A=0;
            key=0;
         }
         else printf(lcd_putc,"\r\nesc   cr    roll\r");
      }
   }

   switch (menu_A)         //hlavni menu
   {
   case 0: min=0xffff;    //zakladni menu po resetu, nastaveni promennych
           max=gmcount=gmcount_sm=gmcount_p=timer_s=timer_n=mincount=dif=avg=bkg=0;
           vypocet=sleep=counters=log=echo_switch=false;
           test_batt = true;
           log_switch=read_eeprom(EE_LOG_SWITCH);
           ee_offset=read_eeprom(EE_LOG_OFFSET);
           output_high(HV_ENABLE);
           printf(lcd_putc,"\r%d:%02d  %d.%d.%02d   \nmenu  off  start",hRTC,mRTC,dRTC,mdRTC,yRTC);
            switch (key)
            {
            case TL1:   menu_A=2;break;
            case TL2:   menu_A=menu_B=0;sleep=true;break;
            case TL3:   menu_A=1;menu_B=0;
                        output_low(HV_ENABLE);
                        printf(lcd_putc,"\fSTART HV 400V");
                        delay_ms(2000);
                        counters=true;
                        break;
            }
            if (key) {refresh=true;clear_lcd();}
            break;
   case 1: menu_proces(key);break;  //skok na menu zobrazujici vypoctene a nacitane promenne
   case 3: if (key==TL2) {log_switch=modulo(++log_switch,3);write_eeprom(EE_LOG_SWITCH,log_switch);log=false;}
           switch (log_switch)
           {
            case 0: printf(lcd_putc,"log \176 rs232");break;
            case 1: printf(lcd_putc,"log \176 eeprom 1m");break;
            case 2: mon=LOG_PERIOD;printf(lcd_putc,"log \176 eeprom %um",mon);break;
            //case 3: printf(lcd_putc,"log \176 off       ");break;
           }
           break;
   case 9: printf(lcd_putc,"OFF (stand by)");
           if (key==TL2) {menu_A=menu_B=0;sleep=true;output_high(HV_ENABLE);}
           break;
   case 8: if (key==TL2) if (echo_switch) echo_switch=0;else echo_switch=1;

           if (echo_switch) printf(lcd_putc,"echo  on");
           else printf(lcd_putc,"echo  off");
           break;
   case 4: if (key==TL2) send_data();
           printf(lcd_putc,"send log \176 rs232");
           break;
   case 5: if (key==TL2) {log=false;ee_offset=2;write_eeprom(EE_LOG_OFFSET,2);}
           printf(lcd_putc,"erase eeprom %3u",(int8)(MAX_EEPROM-ee_offset)/2);
           break;
   case 2: printf(lcd_putc,"STOP & clear");
           if (key==TL2) {menu_A=0;menu_B=0;refresh=true;}
           break;
   case 7: printf(lcd_putc,"batterie ");
           //if (!input(CHARGE)) {printf(lcd_putc,"charge");break;}
           if ( key == TL2 || counters )
           {
            if ( !counters ) {output_low(HV_ENABLE);delay_ms(30);}
            analog = (float) (read_adc());
            analog = 1024/analog*1.5 + 0.18;
            if ( !counters ) output_float(HV_ENABLE);
            printf(lcd_putc,"%1.2fV",analog);
           }
           break;
   case 6: if (key == TL2) {pwm++;if (pwm > 10) pwm --;}
           if (key == TL1) {pwm--;if (pwm < 2) pwm ++;}
           set_pwm1_duty(pwm);
           printf(lcd_putc,"contrast: %d\n -     +    roll",pwm);
           break;
   case 10: if (key==TL2) {set_timer1(0);sRTC=0;mRTC=modulo(++mRTC,60);}
           if (key==TL1) hRTC=modulo(++hRTC,24);
           printf(lcd_putc,"\rtime %2d:%02d\nhod   min   roll",hRTC,mRTC);
           break;
   case 11: if (key==TL1) {mon=mdRTC;year=yRTC;set_date();mdRTC=mon;year=yRTC;}
           if (key==TL2) {mon=dRTC;year=yRTC;dRTC=32;set_date();year=yRTC;}
           if (key==0x30) {mon=mdRTC;yRTC++;dRTC--;set_date();}
           printf(lcd_putc,"\rdate %2d.%02d.%02d\nday   mon   roll",dRTC,mdRTC,yRTC);
           break;
   }
}

void counters_fce()
{
   int8 *adr;
   if (vypocet)      //1x za minutu se provede vypocet a log
   {
      dif=gmcount_sm-gmcount_p;
      gmcount_p=gmcount_sm;
      avg=gmcount_sm/mincount;
      if (dif>max) max=dif;
      if (dif<min) min=dif;
      if (mincount==5) bkg=gmcount_sm/5;
      vypocet=false;
      refresh=true;

      if (log)
      {
         //if (log_switch && (ee_offset>(MAX_EEPROM-2))) {log=false;return;}
         switch (log_switch)
         {
         case 0: printf("\n\r%d:%02d %2d/%02d/%02d dif=%lu",hRTC,mRTC,dRTC,mdRTC,yRTC,dif);break;
         case 2: if (log_interval) break;
         case 1: adr=&dif;adr++;bit_clear(*adr,7);
                 write_eeprom(ee_offset++,*adr);
                 write_eeprom(ee_offset++,*(--adr));
                 write_eeprom(EE_LOG_OFFSET,ee_offset);
                 if (ee_offset > (MAX_EEPROM-2)) log=false;
                 break;
         }
         log_interval=modulo(++log_interval,LOG_PERIOD);
      }
   }
}

void echo_fce()
{
   int8 a,c;
   int16 b;
   if (!echo_tik || !counters) return;
   //if (echo_switch==2) {echo_switch=1;echo_cycle=ECHO_MAX_CYCLE/echo_tik;echo_timer=0;}
   if (echo_tik == 1)
   {
         for (a=0;a<2;a++) {output_high(ECHO_PIN1);delay_us(160);output_low(ECHO_PIN1);delay_us(160);}
         echo_tik=0;
   }
   else /*if ((echo_timer == echo_cycle) || (!echo_timer))*/
   {
      echo_switch=1;
      c=echo_tik;
      if (echo_tik <40) {a=40/c;b=0;}
      if ((echo_tik >=40) && (echo_tik < 60)) {a=0;b=40000/c;}
      if (echo_tik >=60) {a=0;b=160;}
      while (echo_tik && (echo_switch==1) && !kbd_press())
      {
         echo_timer=0;
         output_high(ECHO_PIN1);
         delay_us(160);
         output_low(ECHO_PIN1);
         piezo=~piezo;
         delay_us(b);
         delay_ms(a);
      }

      //output_bit(ECHO_PIN2,piezo);
   }
   echo_timer++;
}

void pin_set_sleep()
{
   #use fast_IO(A)
   set_tris_A(0x15);
   output_A(0x2a);
   #use standard_IO(A)
   #use fast_IO(B)
   set_tris_B(0x70);
   output_B(0x8f);
   #use standard_IO(B)
   #use fast_IO(C)
   set_tris_C(0x2);
   output_C(0x84);
   #use standard_IO(C)
}

void sleep_fce()
{
   /*if (!input(CHARGE))           // pokud se nabiji, pak nespi
   {
      printf(lcd_putc,"\fcharged batt");
      tik_to_sleep = 0;
      while (!tik_to_sleep) if (input(CHARGE)) return;
      kbd_getc();
      sleep = false;
   }
   else */
   {
    
   //setup_timer_0(RTCC_DIV_1|RTCC_INTERNAL);
   //setup_timer_1(T1_DISABLED);
   //setup_timer_2(T2_DIV_BY_16,249,10);
   
      
      counters=false;
      printf(lcd_putc,"\fchrrr");
      delay_ms(1000);
      setup_adc_ports(ADC_OFF);
      setup_ccp1(CCP_OFF);
      //SET_TRIS_A(0xFF);
      //SET_TRIS_B(0xFF);
      //SET_TRIS_C(0xFF);
      pin_set_sleep();
      do {sleep();delay_cycles(1);} while ( kbd_state!=0x20 /*&& input(CHARGE)*/ );
      #use fast_IO(B)
      set_tris_B(0x70);
      output_B(0);
      #use standard_IO(B)
      setup_adc_ports(AN0);
   setup_adc(ADC_CLOCK_DIV_16);
   set_adc_channel(0);
   //setup_timer_0(RTCC_DIV_1|RTCC_EXT_L_TO_H);
   
   //setup_timer_2(T2_DIV_BY_16,249,10);
   //setup_ccp1(CCP_PWM);
  
      menu_A=menu_B=0;
      setup_ccp1(CCP_PWM);
      set_PWM1_duty(pwm);
      delay_ms(200);
      output_low(LCD_ENABLE);
      output_high(HV_ENABLE);
      lcd_init();
      //printf(lcd_putc,"\fhello");
      delay_ms(1000);kbd_getc();
      keyp=0;
      sleep=false;
      refresh=true;
   }
}


void main()
{
   setup_adc_ports(AN0);
   setup_adc(ADC_CLOCK_DIV_16);
   set_adc_channel(0);
   setup_timer_0(RTCC_DIV_1|RTCC_EXT_L_TO_H);
   setup_timer_1(T1_EXTERNAL|T1_DIV_BY_8|T1_CLK_OUT);
   setup_timer_2(T2_DIV_BY_16,249,10);
   setup_ccp1(CCP_PWM);
   set_PWM1_duty(0x5);

   enable_interrupts(INT_TIMER2);
   enable_interrupts(INT_RB);
   enable_interrupts(INT_TIMER1);
   enable_interrupts(GLOBAL);

   output_low(LCD_ENABLE);
   KBD_init();
   LCD_init();
   pwm=5;
   sRTC=0;
   mRTC=0;
   hRTC=0;
   dRTC=18;
   mdRTC=10;
   yRTC=07;

   menu_A=menu_B=0;
   refresh=true;
   keyp=0;


   while (TRUE)
   {
     if (refresh) display(keyp);               //povoleni zobrazeni(menu + fce tlacitek)
     if (counters) { tik_to_sleep = TIME_TO_SLEEP;counters_fce();}    //povoleni citacu,vypoctu a logovani
     else if (! tik_to_sleep) sleep = true;    //necita a zadny stisk tlacitek do TIME_TO_SLEEP pak sleep
     if (echo_switch) echo_fce();
     if (kbd_press())                          //test stisku tlacitek,repeat, delay
     {
        if (!save_key) {save_key=kbd_getc();keyp=save_key;refresh=true;key_timer=KEYDELAY;} //delay key
        if (!key_timer) {refresh=true;keyp=save_key;key_timer=KEYREPEAT;}      //repeat key
     }
     else key_timer=save_key=keyp=0;
            //echo (piezo)
     if (test_batt && counters)                     //test napeti baterie
     {
         if (read_adc() > BATT_ENABLE)
            {
               printf(lcd_putc,"\flow batt");
               delay_ms(2000);
               sleep = true;
            }
         test_batt=false;
     }
     if (sleep) sleep_fce();                            //sleep
     //if (!timertest) {timertest=30;output_bit(PIN_C3,test);test=~test;}
     //timertest--;
   }

}