//**********************************************************************
//            INFO BATTERY PACK
//**********************************************************************
// (c) OK1XGL 2008
// verze 1.0 - uvodni verze
//       1.1 - referencni teplota pro samovybijeni se uklada pri zadani doby mereni samovybijeni 
//
//

#include "ibp2.h"


// nastavi orientaci a klidovy stav portu
//
void init_ports (void) {
  set_tris_a (0b00101011);
  output_a (0b000000000);

  set_tris_b (0b11010000);
  output_b (0b00100000);
  port_b_pullups (FALSE);
}


//////////////////////////////////////////////////
//
// iterrupty
// cele preruseni zabere 33+xx+30 cyklu
//
//////////////////////////////////////////////////

// periodicky casovac
//
#INT_TIMER1
void tmr1_int (void) {
  // natahovani casovace
  set_timer1 (S_TICK_TIME);
  s_tick = 1;

  if (l_timer == 0) {
    l_timer = L_TICK_TIME;
    l_tick = 1;
    blink = ~blink;
    if (blink)
      slow_blink = ~slow_blink;
  }
  else
    l_timer--;
}



///////////////////////////////////////////////////
//
// FUNKCE PRO PRACI S TEPLOMEREM
//
//////////////////////////////////////////////////

// testuje pritomnost teplomeru, pri pritomnosti vraci 1
// zabere  1060 taktu
//
int8 TM_present() {
    int1 present;

    output_low (TM_PIN);
    output_fixed (TM_PIN);
    delay_us (500);
    disable_interrupts (GLOBAL);
    output_float (TM_PIN);
    if(!input (TM_PIN)) {
      enable_interrupts (GLOBAL);
      return (0);
    }
    delay_us (65);
    present = input(TM_PIN);
    enable_interrupts (GLOBAL);
    delay_us (440);
    if (present)
      return (0);
    else
      return (1);
}


// precte bajt z teplomeru
//
int8 TM_read_byte() {
   int8 i,data;

   for(i = 0; i < 8; ++i) {
     disable_interrupts (GLOBAL);
     output_low (TM_PIN);
     output_fixed (TM_PIN);
     delay_us (2);
     output_float (TM_PIN);
     delay_us (8);
     shift_right (&data,1,input(TM_PIN));
     enable_interrupts (GLOBAL);
     delay_us (55);
   }
   return (data);
}


// zapise bajt do teplomeru
//
void TM_write_byte(int8 data) {
   int8 i;

   for (i = 0 ; i < 8; ++i) {
     disable_interrupts (GLOBAL);
     output_low (TM_PIN);
     output_fixed (TM_PIN);
     delay_us (5);
     if (shift_right (&data,1,0))
       output_float (TM_PIN);
     delay_us (60);
     output_float (TM_PIN);
     delay_us (2);
     enable_interrupts (GLOBAL);
   }
}


// vraci 0 pri ukonceni mereni
// vraci 1 pri rozpracovanem mereni
// zabere max 1100 taktu na 1 cyklus
// potrebuje 10 volani pro zmereni teploty
//
int8 measure_temp () {
  static int8 phase;
  static int8 tmp;
  int8 tmp2;

  switch (phase) {
    case 0:
      if (! TM_present ()) {    // over pritomnost teplomeru
        phase = 0;
        TB_val = TB_DEFAULT;
        return (0);             // teplomer neni pritomen, dal nelze pokracovat
      }
    break;

    case 1:
      TM_write_byte (0xcc);     // preskoc na dalsi sadu prikazu
    break;

    case 2:
      TM_write_byte (0x44);     // prikaz spust mereni teploty
      output_high (TM_PIN);     // zapni tvrdy pullup
      output_fixed (TM_PIN);    //
      break;

    case 3:
    case 4:
    case 5:                     // cekej nejmene 750 ms
    break;

    case 6:
      output_float (TM_PIN);   // vyppni tvrdy pullup
      output_low (TM_PIN);     //
      if (! TM_present ()) {   // over pritomnost teplomeru
        phase = 0;
        TB_val = TB_DEFAULT;
        return (0);            // teplomer neni pritomen, dal nelze pokracovat
      }
    break;

    case 7:
      TM_write_byte (0xcc);     // preskoc na dalsi sadu prikazu
    break;

    case 8:
      TM_write_byte (0xbe);     // prikaz precti zmerenou teplotu
    break;

    case 9:
      tmp = TM_read_byte () >> 4; // precti LSB bajt s teplotou a zahod destiny stupne
    break;

    case 10:
      tmp  |= TM_read_byte () << 4;  // precti MSB bajt s teplotou a sloz s LSB
      tmp2 = tmp & 0x7F;             //
      if (tmp2 < 80) {           // teplota je v moznem rozsahu (nahrada kontroly CRC - bylo by casove prilis dlouhe)
        if (tmp & 0x80)
          TB_val = 0;            // zapornou teplotu povazujeme za nulovou
        else
          TB_val = tmp2;
      }
      phase = 0;
      return (0);
    break;

    default:
      phase = 0;
      TB_val = TB_DEFAULT;
      return (0);
  }
  phase++;
  return (1);
}



