//**********************************************************************
//            LCT-METER/PULSER
//**********************************************************************
// (c) OK1XGL 2004
// verze 1.00 - uvodni verze 10.1.2004
// verze 1.01 - pridano ukladani  vlastni Cp/Lp do pameti EEPROM
//            - pridano ukladani stavu (mod, parametry modu ) do pameti EEROM
//
//
// Popis funkcnosti:
// -----------------
// Meri kapacitu, indukcnost, teplotu a jako doplnek obsahuje generator impulzu.
// Jednotlive druhy mereni se prepinaji stiskem tlacitka MODE. Stiskem tlacitka NUL lze
// vynulovat mereni (odecteni vlivu mericich privodu, u mereni teploty odecteni napr. pro mereni otepleni).
//
// Mereni L a C:
// -------------
// Mereni se provadi merenim kmitoctu mericiho oscilatoru. Neznama Lx se pripojuje do serie s vnitrni L oscilatoru.
// Neznama Cx se pripojuje paralelne k vnitrni C oscilatoru. Vypocet nezname Cx/Lx se provadi pomoci nasledujicich vzorcu:
//    Cx=((f1^2/f2^2)-1)*C       Lx=((f1^2/F2^2)-1)*L
// Pripadne se od vysledku odecte hodnota Cv nebo Lv, ktera se ziska stisknutim NUL
// f1 - kmitocet mericiho oscilatoru, ke kteremu neni pripojena zadna Lx nebo Cx, ziska se pri kalibraci
// f2 - kmitocet mericiho oscilatoru s pripojenou neznamou Lx nebo Cx
// C  - kapacita vnitrniho C oscilatoru, ziska se pri kalibraci
// L  - indukcnost vnitrni L oscilatoru, ziska se kalibraci
//
// Kalibrace:
// ----------
// Provadi se pri zapnuti pristroje a probiha takto:
// Zmeri se kmitocet mericiho oscilatoru f1 ,ke kteremu neni pripojena zadna Lx nebo Cx.
// Pripoji se kalibracni kondenzator Ck o zname hodnote, mel by byt co nejpresnejsi a nejstabilnejsi.
// Zmeri se kmitocet  mericiho oscilatoru f2.
// Vypoctou se hodnoty L a C podle vzorcu:
//    C=(Ck*F2^2)/(F1^2-F2^2)   L=(1/(4*PI^2*F1^2*C)
// Odpoji se kalibracni kondenzator Ck a pristroj je pripraven k mereni L a C
//
//
// Mereni teploty:
// ---------------
// Kladna merici svorka je pripojena primo na vstup procesoru. Pro mereni teploty se pouziva teplomer
// fy Dallas DS18B20, se kterym se komunikajue po I2C sbernici.
//
//
// Generovani pulzu:
// -----------------
// Kladna merici svorka je pripojena primo na vstup procesoru. Jsou generovany kladne pulzy se zvolenou sirkou.
// Nulove pulzy maji stejnou sirku, tedy perioda je dvojnasobek sirky pulzu.
//
//
//
// Poznamky k implementaci:
// ------------------------
//
// 1) mereni L a C
// ----------------
// Zakladem je mereni kmitoctu. Presnost mereni byla zvolena na 4Hz, tedy merici perioda bude 250ms. Pro citani impulzu je
// pouzit 16 bitovy citac TMR1, ktery je rozsiren o dalsich 8 bitu promennou counter_H. Pretecenim TMR1 se v preruseni zvysi
// hodnota counter_H o jednicku. Merici periodu odmeruje 8 bitovy citac TMR0, kteremu je predrazen preddelic :256. Cita
// takty procesoru. TMR0 pretece kazdych 50ms.  Promenna sampler_H obsahuje pocet potrebnych 50ms jednotek pro dosazeni
// pozadovaneho casu. Prommena sampler_H se dekrementuje v preruseni. Po uplynuti merici periody (250ms) se zastavi citani
// TMR1, ktery spolu s hodnotou v counter_H bude obsahovat mereny kmitocet s presnosti 4Hz.
// Promenou freq_done se oznami ukonceni mereni kmitoctu. Hodnota  v TMR1 a counter_H se prevede na realne cislo, ktere se
// dale pouzije pro vypocty.
//
// 2) mereni teploty
// -----------------
// Vyuziva se teplomeru fy Dallas DS18B20, ktery komunikuje po I2C sbernici. Pro tento mod se sepne rele, ktere pripoji
// kalibracni kondenzator Ck. Tim dojde k pripojeni svorek na vstup portu pro teplomer. Mereni teploty probiha tak, ze se
// vysle prikaz do teplomeru aby zmeril teplotu a po uplynuti potrebne doby konverze se z teplomeru precte zmerena teplota a
// zobrazi se. Pro odmereni doby konverze se vyuziva mereni frekvence, ktere trva 250ms.
//
// 3) Generovani pulzu
// -------------------
// Pro tento mod se sepne rele, ktere pripoji kalibracni kondenzator Ck. Tim dojde k pripojeni svorek na vystup portu
// pro pulser. Pro generovani pulzu je vyuzivana jednotka PWM1 pro pulzy do sirky 1ms a pro pulzy delsi pak jednotka CCP1.
// Generovani pulzu PWM1 jednotkou je zcela autonomni, jednotce se nastavi perioda (dvojnasobek sirky pulzu) a sirka pulzu
// a generovani probiha mez zasahu programu. Jednotka CCP1 generuje periodicke preruseni po dobe urcene sirkou pulzu.
// Vlastni impulz je generovan v preruseni, kde se periodicky strida nastaveni portu do log.1 a do log.0
//
// 4) Cteni tlacitek
// -----------------
// Pro cteni tlacitek je vyuzivano preruseni od zmeny na portu B. Pri zmene stavu nektereho z tlacitek toto preruseni
// nastavi promenou freq_done. Na miste v programu, kde se testuje tato promenna se precte stav tlacitek a podle
// jejich stavu se dal pokracuje.
//
// 5) Zakladni smycky programu
// ---------------------------
// Hlavni smycka programu je koncipovana jako stavovy automat, kdy jednotlive stavy predstavuji jednotlive mody mereni.
// V jednotlivych stavech se volaji hlavni funkce jednotlivych modu mereni. Tyto funkce jsou koncipovany jako stavove
// automaty, ktere provedou podle potreby uvodni akci a dale sleduji stav promenne freq_done. Na zaklade tohoto stavu
// pokracuji dale nebo prechazeji do stavu jineho.  freq_done ma tyto stavy:
// F_PEACE -klidovy stav
// F_DONE- mereni kmitoctu dokonceno
// F_KEY- mereni kmitoctu preruseno stiskem nejakeho tlacitka.
// Stav F_DONE je vyuzivan v modu mereni Lx a Cx a dale v modu mereni teploty se  vyuziva k odmerovani casu.
// Pri stavu F_KEY se precte stav tlacitek a pri stisku tlacitka MODE se opousti hlavni funkce aktivniho modu mereni a
// prechazi se  zpet se do hlavni smycky programu. V hlavni smycce programu se nasledne prejde do nasledujiciho modu mereni.
// Pri stisku tlacitka NUL se provede pozadavana akce, a zustava se v danem modu mereni.



