/************************************/
/*        CLOCK2 by QZ v0.4b!       */ 
/************************************/
/*           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 248  
#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 100
#define MAX_STEPS 15 // число ступеней

#define LCD_ON_TICKS 65 //110
#define LCD_OFF_TICKS 75 //90
                                     //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 resolution, lcda_buf, lcdk_buf;
unsigned char digit_out[7], mig_dig = 0; // 1 - temp time; 2 - rx; 3 - tx
unsigned char temp_step = 0, ii, *p; // стадия измерения темп-ы (0 - не меряем)
bit key0 = 0, key1 = 0, key2 = 0, key3 = 0, conv_compl = 0;
//unsigned char keys[4]; //0..KEY_LAVEL не нажата, KEY_LAVEL..KEY_TICKS нажата
unsigned char clock_time = 0, beep_time = 0, disp_time = 0, key_time = 0, temp_time = 0, zz, out_time = 0;
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...; 0, 1 sec, 2 min, 3 hour, 4 day, 5 month, 6 year
bit mig = 1, ismin, is05;

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);
  e = e - e/7;
  dofw = DAYOFWEEKS[e];  
}        

void hex_to_dec(void) 
{
  temp = (unsigned char)(temperature & 0x00FF);
  //ismin = temperature & ((unsigned int)(1 << 9));
  //is05 = temperature & ((unsigned int)(1 << 8));
  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: //pn
          digit_out[1] = 13;
          digit_out[2] = 14;
        break;
        case 2: //vt
          digit_out[1] = 8;
          digit_out[2] = 15;
        break;  
        case 3: //sr
          digit_out[1] = 16;
          digit_out[2] = 17;
        break;
        case 4: //4t 
          digit_out[1] = 4;
          digit_out[2] = 15;
        break;
        case 5: //pt 
          digit_out[1] = 13;
          digit_out[2] = 15;
        break;
        case 6: //sb
          digit_out[1] = 16;
          digit_out[2] = 6;
        break;
        case 7: //vs
          digit_out[1] = 8;
          digit_out[2] = 16;
        break;
        default: { //x3
          digit_out[1] = 14;
          digit_out[2] = 3;
        }
      }      
    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);
    } else
    if (nowchange == 1)
      oldmode = mode;  
  }     
}  

void lcd_out(void)
{  
  //j = LCD_ON_TICKS; // Задержка для отображения цифры 20 (40) us
  //while (j--) ;
  
  if (k == 7) k = 0;
    else k++;
  LCDA = 0xFF;   
  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; // Задержка для выключения транзистора 15 (30) us
  while (j--) ;
  LCDK = lcdk_buf;
  LCDA = lcda_buf;  
}

// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{   
  TCNT0 = T0_VAL;
  if (conv_time == 0) {      
    TCCR0 = 0;
    conv_compl = 1; 
  } else conv_time--;     
}  

// 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 (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
	cbi  __w1_port,__w1_bit   ;// порт в 0
	sbi  __w1_port-1,__w1_bit ;// порт - выход, линия в 0
#endasm	
	//__DELAY_USW 0x3C0 ; !!!1111  ;// w8 min 480 us
	//w8_time = 3;	
  //w8_mode = 2;
  //TCCR0 = 0x05;
  //TCNT0 = T0_VAL;
	//delay_us(400);	
	//w8_mode = 0;
	//TCCR0 = 0x00;
	lcd_out();
	lcd_out();
	lcd_out();
#asm 	 
	cbi  __w1_port-1,__w1_bit ;// линию в 1 (порт в чтение)
	
	LDI  R24,LOW(0x25) ;// ждем
__DELAY_USB_LOOP1:
	DEC  R24
	BRNE __DELAY_USB_LOOP1 
	
	sbis __w1_port-2,__w1_bit ; //проверяем PIN 
	ret
#endasm  	
	lcd_out();
#asm 		
	;LDI  R24,LOW(0xCB) ; //ждем
;__DELAY_USB_LOOP2:
	;DEC  R24
	;BRNE __DELAY_USB_LOOP2 
	
	sbis __w1_port-2,__w1_bit ; //еще проверка
	inc  r30
#endasm    
  //w8_mode = 2;
  //TCCR0 = 0x05;
  //TCNT0 = T0_VAL;
	//delay_us(400);	
	//w8_mode = 0;
	//TCCR0 = 0x00;
	lcd_out();
	lcd_out();
	lcd_out();
#asm 	//__DELAY_USW 0x30C ; !!!1111 ; //ждем ~ 480 us
	ret
#endasm
} 
*/

struct __ds18b20_scratch_pad_struct __ds18b20_scratch_pad1;
// temp. conversion time [ms] depending on the resolution
//static flash int conv_delays1[4]={3, 7, 15, 31};
// valid temp. bit mask depending on the resolution
//static flash unsigned bit_mask1[4]={0xFFF8, 0xFFFC, 0xFFFE, 0xFFFF};


unsigned char ds18b20_select1(void)
{
  if (w1_init() == 0) return 0;
  w1_write(0xcc);  
  return 1;
}