//////////////////////////////////////////////////////
//
// FUNNKCE PRO MERENI NAPETI A PROUDU
//
//////////////////////////////////////////////////////

// meri velikost proudu z/do baterie a prumeruje z 8 mereni
// zabere max 1760 taktu (1760 us)
//
int16 measure_IB (void) {
  int16 out;

  set_adc_channel (IB_CAN);
  delay_us (100);
  out = read_adc (ADC_START_AND_READ);
  delay_us (100);
  out += read_adc (ADC_START_AND_READ);
  delay_us (100);
  out += read_adc (ADC_START_AND_READ);
  delay_us (100);
  out += read_adc (ADC_START_AND_READ);
  delay_us (100);
  out += read_adc (ADC_START_AND_READ);
  delay_us (100);
  out += read_adc (ADC_START_AND_READ);
  delay_us (100);
  out += read_adc (ADC_START_AND_READ);
  delay_us (100);
  out += read_adc (ADC_START_AND_READ);
  out = out >> 3;
  return (out);
}


// meri velikost napeti baterie a provadi klouzavy prumer z 8 mereni
// zabere max 320 taktu (320 us)
//
int16 measure_VB (void) {
  static int16 smpl [8];
  static int8 i;
  int16 out;

  if (i >= 8-1)
    i = 0;
  else
    i++;

  set_adc_channel (VB_CAN);
  delay_us (100);
  smpl [i] = read_adc (ADC_START_AND_READ);

  out = smpl [0] + smpl [1] + smpl [2] + smpl [3] +
        smpl [4] + smpl [5] + smpl [6] + smpl [7];
  out = out >> 3;
  return (out);
}


// meri velikost vstupniho napeti zdroje pro nabijeni
// zabere max 200 taktu (200 us)
//
int16 measure_VS (void) {
  int16 out;

  set_adc_channel (VS_CAN);
  delay_us (100);
  out = read_adc (ADC_START_AND_READ);
  return (out);
}


// meri vystupni napeti OZ pro mereni proudu pri nulovem odberu - pro kompenzaci ofsetu OZ
//
int16 measure_offset (void) {
  int16 out;
  int8 i;

  // vypni vsechny spotrebice
  init_ports ();
  disable_interrupts (GLOBAL);
  ADIE = 1;
  PEIE = 1;
  delay_ms (500);

  // mer napeti OZ a vypocti prumer z 8 mereni
  set_adc_channel (IB_CAN);
  delay_us (100);
  out = 0;
  for (i = 0; i < 8; i++) {
    read_adc (ADC_START_ONLY);
    sleep ();
    out += read_adc (ADC_READ_ONLY);
    delay_us (100);
    restart_wdt ();
  }
  out = out >> 3;

  ADIE = 0;
  PEIE = 0;
  enable_interrupts (GLOBAL);
  return (out);
}


/////////////////////////////////////////////////
//
// FUNKCE PRO PRACI S LED
//
/////////////////////////////////////////////////

// rozsviti vsechny led
//
void leds_all_on () {
  output_high (LED4_R);
  output_high (LED3_Y);
  output_high (LED2_Y);
  output_high (LED1_G);
}


// zhasne vsechny led
//
void leds_all_off () {
  output_low (LED4_R);
  output_low (LED3_Y);
  output_low (LED2_Y);
  output_low (LED1_G);
}


// signalizuje kapacitu baterie pri nabijeni
// vraci cislo odpovidajici stavu nabiti
// zabere max 40 cyklu
//
int8  leds_chrg (void) {
  if (B_cap >= B_cap_4_4) {
    leds_all_on ();
    return (4);         // baterie plne nabita
  }

  if (B_cap > B_cap_3_4) {
    output_high (LED4_R);
    output_high (LED3_Y);
    output_high (LED2_Y);
    if (slow_blink)
      output_high (LED1_G);
    else
      output_low (LED1_G);
    return (3);
  }

  if (B_cap > B_cap_2_4) {
    output_high (LED4_R);
    output_high (LED3_Y);
    if (slow_blink)
      output_high (LED2_Y);
    else
      output_low (LED2_Y);
    output_low (LED1_G);
    return (2);
  }

  if (B_cap > B_cap_1_4) {
    output_high (LED4_R);
    if (slow_blink)
      output_high (LED3_Y);
    else
      output_low (LED3_Y);
    output_low (LED2_Y);
    output_low (LED1_G);
    return (1);
  }

  if (slow_blink)
    output_high (LED4_R);
  else
    output_low (LED4_R);
  output_low (LED3_Y);
  output_low (LED2_Y);
  output_low (LED1_G);
  return (0);
}