#include <16f876.h>
#include <16F877_reg.h>
#include "lc.h"
#include <lcd.c>
#include <tm.c>
#include <math.h>

//#define  FREQ // jen mereni frekvence mericiho oscilatoru, urceno pro jeho kalibraci
              // pripojime citac na vystup mericiho oscilatoru a musi ukazovat totez co je na displeji, pokud tomu tak neni,
              // napravu provedeme zmenou konstanty CORIG v lc.h pripadne zmenou kondenzatoru u oscilatoru procesoru

/*****************************************************/
/************* PRERUSOVACI RUTINY ********************/
/*****************************************************/

#USE FAST_IO(A)
#USE FAST_IO(B)
#USE FAST_IO(C)

// prerusovaci rutina spolecna pro vsechny preruseni, napsana v assembleru, protoze cecko to dela neefektivne
#int_GLOBAL
void int_handler()
{
 // uloz W, STATUS a PCLATH
 #asm
 movwf   W_TMP
 swapf   STATUS,W
 clrf    STATUS
 movwf   F_TMP
 movf    PCLATH,W
 movwf   PCLATH_TMP
 clrf    PCLATH

 btfss   PIR1,_TMR1IF
 goto    NO_T1_L
 // preruseni od timeru1
 incf    counter_h,F       // +1 na nejvyssim radu citace impulzu
 bcf     PIR1,_TMR1IF      // nuluj priznak preruseni
                           // pokracujeme hned testem preruseni od timeru0 (odmeruje periodu mereni)
NO_T1_L:
 btfss   INTCON,_T0IF
 goto    NO_T0_L           // na dalsi druhy preruseni
 // preruseni od timeru 0
 bcf     INTCON,_T0IF      // nuluj priznak preruseni
 movlw   CORIG             // pridane spozdeni, aby byl odmereny cas timerem 0 presne
 movwf   INT_TMP
LOOP:
 decfsz  INT_TMP,F
 goto    LOOP
 nop
 movlw   TIME_1S_L         // znovu natahni timer0
 movwf   TMR0
 decf    sampler_H,F       // -1  na citaci opakovani zakladni periody timeru0
 btfss   STATUS,_Z         // perioda mereni vyprsela?
 goto    END_INT_L         // koncime,abychom nezdrzovali dalsimi zdroji preruseni na nich jiz casove nezalezi
 nop
 nop
 nop
 nop
 bcf     T1CON,_TMR1ON     // zastav citani impulzu
 movlw   F_DONE
 movwf   freq_done         // priznak ukonceni citani - mereni OK
 bcf     INTCON,_T0IE      // zakaz preruseni od timeru0
 goto    END_INT_L         // koncime,abychom nezdrzovali dalsimi zdroji preruseni na nich jiz casove nezalezi

NO_T0_L:
 btfss   INTCON,_RBIF
 goto      NO_RB_L
 // preruseni od tlacitek na brane B
 movf    PORTB,W
 movlw   F_KEY
 movwf   freq_done         // nastav priznak , ze mereni frekvence nedopadlo dobre (stisknuto nektere z tlacitek)
 bcf     INTCON,_RBIF
 goto    END_INT_L

NO_RB_L:
 btfss   PIR1,_CCP1IF
 goto    END_INT_L
 // preruseni od komparacni jednotky, pouziva se pouze v rezimu pulser pro generovani pulzu od 1ms vyse
 // nasledujici konstrukce zajistuje pro 1 i 0 nastaveni pinu ve stejny okamzik, doba do bodu B je tez stejna
 btfss   INT_TMP,0
 goto    A
 btfsc   INT_TMP,0
 bcf     PORTC,2
 goto    B
A:
 bsf     PORTC,2
 goto    B
B:
 comf    INT_TMP,F       // priste budeme nastavovat opacnou hodnotu
 bcf     PIR1,_CCP1IF      // nuluj priznak preruseni od CCP1 jednotky

END_INT_L:
 // obnov W, STATUS a PCLATH
 movf    PCLATH_TMP,W
 movwf   PCLATH
 swapf   F_TMP,W
 movwf   STATUS
 swapf   W_TMP,F
 swapf   W_TMP,W
 #endasm
} // int_handler






