/*****************************************************
Project : Термостат
Version :
Date    : 07.11.2009
Author  : Игорь Петлюк
Company : ---
Comments: Термостат с програмированием на неделю
Вариант для Savage

Chip type           : ATmega8
Program type        : Application
Clock frequency     : 4,000000 MHz
Memory model        : Small
External RAM size   : 0
Data Stack size     : 256

*****************************************************/


//==============================================================
// Предустановки
//==============================================================
#include <mega8.h>

// I2C -----------------------
#asm
   .equ __i2c_port=0x15 ;PORTC
   .equ __sda_bit=2
   .equ __scl_bit=1
#endasm
#include <i2c.h>

// DS1307 --------------------
#include <ds1307.h>

// 1 Wire --------------------
#asm
   .equ __w1_port=0x15 ;PORTC
   .equ __w1_bit=0
#endasm
#include <1wire.h>

// DS18B20 -------------------
#include <ds18B20.h>

// LCD -----------------------
#asm
   .equ __lcd_port=0x12 ;PORTD
#endasm
#include <lcd.h>

// Задержки
#include <delay.h>
// Стандартные фун-и
#include <stdio.h>
#include <stdlib.h>

//==============================================================
// GLOBAL DEFINES
//==============================================================
#define scan_delay 15               // Задержка до следующего сканирования клавиатуры = n*16,3ms

#define temp_max 1000                // Максимальная температура уставки = n/10°С  *кратно 5
#define temp_min 80                 // Минимальная температура уставки = n/10°С

#define hyst 5                      // Гистерезис переключения = 0.n°С

                                    // Коды кнопок:
//#define btn_plus   btn_tsk & 0x01   // "+"
//#define btn_minus  btn_tsk & 0x02   // "-"
//#define btn_right  btn_tsk & 0x08   // ">"
//#define btn_left   btn_tsk & 0x04   // "<"
//#define btn_ok     btn_tsk & 0x10   // "ok"

#define btn_plus   btn_tsk & 0x08   // "+"
#define btn_minus  btn_tsk & 0x04   // "-"
#define btn_right  btn_tsk & 0x01   // ">"
#define btn_left   btn_tsk & 0x02   // "<"
#define btn_ok     btn_tsk & 0x10   // "ok"



//==============================================================
// Прототипы функций
//==============================================================
void clk_adj (char cur_pos); // Меню установки часов
void compare (void); // Сравнение температуры и выдача команды на включение
void define_char(unsigned char flash *pc,unsigned char char_code); // Запись спецсимвола в ЖКИ
void e_get_temp (char direction); // Получение или запись температур повышеного и пониженого режимов
void e_temp_adj (char cur_pos); // Запись/чтение значений повыш. и пониж. температур
void get_day_map (char day, char direction); // Получение или запись дневной карты
char get_interval (void); // Получение номера интервала для текущнго времени с дневной карты
char get_mas_pos (char n); // Получение позиции в масиве карты дня для даного интервала
void get_temp (void); // Считывание и вычисление температуры DS18B20
char get_temp_status (char interval); // Получение статуса температуры для заданого интервала с дневной карты
void init (void); // Начальные установки
void int1_dis (void); // Запрет внешненго прерывания
void int1_en (void); // Разрешение внешненго прерывания
void interval_adj (char cur_pos); // Меню настройки интервалов
void m_check (void); // Проверка месяца на 30,29,28 дней
void m_menu (void);  // Главное меню (дисплей)
char not_leap (void); // Проверка на высокостный год
void relay (char s); // Управление реле (0-откл., 1...255-вкл.)
void start_conv (void); // Старт преобразования и проверка на исправность датчика тем-ры
void t1_start (void); // Старт таймера 1
void t1_stop (void);  // Останов таймера 1
void temp_adj (char cur_pos); // Вывод температуры ручной уставки
void tsk_clr (void); // Очистка заданий
char wd_calc (void); // Вычисление дня недели (возвр. номера дня)
void y_check (void); // Проверка года на высокостность для 2-го месяца


//==============================================================
// Глобальные переменные
//==============================================================
flash char d_map [7][4]= // Карта названий для дней
{
    {'B','C','K'},                // 0.ВСК
    {0xA8,'O','H'},                // 1.ПОН
    {'B','T','O'},                // 2.ВТО
    {'C','P','E'},                // 3.СРЕ
    {0xAB,'E','T'},                // 4.ЧЕТ
    {0xA8,0xB1,'T'},        // 5.ПЯТ
    {'C',0xA9,0xA0}                // 6.СУБ
};
flash unsigned char m_map [12][4]= // Карта названий для месяцев
{
    {0xB1,'H','B'},                //  0.ЯНВ
    {0xAA,'E','B'},                //  1.ФЕВ
    {'M','A','P'},                //  2.МАР
    {'A',0xA8,'P'},                //  3.АПР
    {'M','A',0xA6},                //  4.МАЙ
    {0xA5,0xB0,'H'},        //  5.ИЮН
    {0xA5,0xB0,0xA7},        //  6.ИЮЛ
    {'A','B',0xA1},                //  7.АВГ
    {'C','E','H'},                //  8.СЕН
    {'O','K','T'},                //  9.ОКТ
    {'H','O',0xB1},                // 10.НОЯ
    {0xE0,'E','K'}                // 11.ДЕК
};