// signalizuje chybu
//
void leds_err () {
  output_low (LED1_G);
  output_low (LED4_R);
  if (blink) {
    output_high (LED2_Y);
    output_high (LED3_Y);
  }
  else {
    output_low (LED2_Y);
    output_low (LED3_Y);
  }
}


// signalizuje neznamou aktualni kapacitu baterie
//
void leds_invalid_cap () {
  output_low (LED2_Y);
  output_low (LED3_Y);
  if (blink) {
    output_high (LED1_G);
    output_high (LED4_R);
  }
  else {
    output_low (LED1_G);
    output_low (LED4_R);
  }
}


// signalizuje zbyvajici kapacitu baterie pri vybijeni
//
void leds_dis () {
  if (B_cap < B_cap_1_4) {
  output_low (LED1_G);
  output_low (LED2_Y);
  output_low (LED3_Y);
  output_high (LED4_R);
  return;
  }

  if (B_cap < B_cap_2_4) {
  output_low (LED1_G);
  output_low (LED2_Y);
  output_high (LED3_Y);
  output_low (LED4_R);
  return;
  }

  if (B_cap < B_cap_3_4) {
  output_low (LED1_G);
  output_high (LED2_Y);
  output_low (LED3_Y);
  output_low (LED4_R);
  return;
  }

  output_high (LED1_G);
  output_low (LED2_Y);
  output_low (LED3_Y);
  output_low (LED4_R);
}


// signalizuje temer vybitou baterii
//
void leds_VB_low () {
  output_low (LED1_G);
  output_low (LED2_Y);
  output_low (LED3_Y);
  if (slow_blink)
    output_high (LED4_R);
  else
    output_low (LED4_R);
}


// zobrazi cislo v rozsahu 0 - 9 v binarnim kodu na ledkach
//
void num_to_leds (int8 num) {

  switch (num) {
    case 0:
      output_low (LED4_R); // nejnizsi bit
      output_low (LED3_Y);
      output_low (LED2_Y);
      output_low (LED1_G); // nejvyssi bit
    break;
    case 1:
      output_high (LED4_R); // nejnizsi bit
      output_low (LED3_Y);
      output_low (LED2_Y);
      output_low (LED1_G); // nejvyssi bit
    break;
    case 2:
      output_low (LED4_R); // nejnizsi bit
      output_high (LED3_Y);
      output_low (LED2_Y);
      output_low (LED1_G); // nejvyssi bit
    break;
    case 3:
      output_high (LED4_R); // nejnizsi bit
      output_high (LED3_Y);
      output_low (LED2_Y);
      output_low (LED1_G); // nejvyssi bit
    break;
    case 4:
      output_low (LED4_R); // nejnizsi bit
      output_low (LED3_Y);
      output_high (LED2_Y);
      output_low (LED1_G); // nejvyssi bit
    break;
    case 5:
      output_high (LED4_R); // nejnizsi bit
      output_low (LED3_Y);
      output_high (LED2_Y);
      output_low (LED1_G); // nejvyssi bit
    break;
    case 6:
      output_low (LED4_R); // nejnizsi bit
      output_high (LED3_Y);
      output_high (LED2_Y);
      output_low (LED1_G); // nejvyssi bit
    break;
    case 7:
      output_high (LED4_R); // nejnizsi bit
      output_high (LED3_Y);
      output_high (LED2_Y);
      output_low (LED1_G); // nejvyssi bit
    break;
    case 8:
      output_low (LED4_R); // nejnizsi bit
      output_low (LED3_Y);
      output_low (LED2_Y);
      output_high (LED1_G); // nejvyssi bit
    break;
    case 9:
      output_high (LED4_R); // nejnizsi bit
      output_low (LED3_Y);
      output_low (LED2_Y);
      output_high (LED1_G); // nejvyssi bit
    break;
    default:
      output_low (LED4_R); // nejnizsi bit
      output_low (LED3_Y);
      output_low (LED2_Y);
      output_low (LED1_G); // nejvyssi bit
  }
}


///////////////////////////////////////////////////////
//
// FUNKCE PRO CTENI TLACITEK A KONTAKTU
//
///////////////////////////////////////////////////////

