#include "main.h"

__C_task void main(void)
{
  /* Disable comaparator */
  ACSR = 1<<ACD;

  /* Init LED */
  LED_DDR = LED_1 | LED_2;
  LED_OFF(LED_1);
  LED_OFF(LED_2);

  /* Init 7 segment LED indicators */
  INDCTR_SEGMENT_DDR = 0xFF; // segment pins to output
  INDCTR_SEGMENT_PORT = 0x00; // off all segment

  INDCTR_COMMON_DDR = INDCTR_COMMON_ALL; // common pins to output
  INDCTR_COMMON_PORT = ~INDCTR_COMMON_ALL; // off all indikators

  Flag2.needDot = 0;
  Flag2.blinkIndktr = 0;

  /* Timer0 every 4 ms switch showing LED */
  TCCR0 = TIM0_PRESCALER;
  TCNT0 = TIM0_CNT; // load timer
  TIMSK |= 1<<TOIE0; // enable TIM0_OVF interrupt

  /* Timer1 ticks with 1 MHz frequency for DS18B20 delays */
  TCCR1B = TIM1_PRESCALER;
//  TCNT1 = 64536;
//  TIMSK |= 1<<TOIE1; // enable TIM1_OVF interrupt

  /* Timer2 count 1 ms */
  TCCR2 = TIM2_PRESCALER;
  TCNT2 = TIM2_CNT; // load timer
  TIMSK |= 1<<TOV2; // enable TIM2_OVF interrupt

  /* ADC init */
  resultADC = 0;
  ADMUX = (1<<REFS0) | (1<<ADLAR); // Vref = AVcc, channel ADC0, Left adjusted result
  ADCSR = 1<<ADEN | 1<<ADPS2 | 1<<ADPS1; // enable ADC, prescaler = 64

  /* I2C init */
  TWI_MasterInit(100);

  __enable_interrupt();

  // Start tasks
  counterADC = PERIOD_ADC; // every 50 ms start ADC measure
  counterTWI = 4; // small delay
  counterDS18B20s = 12; // small delay
  
  State = show_HHMM;

  while(1) {

    /* check time */
    if (1 == Flag.getTime) {
      Flag.getTime = 0;
      getTime();
    } // end of Flag.getTime

    if  (1 == Flag.newTime) {
      Flag.newTime = 0;
      
      switch (State) {
      case show_HHMM:
        // every 25 and 55 seconds shoe temperature for 5 seconds
        if ((0x25 == Clock.seconds) || (0x55 == Clock.seconds)) {
          State = show_TEMP;
          counterShowTemp = PERIOD_SHT;
          showTEMP();
        } else {
          showHHMM();
        }
        break;
        
      case show_MMSS:
        showMMSS();
        break;

      case set_TIME:
        showHHMM();
        break;
        
      default:
        break;
      }
    } // end of Flag.newTime

    /* Temperature */
    if (1 == Flag.startDS18B20) {
      Flag.startDS18B20 = 0;
      ds18b20_StartMeasure();
    }
    
    if (1 == Flag.readDS18B20) {
      Flag.readDS18B20 = 0;
      ds18b20_ReadTemperature();
    }

    if (1 == Flag.newTemp) {
      Flag.newTemp = 0;
      if (show_TEMP == State) {
        showTEMP();
      }
    } // end of Flag.newTemp

    /* check buttons */
    if (1 == Flag.newBTN) {
      Flag.newBTN = 0;

      uint8_t mm, hh;
      
      btn_t btn = getBTN();
      switch (btn) {
      case btn_1:
        // show HH:MM
        if (State != set_TIME) {
          State = show_HHMM;
          counterShowTemp = 0;
          showHHMM();
        }
        break;

      case btn_2:
        // show MM SS
        if (State != set_TIME) {
          State = show_MMSS;
          Flag2.needDot = 1;
          counterShowTemp = 0;
          showMMSS();
        }
        break;

      case btn_3:
        // show temperature
        if (State != set_TIME) {
          State = show_TEMP;
          Flag2.needDot = 0;
          counterShowTemp = 0;
          showTEMP();
        }
        break;

      case btn_4:
        // time --
        if (set_TIME == State) {
          mm = bcd2bin(Clock.minutes);
          hh = bcd2bin(Clock.hours);
          mm --;
          if (mm > 59) {
            mm = 59;
            hh --;
            if (hh >23 ) {
              hh = 23;
            }
          }
          Clock.minutes = bin2bcd(mm);
          Clock.hours = bin2bcd(hh);
          Flag.newTime = 1;
        }
        break;

      case btn_5:
        // time ++
        if (set_TIME == State) {
          mm = bcd2bin(Clock.minutes);
          hh = bcd2bin(Clock.hours);
          mm ++;
          if (mm > 59) {
            mm = 0;
            hh ++;
            if (hh > 23) {
              hh = 0;
            }
          }
          Clock.minutes = bin2bcd(mm);
          Clock.hours = bin2bcd(hh);
          Flag.newTime = 1;
        }
        break;

      case btn_6:
        // save new time
        if (set_TIME == State) {
          State = show_HHMM;
          Flag2.blinkIndktr = 0;
          setTime();
          counterTWI = PERIOD_TWI;
          Flag.newTime = 1;
        }
        break;

      case btn_7:
        // time set
        if (show_HHMM == State) {
          State = set_TIME;
          Flag2.blinkIndktr = 1;
          Flag2.needDot = 1;
          counterTWI = 0;
        }
        break;

      default:
        break;
      }
      
    } // end of new BTN

    // делать нехрен -- спим, ждём прерывание
    MCUCR = 1<<SE;
    __sleep();

  } // end of while(1)

} // end of main()