flash char str_11[11]={0xE0,'a',0xBF,'a','/',0xB3,'p','e',0xBC,0xC7}; // "Дата/время"
flash char str_12[7]={0xA1,'p','a',0xE4,0xB8,0xBA}; // "График"
flash char str_13[12]={'T','e',0xBC,0xBE,'e','p','a',0xBF,'y','p',0xC3}; // "Температуры"
flash char str_14[6]={'B',0xC3,'x','o',0xE3}; // "Выход"

flash unsigned char c_degree[8]= // Знак градуса
{
0b01110,   //| ooo |
0b01010,   //| o o |
0b01110,   //| ooo |
0b00000,   //|     |
0b00000,   //|     |
0b00000,   //|     |
0b00000,   //|     |
0b00000    //|     |
};
flash unsigned char c_term[8]= // Знак градусник
{
0b01110,   //| ooo |
0b01010,   //| o o |
0b01010,   //| o o |
0b01110,   //| ooo |
0b01110,   //| ooo |
0b11101,   //|ooo o|
0b01110,   //| ooo |
0b00000    //|     |
};
flash unsigned char c_fire[8]= // Знак огонек
{
0b00100,   //|  o  |
0b10101,   //|o o o|
0b01110,   //| ooo |
0b11011,   //|oo oo|
0b01110,   //| ooo |
0b10101,   //|o o o|
0b00100,   //|  o  |
0b00000    //|     |


};
flash unsigned char c_moon[8]= // Знак луна
{
0b00100,   //|  o  |
0b01000,   //| o   |
0b11000,   //|oo   |
0b11000,   //|oo   |
0b11000,   //|oo   |
0b01100,   //| oo  |
0b00011,   //|   oo|
0b00000    //|     |


};


// Время (часы, минуты, секунды)
struct time_str
{
        char hour;
        char min;
        char sec;
} t;
// Дата (день, месяц, год)
struct date_str
{
        char day;
    char month;
    char year;
} d;

// Температура датчика
signed int temp;
// Желаемая температура
signed int temp2=150;

// Темп. повышеного режима
signed int temp_hi;
// Темп. пониженого режима
signed int temp_low;

// Кнопки
char btn_tsk;
/*
  PB2-"+"
  PB3-"-"
  PB0-">"
  PB1-"<"
  PB4-"ok"
*/

// Текущее положение реле "1"-вкл. "0"-выкл.
//char relay_pos=0;

// Текущий интервал
char cur_int;

// Флаг режима антизамерзания
char a_frz;

// Глубина вложености меню
struct menu_str
{
        char deep;    // Глубина меню (0-главн. окно)
    char smenu;   // Субменю
    char q_temp;  // Быстрый доступ к температуре
    char state;   // Номер редактируемого параметра

    char day_sel; // Выбор дня и интервала
    char int_sel; //
} menu;

char lcd_buf[33]; // Буфер для строчного ЖКИ 16х2 (32 симв. + NULL)

char day_int_map[19]; // Буфер для интервальной раскладки дня

// Раскладка интервалов на неделю по умолчанию
// S-статус, H-часы, M-минуты
eeprom char intervals[7][19]=
{
// 00:00                                                       24:00
// |                                                           |
// | -> 1 -> |    2 -> |    3 -> |    4 -> |    5 -> |    6 -> |
// | S  H  M | S  H  M | S  H  M | S  H  M | S  H  M | S  H  M |
        {1,07,00,  1,11,00,  1,14,00,  1,15,00,  1,16,00,  0,23,00}, // ВОС.
    {1,07,00,  0,11,00,  0,14,00,  0,15,00,  1,16,00,  0,23,00}, // ПОН.
    {1,07,00,  0,11,00,  0,14,00,  0,15,00,  1,16,00,  0,23,00}, // ВТО.
    {1,07,00,  0,11,00,  0,14,00,  0,15,00,  1,16,00,  0,23,00}, // СРЕ.
    {1,07,00,  0,11,00,  0,14,00,  0,15,00,  1,16,00,  0,23,00}, // ЧЕТ.
    {1,07,00,  0,11,00,  0,14,00,  0,15,00,  1,16,00,  0,23,00}, // ПЯТ.
    {1,07,00,  1,11,00,  1,14,00,  1,15,00,  1,16,00,  0,23,00}  // СУБ.
};

eeprom int e_temp[2]={190,230}; // Повышеные и пониженые температуры
eeprom int af_temp=80; // Температура режима антизамерзания (ручной режим)
eeprom char e_a_frz=0; // Флаг установки режима антизамерзания