/**************************************************/
/*********** FUNKCE MERENI KMITOCTU ***************/
/**************************************************/
// zahajeni mereni frekvence
//
void start_freq()
{
 #asm
 bcf     T1CON,_TMR1ON     // zastav citac TMR1
 clrf    TMR1L             // nuluj citac TMR1, ktery cita vstupni implulzy
 clrf    TMR1H
 clrf    counter_H
 movlw   F_PEACE
 movwf   freq_done         // nuluj priznak dokonceni mereni frekvence
 bsf     T1CON,_TMR1ON     // spust citac TMR1
 movlw   TIME_1S_L         // natahni casovac TMR0, ktery meri periodu mereni
 movwf   TMR0
 movlw   TIME_1S_H
 movwf   sampler_H
 bcf       INTCON,_T0IF    // nuluj priznak preruseni od timeru0
 bsf     INTCON,_T0IE      // povol preruseni od timeru0
 #endasm
} // start_freq


// zmereni frekvence a jeji umocneni
//
float mfreq_sqr()
{
 float f;

  start_freq();                                      // odstartuj mereni frekvence
  while(freq_done==F_PEACE);                         // cekej na dokonceni zmereni frekvence
  f=F_CORRECT*(float)make32(counter_H,get_timer1()); // preved zmerenou frekvennci na float
#ifdef  FREQ
  return(f);                                         // pro mereni frekvence vracej frekvenci
#else
  return(f*f);                                       // vrat kvadrat
#endif
} // mfreq_sqr





/*******************************************************************/
/*********** FUNKCE PRO PREPINANI TYPU MERENI (rele) ***************/
/*******************************************************************/
// prepne rele na mod mereni C
//
rele_measure_C()
{
 output_high(RE1_B);
 output_high(RE2_B);
 delay_ms(RELE_PULSE);
 output_low(RE1_B);
 output_low(RE2_B);
} // rele_measure_C


// prepne rele na mod mereni L
//
rele_measure_L()
{
 output_high(RE1_A);
 output_high(RE2_B);
 delay_ms(RELE_PULSE);
 output_low(RE1_A);
 output_low(RE2_B);
} // rele_measure_L


// prepne rele na kalibraci a tez na mody mereni teploty a pulser
//
rele_calib()
{
 output_high(RE1_B);
 output_high(RE2_A);
 delay_ms(RELE_PULSE);
 output_low(RE1_B);
 output_low(RE2_A);
} // rele_calib





/*******************************************************************/
/*********** POMOCNE FUNKCE JEDNOTLIVYCH MODU MERENI ***************/
/*******************************************************************/
// vraci cislo radu (0=1E0, 1=1E3 2=1E6 atd.) a upravi parametr aby byl do 999.999
//
int8 range(float &Xv)
{
 int8 ret_val;

 ret_val=0;
 while(fabs(Xv)>1E3)
 {
  Xv=Xv*1E-3;
  ret_val++;
 }
 return(ret_val);
} // range


// vraci pocet celych cislic realneho argumentu
//
int8 num_int_digit(float X)
{
 int8 ret_val;

 ret_val=1;
 while(fabs(X)>1E1)
 {
  X=X*1E-1;
  ret_val++;
 }
 return(ret_val);
} // num_int_digit


// smaze 1.radek LCD displeje
//
clr_1Line()
{
 int8 i;
 lcd_gotoxy(1,1);
 for(i=0;i<16;i++) lcd_putc(' ');
 lcd_gotoxy(1,1);
}


