/************************************/
/*      CLOCK2 by QZ v0.5FINAL      */ 
/************************************/
/*           M16 8 MHz              */
/************************************/

#include <mega16.h>

// I2C Bus functions
#asm
   .equ __i2c_port = 0x15 ;PORTC
   .equ __sda_bit = 1
   .equ __scl_bit = 0
#endasm
#include <i2c.h>

// DS1307 Real Time Clock functions
#include <ds1307.h>

// 1 Wire Bus functions
#asm
   .equ __w1_port = 0x15 ;PORTC
   .equ __w1_bit = 7
#endasm
#include <1wire.h>

// DS18B20 Temperature Sensor functions
#include <ds18B20.h>
#include <delay.h> 

#define LCDK PORTD
#define LCDA PORTA
#define BTNS PINB

#define maxday 31
#define T0_VAL 0xD0
#define BEEP_EN PORTC.3
#define BEEP_1H_EN 1 

#define DISP_TICKS 15
#define TIME_TICKS 15
#define BEEP_TICKS 100
#define KEY_TICKS 13
#define TEMP_TICKS 100
#define OUT_TICKS 10
#define CONV_TICKS 3
//#define MAX_STEPS 9 // число ступеней

#define LCD_ON_TICKS 75 
#define LCD_OFF_TICKS 60 
                                     //0     1     2     3     4     5     6     7     8     9     -     _     .     П     H     t     C     P     F     '