//==============================================================
// External Interrupt 1
//==============================================================
// Прерывание от DS1307 ежесекундно
interrupt [EXT_INT1] void ext_int1_isr(void)
{
    static char temp_status, temp_char;
    char temp_sign;

    get_temp(); // Вычисление температуры
    start_conv(); // Старт следующего преобразования

    rtc_get_date(&d.day,&d.month,&d.year); // Считываем время и дату
    rtc_get_time(&t.hour,&t.min,&t.sec);   //

    if (t.hour==0 && t.min==0 && t.sec==0) // Определяем переход на следующий день
    {
            get_day_map(wd_calc(),0); // Загружаем дневную карту для нового дня
        menu.q_temp=0; // Сбрасываем быструю установку температуры
    }
    else if (cur_int<get_interval()) // Определяем переход на следующий интервал
         {
             menu.q_temp=0; // Сбрасываем быструю установку температуры
         };


    cur_int=get_interval(); // Получаем № интервала для даного времени
    temp_status=get_temp_status(cur_int); // Получаем статус температуры для текущего интервала

    // Определяем знак и температурный режим
    //--------------------------------------
    if(menu.q_temp==0 && a_frz==0) // Если сняты флаги быстрой температуры и режима антизамерзания
    {
        if(temp_status)  // Если повыш. температурный режим
        {
            temp_char=0x03;
            temp2=temp_hi; // Установить повышеную температуру
        }
        else             // Если пониж. температурный режим
        {
            temp_char=0x04;
            temp2=temp_low; // Установить пониженую температуру
        };
    }
    else if(menu.q_temp) // Если уст. флаг быстрой установки температуры
    {
            if(temp_status)  // Если повыш. температурный режим
        {
                if(temp2>temp_hi) temp_char=0xD9;      // Выбор стрелки вверх или вниз
            else if(temp2<temp_hi) temp_char=0xDA;
                 else if(temp2==temp_hi)           // Выход из режима быстрой установки при совпадении
                      {
                          temp_char=0x03;
                          menu.q_temp=0;
                      };
        }
        else             // Если пониж. температурный режим
        {
           if(temp2>temp_low) temp_char=0xD9;      // Выбор стрелки вверх или вниз
           else if(temp2<temp_low) temp_char=0xDA;
                else if(temp2==temp_low)           // Выход из режима быстрой установки при совпадении
                     {
                         temp_char=0x04;
                         menu.q_temp=0;
                     };
        };
    }
    else if(a_frz) // Если уст. режим антизамерзания
    {
            temp_char=0xEE;
    };

    // Определение знака температуры
    if(__ds18b20_scratch_pad.temp_msb & 0xF8)
    {
        temp_sign='-';
    }
    else
    {
        temp_sign='+';
    };

    // ДИСПЛЕЙ:
    // Верхняя строка
    sprintf (&lcd_buf[0],"%c%c%02i.%i%c %u%c%+03i.%i%c",0x02,temp_sign,abs(temp/10),abs(temp%10),0x01,cur_int,temp_char,temp2/10,abs(temp2%10),0x01);
    lcd_clear();
    lcd_puts(&lcd_buf[0]);

    // Нижняя строка
    sprintf (&lcd_buf[0],"  %p %02u:%02u:%02u",&d_map[wd_calc()][0],t.hour,t.min,t.sec);
    lcd_gotoxy(0,1);
    lcd_puts(&lcd_buf[0]);

    compare(); // Сравнение температур
}

//==============================================================
// Timer 0 overflow interrupt
//==============================================================
// Расчет прерывания по переполнению:
// -> 4MHz/256 -> 15'625Hz/256=61.035Hz Интервал прерывания: t=0.0163s=16.3ms
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
static char comb1,comb2,stat,delay=1;  // stat=0-первичная проверка; stat=1-вторичная проверка

    if (--delay==0) //проверка задержки
    {
        delay=1;

        if (stat==0) //запись в первую комбинацию через маску
        {
            comb1=((~PINB) & 0x1F);
        };

        if (stat==1) //проверка записи первой комбинации
        {
            comb2=((~PINB) & 0x1F); //запись во вторую комбинацию через маску, если была запись в первую
            if (comb1==comb2) //проверка комбинаций на соответствие
            {
            btn_tsk=comb2;    //записать в переменную комбинации, если соответствует
            delay=scan_delay; //и установить задержку нечуствительности к след. нажатию
            };
        stat=0;           //обнулить флаг статуса
        comb1=0; comb2=0; //и регистры комбинаций
        };

        if (comb1!=0) //если была нажата кнопка, то уст. повторную проверку
        {
        stat=1;
        };
    };
}

//==============================================================
// Timer 1 overflow interrupt
//==============================================================
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
        t1_stop();         // Останов таймера
        tsk_clr();         // Очистка

    //***************
//    lcd_gotoxy(15,1);  // !!!Временно для отладки выхода по времени
//    lcd_putchar('*');  // закоментировать для отключения
//    delay_ms(500);
//    lcd_gotoxy(15,1);
//    lcd_putchar(' ');
    //***************

    // В меню установки быстрой температуры...
    if(menu.q_temp==1 || a_frz==1)
    {
            btn_tsk=0x10;
    };
    // В главном меню...
    if(menu.deep==1)
    {
            menu.smenu=4;
            btn_tsk=0x10;
    };
    // В меню ДАТА/ВРЕМЯ, ГРАФИК, ТЕМПЕРАТУРЫ
    if(menu.deep==3)
    {
            btn_tsk=0x10;
    };

}

