/*******************************************
Code: adver (http://radiokot.ru/forum/viewtopic.php?p=3112844#p3112844)
Ported to AVR: Mickle T.
Edited: serg-el
*******************************************
Compiler                : WinAVR
System Clock            : 20 MHz
*******************************************/
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
//******************************************
// Служебные переменные, константы и алиасы
//******************************************
#define BASIS 14            // 16 bits of fractional part (8+16bit total ~0,873553171 uV resolution)
#define DIVISOR 16384  
#define Key_Up()   !(PINB & ~(1<<PB1))		// Нажата клавиша "Вверх"?
#define Key_Down() !(PINB & ~(1<<PB2))		// Нажата клавиша "Вниз"?
#define Beep_On (PORTB |= (1 << 5)) 		// Зуммер включен
#define Beep_Off (PORTB &= ~(1 << 5))		// Зуммер выключен

// [256/OCR0A] - коэффициент усиления масштабного ОУ с ШИМ ООС 
// 256/174=1,47126 >>  *6.823=10V
// Value задаётся в формате с фиксированной точкой 8.16
// Если напряжение стабилитрона равно U_Zener, то Value = (U_Zener/10.000 V)*PWMmax*DIVISOR (BASISmax)
unsigned long Value = 11447930UL;            
// Внутренние переменные MASH
volatile unsigned char ValInt;                        // Целая часть Value, ~174
volatile unsigned int ValFrac;                       // Дробная часть Value
volatile unsigned int q[3]={1,0,0}; 	                // Latch outputs 16bit
volatile unsigned char c[3][3]={{0,0,0}, {0,0,0}, {0,0,0}};   // Carry flip flops 1bit
volatile unsigned int d[3];    						  		// Adder output / latch data input 32bit

volatile unsigned int temp = 0;  						// переменная для счётчика задержек 
volatile unsigned int temp2 = 0;						// переменная 2 для счётчика задержек 
volatile unsigned int temp3 = 0;						// переменная 3 для проверки алгоритма 



char key_up_press_temp = 0;
char key_down_press_temp = 0;
char key_up_and_down_press_temp = 0;

char key_up_press_on = 0;
char key_down_press_on = 0;
char key_up_and_down_press_on = 0;

char key_up_press_run = 0;
char key_down_press_run = 0;
char key_up_and_down_press_run = 0;

      

         
//******************************************
// Конфигурация ИОН, сохраняемая в EEPROM
EEMEM unsigned long CFG_CAL_VALUE = 11447930UL;
//******************************************
// Инициализация портов, таймера и пр.
//****************************************** 
void Init(void) 
{
    DDRB |= ((1 << PB0) | (1 << PB5));                 // PWM активен, Beep активен вместо reset
    DDRB &= ~((1<<PB1) | (1<<PB2));     // Пины кнопок на ввод ,
	PORTB |= (1 << PB1) | (1 << PB2);   // Внутренние резисторы на +5 В, установка битов

    ValInt = Value >> BASIS;
    ValFrac = Value % DIVISOR;
    OCR0A = ValInt-1;          			// Начальное значение регистра сравнения

    TCCR0A = (1<<COM0A1) | (1<<COM0A0) | (0<<COM0B1) | (0<<COM0B0) | (1<<WGM01) | (1<<WGM00);       
    // Set OC0A on Compare Match, BOTTOM, Normal mode, OC0B disconnected, Fast PWM Mode (WGM = 3) 
    //TCCR0B = (0<<WGM02) | (0<<CS02) | (1<<CS01) | (0<<CS00);    // Prescaler CK/8 
	TCCR0B = (0<<WGM02) | (0<<CS02) | (0<<CS01) | (1<<CS00);    // Prescaler off

    TIMSK = (1 << TOIE0);  				// Enable overflow interrupt
    //sei(); // выключил здесь, включается после загрузки из памяти
}
//******************************************
// Обработчик прерываний таймера 
//****************************************** 
ISR (TIM0_OVF_vect)
{
	int i=0;  //инициализация переменной
    // Carry flip-flops
    c[1][1] = c[1][0];  c[2][2] = c[2][1]; c[2][1] = c[2][0];
    // Adders
    d[0] = (q[0]+ValFrac) % DIVISOR;
    d[1] = (q[1]+d[0]) % DIVISOR;
    d[2] = (q[2]+d[1]) % DIVISOR;
    // Carries
    c[0][0] = (q[0]+ValFrac) >> BASIS;
    c[1][0] = (q[1]+d[0]) >> BASIS;
    c[2][0] = (q[2]+d[1]) >> BASIS;
    // Latch
    for (i=0; i<3; i++) q[i] = d[i];
    OCR0A = ValInt - 1 
            + c[0][0]
            + c[1][0] -   c[1][1]
            + c[2][0] - 2*c[2][1] +   c[2][2];
	temp ++; 									 // прибавляем к счетчику задержек по 1 каждый тик
	temp2 ++; 									 // прибавляем к счетчику задержек по 1 каждый тик
}
//******************************************
// Обновление переменных и решистров
//****************************************** 
void Update(void) 
{
    //cli();
    ValInt = Value >> BASIS;
    ValFrac = Value % DIVISOR;
    //OCR0A = ValInt-1;          
    //sei();
}
//******************************************
// Загрузка конфигурации ИОН из EEPROM,
// если при старте НЕ нажаты обе клавиши
//******************************************
void ConfigLoad (void)
{
    if (!Key_Up() && !Key_Down())
	 {
	   Beep_On;
	  // _delay_ms(1500);
	   eeprom_read_block(&Value,&CFG_CAL_VALUE,4);
      // _delay_ms(3000); 
	   Beep_Off;	
	 
		ValInt = Value >> BASIS;
	    ValFrac = Value % DIVISOR;
	    OCR0A = ValInt-1;          			// Начальное значение регистра сравнения
	 };
	  sei(); // включение прерываний
}
//******************************************
// Обновление конфигурации в EEPROM
//******************************************
void ConfigUpdate (void)
	{
		cli(); //запрет прерываний на время доступа к eeprom
        eeprom_update_block(&Value,&CFG_CAL_VALUE,4);
        sei(); // включить прерывания заново
	}