/**
 *  S u b r o u t i n e s
 */
static uint8_t bcd2bin(uint8_t bcd) {
//  return ((10*(bcd>>4))|(bcd&0x0f));
    uint8_t Temp = bcd & 0x0F;
    while (bcd>=0x10)
    {
        Temp += 10;
        bcd -= 0x10;
    }
    return Temp;
}

static uint8_t bin2bcd(uint8_t bin) {
//  return (((bin/10)<<4)|(bin%10));
    uint8_t Temp = 0;
    while(bin>9)
    {
        Temp += 0x10;
        bin-=10;
    }
    return Temp+bin;
}

static void showHHMM(void) {
  if (Clock.hours > 9) {
    Indicator1 = IndctrNums[(0x0F & (Clock.hours>>4))];
  } else {
    Indicator1 = Sym_blank;
  }
  Indicator2 = IndctrNums[(0x0F & Clock.hours)];
  Indicator3 = IndctrNums[(0x0F & (Clock.minutes>>4))];
  Indicator4 = IndctrNums[(0x0F & Clock.minutes)];
}

static void showMMSS(void) {
  Indicator1 = IndctrNums[(0x0F & (Clock.minutes>>4))];
  Indicator2 = IndctrNums[(0x0F & Clock.minutes)];
  Indicator3 = IndctrNums[(0x0F & (Clock.seconds>>4))];
  Indicator4 = IndctrNums[(0x0F & Clock.seconds)];
}

static void showTEMP(void) {
  int8_t t = Temperature;
  
  if (t < 0) {
    Indicator1 = Sym_minus;
    t = -t;
  } else {
    Indicator1 = Sym_blank;
  }

  // convert to BCD
  t = bin2bcd(t);

  Indicator2 = IndctrNums[(0x0F & (t>>4))];
  Indicator3 = IndctrNums[(0x0F & t)];
  Indicator4 = Sym_gradus;
  
  Flag2.needDot = 0;
}

static btn_t getBTN(void) {
  uint8_t btn_code = resultADC;
  if (btn_code > 0xED) {
    return btn_no;
  } else if (btn_code > 0xD8) {
    return btn_1;
  } else if (btn_code > 0xD0) {
    return btn_2;
  } else if (btn_code > 0xC5) {
    return btn_3;
  } else if (btn_code > 0xB4) {
    return btn_4;
  } else if (btn_code > 0x94) {
    return btn_5;
  } else if (btn_code > 0x3F) {
    return btn_6;
  } else {
    return btn_7;
  }
}

static void getTime(void) {
  static uint8_t old_sec;

  /* Sync with indicators */
  Flag2.waitIndktr = 1;
  while (Flag2.waitIndktr == 1);
 
  /*устанавливаем указатель DS1307 на нулевой адрес*/
  twi_buf[0] = (DS1307_ADR<<1)|0; //адресный пакет
  twi_buf[1] = 0;                 //адрес регистра   
  TWI_SendData(twi_buf, 2);

  /*считываем время с DS1307*/
  twi_buf[0] = (DS1307_ADR<<1)|1;
  TWI_SendData(twi_buf, 5);

  /*переписываем данные буфера драйвера в свой буфер*/
  TWI_GetData(twi_buf, 5);

  Clock.seconds = twi_buf[1];
  Clock.minutes = twi_buf[2];
  Clock.hours = twi_buf[3];
  
  if (show_HHMM == State) {
    if (old_sec != Clock.seconds) {
      Flag2.needDot = 1;
      old_sec = Clock.seconds;
    } else {
      Flag2.needDot = 0;
    }
  }

  Flag.newTime = 1;
}