//==============================================================
// Начальная инициализация !!!
//==============================================================
void init (void)
{
// Input/Output Ports initialization
// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=P State6=P State5=P State4=P State3=P State2=P State1=P State0=P
PORTB=0xFF;
DDRB=0x00;

// Port C initialization
// Func6=In Func5=Out Func4=Out Func3=Out Func2=In Func1=In Func0=In
// State6=T State5=0 State4=0 State3=0 State2=T State1=T State0=T
PORTC=0x00;
DDRC=0x38;

// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x00;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 15,625 kHz
TCCR0=0x04;
TCNT0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x05;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;

        //relay(0); // Отключаем реле

// LCD module initialization
        lcd_init(16);

// Определение нестандартных символов
    define_char(&c_degree[0],0x01); // Знак градуса 0х01
    define_char(&c_term[0],0x02); // Градусник 0х02
    define_char(&c_fire[0],0x03); // Огонек 0х03
    define_char(&c_moon[0],0x04);// Луна 0х04

// I2C Bus initialization
        i2c_init();

// DS1307 Real Time Clock initialization
// Square wave output on pin SQW/OUT: On
// Square wave frequency: 1Hz
        rtc_init(0,1,0);

// 1 Wire Bus initialization
        w1_init();

        ds18b20_init(0,-20,35,DS18B20_12BIT_RES); // Точность 0.0625 12-разр. tпр.=800ms

    start_conv(); // Первое преобразование температуры

        lcd_clear();                 // Стартовый показ версии 1,5 сек.
    lcd_putsf("   Ver.:1.3.6.s");
    delay_ms(1500);
    
   
    rtc_get_time(&t.hour,&t.min,&t.sec);   // Получаем текущее время и дату с RTC
    rtc_get_date(&d.day,&d.month,&d.year);
    get_day_map(wd_calc(),0);  // Получаем раскладку для даного дня с EEPROM

    cur_int=get_interval(); // Получаем интервал для текущего времени

    e_get_temp(0); // Получаем значения температуры для двух режимов

    a_frz=e_a_frz; // Считываем с EEPROM статус режима антизамерзания

    if(a_frz) temp2=af_temp; // Если вкл. режим антизамерзания, считать температуру антизамерзания

    // Старт внешнего прерывания
    GICR|=0x80;
    MCUCR=0x08;
    GIFR=0x80;

#asm ("sei")
}

//==============================================================
// Вичисление дня недели через день, месяц и год
//==============================================================
char wd_calc (void)
{
unsigned int a,y,m;

    a=((14-d.month)/12);
    y=(d.year-a);
    m=(d.month+(12*a)-2);

    return (char)((7000+(d.day+y+(y/4)-(y/100)+(y/400)+((31*m)/12)))%7);
    // Возврат: 0-ВСКР. 1-ПОН. 2-ВТО. 3-СРЕ. 4-ЧЕТ. 5-ПЯТ. 6-СУБ.
}

//==============================================================
// Вычисление температуры DS18B20
//==============================================================
void get_temp (void)
{
char integer;
int frac;

ds18b20_read_spd(0); // Читаем scratcpad

        if (__ds18b20_scratch_pad.temp_msb & 0xF8) // Проверка знака температуры, если "-" то выполнить
    {
    integer=((((~__ds18b20_scratch_pad.temp_msb) & 0x07)<<4)|((~__ds18b20_scratch_pad.temp_lsb)>>4));
    frac=(10000-((__ds18b20_scratch_pad.temp_lsb & 0x0F)*625));
        if (frac==10000)
        {
                integer++;
            frac=0;
        };
        if (integer>=50)
        {
                integer=50;
                frac=0;
        };
        temp=0-(((int)integer)*10+frac/1000);
    }
    else
    {
    integer=(((__ds18b20_scratch_pad.temp_msb & 0x07)<<4)|(__ds18b20_scratch_pad.temp_lsb>>4));
        frac=((__ds18b20_scratch_pad.temp_lsb & 0x0F)*625);
        if (integer>=99)
        {
                integer=99;
                frac=0;
        };
        temp=(((int)integer)*10+frac/1000);
    };
}

//==============================================================
// Запись символа в CGRAM
//==============================================================
void define_char(unsigned char flash *pc,unsigned char char_code)
{
unsigned char j,a;
a=(char_code<<3)|0x40;
for (j=0; j<8; j++)
        {
                lcd_write_byte(a++,*pc++);
    };
}

//==============================================================
// Старт преобразования DS18B20
//==============================================================
void start_conv (void)
{
    while(w1_init()==0)
    {
                lcd_clear();
                lcd_putsf("1W ERROR!");
        relay(0);
                delay_ms(1000);
        }

        w1_init(); // 1W инициализация, старт записи
        w1_write(0xCC); // пропустить ROM
        w1_write(0x44); // старт преобразования
}

//==============================================================
// Очистка заданий
//==============================================================
void tsk_clr (void)
{
btn_tsk=0x00;
}

//==============================================================
// Управление реле
//==============================================================
void relay (char s)
{
    if (s)
    {
        PORTC &= ~0x20;    

    }
    else
    {
        PORTC |= 0x20;    
    };
}

//==============================================================
// Сравнение температур и выдача команды
//==============================================================
void compare (void)
{

        if ((temp2-hyst)>=temp) // Желаемая тем-ра больше измеренной - вкл. реле
        {
            relay(1);
    }
    else if (temp>=(temp2+hyst)) // Желаемая тем-ра меньше измеренной - откл. реле
    {
            relay(0);
    };
}

//==============================================================
// Старт таймера 1
//==============================================================
void t1_start (void)
{
TCNT1H=0x00; // Регистры таймера
TCNT1L=0x00;
TCCR1A=0x00; // Регистры контроля
TCCR1B=0x05; // F_osc./1024
}

//==============================================================
// Останов таймера 1
//==============================================================
void t1_stop (void)
{
TCCR1A=0x00; // Регистры контроля
TCCR1B=0x00;
PORTC &=0<<3;  // Здесь переводим порт С в логический ноль
}