// je pripojena zatez?
// vraci 1 pri pripojene zatezi
//
int1 is_load () {
  int1 load;
  port_b_pullups (TRUE);
  delay_us (100);
  load = input (LOAD_ON);  // precti stav kontaktu pripojeni zateze
  port_b_pullups (FALSE);
  return (load);
}


// cte stav nastavovacich tlacitek
// vraci masky stisknutych tlacitek, 0 = klidovy stav
//
int8 read_keys () {
  static int1 up_key_old;
  static int1 set_key_old;
  static int8 keys;
  int1 key_state;

  key_state = input (UP);
  if (key_state != up_key_old)
    up_key_old = key_state;
  else
    if (! key_state)
      keys |= UP_MASK;
    else
      keys &= ~UP_MASK;

  key_state = input (SET);
  if (key_state != set_key_old)
    set_key_old = key_state;
  else
    if (! key_state)
      keys |= SET_MASK;
    else
      keys &= ~SET_MASK;
  return (keys);
}


///////////////////////////////////////////////////
//
// VYKONNE FUNKCE
//
///////////////////////////////////////////////////

// vybijeni baterie
//
void do_discharge () {
int16 IB_val;
int16 I_zero;
int16 VB_val;
int16 dec_cap;
int1 load, load_old;
int1 err;

#ifdef DEBUG
  int16 max,min;
  int16 cnt;
#endif

  // signalizuj neduveryhodnost zbyvajici kapacity baterie
  if (invalid_cap) {
    blink = 1;
    leds_invalid_cap ();
    delay_ms (500);
    leds_all_off ();
  }

  // nastaveni AD prevodniku: reference je 2.5V posun  mereni proudu o 2,51V => cim vetsi prout, tim nizzsi cislo z prevodniku
  setup_adc_ports (sAN0 | sAN1 | sAN2 | sAN3 | sAN5 | VSS_VREF);
  setup_vref (VREF_HIGH | VREF_A2 | 8);  // na A2 vystup reference 2.51V
  delay_ms (1);

  I_zero = measure_offset ();  // precti klidovou hodnotu, tedy offset OZ - nulovy odebirany proud

  output_high (BATT_ON);
  leds_dis ();        // signalizuj zbyvajici kapacitu bateie

  err = 0;
  blink = 1;
  l_tick = 0;
  s_tick = 0;

  // jeden pruchod smycku zabere max 2000 taktu
  for(;;) {
    restart_wdt ();
    if (s_tick) {
      s_tick = 0;
      IB_val = measure_IB ();     // mer proud odebirany z baterie
      if (IB_val == 0 ) {
        // proudove pretizeni
        output_low (BATT_ON);     // odpoj baterii
        err = 1;                  // signalizuj chybu
      }
      dec_cap = I_zero - IB_val;  // zbav se ofsetu OZ
      if (B_cap > dec_cap)
        B_cap = B_cap - dec_cap;  // uprav zbyvajici kapacitu baterie

      if (! err)
        VB_val = measure_VB ();   // napeti baterie je mozne merit jen pokud neni odpojena

      #ifdef DEBUG
        if (IB_val > max)
          max = IB_val;
        if (IB_val < min)
          min = IB_val;
        cnt++;
      #endif
    }

    if (l_tick) {
      l_tick = 0;
      if (! is_load ()) {
        return;             // koncime, zatez odpojena
      }

      if (VB_val < VB_MIN) {
        // baterie zcela vybita
        B_cap = 0;          // nastav prazdnou kapacitu
        invalid_cap = 0;    // aktualni kapacita baterie je nyni znama
        return;
      }

      if (err) {
        leds_err ();            // signalizuj chybu
      }
      else {
        if (VB_val < VB_LOW) {
          leds_VB_low ();       // signalizuj temer vybitou baterii
        }
        else {
          leds_dis ();          // signalizuj zbyvajici kapacitu bateie
        }
      }

      #ifdef DEBUG
        printf ("cnt:%lu z:%lu max:%ld delta:%u dec:%lu\n\r",cnt,I_zero,max,(int8)(max-min),dec_cap);
        max = 0;
        min = 1024;
        cnt = 0;
      #endif
    }
  }
}