static void setTime(void) {
  /* Sync with indicators */
  Flag2.waitIndktr = 1;
  while (Flag2.waitIndktr == 1);
 
   /* prepare new time */
  twi_buf[0] = (DS1307_ADR<<1)|0; //адресный пакет
  twi_buf[1] = 0;                 //адрес регистра
  twi_buf[2] = 0;                 //значение секунд 
  twi_buf[3] = Clock.minutes;     //значение минут 
  twi_buf[4] = Clock.hours;       //значение часов

  /* senr to rtc */
  TWI_SendData(twi_buf, 5);
}

/* DS18B20 functions */
static uint8_t ds18b20_Reset(void) {
  uint8_t i;

  /* Pull line low and wait for 480uS */
  DS18B20_LOW;
  DS18B20_OUTPUT_MODE;

  //ds18b20_delay(us(480));
  TCNT1 = 0;
  while (TCNT1 <= 480) {
  }

  /* Release line and wait for 60uS */
  DS18B20_INPUT_MODE;
  //ds18b20_delay(us(60));
  TCNT1 = 0;
  while (TCNT1 <= 60) {
  }

  /* Store line value and wait until the completion of 480uS period */
  i = DS18B20_VALUE;
  //ds18b20_delay(us(420));
  //OCR1B = 420;
  TCNT1 = 0;
  while (TCNT1 <= 420) {
  }

  /* Return the value read from the presence pulse (0=OK, else=WRONG) */
  return i;
}

static void ds18b20_WriteBit(uint8_t bit) {
  /* Pull line low for 1uS */
  DS18B20_LOW;
  DS18B20_OUTPUT_MODE;
  //ds18b20_delay(us(1));
  TCNT1 = 0;
  while (TCNT1 <= 1);

  /* If we want to write 1, release the line (if not will keep low) */
  if(1 == bit) {
    DS18B20_INPUT_MODE;
  }

  /* Wait for 60uS and release the line */
  //ds18b20_delay(us(60));
  TCNT1 = 0;
  while (TCNT1 <= 60) {
  }

  DS18B20_INPUT_MODE;
}

static uint8_t ds18b20_ReadBit(void) {
  uint8_t bit=0;

  /* Pull line low for 1uS */
  DS18B20_LOW;
  DS18B20_OUTPUT_MODE;
  //ds18b20_delay(us(1));
  TCNT1 = 0;
  while (TCNT1 <= 1);

  /* Release line and wait for 14uS */
  DS18B20_INPUT_MODE;
  //ds18b20_delay(us(14));
  TCNT1 = 0;
  while (TCNT1 <= 14) {
  }

  /* Read line value */
  if (DS18B20_VALUE != 0) {
    bit = 0x80;
  }

  /* Wait for 45uS to end and return read value */
  //ds18b20_delay(us(45));
  TCNT1 = 0;
  while (TCNT1 <= 45) {
  }

  return bit;
}

static uint8_t ds18b20_ReadByte(void) {
  uint8_t i=8, n=0;

  while (i--) {
    /* Shift one position right and store read value */
    n >>= 1;
    n |= ds18b20_ReadBit();
  }
  return n;
}

static void ds18b20_WriteByte(uint8_t byte) {
  uint8_t i=8;

  while (i--) {
    /* Write actual bit and shift one position right to make the next bit ready */
    ds18b20_WriteBit(byte & 0x01);
    byte >>= 1;
  }
}

static void ds18b20_StartMeasure(void) {
  /* Sync with indicators */
  Flag2.waitIndktr = 1;
  while (Flag2.waitIndktr == 1);
 
  /* Reset, skip ROM and start temperature conversion */
  if (ds18b20_Reset() != 0) {
    Temperature = -99;
    Flag.newTemp = 1;
  } else {
    ds18b20_WriteByte(DS18B20_CMD_SKIPROM);
    ds18b20_WriteByte(DS18B20_CMD_CONVERTTEMP);
    counterDS18B20r = PERIOD_DS18B20r;
  }
}