//==============================================================
// Проверка на высокосный год
//==============================================================
char not_leap (void)
{
        int year;

    year=2000+(int)d.year;

    if (!(year%100))
            return (char)(year%400);
        else
            return (char)(year%4);
}

//==============================================================
// Проверки на 30-й, 29-й, 28-й день
//==============================================================
void m_check (void)
{
    if (d.day>30 && (d.month==4 || d.month==6 || d.month==9 || d.month==11)) d.day=30;
    else if(d.day>28 && d.month==2 && not_leap() ) d.day=28;
         else if (d.day>29 && d.month==2) d.day=29;
}
void y_check (void)
{
        if (d.month==2 && d.day>28 && not_leap() ) d.day=28;
        else if (d.month==2 && d.day>29) d.day=29;
}

//==============================================================
// Разрешение и запрет INT1
//==============================================================
void int1_en (void)
{
GICR |=0x80; //Разрешение прерывания по INT1
}
void int1_dis (void)
{
GICR &=0x7F; //Запрет прерывания по INT1
}

//==============================================================
// Получение позиции интервала в масиве
//==============================================================
char get_mas_pos (char n)
{
        return ((n-1)*3);
}

//==============================================================
// Получение и запись дневной раскладки в дневн. буфер с EEPROM
// day - номер дня; direction - направление (1-запись, 0-чтение)
//==============================================================
void get_day_map (char day, char direction)
{
    char i;
    eeprom char *eep;
    char *ram;

    eep=&intervals[day][0]; // Инициализируем поинтеры на
    ram=&day_int_map[0];    // EEPROM и RAM

    #asm ("cli")
    for(i=0;i<18;i++)
    {
            if (direction) // Direction == 1
        {
                *eep++=*ram++; // Запысываем 18 чисел в EEPROM
        }
        else           // Direction == 0
        {
                *ram++=*eep++; // Считываем 18 чисел во временной буфер (day_int_map)
        };
    };
    #asm ("sei")
}

//==============================================================
// Получение и запись значений температур в буферы тем-ры с EEPROM
// direction - направление (1-запись, 0-чтение)
//==============================================================
void e_get_temp (char direction)
{
    #asm ("cli")
    if(direction)
    {
        e_temp[0]=temp_low;
        e_temp[1]=temp_hi;
    }
    else
    {
            temp_low=e_temp[0];
        temp_hi=e_temp[1];
    };
    #asm ("sei")
};

//==============================================================
// Получение текущего интервала, возврат № интервала 0...6
//==============================================================
char get_interval (void)
{
        char n=1;
    char status=1;
    char hour,min;

    while(status)
    {
            hour=day_int_map[get_mas_pos(n)+1];
        min=day_int_map[get_mas_pos(n)+2];

        if(hour>t.hour) status=0;
        else if(hour==t.hour &&  min>t.min) status=0;
             else if(++n==7)
                  {
                      status=0;
                  };
    };
    return n-1;
}

//==============================================================
// Получение статуса температуры текущего интервала, возврат
// статуса (1-повыш., 0-пониж.), передача № интервала
//==============================================================
char get_temp_status (char interval)
{
    char day;
    eeprom char *eep;

        if(interval==0)       // Если значение интервала "0" то возвращает значение
    {                     // температуры "6"-го интервала для предыдущего дня
            day=wd_calc();
            if((--day)==255) day=6;
        eep=&intervals[day][15];
        return *eep;
    }
    else
    {
            return day_int_map[get_mas_pos(interval)];
    };
}


//               Процедуры вывода меню на дисплей
//           ----------------------------------------
//==============================================================
// Быстрый выбор температуры
//==============================================================
void temp_adj (char cur_pos)
{
char status;

if(a_frz==0) status='A';
else status='P';

sprintf (&lcd_buf[0],"t%+03i.%i%cC <%c>",temp2/10,abs(temp2%10),0x01,status);
lcd_clear();
lcd_gotoxy(2,0);
lcd_puts(&lcd_buf[0]);

if(cur_pos==1) lcd_gotoxy(2,0);
else if(cur_pos==2) lcd_gotoxy(12,0);

_lcd_ready();              // Вкл. мигающеее знакоместо
_lcd_write_data(0x0D);
}

//==============================================================
// Основное меню
//==============================================================
void m_menu (void)
{
sprintf(&lcd_buf[0],"MEH%c:        %u/4",0xB0,menu.smenu);
lcd_clear();
lcd_puts(&lcd_buf[0]);
lcd_gotoxy(0,1);

if (menu.smenu==1) lcd_putsf(str_11);
else if(menu.smenu==2) lcd_putsf(str_12);
         else if(menu.smenu==3) lcd_putsf(str_13);
          else if(menu.smenu==4) lcd_putsf(str_14);
}

//==============================================================
// Установка часов реального времени DS1307
//==============================================================
void clk_adj (char cur_pos)
{

rtc_get_time(&t.hour,&t.min,&t.sec);
rtc_get_date(&d.day,&d.month,&d.year);

sprintf(&lcd_buf[0],"%02u:%02u:%02u",t.hour,t.min,t.sec); // Верхняя строка
lcd_clear();
lcd_gotoxy(4,0);
lcd_puts(&lcd_buf[0]);

sprintf(&lcd_buf[0],"%02u-%p.-%02u",d.day,&m_map[d.month-1][0],d.year); // Нижняя строка
lcd_gotoxy(3,1);
lcd_puts(&lcd_buf[0]);

if(cur_pos==1) lcd_gotoxy(4,0);
else if(cur_pos==2) lcd_gotoxy(7,0);
     else if(cur_pos==3) lcd_gotoxy(3,1);
          else if(cur_pos==4) lcd_gotoxy(6,1);
               else if(cur_pos==5) lcd_gotoxy(11,1);

_lcd_ready();              // Вкл. мигающеее знакоместо
_lcd_write_data(0x0D);
}