//******************************************
int main( void ) 
 {
   // _delay_ms (500); //пауза при запуске 
    Init();
	ConfigLoad(); //сначала инициализация, потом загрузка eeprom (если не нажаты клавиши)

    while(1) 
	{
		
		if (Key_Up() && key_up_press_temp == 0 )
	  	{ 
			temp2 = 0;	
			key_up_press_temp = 1; 
		}
	
		
		if (key_up_press_temp == 1 && temp2 == 500 && Key_Up())
		{ 
			key_up_press_on = 1;
		}
			

		if (Key_Down() && key_down_press_temp == 0 )
	  	{ 
			temp2 = 0;
			key_down_press_temp = 1;
		}
		
		if (key_down_press_temp == 1 && temp2 == 500 && Key_Down())
		{ 
			key_down_press_on = 1;
		}
			
	  
		if (Key_Down() && Key_Up() && key_up_and_down_press_temp == 0 )
	  	{ 
			temp2 = 0;
			key_up_and_down_press_temp = 1;
		}
		
		if (key_up_and_down_press_temp == 1 && temp2 == 400 && Key_Up() && Key_Down())
		{
			key_up_and_down_press_on = 1;
			key_up_press_temp = 0;
			key_down_press_temp = 0;
		}
			
		


	  if (key_up_and_down_press_on == 1 && key_up_and_down_press_run == 0 ) 
	    {
	    	Beep_On;	 
			ConfigUpdate(); 
			temp = 0;
			key_up_and_down_press_run = 1;
		}
	  	   
      if (key_up_press_on == 1 && key_up_press_run == 0 ) 
		{
	    	Beep_On;
            Value ++;
            Update(); 
			temp = 0;
			key_up_press_run = 1;
		}

      if (key_down_press_on == 1 && key_down_press_run == 0 )
		{
	        Beep_On;
            Value --;
            Update(); 
			temp = 0;
			key_down_press_run = 1;			
		}

	if (key_up_and_down_press_run == 1  && temp == 15000) 
		{ 
		Beep_Off;
		key_up_and_down_press_run = 0;
		key_up_and_down_press_on = 0;
		key_up_and_down_press_temp = 0;
		}

	if (key_up_press_run == 1  && temp == 2000) 
		{ 
		Beep_Off;
		key_up_press_run = 0;
		key_up_press_on = 0;
		key_up_press_temp = 0;
		}

	if (key_down_press_run == 1  && temp == 2000) 
		{
		Beep_Off;
		temp=0;
		key_down_press_run = 0;
		key_down_press_on =0;
		key_down_press_temp =0;
		}


	if (temp == 40000)
		{ 
		temp = 0;
		}

	if (temp2 == 1000)
		{ 
		temp2 = 0;
		}
	temp3++; //проверка работы ... 57 тактов temp2 на 1 temp3
	if (temp3 == 1000)
		{ 
		temp3 = 0;
		}
	
   	};
 }
