/*****************************************************
Chip type           : ATmega8
Program type        : Application
Clock frequency     : 8,0000 MHz
Memory model        : Small
External SRAM size  : 0
Data Stack size     : 256
*****************************************************/

#include <mega8.h>
#include <stdio.h>
#include <math.h>
#include <delay.h>

// Объявляем порт к которому подсоединен LCD дисплей
#asm
   .equ __lcd_port=0x12 ; PORTD 
#endasm
#include <lcd.h>
//-----------------------------------------------------
#define ADC_VREF_TYPE 0xC0             // ????? для чего?

#define	KEY1_DOWN	(PINC.1 == 0)  // 24 нога (-)
#define	KEY2_DOWN	(PINC.2 == 0)  // 25 нога (P3)
#define	KEY3_DOWN	(PINC.3 == 0)  // 26 нога (SETUP)
#define	KEY4_DOWN	(PINB.5 == 0)  // 19 нога (Р2)
#define	KEY5_DOWN	(PINB.4 == 0)  // 18 нога (+)
#define	KEY6_DOWN	(PINB.3 == 0)  // 17 нога (Р1)

#define BEEP            PORTB.0        // 14 нога Пищалка

unsigned char lcd_buffer1[9] = "        ";  // Объявляем буфер1 для записи в дисплей 8 символов (+1 в конце служебный)
unsigned char lcd_buffer2[9] = "        ";  // Объявляем буфер2 для записи в дисплей 8 символов (+1 в конце служебный)
volatile unsigned int adc_data = 0;         // Преобразованный из аналога в цифру КОД
volatile unsigned int T;                    // Температура текущая  ????
volatile unsigned int ee_tmprSet = 0;       // Установленная температура для поддержания (выставленная)
volatile unsigned int T_prev = 0;           // Температура предыдущая ??????????
volatile unsigned int T_now = 0;            // Температуры сейчас   ?????
volatile unsigned int T_disp = 0;           // Температура на дисплей

volatile int ReadKey = 0;                   // Нажатая кнопка 
volatile int KeyDelay = 0;                  // Задержка нажатия кнопки
volatile int Mode = 0;                      // Режим работы
                                            // Mode = 0 - нормальная работа
                                            // Mode = 1 - запись тепмературы в ячейку 1 памяти (в переменную)
                                            // Mode = 2 - запись тепмературы в ячейку 2 памяти
                                            // Mode = 3 - запись тепмературы в ячейку 2 памяти
                                            // Mode = 4 - запись тепмературы в ячейку 2 памяти

volatile int program = 1;                   // Введенная программная температура (из какой ячейки)
//-----------------------------------------------------------------------
unsigned int i=1, T0 = 3, Kp = 70;  // Коэффициенты нужно подбирать!
volatile int pwm_val = 0;           // Для хранения величины ШИМ в 1/1024
//-----------------------------------------------------------------------
eeprom unsigned int T_prog[3] = { 300, 150, 400 };   // Температурные режимы в EEPROM
unsigned int T_set[3];                               // Объявляем массив для температур

// Объявляем функции и прерывания используемые в программе
static void avr_init();                              // Инициализация контроллера
void green(void);                                    // ВКЛ зеленый светодиод
void red(void);                                      // ВКЛ красный светодиод 
void my_beep(void);                                  // Пикнуть :)
unsigned int read_adc(unsigned char adc_input);      // Чтение ?????


// ADC interrupt service routine                     // Если прерывание ADC_INT (изменилось состояние входа АЦП)
interrupt [ADC_INT] void adc_isr(void)
{
adc_data=ADCW;                                       // Читаем преобразованный цифровой код
}

// Read the AD conversion result with noise canceling
unsigned int read_adc(unsigned char adc_input)       // Чтение ?????
{
ADMUX=adc_input|ADC_VREF_TYPE;                       // Настройка ШИМ ??????????
#asm
    in   r30,mcucr
    cbr  r30,__sm_mask
    sbr  r30,__se_bit | __sm_adc_noise_red
    out  mcucr,r30
    sleep
    cbr  r30,__se_bit
    out  mcucr,r30
#endasm
return adc_data;
}


// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)    // Обработка прерывания по переполнению ТАЙМЕРА 0
{
        TCNT0=0x00;                               // Сброс регистра ТАЙМЕРА0 в 0
        if (ReadKey == 0) {                       // Если нажатие кнопоки уже обработано
	        if (KeyDelay == 0) {              // Если KeyDelay == 0, то можно нажимать опять 		
	                KeyDelay = 0;
	                if (KEY1_DOWN)	{         // Нажата кнопка 1
	                        ReadKey = 1;
	                        my_beep();
	                }
	                if (KEY2_DOWN)	{         // Нажата кнопка 2
	                        ReadKey = 2;
	                        my_beep();
	                }
	                if (KEY3_DOWN)	{         // Нажата кнопка 3
	                        ReadKey = 3;
	                        my_beep();
	                }
	                if (KEY4_DOWN)	{         // Нажата кнопка 4
	                        ReadKey = 4;
	                        my_beep();
	                }
	                if (KEY5_DOWN)	{         // Нажата кнопка 5  (+)
	                        ReadKey = 5;
	                        my_beep();
	                }
	                if (KEY6_DOWN)	{         // Нажата кнопка 6
	                        ReadKey = 6;
	                        my_beep();
	                }

                        if (ReadKey) {             // Если была нажата хотя бы одна кнопка
                                KeyDelay = 10;     // Ставим задержку 10
                        }
                } else {                           // В противном случае 
			KeyDelay--;                // KeyDelay = KeyDelay - 1
		}
	}    // if  
	i--;                                       // i = i - 1  ????????? Это что?
	    
	if (i==0) {                                // ???????
                T_disp = T;                        // Текущуюю температуру на дисплей
	        i=15;                              // ????????? Это что и зачем?
	}
}

void main(void)
{
        avr_init();                    // Первоначальная инициализация процессора
        
        adc_data=read_adc(0);          // ?????        
        T_now = adc_data / 2;          // почему на 2 ?
        //T_now = adc_data;
        T_prev = T_now;

while (1)                                // Бесконечный цикл
{
        adc_data=read_adc(0);            // ?????????????????
        //T_now = (1000 - adc_data) / 2;
        //T_now = adc_data; 
        T_now = adc_data / 2;            // почему на 2 ?
        
        
        if ((T_now < (T_prev - 4)) || (T_now > (T_prev + 4))) {      // ????????
                T = T_prev;
        } else {
                T = T_now;
                T_prev = T_now;
        }
	                        
        pwm_val = Kp * (ee_tmprSet - T + T0);          // Расчет величины ШИМ от 0 до 1024
        
        if (pwm_val > 1023) pwm_val = 1023;
        if (pwm_val < 0) pwm_val = 0;
        
        if ((T > (ee_tmprSet - 10)) && (T < (ee_tmprSet + 10))) {   // Если попали в +/- 10 градусов
                green();                                            // Вкл. зеленый светодиод
        } else {                                 
                red();                                              // Вкл. красный светодиод
        }
                                                
        if (ReadKey == 5)                       // была нажата кнопка (+) прибавляем 10 градусов
        {       
                if (Mode == 1 || Mode == 2 || Mode == 3) {
                        T_set[Mode - 1] = T_set[Mode - 1] + 10; 
                } else {
                        ee_tmprSet = ee_tmprSet + 10;
                }
                ReadKey = 0;                
        }
        if (ReadKey == 1)                       // была нажата кнопка (-) вычитаем 10 градусов
        {
                if (Mode == 1 || Mode == 2 || Mode == 3) {
                        T_set[Mode - 1] = T_set[Mode - 1] - 10; 
                } else {
                        ee_tmprSet = ee_tmprSet - 10;
                }
                ReadKey = 0;
        }
        if (ReadKey == 3)                       // была нажата кнопка SETUP
        {
                Mode++;                         // Mode = Mode + 1
                if (Mode == 4) {                // Если уже введены 3 температуры контроля
                        #asm("cli")             // Запретить все прерывания
                        T_prog[0] = T_set[0];   // Переписать в ячейки ЕЕПРОМ
                        T_prog[1] = T_set[1];
                        T_prog[2] = T_set[2];
                        #asm("sei")             // Разрешить все прерывания
                        Mode = 0;
                }
                ReadKey = 0;
        }
        
        if (Mode == 1 || Mode == 2 || Mode == 3) {                 // При записи в память 
                sprintf(lcd_buffer1, "Tp %i:   ", Mode);           // подготовка буферов для дисплея
                sprintf(lcd_buffer2, "%03i     ", T_set[Mode - 1]);        
        }
        
        
        if (ReadKey == 6)                    // была нажата кнопка Р1 
        {
                ee_tmprSet = T_set[0];       // достаем Т контроля из 1 ячейки
                program = 1;                 // Для дисплея (Т контроля из 1 ячейки )
                ReadKey = 0;
        }
        if (ReadKey == 4)                    // Была нажата кнопка Р2
        {
                ee_tmprSet = T_set[1];       // Достаем Т контроля из 2 ячейки
                program = 2;                 // Для дисплея (Т контроля из 2 ячейки )
                ReadKey = 0;
        }        
        if (ReadKey == 2)                    // была нажата кнопка Р3 (достаем Т из 3 ячейки)
        {
                ee_tmprSet = T_set[2];       // Достаем Т контроля из 3 ячейки
                program = 3;                 // Для дисплея (Т контроля из 3 ячейки )
                ReadKey = 0;
        }        
        
        if (Mode == 0) {
                sprintf(lcd_buffer1, "Tc=%03i T", T_disp);                 // При нормальной работе 
                sprintf(lcd_buffer2, "p=%03i P%i", ee_tmprSet, program);   // подготовка буферов для дисплея
                //sprintf(lcd_buffer2, "p=%04i  ", abs(T_now[1] - T_now[0]));
        }
        
        lcd_gotoxy(0, 0);                      // Курсор в 0,0
        lcd_puts(lcd_buffer1);                 // Вывод буфера 1 на дисплей (символы 1-8)
        lcd_gotoxy(0, 1);                      // Курсор в 0,0
        lcd_puts(lcd_buffer2);                 // Вывод буфера 2 на дисплей (символы 9-16)
        
        
        OCR1AH = (unsigned char)(pwm_val>>8);  // Загрузка регистров таймера\счетчика для ШИМ       
        OCR1AL = (unsigned char)pwm_val;       // Загрузка регистров таймера\счетчика для ШИМ
        
      };
}