//cekani na uvolneni tlacitek
//
wait_release_keys()
{
  while(!input(NUL_KEY) || !input(MODE_KEY))  delay_ms(20);      // cekej na uvolneni tlacitka
  freq_done=F_PEACE;
} // wait_release_keys


// kalibrace
//
void calibration(float &f1_sqr, float &C, float &L)
{
 float f2_sqr;

 lcd_putc("\f");
 printf(lcd_putc,CALIB_MSG);
 rele_measure_C();                    // rele do stavu odpojeni kalibracniho kondenzatoru a oscilator musi bezet
 delay_ms(FREQ_CALM_TIME);            // pockej na ustaleni oscilatoru
 // mereni f2
 rele_calib();                         // pripni kalibracni kondenzator
 delay_ms(FREQ_CALM_TIME);             // pockej na ustaleni oscilatoru
 f2_sqr=mfreq_sqr();                   // zmer kvadrat f2
 // vypocet C a L
 C=(C_CALIB*f2_sqr)/(f1_sqr-f2_sqr);   // C1 v pF
 L=1e9/(39.4784176*f1_sqr*C*1e-12);    // L1 v nH
/*
 // zobraz zmerene kalibracni hodnoty
 lcd_putc("\f");
 printf(lcd_putc,"C=%3.2f pF",C);
 lcd_gotoxy(1,2);
 printf(lcd_putc,"L=%3.2f nH",L);
 while(input(NUL_KEY));  // cekej na stisk klavesy
 lcd_putc("\f");
*/
} // calibration





/******************************************************************/
/*********** HLAVNI FUNKCE JEDNOTLIVYCH TYPU MERENI ***************/
/******************************************************************/
// mereni L
//
#inline
void measure_L(float &f1_sqr,float &C,float &L)
{

 float f2_sqr;    // kvadrat kmitoctu mericiho oscilatoru s pripojenou Lx
 float X;         // zmerena hodnota Lx
 float Xp;        // vlastni Lp (odecita se od vysledku)
 float Xv;        // vysledna hodnota Lx
 int8  prefix;    // index do poli urcijici pismenko radu
 int8  Xp_p;      // pointer na promennou Xp
 int8  ee_adr;    // adresovy citac pameti EEROM


  // vyzvedni vlastni indukcnost
  ee_adr=LP_ADR_LOW;
  for(Xp_p=&Xp;Xp_p<&Xp+4;Xp_p++) *Xp_p=read_eeprom(ee_adr++);
  for(;;)
  {
   f2_sqr=mfreq_sqr();             // zmer kvadrat F2
   if(freq_done!=F_DONE)           // zmerena frekvence neni platna (stisknuto nektere z tlacitek)
    {
     if(!input(MODE_KEY)) break;   // stisknuto tlacitko mode, koncime
     if(!input(NUL_KEY))
     {
      // stisknuto tlacitko NULL, poznamenej si vlastni L
      Xp=X;
      // uloz vlastni indukcnost
      ee_adr=LP_ADR_LOW;
      for(Xp_p=&Xp;Xp_p<&Xp+4;Xp_p++) write_eeprom(ee_adr++,*Xp_p);
      clr_1Line();
      lcd_gotoxy(9,1);
      printf(lcd_putc,"0  nH");
      wait_release_keys();        // cekej na uvolneni tlacitka
     }
    } else
      {
       if(f2_sqr<1E2)             // bezi oscilator? (kmitocet pod 10Hz povazujeme za nebezici oscilator)
       {
        // oscilator NEBEZI
        clr_1Line();
        printf(lcd_putc,CONNECT_LX_MSG);
       } else
         {
          // oscilator BEZI
          X=(f1_sqr/f2_sqr-1)*L;  // vypocti hodnotu Lx
          Xv=X-Xp;                // odecti vlastni L
          // zobraz hodnotu Lx
          clr_1Line();
          prefix=range(Xv);                             // uprav rozsah a zjisti cislo radu
          lcd_gotoxy(6-num_int_digit(Xv),1);            // umisti hodnotu tak, aby radova carka zustavala na stejnem miste
          if(Xv>=0F) lcd_putc(' ');                     // kladne cislo nema minus, vloz mezeru
          if(prefix==0) printf(lcd_putc,"   %3.0f",Xv); // u nH zobrazuj jen cela cisla
          else          printf(lcd_putc,"%6.3f",Xv);    // u ostatnich na 3 desetinna mista
          // zobraz jednotky
          lcd_gotoxy(12,1);
          lcd_putc(L_PREFIX[prefix]);
          lcd_putc('H');
          }
       }
    } // od hlavni smycky (for(;;))
} // measure_L