const unsigned char convert[20] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0xBF, 0xFF, 0x7F, 0xC8, 0x89, 0x87, 0xC6, 0x8C, 0x8E, 0xFD};
const unsigned char YEARS[4] = {0, 2, 3, 4};
const unsigned char MONTHS[13] = {0, 0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
const unsigned char DAYOFWEEKS[7] = {7, 1, 2, 3, 4, 5, 6};

unsigned char k = 0, i, j, temp, conv_time; 
unsigned int temperature;
signed int wtf; 
unsigned char lcda_buf, lcdk_buf;
unsigned char digit_out[7], mig_dig = 0; 
unsigned char ii, *p; 
bit key0 = 0, key1 = 0, key2 = 0, key3 = 0;
unsigned char clock_time = 0, beep_time = 0, disp_time = 0, key_time = 0, temp_time = 0, zz;
unsigned char sec, min, hour, day, month, year, dofw;
unsigned char mode, oldmode, nowchange; //0 - normal(hh:mm,tt), 1 - mm:ss,tt, 2 - hh:mm,ss, 3 - dd:mn,yy, 4 - hh:mm,dofw, 5 - hh:mm, dd;
 // 0, 1 sec, 2 min, 3 hour, 4 day, 5 month, 6 year (nowchange)
bit mig = 1, ismin, is05, isconv = 0, is_date_changed = 0; // mig - мигает)), ismin - флаг отрицательной температуры, is05 - флаг 0.5 градуса, isconv - флаг конвертации темп
struct __ds18b20_scratch_pad_struct __ds18b20_scratch_pad1;

void day_of_week(void)
{
  unsigned char a, b, c, e, tmp;
  tmp = year + 4;
  a = tmp & 3;
  b = (tmp >> 2) + (tmp & 0xFC);
  c = MONTHS[month];
  if (a == 0 && month > 2) c++;
  a = YEARS[a];
  e = (a + b + c + day)%7;  
  dofw = DAYOFWEEKS[e];  
}        

void hex_to_dec(void) // готовим темп к выводу
{
  temp = (unsigned char)(temperature & 0x00FF);  
  if (temp == 255)
  {
    digit_out[0] = 10;
    digit_out[1] = 10;
    digit_out[2] = 10;
    is05 = 0;
    return;
  }    
  if (temp == 0)
  {
    digit_out[1] = 11;
    digit_out[2] = 0;
    if (ismin && is05)
    {
      digit_out[0] = 10;
      is05 = 0;
    }  
    else digit_out[0] = 11;     
    return;
  }        
  i = temp/100;    
  if (i == 0) i = 11 - (ismin);
  else temp = temp%100;
  digit_out[0] = i;
  i = temp/10;    
  if (i == 0) i = 11;  
  digit_out[1] = i;
  digit_out[2] = temp%10;
}

void disp(void) // подготовка инфы к выводу
{
  switch (mode)
  {
    case 0: 
      if (hour < 10)
        digit_out[3] = 11;
      else digit_out[3] = hour/10;
      digit_out[4] = hour%10;
      digit_out[5] = min/10;
      digit_out[6] = min%10;
      hex_to_dec();
    break;
    case 1:
      digit_out[3] = min/10;
      digit_out[4] = min%10;
      digit_out[5] = sec/10;
      digit_out[6] = sec%10;
      hex_to_dec();
    break;  
    case 2:
      if (hour < 10)
        digit_out[3] = 11;
      else digit_out[3] = hour/10;
      digit_out[4] = hour%10;
      digit_out[5] = min/10;
      digit_out[6] = min%10;
      digit_out[0] = 11;
      digit_out[1] = sec/10;
      digit_out[2] = sec%10;      
    break;
    case 3:
      if (day < 10)
        digit_out[3] = 11;
      else digit_out[3] = day/10;      
      digit_out[4] = day%10;
      if (month < 10)
        digit_out[5] = 11;
      else digit_out[5] = month/10;      
      digit_out[6] = month%10;
      digit_out[0] = 19; // '
      digit_out[1] = year/10;
      digit_out[2] = year%10;
    break;
    case 4:
      if (hour < 10)
        digit_out[3] = 11;
      else digit_out[3] = hour/10;
      digit_out[4] = hour%10;
      digit_out[5] = min/10;
      digit_out[6] = min%10;
      digit_out[0] = 11; // _
      switch (dofw)
      {
        case 1: //ПH
          digit_out[1] = 13;
          digit_out[2] = 14;
        break;
        case 2: //8t
          digit_out[1] = 8;
          digit_out[2] = 15;
        break;  
        case 3: //CP
          digit_out[1] = 16;
          digit_out[2] = 17;
        break;
        case 4: //4t 
          digit_out[1] = 4;
          digit_out[2] = 15;
        break;
        case 5: //Пt 
          digit_out[1] = 13;
          digit_out[2] = 15;
        break;
        case 6: //C6
          digit_out[1] = 16;
          digit_out[2] = 6;
        break;
        case 7: //8C
          digit_out[1] = 8;
          digit_out[2] = 16;
        break;
        default: { //H3
          digit_out[1] = 14;
          digit_out[2] = 3;
        }        
      }      
    break; 
    case 5:
      if (hour < 10)
        digit_out[3] = 11;
      else digit_out[3] = hour/10;
      digit_out[4] = hour%10;
      digit_out[5] = min/10;
      digit_out[6] = min%10;
      if (day < 10)
        digit_out[1] = 11;
      else digit_out[1] = day/10;      
      digit_out[2] = day%10;
    break;   
  }
}  

void change(void)
{    
  //unsigned char is_date_changed = 0;                
    switch (nowchange)
    {      
      case 0: break;
      case 1:        
        mode = 2;
        //disp();         
        mig_dig = 0x06;
                               
          if (key1) // +
          {
            sec = 30; 
            rtc_set_time(hour, min, sec);             
          }
          if (key2) // -
          { 
            sec = 0; 
            rtc_set_time(hour, min, sec);             
          }  
          //rtc_get_time(&hour, &min, &sec);                
      break;
      case 2:
        mode = 2;
        //disp();
        mig_dig = 0x60;
                    
          if (key1) // +
          {
            if (min == 59) min = 0;
            else min++; 
            rtc_set_time(hour, min, sec);             
          }
          if (key2) // -
          {
            if (min == 0) min = 59;
            else min--;       
            rtc_set_time(hour, min, sec);       
          }  
          //rtc_get_time(&hour, &min, &sec);
          //disp();        
              
      break;
      case 3:
        mode = 2;
        //disp();
        mig_dig = 0x18;
                    
          if (key1) // +
          {
            if (hour == 23) hour = 0;
            else hour++;    
            rtc_set_time(hour, min, sec);          
          }
          if (key2) // -
          {
            if (hour == 0) hour = 23;
            else hour--;    
            rtc_set_time(hour, min, sec);          
          }  
          //rtc_get_time(&hour, &min, &sec);
          //disp();          
               
      break;
      case 4:
        mode = 3;
        //disp();
        mig_dig = 0x18;
                    
          if (key1) // +
          {
            if (day == maxday) day = 1;
            else day++;    
            is_date_changed = 1;
          }
          if (key2) // -
          {
            if (day == 1) day = maxday;
            else day--;    
            is_date_changed = 1;
          }  
          //disp();          
              
      break;
      case 5:
        mode = 3;
        //disp();
        mig_dig = 0x60;
               
          if (key1) // +
          {
            if (month == 12) month = 1;
            else month++;    
            is_date_changed = 1;          
          }
          if (key2) // -
          {
            if (month == 1) month = 12;
            else month--;    
            is_date_changed = 1;          
          }  
          //disp();          
              
      break;
      case 6:
        mode = 3;
        //disp();
        mig_dig = 0x07;
        
          if (key1) // +
          {
            if (year == 99) year = 0;
            else year++;    
            is_date_changed = 1;          
          }
          if (key2) // -
          {
            if (year == 0) year = 99;
            else year--;  
            is_date_changed = 1;            
          }  
          //disp();
        
      break;
    }
      
  if (key3) { //select
    if (++nowchange > 6)
    {
      mode = oldmode;
      mig_dig = 0;
      nowchange = 0; 
      if (is_date_changed) {
        rtc_set_date(day, month, year);
        day_of_week();
        is_date_changed = 0;
      }  
    } else
    if (nowchange == 1)
      oldmode = mode;  
  }     
}  

//void lcd_out(void)
//{    
  
    
//}

// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{  
  TCNT0 = T0_VAL;
  //lcd_out();     
  
  //j = LCD_ON_TICKS; // Задержка для отображения цифры 20 (40) us
  //while (j--) ;
  LCDA = 0xFF; // вырубаю аноды
  LCDK = 0xFF; // вырубаю катоды
  if (k == 7) k = 0;
    else k++;   
  lcda_buf = (~(1 << (k + 1))) | ((mig_dig & (mig << k)) << 1);
  switch (k)
  {
    case 0: 
      lcdk_buf = (convert[digit_out[0]]);
    break;
    case 1: 
      lcdk_buf = (convert[digit_out[1]]);
    break;
    case 2: 
      if (is05) lcdk_buf = (convert[digit_out[2]]) & 0x7F;
      else lcdk_buf = (convert[digit_out[2]]);
    break;  
    case 3: 
      lcdk_buf = (convert[digit_out[3]]); 
    break;
    case 4: 
      lcdk_buf = (convert[digit_out[4]]); 
    break;
    case 5: 
      if (mig) lcdk_buf = (convert[digit_out[5]]) & 0x7F;
      else lcdk_buf = (convert[digit_out[5]]);
    break;
    case 6: 
      if (mig) lcdk_buf = (convert[digit_out[6]]) & 0x7F;
      else lcdk_buf = (convert[digit_out[6]]);
    break;
  } 
  //j = LCD_OFF_TICKS; // Задержка для выключения транзистора 
  //while (j--) ;     
  LCDK = lcdk_buf;
  LCDA = lcda_buf;    
}  

