/*****************************************************
CodeWizardAVR V1.25.9 Professional
Chip type           : ATmega8
Program type        : Application
Clock frequency     : 1,000000 MHz
Memory model        : Small
External SRAM size  : 0
Data Stack size     : 256

Таймер 2 - системные часы
Режим работы CTC top=OCR2
Тактовая частота таймера 125,000 кГц (прескалер 8)
Таймер считает до 125
Прерывание по совпадению - каждую миллисекунду
*****************************************************/

#include <mega8.h>

#define KEY_PORT PORTC // Порт, к которому подключены кнопки
#define KEY_PORT_SET_DIRECTION DDRC // установка направлений линий порта
#define KEY_PORT_DIRECTION 0b00000000 // сами направления
#define KEY_PORT_PULLUP 0b00111110 // подтягивающие резисторы для кнопок и датчиков
#define KEY_PINS PINC // чтение состояния выводов, к которым подключены кнопки
#define KEY_MASK 0b00111100 // маска для кнопок и концевика "hook"
#define KNOB_MASK 0b00011100 // маска только для кнопок
#define KEY_SELECT 0b00000100 // номер кнопки и одновременно маска для кнопки SELECT
#define KEY_UP 0b00001000 // номер кнопки и одновременно маска для кнопки UP
#define KEY_DOWN 0b00010000 // номер кнопки и одновременно маска для кнопки DOWN
#define KEY_HOOK 0b00100000 // номер кнопки и одновременно маска для концевика hook
#define KEY_PUSH_TIME 500  // это время в "тиках" таймера 2 сколько надо продержать кнопку нажатой,
                        // чтобы начался автоповтор.
#define KEY_AUTO_TIME 30 //это время в "тиках" таймера 2 - интервал автоповтора  
#define KEY_STABLE_TIME 10 // время в тиках таймера 2 , в течение которого номер нажатой кнопки один и тот же

volatile unsigned char key_state = 0; // состояние кнопок - величина, зависящая от того, какие кнопки нажаты
volatile unsigned char key_done = 0; // флаг. 1 - нажатие кнопки обработано 0 - не обработано

// Timer 2 output compare interrupt service routine
interrupt [TIM2_COMP] void timer2_comp_isr(void)
{
static char key_time = KEY_STABLE_TIME; // счётчик времени в течение которого состояние кнопок стабильно
static char key_number_old = 0; // предыдущее состояние кнопок
static char key_number_new = 0; // новое  состояние кнопок
static char key_push_time = KEY_PUSH_TIME;  // это время в "тиках" сколько надо продержать кнопку нажатой,
                        // чтобы начался автоповтор.
static char key_auto_time = KEY_AUTO_TIME; //это время в "тиках" - интервал автоповтора 
static unsigned int tmp_counter = 0; // временный счётчик тиков
 
//PORTB.6 = 1; // для отладки
// сюда попадаем каждую миллисекунду

// считываем состояние кнопок
key_number_new = KEY_MASK & (~KEY_PINS);
// проверяем, изменилось ли состояние кнопок
if(key_number_new == key_number_old)
{
    // состояние кнопок не изменилось
    // проверяем, сколько времени длится стабильное состояние кнопок
    if(key_time)
    {
        // заданное время не вышло
        key_time--; // уменьшаем счётчик времени
    }
    else
    {
        // состояние кнопок стабильно в течение времени KEY_STABLE_TIME
        //PORTB.6 = 1; // для отладки
        key_state = key_number_new; // сохраняем состояние кнопок
        // проверяем, какое состояние кнопок. какие-то нажаты или отпущены все
        if(key_state & KNOB_MASK)
        {
            // что-то нажато. проверяем, как долго нажато
            if(key_push_time)
            {
                //недостаточно долго
                key_push_time--; // уменьшаем счётчик времени
            }
            else
            {
                // продержали достаточно долго, чтобы начался автоповтор
                // выдерживаем интервал автоповтора
                if(key_auto_time)
                {
                    // интервал не зекончился
                    key_auto_time--;  // уменьшаем счётчик
                }
                else
                {
                    key_auto_time = KEY_AUTO_TIME; // устанавливаем счётчик
                    if(key_done == 1)
                    {
                        key_done = 0; // сбрасываем флаг кнопка обработана
                    }    
                }
            }    
        }
        else
        {
            // ни одна из кнопок не нажата
            if(key_done == 1)
            {
            key_done = 0; // сбрасываем флаг кнопка обработана
            }
        }
    }
}
else
{
    // состояние кнопок изменилось
    key_time = KEY_STABLE_TIME; // устанавливаем счётчик
    key_push_time = KEY_PUSH_TIME; // устанавливаем счётчик
    key_number_old = key_number_new; // присваеваем новое значение старому
}
//PORTB.6 = 0; // для отладки
}

// Analog Comparator interrupt service routine
interrupt [ANA_COMP] void ana_comp_isr(void)
{
// Place your code here

}


// ADC interrupt service routine
interrupt [ADC_INT] void adc_isr(void)
{
// Read the AD conversion result
adc_data=ADCW;

}



void main(void)
{

unsigned char key_number = 0; //номер кнопки
unsigned char key_complete = 0; //флаг. 1 - обработка кнопки завершена  0 - нет


startInit(); // инициализация после включения/сброса


// Global enable interrupts
#asm("sei")

while (1)
    {
        // проверяем состояние кнопок
        #asm("cli") // запрещаем все прерывания прежде чем менять переменную...
        key_number = key_state; // считываем номер нажатой кнопки
        key_complete = key_done; // считываем состояние кнопки
        #asm("sei") // разрешаем прерывания        
        
        // проверяем, нажата ли какя кнопка
        if(key_number & KNOB_MASK)
        {
            // нажата какая-то из кнопок 
            // проверяем, было ли обработано нажатие кнопки
            if(key_complete == 0)
            {
                // нажатие кнопки ещё не обработано 
                // проверяем, какая из кнопок нажата и обрабатываем её
                switch(key_number & KNOB_MASK)
                {
                    case KEY_SELECT:
                        // нажата кнопка "SELECT"
                        executeKeySelect(); // Обработка нажатия кнопки "SELECT"
                        break;
                    case KEY_UP:
                        // нажата кнопка "UP"
                        executeKeyUp(); // Обработка нажатия кнопки "UP"
                        break;
                    case KEY_DOWN:
                        executeKeyDown(); // Обработка нажатия кнопки "DOWN"                    
                        break;
                    default:
                        break;
                }
                // кнопка обработана
                #asm("cli") // запрещаем все прерывания прежде чем менять переменную...
                key_done = 1; // кнопка обработана
                #asm("sei") // разрешаем прерывания
            }
        }
        // с кнопками разобрались
        // проверяем состояние концевого выключателя
        if(key_number & KEY_HOOK)
        {
            // концевой выключатель замкнут - паяльник на подставке
            executeKeyOnHook(); // Обработка замкнутого концевого выключателя
        }
        else
        {
            // концевой выключатель разомкнут - паяльника на подставке нет
            executeKeyOffHook();
        }
    }