// proces nabijeni baterie
//
void do_charge () {
int1 chrg;
int1 err;
int16 I_zero;
int16 IB_val;
int16 inc_cap;
int32 fast_chrg;
int8 pwm;
int16 duty;

#ifdef DEBUG
  int16 max,min;
  int16 vs_val;
#endif

  // vypocti mez do ktere budeme nabijet vyssim proudem
//  fast_chrg = (B_cap_4_4 - B_cap) / 64;
  fast_chrg = (B_cap_4_4 - B_cap) / 72;
  fast_chrg *= chrg_eff;
  fast_chrg += B_cap;
#ifdef DEBUG
//  printf ("%lu %u %lu %lu\n\r\n\r",B_cap_4_4, chrg_eff,B_cap, fast_chrg);
#endif

  err = 0;
  s_tick = 0;
  l_tick = 0;
  pwm = 0;
  chrg = 0;

  // nastaveni AD prevodniku, reference je VDD = 5V , zadny posun napeti pri  mereni proudu => cim vyssi proud, tim vyssi cislo z prevodniku
  setup_adc_ports (sAN0 | sAN1 | sAN2 | sAN3 | sAN5 | VSS_VDD);
  setup_vref (VREF_LOW | VREF_A2 | 2);  // na A2 vystup reference 0.416V
  delay_ms (1);
  I_zero = measure_offset ();  // precti klidovou hodnotu, tedy ofset OZ - nulovy nabijeci proud

  if (invalid_cap == 0 &&
        TB_val < TB_MAX &&
        TB_VAL > TB_MIN) {
    // kapacita baterie je znama a teplota baterie je v mezich
    output_high (BATT_ON);   // pripoj baterii
    output_high (CHRG_ON);   // pripoj zdroj nabijeni
    delay_ms (500);          // cekej na ustaleni menice
    chrg = 1;
  }

  if (measure_VS () < VS_MIN) {  // kontrola napeti zatizeneho menice
    // napajeci zdroj je prilis mekky
    err = 1;                  // signalizuj chybu
    chrg = 0;                 // ukonci nabijeni
    output_low (CHRG_ON);     // odpoj zdroj nabijeni
    output_low (BATT_ON);     // odpoj baterii
    delay_ms (300);           // cekej na ustaleni menice
  }

  // jeden pruchod smyckou zabere max 3000 taktu
  for (;;) {
    restart_wdt ();
    if (s_tick) {
      s_tick = 0;
      IB_val = measure_IB ();  // mer proud dodavany do baterie 1760 taktu
      if (IB_val > I_zero)     // zbav se ofsetu OZ
        IB_val -= I_zero;
      else
        IB_val = 0;
      // nasledujici sekvence zabere max 300 taktu
      if (chrg) {
        if (IB_val == 0) {   // tece vubec nejaky nabijeci proud ?
          // NE netece
          err = 1;              // signalizuj chybu
          chrg = 0;             // ukonci nabijeni
          output_low (CHRG_ON);
          output_low (BATT_ON);
        }
        else {
          // ANO tece
          inc_cap = IB_val * chrg_eff;    // uprav koeficientem ucinnosti nabijeni
          inc_cap = inc_cap >> (6-1);     // zbav se vynasobeni 64x a vynasob dvema, protoze krok proudu pri nabijeni je dvojnasobny proti vybijeni
          B_cap = B_cap + inc_cap;        // uprav kapacitu baterie
        }
      }
      #ifdef DEBUG
      if (IB_val > max)
        max = IB_val;
      if (IB_val < min)
        min = IB_val;
      #endif
    }

    if (l_tick) {
      l_tick = 0;

      measure_temp ();    // mer teplotu baterie - 1100 taktu

      if (measure_VS () < VS_MIN)
        return;           // koncime, napajeci zdroj byl odpojen

      if (is_load ())
        return;           // koncime, je pripojena zatez

      if (invalid_cap)
        leds_invalid_cap ();   // signalizace nezname aktualni kapacity baterie - nutno nejdive baterii vybit
      else
        if (err)
          leds_err ();          // signalizace chyby
        else {
          // signalizace nabiti baterie
          if ( leds_chrg () == 4 || TB_val > TB_MAX) {
            // baterie je plne nabita
            output_low (CHRG_ON);
            output_low (BATT_ON);
            chrg = 0;
            B_cap = B_cap_4_4;
          }
          else {
            if (B_cap > fast_chrg)
              duty = chrg_01C;  // temer nabito dale nabijime 0.1C
            else
              duty = chrg_02C;  // nabijeni proudem max. 0.2C

            if (pwm == 0) {
              output_high (CHRG_ON);
              chrg = 1;
            }
            else {
              if (pwm >= duty / IB_val) {  // 400 taktu
                output_low (CHRG_ON);
                chrg = 0;
              }
            }
            pwm++;
          }
        }

      #ifdef DEBUG
        restart_wdt ();
//        printf ("z:%lu h:%ld add:%lu cap:%lu T:%u\n\r",I_zero,max,inc_cap,B_cap,TB_val);
//        printf ("pwm:%u z:%lu max:%lu d:%lu\n\r",pwm,I_zero,max,max-min);
//        printf ("%lu %lu\n\r",B_cap, fast_chrg);
          printf ("z:%lu max:%lu delta:%u inc:%lu\n\r",I_zero,max,(int8)(max-min),inc_cap);

        max = 0;
        min = 1024;
      #endif
    }
  }
}