// Timer 2 overflow interrupt service routine
interrupt [TIM2_OVF] void timer2_ovf_isr(void)
{    
  if (key_time) key_time--;
  if (clock_time) clock_time--;
  if (disp_time) disp_time--; 
  if (conv_time) conv_time--;
  //if (beep_time) beep_time--;
  if (temp_time) temp_time--;
  if (!zz) mig ^= 1;
  if (++zz > 14) zz = 0; // ~ 2 Hz
} 

unsigned char w1_init1(void)
{
 #asm
	clr  r30
	
	cli ; //INT OFF
	
	cbi  __w1_port,__w1_bit   ; // порт в 0
	sbi  __w1_port-1,__w1_bit ; // порт - выход, линия в 0
	
  sei ; //INT ON
  
	LDI  R24,LOW(0x3C0) ; // ждем
	LDI  R25,HIGH(0x3C0)
__DELAY_USW_LOOP1:
	SBIW R24, 1
	BRNE __DELAY_USW_LOOP1
	
	cli ; //INT OFF

	cbi  __w1_port-1,__w1_bit ;// линию в 1 (порт в чтение)
	
	LDI  R24,LOW(0x25) ; // ждем
__DELAY_USB_LOOP1:
	DEC  R24
	BRNE __DELAY_USB_LOOP1
	
	sei ; //INT ON
	
	sbis __w1_port-2,__w1_bit ; //проверяем PIN 
	ret
 	
 	cli ; //INT OFF
 	
	LDI  R24,LOW(0xCB) ; //ждем
__DELAY_USB_LOOP2:
	DEC  R24
	BRNE __DELAY_USB_LOOP2	
	
	sbis __w1_port-2,__w1_bit ; //еще проверка
	inc  r30

  sei ; //INT ON

  LDI  R24,LOW(0x30C) ; // ждем
	LDI  R25,HIGH(0x30C)
__DELAY_USW_LOOP2:
	SBIW R24, 1
	BRNE __DELAY_USW_LOOP2
	
	ret
#endasm
} 

unsigned char ds18b20_select1(void)
{
  if (w1_init1() == 0) return 0; //1
  #asm("cli")
  w1_write(0xcc);
  #asm("sei")
  return 1;
}