//==============================================================
// Установка временных интервалов
//==============================================================
void interval_adj (char cur_pos)
{
char symbol,hour,min;

if(get_temp_status(menu.int_sel)) symbol=0x03;
else symbol=0x04;

hour=day_int_map[get_mas_pos(menu.int_sel)+1];
min=day_int_map[get_mas_pos(menu.int_sel)+2];

sprintf(&lcd_buf[0]," %p.\n %u: <%c> %02u:%02u",&d_map[menu.day_sel][0],menu.int_sel,symbol,hour,min);
lcd_clear();
lcd_puts(&lcd_buf[0]);

if(cur_pos==1) lcd_gotoxy(1,0);
else if(cur_pos==2) lcd_gotoxy(1,1);
     else if(cur_pos==3) lcd_gotoxy(5,1);
          else if(cur_pos==4) lcd_gotoxy(8,1);
               else if(cur_pos==5) lcd_gotoxy(11,1);

_lcd_ready();              // Вкл. мигающеее знакоместо
_lcd_write_data(0x0D);
}

//==============================================================
// Установка температур
//==============================================================
void e_temp_adj (char cur_pos)
{
sprintf(&lcd_buf[0],"%c:%+03i.%i%cC\n%c:%+03i.%i%cC",0x03,temp_hi/10,abs(temp_hi%10),0x01,0x04,temp_low/10,abs(temp_low%10),0x01);
lcd_clear();
lcd_puts(&lcd_buf[0]);

if(cur_pos==1) lcd_gotoxy(9,0);
else if(cur_pos==2) lcd_gotoxy(9,1);

_lcd_ready();              // Вкл. мигающеее знакоместо
_lcd_write_data(0x0D);
}