// upravi kapacitu baterie dle samovybijeni
// zabere max. 2000 cyklu
//
void selfdischarge (int8 day_cnt) {
  int8  k2;
  int16 temp;
  int32 dec_cap;

  // realizuje upravu koeficientu samovybijeni podle teploty k2 = k * Z * delta_TB
  // zmena Z je 1.05 na stupen
  if (TB_avr24 > TB_ref)
    temp = (3 * (TB_avr24 - TB_ref)) + 64;
  else {
    temp = 3 * (TB_ref - TB_avr24);
    if (temp > 64)
      temp = 0;
    else
      temp = 64 - temp;
  }
  temp = (temp * k) / 64;
  k2 = (int8)temp;

  // v prvnich 5 dnech je samovybijeni strmejsi
  if (day_cnt < 5)
    k2 = k2 * 2;
  dec_cap = k2 * CAP_BATT;
  if (B_cap > dec_cap)
    B_cap -=dec_cap;
#ifdef DEBUG
//  printf ("k:%u k2:%u dec_cap:%lu cap:%lu ",k,k2,dec_cap,B_cap);
#endif
}


//////////////////////////////////////////////////////
//
// PRIPRAVNE A NASTAVOVACI FUNKCE
//
//////////////////////////////////////////////////////

// pripravi promenne pro beh programu
//
void prepare_var () {
  int16 B_cap;
  int8 tmp8;
  int16 tmp16;
  int32 tmp32;
  float tmpf;

  // priprav meze pro signalizaci stavu vybiti baterie
  B_cap = read_eeprom (B_CAP_L_ADDR);
  B_cap *= 100;
  tmp16 = read_eeprom (B_CAP_H_ADDR);
  tmp16 *= 1000;
  B_cap += tmp16;   // kapacita baterie v mAh

  B_cap_4_4 = B_cap * CAP_BATT;
//  B_cap_4_4 = 100 * CAP_BATT;
  B_cap_2_4 = B_cap_4_4 / 2;
  B_cap_1_4 = B_cap_2_4 / 2;
  B_cap_3_4 = B_cap_2_4 + B_cap_1_4;

  // priprav omezeni nabijecich proudu
  tmpf = B_cap / 10;
  tmpf *= 256;
  tmpf /= 4.88;
  chrg_01C = (int16)tmpf;
  chrg_02C = chrg_01C * 2;

  // priprav koeficient ucinnosti nabijeni
//  eff = 1+(( read_eeprom (EFF_ADDR) * 5) / 100.0);  // koeficient s jednickou
//  eff -= 0.1;
//  eff = 64 / eff;          // ucinnost nabijeni (0-1) nasovena 64
//  chrg_eff = (int8)(eff);  // ucinnost nabijeni vhodna pro celociselnou aritmetiku

  chrg_eff = CHRG_EFF_TAB [read_eeprom (EFF_ADDR)];  // toto je vyhodnejsi varianta

  // priprav koeficient samovybijeni
  tmp8 = read_eeprom (LOSS_CAP_H_ADDR) * 10 + read_eeprom (LOSS_CAP_L_ADDR);  // ztracena kapacita v procentech
  tmp32 = B_cap;
  tmp32 *= tmp8;
  tmp16 = tmp32 / 100;                  // ztracena kapacita v mAh
  tmp8 = read_eeprom (LOSS_DAY_H_ADDR) * 10 + read_eeprom (LOSS_DAY_L_ADDR);  // pocet dni, za kterou se kapacita ztratila
  k = tmp16 / tmp8;                     // strmost poklesu samovybijeni v mAh / den

  TB_ref = read_eeprom (TB_REF_ADDR);   // teplota, pri ktere k samovybijeni dochazelo

#ifdef DEBUG
//  printf ("chrg_eff:%lu ",B_cap);
//  printf ("chrg_eff:%u ",chrg_eff);
//  printf ("k:%u ",k);
#endif
}


// pripravi konstantu pro odmereni 1 hod pomoci wdt
//
prepare_hour_time () {
  int32 hour_time_cor;

  // vypocti potrebny pocet tiku wdt pro odmereni jedne hodiny
  hour_time_cor = 400 - hour_time;
  hour_time_cor *= HOUR_TIME_WDT;
  hour_time_cor /= hour_time;
  hour_time = HOUR_TIME_WDT + (int16)hour_time_cor;
}


// priprav vse pro minimalizaci spotreby ve spanku
//
void prepare_sleep () {
  leds_all_off ();
  output_low (BATT_ON);
  output_low (CHRG_ON);
  setup_vref (FALSE);
}