// mereni C
//
#inline
void measure_C(float &f1_sqr,float &C,float &L)
{

 float f2_sqr;    // kvadrat kmitoctu mericiho oscilatoru s pripojenou Cx
 float X;         // zmerena hodnota Cx
 float Xp;        // vlastni Cp (odecita se od vysledku)
 float Xv;        // vysledna hodnota Cx
 int8  prefix;    // index do poli s pismenky radu
// int8  pom;
 int8  Xp_p;      // pointer do promenne Xp
 int8  ee_adr;

  // vyzvedni vlastni kapacitu
  ee_adr=CP_ADR_LOW;
  for(Xp_p=&Xp;Xp_p<&Xp+4;Xp_p++) *Xp_p=read_eeprom(ee_adr++);
  for(;;)
  {
   f2_sqr=mfreq_sqr();             // zmer F2
   if(freq_done!=F_DONE)           // zmerena frekvence neni platna (stisknuto nektere z tlacitek)
    {
     if(!input(MODE_KEY)) break;   // stisknuto tlacitko mode, koncime
     if(!input(NUL_KEY))
     {
      // stisknuto tlacitko NULL, poznamenej si vlastni Cp
      Xp=X;
      // uloz vlastni kapacitu
      ee_adr=CP_ADR_LOW;
      for(Xp_p=&Xp;Xp_p<&Xp+4;Xp_p++) write_eeprom(ee_adr++,*Xp_p);
      clr_1Line();
      lcd_gotoxy(6,1);
      printf(lcd_putc,"0.00  pF");
      wait_release_keys();        // cekej na uvolneni tlacitka
     }
    } else
      {
       if(f2_sqr<1E2)             // bezi oscilator? (kmitocet pod 10Hz povazujeme za nebezici oscilator)
       {
        // oscilator NEBEZI
        clr_1Line();
        printf(lcd_putc,Error_Cx_MSG);
       } else
         {
           // oscilator BEZI
          X=(f1_sqr/f2_sqr-1)*C;  // vypocti hodnotu Cx nebo Lx
          Xv=X-Xp;                // odecti vlastni Cp
          // zobraz hodnotu Cx
          clr_1Line();
          prefix=range(Xv);                             // uprav rozsah a zjisti cislo radu
          lcd_gotoxy(6-num_int_digit(Xv),1);            // umisti hodnotu tak, aby radova carka zustavala na stejnem miste
          if(Xv>=0F) lcd_putc(' ');                     // kladne cislo nema minus, vloz mezeru
          if(prefix==0) printf(lcd_putc,"%5.2f",Xv);    // u pF zobrazuj na 2 mista
          else printf(lcd_putc,"%6.3f",Xv);             // u ostatnich na 3 mista
          // zobraz jednotky
          lcd_gotoxy(12,1);
          lcd_putc(C_PREFIX[prefix]);
          lcd_putc('F');
         }
       }
    } // od hlavni smycky (for(;;))
} // measure_C