//==============================================================
//                            MAIN !!!
//==============================================================
void main(void)
{
init(); // Начальная инициализация

while (1)
{
while(!btn_tsk);

    //==========================================================
        // Вызов ручной уставки желаемой температуры
    // Переход по нажатию кн. "+" или "-"
    //==========================================================
    if ((btn_plus || btn_minus) && menu.deep==0)
    {
    int1_dis(); // Запрет внешнего прерывания
    menu.q_temp=1; // Флаг входа в меню быстр. доступа к температуре
    menu.state=1;
    PORTC |=1<<3;
    t1_start(); // Старт отсчета на выход

        while (!(btn_ok)) // Выполнять если "ОК" не нажата
        {
            #asm ("cli")
            if(btn_right) // Нажато "вправо"
            {
                    if(++menu.state==3) menu.state=1;
                tsk_clr(); 
                PORTC &=1<<3;  // переводим порт С3 в логическую 1 
                    t1_start();
                        temp_adj(menu.state);
            };

            if(btn_left) // Нажато "влево"
            {
                if(--menu.state==0) menu.state=2;
                tsk_clr(); 
                PORTC |=1<<3;
                    t1_start();
                        temp_adj(menu.state);
            };

                if (btn_plus) // Нажато "минус"
            {

                if(menu.state==1)
                {
                    if (temp2>=temp_max) {} // Если тем-ра >= t_max то ничего не делать
                    else // Если меньше то добавить 0.5С
                    {
                        temp2+=5;
                    };
                    if(a_frz) menu.q_temp=0;

                }
                else if(menu.state==2)
                     {
                         a_frz=1;
                         e_a_frz=a_frz;
                         menu.q_temp=0;
                     };

            tsk_clr(); 
            PORTC |=1<<3;
            t1_start();
                temp_adj(menu.state);
                };

            if (btn_minus) // Нажато "плюс"
            {

                if(menu.state==1)
                {
                    if (temp2<=temp_min) {} // Если тем-ра <= t_min то ничего не делать
                    else // Если больше то отнять 0.5С
                    {
                        temp2-=5;
                    };
                    if(a_frz) menu.q_temp=0;
                }
                else if(menu.state==2)
                     {
                         a_frz=0;
                         e_a_frz=a_frz;
                         menu.q_temp=1;
                     };
            tsk_clr();
            PORTC |=1<<3;
            t1_start();
                temp_adj(menu.state);
            };
            #asm ("sei")
        };
        // После нажатия кнопки "ОК"
        #asm ("cli")
        if(a_frz) af_temp=temp2;
        rtc_get_date(&d.day,&d.month,&d.year); // Считываем дату и загружаем карту дня
        get_day_map(wd_calc(),0);
        #asm ("sei")
            tsk_clr();
        t1_stop();
            int1_en(); // Разрешение внешнего прерывания
    };

    //==========================================================
    // Вход в главное меню
    // Переход по нажатию кн. "OK"
    //==========================================================
    if (btn_ok && menu.deep==0)
    {
            if(a_frz)
            {
            #asm ("cli")
                a_frz=0;
            e_a_frz=a_frz;
            tsk_clr();
            #asm ("sei")
            }
            else
            {
        int1_dis(); // Запрет внешнего прерывания
        tsk_clr(); 
        PORTC |=1<<3;
        t1_start();
        menu.deep=1;
        menu.smenu=1;
        m_menu();

            while (!(btn_ok)) // Выполнять если "ОК" не нажата
            {
                #asm ("cli")
                if(btn_right) // Нажато "вправо"
                {
                    if(++menu.smenu==5) menu.smenu=1;
                    tsk_clr();  
                    PORTC |=1<<3;
                    t1_start();
                    m_menu();
                };

                if(btn_left) // Нажато "влево"
                {
                    if(--menu.smenu==0) menu.smenu=4;
                    tsk_clr(); 
                    PORTC |=1<<3;
                    t1_start();
                
                    m_menu();
                };
                #asm ("sei")
            };
            // После нажатия кнопки "ОК"
            tsk_clr();
            t1_stop();
            menu.deep=2;
        }
    };

    //----------------------------------------------------------
    // "Дата/время"
    //----------------------------------------------------------
    if (menu.deep==2 && menu.smenu==1)
    {
            tsk_clr(); 
            PORTC |=1<<3;
        t1_start();
        menu.deep=3;
        menu.state=1;
        clk_adj(menu.state); // Вывод меню времени

            while (!(btn_ok))
        {
            clk_adj(menu.state);
            delay_ms(200);
                #asm ("cli")
            if(btn_right) // Нажато "вправо"
            {
                    if(++menu.state==6) menu.state=1;
                tsk_clr(); 
                PORTC |=1<<3;
                t1_start();
                clk_adj(menu.state);
            };

            if(btn_left) // Нажато "влево"
            {
                    if(--menu.state==0) menu.state=5;
                tsk_clr(); 
                PORTC |=1<<3;
                t1_start();
                clk_adj(menu.state);
            };

            if(btn_plus) // Нажато "плюс"
            {
                    if(menu.state==1) // *Часы
                {
                        if(++t.hour>23) t.hour=0;
                    t.sec=0;
                }
                else if(menu.state==2) // *Минуты
                     {
                              if(++t.min>59) t.min=0;
                         t.sec=0;
                     }
                     else if(menu.state==3) // *День
                          {
                              if(++d.day>31) d.day=1;
                              else if(d.day>30 && (d.month==4 || d.month==6 || d.month==9 ||d.month==11)) d.day=1;
                                   else if(d.day>29 && d.month==2) d.day=1;
                                        else if (d.day>28 && d.month==2 && not_leap() ) d.day=1;
                          }
                          else if(menu.state==4) // *Месяц
                               {
                                   if(++d.month==13) d.month=1;
                                   else m_check();
                               }
                               else if(menu.state==5) // *Год
                                    {
                                        if(++d.year==99) d.year=0;
                                        y_check();
                                    };

            tsk_clr();
            PORTC |=1<<3;
            t1_start();
            rtc_set_time(t.hour,t.min,t.sec);   //Установить время и дату
            rtc_set_date(d.day,d.month,d.year);
            clk_adj(menu.state); // Вывод меню времени
            };

            if(btn_minus) // Нажато "минус"
            {
                    if(menu.state==1) // *Часы

                {
                        if(--t.hour==255) t.hour=23;
                    t.sec=0;
                }
                else if(menu.state==2) // *Минуты
                     {
                         if(--t.min==255) t.min=59;
                         t.sec=0;
                     }
                     else if(menu.state==3) // *День
                          {
                              if(--d.day==0)
                              {
                                  d.day=31;
                                  if(d.month==4 || d.month==6 || d.month==9 || d.month==11) d.day=30;
                                  else if(d.month==2 && not_leap() ) d.day=28;
                                       else if(d.month==2) d.day=29;
                              };
                          }
                          else if(menu.state==4) // *Месяц
                               {
                                   if(--d.month==0) d.month=12;
                                   else m_check();

                               }
                               else if(menu.state==5) // *Год
                                    {
                                        if(--d.year==255) d.year=99;
                                        y_check();
                                    };

            tsk_clr();
            PORTC |=1<<3;
            t1_start();
            rtc_set_time(t.hour,t.min,t.sec);   //Установить время и дату
            rtc_set_date(d.day,d.month,d.year);
            clk_adj(menu.state); // Вывод меню времени
            };
            #asm ("sei")

        };
        t1_stop();
        menu.deep=0;
    };

    //----------------------------------------------------------
    // "График"
    //----------------------------------------------------------
    if (menu.deep==2 && menu.smenu==2)
    {
        tsk_clr(); 
        PORTC |=1<<3;
        t1_start();
        menu.deep=3;
        menu.state=1;
        menu.day_sel=1;
        menu.int_sel=1;

        get_day_map(menu.day_sel,0);
        interval_adj(menu.state);

            while(!(btn_ok))
        {
            #asm ("cli")
            if(btn_right) //Кнопка "вправо"
            {
                    if(++menu.state==6) menu.state=1;
                tsk_clr(); 
                PORTC |=1<<3;
                t1_start();
                interval_adj(menu.state);
            };

            if(btn_left) //Кнопка "влево"
            {
                if(--menu.state==0) menu.state=5;
                tsk_clr(); 
                PORTC |=1<<3;
                t1_start();
                interval_adj(menu.state);
            };

                if(btn_plus) //Кнопка "плюс"
            {
                    if(menu.state==1) // *День недели
                {
                    get_day_map(menu.day_sel,1);

                        if(++menu.day_sel==7) menu.day_sel=0;
                    get_day_map(menu.day_sel,0);
                }
                else if(menu.state==2) // *Интервал
                     {
                     if(++menu.int_sel==7) menu.int_sel=1;
                     }
                     else if(menu.state==3) // *Статус
                          {
                              day_int_map[get_mas_pos(menu.int_sel)]=1;
                          }
                          else if(menu.state==4) // *Часы
                               {
                                   if(menu.int_sel==6)
                                   {
                                   if(++day_int_map[get_mas_pos(menu.int_sel)+1]==24) day_int_map[get_mas_pos(menu.int_sel)+1] =23;
                                   }
                                   else if(++day_int_map[get_mas_pos(menu.int_sel)+1] < day_int_map[get_mas_pos(menu.int_sel+1)+1]) {}
                                        else
                                        {
                                            --day_int_map[get_mas_pos(menu.int_sel)+1];
                                        };
                               }
                               else if(menu.state==5) // *Минуты
                                    {
                                        if(++day_int_map[get_mas_pos(menu.int_sel)+2]==60) day_int_map[get_mas_pos(menu.int_sel)+2]=0;
                                    };
                        tsk_clr();
                        PORTC |=1<<3;
                t1_start();
                interval_adj(menu.state);
            };

            if(btn_minus) //Кнопка "минус"
            {
                if(menu.state==1) // *День недели
                {
                    get_day_map(menu.day_sel,1);

                        if(--menu.day_sel==255) menu.day_sel=6;
                    get_day_map(menu.day_sel,0);
                }
                else if(menu.state==2) // *Интервал
                     {
                     if(--menu.int_sel==0) menu.int_sel=6;
                     }
                     else if(menu.state==3) // *Статус
                          {
                              day_int_map[get_mas_pos(menu.int_sel)]=0;
                          }
                          else if(menu.state==4) // *Часы
                               {
                                   if(menu.int_sel==1)
                                   {
                                   if(--day_int_map[get_mas_pos(menu.int_sel)+1]==255) day_int_map[get_mas_pos(menu.int_sel)+1]=0;
                                   }
                                   else if( --day_int_map[get_mas_pos(menu.int_sel)+1] > day_int_map[get_mas_pos(menu.int_sel-1)+1] ) {}
                                        else
                                        {
                                            ++day_int_map[get_mas_pos(menu.int_sel)+1];
                                        };
                               }
                               else if(menu.state==5) // *Минуты
                                    {
                                        if(--day_int_map[get_mas_pos(menu.int_sel)+2]==255) day_int_map[get_mas_pos(menu.int_sel)+2]=59;
                                    };
                        tsk_clr(); 
                        PORTC |=1<<3;
                t1_start();
                interval_adj(menu.state);
            };
            #asm ("sei")
        };
        t1_stop();
        get_day_map(menu.day_sel,1); // Сохраняем карту для дня
        menu.deep=0;
    };

    //----------------------------------------------------------
    // "Температуры"
    //----------------------------------------------------------
    if (menu.deep==2 && menu.smenu==3)
    {
        tsk_clr();  
        PORTC |=1<<3;
        t1_start();
        menu.deep=3;
        menu.state=1;
        e_get_temp(0); // Считать тем-ры с EEPROM
        e_temp_adj(menu.state);

            while(!(btn_ok)) // Кнопка "ОК"
        {
            #asm ("cli")
            if(btn_left) // Кнопка "влево"
            {
                    if(--menu.state==0) menu.state=2;
                tsk_clr();   
                PORTC |=1<<3;
                        t1_start();
                e_temp_adj(menu.state);
            };

            if(btn_right) // Кнопка "вправо"
            {
                if(++menu.state==3) menu.state=1;
                tsk_clr(); 
                PORTC |=1<<3;
                        t1_start();
                e_temp_adj(menu.state);
            };

            if(btn_plus) // Кнопка "плюс"
            {
                    if(menu.state==1) // Температура повышеная
                {
                        if(temp_hi>=temp_max) {}
                    else temp_hi+=5;
                }
                else if(menu.state==2) // Температура пониженая
                     {
                         if(temp_low>=temp_max) {}
                         else temp_low+=5;
                     };

                tsk_clr();
                PORTC |=1<<3;
                        t1_start();
                e_temp_adj(menu.state);
            };

            if(btn_minus) // Кнопка "минус"
            {
                if(menu.state==1) // Температура повышеная
                {
                    if(temp_hi<=temp_min) {}
                    else temp_hi-=5;
                }
                else if(menu.state==2) // Температура пониженая
                     {
                         if(temp_low<=temp_min) {}
                         else temp_low-=5;
                     };

                tsk_clr(); 
                PORTC |=1<<3;
                        t1_start();
                e_temp_adj(menu.state);
            };
            #asm ("sei")

        };
        t1_stop();
        e_get_temp(1); // Записать тем-ры в EEPROM
        menu.deep=0;
    };

    //----------------------------------------------------------
    // "Выход"
    //----------------------------------------------------------
    if (menu.deep==2 && menu.smenu==4)
    {
    menu.deep=0; // Сброс положения меню
    menu.smenu=0;
    menu.q_temp=0; // Сброс флага уст. быстрой температуры
    rtc_get_date(&d.day,&d.month,&d.year);
    get_day_map(wd_calc(),0);
    int1_en(); // Разрешение внешнего прерывания
    };
};

}