static void ds18b20_ReadTemperature(void) {
  uint8_t temperature[2];
  uint8_t decimal;

  /* Sync with indicators */
  Flag2.waitIndktr = 1;
  while (Flag2.waitIndktr == 1);
 
  /* Reset, skip ROM and send command to read Scratchpad */
  if (ds18b20_Reset() != 0) {
    Temperature = -98;
  } else {
    ds18b20_WriteByte(DS18B20_CMD_SKIPROM);
    ds18b20_WriteByte(DS18B20_CMD_RSCRATCHPAD);

    /* Read Scratchpad (only 2 first bytes) */
    temperature[0]=ds18b20_ReadByte();
    temperature[1]=ds18b20_ReadByte();

    //ds18b20_reset();

    /* Store temperature integer digits */
    Temperature = ((temperature[1]&0x7)<<4) | (temperature[0]>>4);

    // get decimal part
    decimal = (temperature[0] & 15);
    
    // convert to binary
    //decimal *= 10; //(tempDecimals << 1) + (tempDecimals << 3);// Умножаем на 10
    //decimal >>= 4; //(tempDecimals >> 4);//делим на 16 или умножаем на 0.0625
    
    // round integer part
    if (decimal > 7) {
      Temperature ++;
    }
  }
  
  Flag.newTemp = 1;
}


/**
 *  I n t e r r u p t   h a n d l e r s
 */

#pragma vector=TIMER0_OVF_vect
__interrupt void TIMER0_OVF_ISR(void) {
  static uint8_t indicator = 0;
  static uint8_t blank_time = 0; // blanking timer
  uint8_t flag = 1;

  TCNT0 = TIM0_CNT; // reload timer

  INDCTR_COMMON_PORT &= ~INDCTR_COMMON_ALL; // off all indikators
  INDCTR_SEGMENT_PORT = 0;

  Flag2.waitIndktr = 0;

  if (1 == Flag2.blinkIndktr) {
    if (blank_time <= 150) {
      flag = 1;
      blank_time ++;
    } else if (blank_time <= 250) {
      blank_time ++;
      flag = 0;
    } else {
      blank_time = 0;
      flag = 1;
    }
  }

  if (1 == flag) {
    switch (indicator) {
    case 0:
      INDCTR_SEGMENT_PORT = Indicator1;
      INDCTR_COMMON_PORT |= 0x01;
      indicator = 1;
      break;

    case 1:
      if (1 == Flag2.needDot) {
        INDCTR_SEGMENT_PORT = Indicator2 | Sym_dot;
      } else {
        INDCTR_SEGMENT_PORT = Indicator2;
      }
      INDCTR_COMMON_PORT |= 0x02;
      indicator = 2;
      break;

    case 2:
      INDCTR_SEGMENT_PORT = Indicator3;
      INDCTR_COMMON_PORT |= 0x04;
      indicator = 3;
      break;

    case 3:
      INDCTR_SEGMENT_PORT = Indicator4;
      INDCTR_COMMON_PORT |= 0x08;

    default:
      indicator = 0;
      break;
    } // end of switch
  }
}

#pragma vector=TIMER1_COMPA_vect
__interrupt void TIMER1_COMPA_ISR(void) {
}

#pragma vector=TIMER1_COMPB_vect
__interrupt void TIMER1_COMPB_ISR(void) {
}

#pragma vector=TIMER1_OVF_vect
__interrupt void TIMER1_OVF_ISR(void) {
//  Flag.T1OC = 1;
}

#pragma vector=TIMER2_OVF_vect
__interrupt void TIMER2_OVF_ISR(void) {
  TCNT2 = TIM2_CNT; // reload timer

  if (counterADC > 0) {
    counterADC --;
    if (counterADC == 0) {
      counterADC = PERIOD_ADC;
      // enable interrupt and start conversion
      ADCSR |= ((1<<ADSC) | (1<<ADIE));
    }
  }

  if (counterTWI > 0) {
    counterTWI --;
    if (counterTWI == 0) {
      counterTWI = PERIOD_TWI;
      Flag.getTime = 1;
    }
  }

  if (counterShowTemp > 0) {
    counterShowTemp --;
    if (counterShowTemp == 0) {
      State = show_HHMM;
    }
  }
  
  if (counterDS18B20s > 0) {
    counterDS18B20s --;
    if (counterDS18B20s == 0) {
      counterDS18B20s = PERIOD_DS18B20s;
      Flag.startDS18B20 = 1;
    }
  }
  
  if (counterDS18B20r > 0) {
    counterDS18B20r --;
    if (counterDS18B20r == 0) {
      Flag.readDS18B20 = 1;
    }
  }
}

#pragma vector=ADC_vect
__interrupt void ADC_ISR(void) {
  resultADC = ADCH;
  ADCSR &= ~(1<<ADIE); // disable interrupt
  Flag.newBTN = 1;
}