// mereni teploty
//
#separate
void measure_T()
{
 int8 record[10];  //  bafr pro ulozeni zaznamu z teplomeru
 int8 i;
 int8 stat;        // stavovy automat mereni teploty
 int8 wait;        // pro odmereni casu prevodu teplomeru
 int1 delta;       // oznamuje, ze merime rozdil teplot
 float temp;       // zmerena teplota
 float temp_p;     // odecitana hodnota teploty pri mereni rozdilu teplot
 float temp_v;     // vysledna teplota

 stat=T_MEASURE;                       // zahajime mereni teploty
 delta=0;                              // nemerime teplotni rozdil
 temp_p=0;                             // nic od zmerene teploty neodecitame

 for(;;)
 {
  if(freq_done==F_KEY)                 // stisknuto nejake tlacitko
  {
   if(!input(MODE_KEY))                // stisknuto tlacitko mode, koncime
   {
    #USE STANDARD_IO(A)
    #USE STANDARD_IO(B)
    #USE STANDARD_IO(C)
    output_float(TM_PIN);              // TM jako vstup, muze zustat jako vystup pri preruseni mereni
    #USE FAST_IO(A)
    #USE FAST_IO(B)
    #USE FAST_IO(C)
    break;
   }
   if(!input(NUL_KEY))                 // stisknuto tlacitko NULL, budeme merit rozdil teplot, zapamatuj si aktualni teplotu
   {
     temp_p=temp;                      // zapamatuj si aktualni hodnotu teploty
     delta=1;                          // oznam, ze merime rozdil teplot
     stat=T_MEASURE;
     clr_1line();
     lcd_gotoxy(6,1);
     printf(lcd_putc,"0.0 \20\21C");   // \20 je znak delta \21 je znak stupen
     wait_release_keys();              // cekej na uvolneni tlacitka
   }
  }
  // STAVOVY AUTOMAT MERENI TEPLOTY
  switch(stat)
  {
   case T_WAIT:
    if(--wait==0) stat=T_DISPLAY;                        // cekej az teplomer zmeri frekvenci
   break;
   case T_DISPLAY:
    if(!TM_present())
    {
     // teplomer NENI pritomen
     clr_1line();
     printf(lcd_putc,CONNECT_TEMP_MSG);
     temp_p=0;                                           // nuluj pripadnou teplotu pro odecteni
     delta=0;                                            // zrus pripadne mereni rozdilu teplot
    } else
      {
       // teplomer JE pritomen
       TM_write_byte(0xCC);                              // prikaz preskoc na dalsi sadu prikazu
       TM_write_byte(0xBE);                              // prikaz precti zmerenou teplotu
       for(i=0;i<9;i++) record[i]=TM_read_byte();        // precti zaznam do buferu
       if(TM_check_CRC(record,9))                        // kontrola CRC
       {
        // vypocet teploty viz datasheet
        record[0]=record[0]>>1;                           // zahod rad 2^-1,
        if(bit_test(record[1],0)) bit_set(record[0],7);   // je-li cislo zaporne, je treba nejvyssi bit nastavit na 1
        temp=(float)make16(record[1],record[0]);          // prechod na realna cisla
        temp=temp-0.25+((0x10-(float)record[6])/0x10);    // zvetseni presnosti na 12 bitu
        temp_v=temp-temp_p;
        clr_1line();
        lcd_gotoxy(6-num_int_digit(temp_v),1);            // umisti udaj tak, aby radova carka byla porad na stejnem miste
        if(temp_v>=0F) lcd_putc(' ');                     // pri kladne hodnote zobraz misto minus mezeru
        printf(lcd_putc,"%4.1f ",temp_v);
        if(delta) lcd_putc('\20');                        // pri mereni rozdilu teplot zobraz znak delta
        lcd_putc('\21');                                  // znak  stupne
        lcd_putc('C');
       }
      }
    stat=T_MEASURE;                                       // prejdi do stavu vyvolani mereni teploty
   case T_MEASURE:
    if(!TM_present())
    {
     // teplomer NENI pritomen
     clr_1line();
     printf(lcd_putc,CONNECT_TEMP_MSG);
     temp_p=0;                                            // nuluj pripadnou teplotu pro odecteni
     delta=0;                                             // zrus pripadne mereni rozdilu teplot
    } else
      {
       // teplomer JE pritomen
       TM_write_byte(0xCC);                               // prikaz preskoc na dalsi sadu prikazu
       TM_write_byte(0x44);                               // prikaz zmer teplotu
       #USE STANDARD_IO(A)
       #USE STANDARD_IO(B)
       #USE STANDARD_IO(C)
       output_high(TM_PIN);                               // vystup do 1, zajistuje napajeni behem mereni teploty
       #USE FAST_IO(A)
       #USE FAST_IO(B)
       #USE FAST_IO(C)
       wait=CONV_WAIT;
       stat=T_WAIT;                                       // prejdi do stavu cekani na dokonceni mereni (konverze)
    }
   break;
  }  // od switch
  mfreq_sqr();                                            // zastupuje zde spozdeni 250ms, ktere lze prerusit stiskem tlacitka
 } // od for(;;)
} // measure_T