void green(void) {                             // ВКЛ зеленый светодиод
        PORTC.4 = 0;
        PORTC.5 = 1;
}

void red(void) {                               // ВКЛ красный светодиод
        PORTC.4 = 1;
        PORTC.5 = 0;
}
void my_beep(void) {                           // Пикнуть :)
        BEEP = 1;
        delay_ms(10);
        BEEP = 0;
}

static void avr_init() {
// Input/Output Ports initialization
// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=Out Func0=Out 
// State7=T State6=T State5=P State4=P State3=P State2=P State1=0 State0=0 
PORTB=0x3C;
DDRB=0x03;

// Port C initialization
// Func6=In Func5=Out Func4=Out Func3=In Func2=In Func1=In Func0=In 
// State6=T State5=0 State4=1 State3=P State2=P State1=P State0=T 
PORTC=0x1E;
DDRC=0x30;

// 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;

// Инициализация таймера/счетчика 0 
TCCR0=0x05;  // Clock source: System Clock
             // Clock value: 3,600 kHz
TCNT0=0x00;

// Инициализация таймера/счетчика 1 
// Clock source: System Clock
// Clock value: 125,000 kHz
// Mode: Ph. correct PWM top=03FFh
// OC1A output: Non-Inv.
// 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=0x83; 
TCCR1B=0x03;  
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;

// Инициализация внешних прерываний
// INT0: Off
// INT1: Off
MCUCR=0x00;                          // Внешние прерывания отключить

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x01;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;

// ADC initialization
// ADC Clock frequency: 115,200 kHz
// ADC Voltage Reference: int
ADMUX=ADC_VREF_TYPE;
ADCSRA=0x8E;


lcd_init(16);                  // Инициализация LCD дисплея 16 символов


if (KEY6_DOWN) {               // Если при включении нажата кнопка KEY6_DOWN установка температур в 0
        T_set[0] = 0;
        T_set[1] = 0;
        T_set[2] = 0; 
} else {                       // Если не нажата кнопка KEY6_DOWN то температуры из EEPROM
        T_set[0] = T_prog[0];
        T_set[1] = T_prog[1];
        T_set[2] = T_prog[2];
}
ee_tmprSet = T_set[0];         // Запись температуры контроля из ячейки 1 

// Global enable interrupts
#asm("sei")                    // Разрешить все прерывания

}