unsigned char ds18b20_read_spd1(void)
{
  unsigned char ii;
  unsigned char *p;
  if (ds18b20_select1() == 0) return 0;  
  w1_write(0xbe);
  //lcd_out();
  ii = 0;
  p = (char *) &__ds18b20_scratch_pad1;  
  do
    *(p++) = w1_read();
  while (++ii < 9);
  //lcd_out();
  return !w1_dow_crc8(&__ds18b20_scratch_pad1,9);
}

/*
unsigned char ds18b20_read_spd1(void)
{
  unsigned char i;
  unsigned char *p;
  w1_init1();
  //!!1
  if (w1_init2() == 0) return 0;
  w1_write(0xcc);
 // lcd_out();
  
  w1_init1();
  //!!1
  if (w1_init2() == 0) return 0;
  w1_write(0xcc);
  
  w1_write(0xbe);
  i=0;
  p=(char *) &__ds18b20_scratch_pad1;
 // lcd_out();
  do
    *(p++)=w1_read();
  while (++i<9);  
  //lcd_out();
  return !w1_dow_crc8(&__ds18b20_scratch_pad1,9);
}
*/
/*
void ds18b20_temperature1(void)
{  
  if (ds18b20_read_spd1()==0) {
    temperature = 255;
    return ;
  }    
  resolution = (__ds18b20_scratch_pad1.conf_register >> 5) & 3;
  if (ds18b20_select1() == 0) {
    temperature = 255;
    return ;
  }  
    
  w1_write(0x44);  
  conv_time = 781;//conv_delays1[resolution];
  conv_compl = 0;
  w8_mode = 1;
  TCCR0 = 0x05;
  TCNT0 = T0_VAL;
  //lcd_out();
}  
*/
  //delay_ms(conv_delay[resolution]);//!!!!!!!1
/*  
unsigned int get_temp9(void)
{
  signed int wtf;
  unsigned int temp_out; // | | | | | | |s|5|     |t|t|t|t|t|t|t|t|
  if (ds18b20_read_spd1() == 0) return 255;
  //lcd_out();
  w1_init1();
  wtf = *((int *) &__ds18b20_scratch_pad1.temp_lsb);    
  wtf >>= 3;
  if (wtf < 0) {
    temp_out = -wtf;
    temp_out >>= 1;
    ismin = 1;
    is05 = wtf & 1;    
  } else {
    temp_out = (wtf >> 1);
    ismin = 0;
    is05 = wtf & 1;
  }  
  return temp_out;
}
*/
void refresh_temp(void) // последовательно принимаем темп из датчика
{
  unsigned char error = 0;
  switch (temp_step)
  {
    case 0: 
    break;    
    case 6: resolution = 0;//(__ds18b20_scratch_pad1.conf_register >> 5) & 3; // по идее всегда 0 (9 бит режим)
    case 1:
    case 10:      
      error = (w1_init() == 0);   //инициализация  
      temp_step++;
    break;
    case 2:
    case 7:
    case 11:
      w1_write(0xcc); //skip ROM cmd
      temp_step++;
    break;
    case 3:
    case 12:
      w1_write(0xbe); //read scratchpad cmd
      ii = 0;
      temp_step++;
    break; 
    case 4:  
    case 13:    
      p = (char *) &__ds18b20_scratch_pad1;  
      do
        *(p++) = w1_read();
      while (++ii < 9); //read scratchpad 
      temp_step++;   
    break;
    case 5:
    case 14:
      error = !w1_dow_crc8(&__ds18b20_scratch_pad1,9); // создаем структуру с инфой о датчике      
      if (!error) temp_step++;
    break;
    case 8:
      w1_write(0x44); // convert temp cmd
      temp_step++;
    break;
    case 9:
      conv_time = CONV_TICKS; // запускаем таймер на время преобразования (~100 ms)
      conv_compl = 0;
      //w8_mode = 1;
      TCCR0 = 0x05;
      TCNT0 = T0_VAL;
      temp_step = 0;
    break;
    case 15:
      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;
      }
      temp_step = 1;
    break;
  }
  if (!error) {
    temperature = 255;
    temp_step = 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 = 0x00;//4
  //TCNT0 = T0_VAL;

  TCCR2 = 0x07;
  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);
  temp_step = 1;
  //ds18b20_temperature1();
  //delay_ms(50);
  mode = 0;

  #asm("sei") // #asm("cli"); //disable all interrupts

  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 == 0) { // ~ 2 раза в сек
      rtc_get_time(&hour, &min, &sec);
      //lcd_out();
      //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 == 0) && (key1 | key2)) { //смена режима
      mode += key1-key2;
    }    
    change();    // обработка клавиш (меню)
    //lcd_out();
    // temp!!!
    refresh_temp();
    //if (temp_time == 0 && (mode == 0 || mode == 1))
    //{
    //  ds18b20_temperature1();
    //  temp_time = TEMP_TICKS;
    //}
    if (conv_compl) {
      //temperature = get_temp9();
      conv_compl = 0;
      temp_step = 10;
      //lcd_out();      
    }  
    if (disp_time == 0) {  // (подготовка инфы к выводу)  ~ 2 в секунду
      disp();
      disp_time = DISP_TICKS;
    }
   // if (out_time == 0) {
    lcd_out(); // вывод
    //  out_time = OUT_TICKS;
    //}  
  }
}