// generovani pulzu
//
#separate
void pulser()
{
  int8 pulse;

 #USE STANDARD_IO(A)
 #USE STANDARD_IO(B)
 #USE STANDARD_IO(C)
 output_high(PULSER_PIN);                 // pin pulseru naorientuj jako vystup
 #USE FAST_IO(A)
 #USE FAST_IO(B)
 #USE FAST_IO(C)

 INT_TMP=0;                               // pouzije se jako zrcadlo vystupniho pinu generatoru pulzu (vystup do 0)
 setup_timer_0(RTCC_EXT_L_TO_H);          // zastav TMR0, vadil by nam v preruseni, (preruseni od nej zastavuje TMR1)
 pulse=read_eeprom(WIDTH_ADR);            // vyzvedni naposledy pouzitou sirku impulzu

for(;;)
{
 clr_1line();
 switch(pulse)
 {
  case P_10US:
   printf(lcd_putc,PULSE_10us_MSG);
   setup_timer_2(T2_DIV_BY_1,20-1,1);          // perioda 20us
   setup_ccp1(CCP_PWM);                        // generujeme pomoci PWM jednotky
   CCP_1 = 20/2;                               // strida 1:1
  break;
  case P_20US:
   printf(lcd_putc,PULSE_20us_MSG);
   setup_timer_2(T2_DIV_BY_1,40-1,1);          // perioda 40us
   setup_ccp1(CCP_PWM);                        // generujeme pomoci PWM jednotky
   CCP_1 = 40/2;                               // strida 1:1
  break;
  case P_50US:
   printf(lcd_putc,PULSE_50us_MSG);
   setup_timer_2(T2_DIV_BY_1,100-1,1);         // perioda 100us
   setup_ccp1(CCP_PWM);                        // generujeme pomoci PWM jednotky
   CCP_1 = 100/2;                              // strida 1:1
  break;
  case P_100US:
   printf(lcd_putc,PULSE_100us_MSG);
   setup_timer_2(T2_DIV_BY_1,200-1,1);         // perioda 200us
   setup_ccp1(CCP_PWM);                        // generujeme pomoci PWM jednotky
   CCP_1 = 200/2;                              // strida 1:1
  break;
  case P_200US:
   printf(lcd_putc,PULSE_200us_MSG);
   setup_timer_2(T2_DIV_BY_4,100-1,1);         // perioda 400us
   setup_ccp1(CCP_PWM);                        // generujeme pomoci PWM jednotky
   CCP_1 = 100/2;                              // strida 1:1
  break;
  case P_500US:
   printf(lcd_putc,PULSE_500us_MSG);
   setup_timer_2(T2_DIV_BY_4,250-1,1);         // perioda 1000us
   setup_ccp1(CCP_PWM);                        // generujeme pomoci PWM jednotky
   CCP_1 = 250/2;                              // strida 1:1
  break;
  case P_1MS:
   setup_timer_2(T2_DISABLED,0,1);
   printf(lcd_putc,PULSE_1ms_MSG);
   setup_ccp1(CCP_COMPARE_RESET_TIMER);        // generujeme pomoci CPP1 jednotky ( portem se hejbe v preruseni)
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
   CCP_1 =1000;                                // pulz 1ms - perioda 2ms
   enable_interrupts(INT_CCP1);                // povol preruseni od CCP1 jednotky
  break;
  case P_2MS:
   printf(lcd_putc,PULSE_2ms_MSG);             // generujeme pomoci CPP1 jednotky ( portem se hejbe v preruseni)
   CCP_1 =2000;                                // pulz 2ms - perioda 4ms
  break;
  case P_5MS:
   printf(lcd_putc,PULSE_5ms_MSG);             // generujeme pomoci CPP1 jednotky ( portem se hejbe v preruseni)
   CCP_1 =5000;                                // pulz 5ms - perioda 10ms
  break;
  case P_10MS:
   printf(lcd_putc,PULSE_10ms_MSG);            // generujeme pomoci CPP1 jednotky ( portem se hejbe v preruseni)
   CCP_1 =10000;                               // pulz 10ms - perioda 20ms
  break;
  case P_20MS:
   printf(lcd_putc,PULSE_20ms_MSG);            // generujeme pomoci CPP1 jednotky ( portem se hejbe v preruseni)
   CCP_1 =20000;                               // pulz 20ms - perioda 40ms
  break;
  case P_50MS:
   printf(lcd_putc,PULSE_50ms_MSG);            // generujeme pomoci CPP1 jednotky ( portem se hejbe v preruseni)
   CCP_1 =50000;                               // pulz 50ms - perioda 100ms
  break;
  case P_500MS:
   printf(lcd_putc,PULSE_500ms_MSG);           // generujeme pomoci CPP1 jednotky ( portem se hejbe v preruseni)
   setup_timer_1(T1_INTERNAL | T1_DIV_BY_8);
   CCP_1 =62500 ;                              // pulz 500ms - perioda 1000ms
  break;
  case P_5US:
   printf(lcd_putc,PULSE_5us_MSG);
   disable_interrupts(INT_CCP1);               // zakaz preruseni od CCP1, pro kratke casy ji nepouzivme, pouzivame PWM jednotku
   setup_timer_2(T2_DIV_BY_1,10-1,1);          // perioda 10us
   setup_ccp1(CCP_PWM);                        // pouzivame PWM jednotku
   CCP_1 = 10/2;                               // strida 1:1
  break;
 } // od switch
 while(freq_done!=F_KEY);                      // cekame na stisk nejakeho tlacitka
 if(!input(MODE_KEY))                          // stisknuto tlacitko MODE, koncime
 {
  // uved preruseni a nastaveni timeru1 do puvodniho stavu a konci mod
  setup_timer_0(RTCC_INTERNAL | RTCC_DIV_256); // pro mereni periody vzorkovani (500ms)
  setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);    // pro citani vzorku//
  bit_clear(*T1CON,_TMR1ON);                   // zastav citani impulzu
  disable_interrupts(INT_CCP1);                // cakaz preruseni od CCP1 ednotky
 #USE STANDARD_IO(A)
 #USE STANDARD_IO(B)
 #USE STANDARD_IO(C)
  output_float(PULSER_PIN);                    // vystup pulseru jako vystup
 #USE FAST_IO(A)
 #USE FAST_IO(B)
 #USE FAST_IO(C)
  break;    // koncime
 }
 if(!input(NUL_KEY))                           // stisknuto tlacitko WIDTH
 {
  if(pulse==P_5US) pulse=P_10US;              // po dosazeni stavu P_5US se posloupnost opakuje
  else pulse++;
  write_eeprom(WIDTH_ADR,pulse);               // uloz novou sirku impulzu
  wait_release_keys();                         // cekej na uvolneni tlacitek po nastaveni sirky pulzu
 }
}
} // pulser





/*************************************************/
/********  HLAVNI FUNKCE PROGRAMU ****************/
/*************************************************/