// nastavi parametry programu
//
void do_set_par () {
  int8 par_num;       // cislo parametru
  int8 par_val;       // hodnota parametru
  int8 key_timer;     // pro osetreni zakmitu nastavovacich tlacitek
  int8 keys;          // stav nastavovacich tlacitek
  int1 set_par;       // jsme v rezimu nastavovani hodnoty parametru
  int1 wait_release;  // cekame na uvolneni tlacitka

  leds_all_on ();     // oznam, ze jsme v nastavovacim rezimu
  delay_ms (1000);

  par_num = 0;
  s_tick = 0;
  l_tick = 0;
  key_timer = 0;
  wait_release = 0;
  set_par = 0;
  num_to_leds (par_num);                // zobraz zvoleny parametr

  for (;;) {
    restart_wdt ();
    if (s_tick) {
      s_tick = 0;
      if (key_timer != 0)
        key_timer--;
      else {
        key_timer = 5;
        keys = read_keys ();            // precti stav tlacitek
        if (keys & SET_MASK) {
          // tlacitko SET je stisknuto  - rezim vyberu parametru
          if (set_par) {
            if (par_val != read_eeprom (par_num)) {   // prechazime z rezimu nastaveni parametru, uloz zvoleny parametr
              write_eeprom (par_num, par_val);        // uloz jen pokud byl zmenen
              if (par_num == LOSS_DAY_H_ADDR ||
                  par_num == LOSS_DAY_L_ADDR)
                write_eeprom (TB_REF_ADDR,TB_avr24);  // pokud byla zmenena hodnota ztracene kapacity samovybijenim, uloz TB_avr24 jako refrerencni
            }
            num_to_leds (par_num);                    // zobraz zpatky zvoleny parametr
            set_par = 0;
          }
          else {
            // vybirame parametr
            if (keys & UP_MASK) {
              if (! wait_release) {
                if (par_num < 6)
                  par_num++;
                else
                  par_num = 0;
                num_to_leds (par_num);    // zobrazeni vybraneho parametru
                wait_release = 1;         // cekame na uvolneni tlacitka UP
              }
            }
            else
              wait_release = 0;           // tlacitko UP bylo uvolneno
          }
        }
        else {
          // tlacitko SET je uvolneno - rezim nastaveni parametru
          if (! set_par) {
            par_val = read_eeprom (par_num);    // prechazime z rezimu vyberu parametru, vyzvedni zvoleny parametr
            num_to_leds (par_val);              // zobraz hodotu parametru
            set_par = 1;
          }
          else {
            // nastavujeme parametr
            if (keys & UP_MASK) {
              if (! wait_release) {
                if (par_val < PAR_MAX_TAB [par_num])
                  par_val++;
                else
                  par_val = 0;
                num_to_leds (par_val);   // zobraz hodnotu parametru
                wait_release = 1;        // cekame na uvolneni tlacitka UP
              }
            }
            else
              wait_release = 0;
          }
        }
      }
    }

    if (l_tick) {
      l_tick = 0;
      if (measure_VS () < VS_MIN)
        break;      // koncime, napajeci zdroj odpojen
    }
  }
  prepare_var ();    // konec nastavovani, aktualizuj promenne programu
}



///////////////////////////////////////////////////
//              HLAVNI FUNKCE
///////////////////////////////////////////////////