unsigned char ds18b20_read_spd1(void)
{
  if (ds18b20_select1() == 0) return 0;
  #asm("cli")
  w1_write(0xbe);
  #asm("sei")
  ii = 0;
  p = (char *) &__ds18b20_scratch_pad1;
  do {
    #asm("cli")
    *(p++) = w1_read();
    #asm("sei")
  }
  while (++ii < 9);
  return !w1_dow_crc8(&__ds18b20_scratch_pad1,9);
}

void ds18b20_temperature1(void)
{
  if (ds18b20_select1() == 0) {
    temperature = 255;
    return ;
  }
  #asm("cli")
  w1_write(0x44);
  #asm("sei")
  conv_time = CONV_TICKS; // ждем (~100 ms)
  isconv = 1;
}
  
void get_temp9(void)
{
  if (ds18b20_read_spd1() == 0) {
    temperature  = 255;
    return ;
  }
  w1_init1(); //1
  wtf = *((int *) &__ds18b20_scratch_pad1.temp_lsb);
  wtf >>= 3;
  if (wtf < 0) {
    temperature = -wtf;
    temperature >>= 1;
    ismin = 1;
    is05 = wtf & 1;
  } else {
    temperature = (wtf >> 1);
    ismin = 0;
    is05 = wtf & 1;
  }
}

unsigned char ds18b20_init1(signed char temp_low,signed char temp_high,
  unsigned char res)
{
  if (ds18b20_select1() == 0) return 0;
  res = (res << 5) | 0x1F;
  w1_write(0x4e);
  w1_write(temp_high);
  w1_write(temp_low);
  w1_write(res);
  if (ds18b20_read_spd1() == 0) return 0;
  if ((__ds18b20_scratch_pad1.temp_low!=temp_low) ||
    (__ds18b20_scratch_pad1.temp_high!=temp_high) ||
    (__ds18b20_scratch_pad1.conf_register!=res)) return 0;
  if (ds18b20_select1() == 0) return 0;
  w1_write(0x48);
  delay_ms(15);
  return w1_init();
}

void main(void)
{
  PORTA = 0xFF;
  DDRA = 0xFF;

  PORTB = 0x1F;
  DDRB = 0x00;

  PORTC = 0x78;
  DDRC = 0x08;

  PORTD = 0xFF;
  DDRD = 0xFF;

  TCCR0 = 0x04; // Fosc/256
  TCNT0 = T0_VAL;

  TCCR2 = 0x07; // Fosc/1024
  TCNT2 = 0x00;

  TIMSK = 0x41;

  ACSR = 0x80;

  i2c_init();
  rtc_init(0,0,1);
  rtc_get_time(&hour, &min, &sec);
  rtc_get_date(&day, &month, &year);
  day_of_week();
  
  temperature = 255;
  w1_init();
  ds18b20_init1(-49, 124, DS18B20_9BIT_RES);
  ds18b20_temperature1();
  
  mode = 0;

  #asm("sei")

  while (1) {
    
    //----------- KEYS ---------------
    key1 = 0;
    key2 = 0;
    key3 = 0;
    if (key_time == 0) {
    //key0 = ((BTNS & 0x01) == 0);
      key1 = ((BTNS & 0x02) == 0);
      key2 = ((BTNS & 0x04) == 0);
      key3 = ((BTNS & 0x08) == 0);
      if (key1 | key2 | key3) key_time = KEY_TICKS;
    }
    //-------------------------------
    if (!clock_time) { // ~ 2 раза в сек
      rtc_get_time(&hour, &min, &sec);      
      //if (!beep_time) BEEP_EN = 1; // beep off
      if ((min & sec) == 0) {
        //BEEP_EN = !BEEP_1H_EN; // beep on
        //beep_time = BEEP_TICKS;
        if (hour == 0) {
          rtc_get_date(&day, &month, &year);
          day_of_week();
        }
      }
      clock_time = TIME_TICKS;
    }
    if ((!nowchange) && (key1 | key2)) { //смена режима
      if (key1 && mode < 5) mode++;
      if (key2 && mode > 0) mode--;
      //mode += key1-key2;
    }
    
    change();    // обработка клавиш (меню)    
    
    if (!temp_time)
    {
      ds18b20_temperature1();
      temp_time = TEMP_TICKS;
    }
    if (!conv_time && isconv) {
      get_temp9();
      isconv = 0;
    }
    if (!disp_time) {  // (подготовка инфы к выводу)  ~ 2 в секунду
      disp();
      disp_time = DISP_TICKS;
    }
  }
}