void main()
{
 float f1_sqr;    // kvadrat kmitoctu mericiho scilatoru bez pripojene Lx nebo Cx
 float C;         // hodnota kondenzatoru v mericim oscilatoru v pF (ziska se kalibraci)
 float L;         // hodnota indukcnosti v mericim oscilatoru v nH (ziska se kalibraci)
 int8  MODE;      // promenna stavoveho automatu hlavni smycky (mod mereni)
 int1  _calib;    // priznak, ze jiz doslo ke kalibraci


 // nastaveni citacu
 port_b_pullups(TRUE);                            // zapnu puulupy na brane B (pro tlacitka)
 setup_timer_0(RTCC_INTERNAL | RTCC_DIV_256);     // pro mereni periody vzorkovani (500ms)
 setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);        // pro citani impulzu z mericiho oscilatoru
 bit_clear(*T1CON,_TMR1ON);                       // zastav citani impulzu

 //nastaveni vstupu a vystupu
#USE STANDARD_IO(A)
#USE STANDARD_IO(B)
#USE STANDARD_IO(C)
 output_low(RE1_A);                  // signaly relatek jako vystupy a do 0
 output_low(RE1_B);
 output_low(RE2_A);
 output_low(RE2_B);
 output_float(PULSER_PIN);           // signal pro pulser jako vstup
 output_float(TM_PIN);               // vstup pro TM jako vstup
 output_float(TM_PULLUP);            // zapni pullup pro TM
 output_low(PIN_C1);                 // nepouzit,dej do 0
 output_low(PIN_C3);                 // nepouzit,dej do 0
#USE FAST_IO(A)
#USE FAST_IO(B)
#USE FAST_IO(C)

 lcd_init();
 lcd_define_char(0,LCD_CHAR_DELTA);  // nadefinovani znaku delta
 lcd_define_char(1,LCD_CHAR_DEC);    // nadefinovani znaku stupen
 rele_measure_C();                   // rele do stavu odpojeni kalibracniho kondenzatoru a oscilator musi bezet
 input_b();
 enable_interrupts(INT_TIMER1);      // povol preruseni od citace impulzu mericiho oscilatoru
 enable_interrupts(INT_RB);          // povol preruseni od tlacitek
 enable_interrupts(GLOBAL);


#ifdef FREQ
 // pro kalibraci mereni kmitoctu
 for(;;) {
  f1_sqr=mfreq_sqr();
  lcd_putc("\f");
  printf(lcd_putc,"F=%6.2f KHz",f1_sqr*1E-3);
  delay_ms(500);
 }
#endif

 // UVOD
 lcd_putc("\f");
 printf(lcd_putc,VERSION_MSG);
 lcd_gotoxy(1,2);
 printf(lcd_putc,COPYRIGHT_MSG);
 delay_ms(FREQ_CALM_TIME);            // pockej na ustaleni oscilatoru
 f1_sqr=mfreq_sqr();                  // zmer f1
 _calib=0;                            // kalibrace neni provedena
 freq_done=F_PEACE;
 mode=read_eeprom(MODE_ADR);          // vyzvedni naposledy pouzity mod

 // VLASTNI MERICI SMYCKA
 for(;;)
 {
  switch(mode)
  {
   case MODE_C:                      // mereni Cx
     if(!_calib)
     {
      calibration(f1_sqr,C,L);       // proved kalibraci (vypocti L a C)
      _calib=1;                      // poznamenej si, ze se kalibrace uz provedla
     }
     rele_measure_C();
     lcd_gotoxy(1,2);
     printf(lcd_putc,MODE_CX_NULL_MSG);
     measure_C(f1_sqr,C,L);
     // prechazime na mereni L
     mode=MODE_L;
   break;
   case MODE_L:                     // mereni Lx
     if(!_calib)
     {
      calibration(f1_sqr,C,L);      // proved kalibraci (vypocti L a C)
      _calib=1;                     // poznamenej si, ze se kalibrace uz provedla
     }
     rele_measure_L();
     lcd_gotoxy(1,2);
     printf(lcd_putc,MODE_LX_NULL_MSG);
     measure_L(f1_sqr,C,L);
     // prechazime na mereni T
     mode=MODE_T;
   break;
   case MODE_T:                     // merei teploty
     rele_calib();
     lcd_gotoxy(1,2);
     printf(lcd_putc,MODE_TMP_NULL_MSG);
     bit_clear(*T1CON,_TMR1ON);     // zastav TMR1, vadil by
     measure_T();
     // prechazime na generovani pulzu
     mode=MODE_P;
   break;
   case MODE_P:                     // generovani pulzu
     rele_calib();
     lcd_gotoxy(1,2);
     printf(lcd_putc,MODE_PLZ_WIDTH_MSG);
     pulser();
     // prechazime na mereni C
     mode=MODE_C;
   break;
  } // od switch
   write_eeprom(MODE_ADR,mode);     // uloz ktery mod je nyni aktivni
   wait_release_keys();             // cekej na uvolneni tlacitek
   delay_ms(10);
 } // for(;;)
}  // main

// End of File