main () {
int1 load_old;
int1 load;
int1 no_load;
int1 start_TB;       // spust mrereni teploty baterie
int16 wdt_timer;     // pro odmereni hodiny pomoci wdt
int8 hour_timer;     // pro odmerovani hodin
int8 day_timer;      // pro odmerovani dni od nabiti
int16 TB_avr_tmp;    // pro vypocet prumerne teploty


#ifdef DEBUG
int16 c;
int16 inc_cap,IB_val,I_zero;
int8 val;
#endif

  init_ports ();

  if (restart_cause () != WDT_TIMEOUT) {
    // mereni skutecne doby behu wdt proved pro vseshny resety krome wdt
    setup_oscillator (OSC_4MHZ | OSC_NORMAL);
    delay_ms (100);
    setup_timer_1 (T1_INTERNAL | T1_DIV_BY_1);
    setup_wdt (WDT_288MS);
    hour_time = 0;
    set_timer1 (~1000);
    restart_wdt ();
    for (;;) {
      if (TMR1IF) {
        hour_time++;
        TMR1IF = 0;
        set_timer1 (~1000);
      }
    }
  }

  setup_oscillator (OSC_4MHZ | OSC_NORMAL);
  setup_timer_0 (RTCC_INTERNAL| RTCC_DIV_1);
  setup_wdt (WDT_288MS);
  setup_timer_1 (T1_INTERNAL | T1_DIV_BY_1);
  set_timer1 (S_TICK_TIME);
  setup_spi (FALSE);
  setup_comparator (NC_NC_NC_NC);
  setup_vref (FALSE);
  setup_ccp1 (CCP_OFF);
  setup_adc (ADC_CLOCK_INTERNAL);  // doba prevodu cca 48 uS
  setup_adc_ports (sAN0 | sAN1 | sAN2 | sAN3 | sAN5 | VSS_VDD);

  enable_interrupts (INT_TIMER1);
  enable_interrupts (GLOBAL);

  prepare_var ();     // priprav promenne pro beh programu z udaju o vlastnostech baterie
  prepare_hour_time ();


//////////////////////////////////////////////////

  // uvodni bliknuti ledkou
  output_high (LED1_G);
  delay_ms (250);
  output_low (LED1_G);


/*
#ifdef DEBUG
    output_high (BATT_ON);
    printf ("Ahoj %lu\n\r",hour_time);

  for (;;) {
    restart_wdt ();
    if (is_load ())
      break;           // napajeci zdroj byl pripojen
    delay_ms (100);
  }

  B_cap = B_cap_2_4;
  invalid_cap = 0;

  set_timer1 (0);
  // zde se meri doba trvani funkci nebo vybraneho kodu
//   selfdischarge (0); //OK
    do_discharge ();

    c = get_timer1 ();
//    printf ("T:%lu %lu %u \n\r",c,B_cap,chrg_eff);

    printf ("T:%lu\n\r",c);
    output_low (BATT_ON);
    delay_ms (250);
    for (;;)
    restart_wdt ();
#endif
*/

#ifdef DEBUG
  invalid_cap = 0;
//  B_cap = B_cap_3_4;
  B_cap = B_cap_2_4;
#else
  invalid_cap = 1;      // nezname zbyvajici kapacitu baterie
  B_cap = 0;            // povazujeme ji za vybitou
#endif

  TB_avr24 = TB_DEFAULT;
  TB_avr_tmp = 0;
  wdt_timer = 0;
  hour_timer = 0;
  day_timer = 0;
  start_TB = 1;
  no_load = 0;

  // hlavni programova smycka
  for (;;) {
    restart_wdt ();
    prepare_sleep ();
    sleep ();
    setup_adc_ports (sAN0 | sAN1 | sAN2 | sAN3 | sAN5 | VSS_VDD);
    load = is_load ();    // precti stav kontaktu pripojeni zateze
    if (load != load_old)
      load_old = load;    // stav kontaktu neni platny
    else {
      if (load) {
        if (! no_load) {
          do_discharge ();  // zatez pripojena, vybijime  - ma vyssi prioritu
          no_load = 1;      // dalsi vybijeni je mozne az po odpojeni zateze
          wdt_timer = 0;
        }
      }
      else {
        no_load = 0;        // zatez byl odpojena
        if (measure_VS () > VS_MIN) {   // ma zdroj pro nabijeni dostatecne napeti?
          if (input (SET))
            do_charge ();               // zatez odpojena a pripojen zdroj pro nabijeni, nabijime - ma nizsi pioritu
          else
            do_set_par ();              // prechazime do rezimu pro nastavovani parametru programu
          TB_avr_tmp = 0;
          wdt_timer = 0;
          hour_timer = 0;
          day_timer = 0;
        }
      }
    }

    // reseni samovybijeni
    if (wdt_timer < hour_time)
      wdt_timer++;
    else {
      // uplynula hodina
      start_TB = 1;               // odstartuj mereni teploty
      wdt_timer = 0;              // natahni odmerovani hodiny
      if (hour_timer < 23)
        hour_timer++;
      else {
        // uplynul den
        TB_avr24 = TB_avr_tmp / 24;     // vypocti prumernou teplotu za den
        if (day_timer < 90)
          day_timer++;
        else
          invalid_cap = 1;           // po 90 dnech uz neverime kapacite baterie
        selfdischarge (day_timer);   // uprav kapacitu baterie s ohledem na samovybijeni
        #ifdef DEBUG
//        printf ("TB:%u\n\r",TB_avr24);
        #endif
        TB_avr_tmp = 0;
        hour_timer = 0;
      }
    }

    // realizace mereni teploty - je v mnoha krocich
    if (start_TB) {
      if (measure_temp () == 0) {
        #ifdef DEBUG
//        printf("T:%u ",TB_val);
        #endif
        TB_avr_tmp += TB_val;           // pocitame prumer teploty
        start_TB = 0;
      }
    }
  }

}