/*
Cyclic timer.
 This is an example of using the Arduino Pro Mini serial segment indicator / encoder unit.
 Connection scheme: example.pdf in the Design Files section.
 The timer counts the time T1 and T2.
 Time T1 is indicated by a low level at the output of RELT1 and a high level at the output of RELT2.
 Time T2 is indicated by the LED2 (RED) on, the low level at the output of RELT2 and the high level at the output of RELT1.
 The outputs RELT1 and RELT2 are designed to control external devices.
 After switching on, the timer is in standby mode and the time T1 is displayed.
 The timer is started by a short press of the encoder button. Countdown T1 begins. After the end of time T1, the countdown of time T2 begins.
 After the end of time T2, the timer goes into standby mode or repeats the cycle T1, T2 if cyclic mode is enabled.
 The timer is interrupted by a long press of a button.
 The timer is interrupted by a long press of a button.
 The cyclic mode is turned on (BLUE LED0 ON) / turned off (BLUE LED0 OFF) by turning the encoder to the right.
 Turning the encoder to the left turns on (GREEN LED1 ON) / turns off (GREEN LED1 OFF) the way the time is displayed. If LED1 is on, the time is displayed by increasing,
 if LED1 is off, the time is indicated by decreasing.
 From standby mode, the timer switches to the setting mode for times T1 and T2 by a long press of a button. Hours, minutes, seconds of time are set sequentially
 T1 and hours, minutes, seconds of time T2. The selected parameter is set by turning the encoder; the parameter is switched by pressing a button. Parameters are remembered
 in the non-volatile memory of the controller.
 The program is developed in the WINAVR environment.


 Циклический таймер.
 Это пример использования блока последовательного сегментного индикатора/энкодера c Arduino Pro Mini.
 Схема подключения: example.pdf в разделе Design Files.
 Таймер отсчитывает время T1 и Т2.
 Время Т1 индицируется низким уровнем на выходе RELT1 и высоким уровнем на выходе RELT2.
 Время Т2 индицируется включенным светодиодом LED2(RED), низким уровнем на выходе RELT2 и высоким уровнем на выходе RELT1.
 Выходы RELT1 и RELT2 предназначены для управления внешними устройствами.
 После включения таймер находится в режиме ожидания и на индикаторе отображается время T1.
 Запуск таймера выполняется коротким нажатием кнопки энкодера. Начинается отсчет времени T1. После окончания времени T1 начинается отсчет времени T2. 
 После окончания времени T2 таймер переходит в режим ожидания или повторяет цикл T1,T2 если разрешен циклический режим.
 Работа таймера  прерывается продолжительным нажатием кнопки. 
 Циклический режим включается(BLUE LED0 ON)/выключается(BLUE LED0 OFF) поворотом энкодера вправо.
 Поворот энкодера влево включает(GREEN LED1 ON)/выключает(GREEN LED1 OFF)  способ отображения времени.  Если LED1 включен, время отображается увеличением,
 если LED1 выключен, время отображается уменьшением.
 Из режима ожидания таймер переходит в режим установки времен T1 и T2 продолжительным нажатием кнопки. Последовательно устанавливаются часы, минуты, секунды времени
 T1 и часы, минуты, секунды времени T2. Выбранный параметр устанавливается поворотом энкодера, переключение параметра выполняется нажатием кнопки.  Параметры запоминаются 
 в енергонезависимой памяти контроллера.  
 Программа разработана в среде WINAVR. 
 
*/

#define MAIN_GLOBALS
#include "include.h"

const char locn[]={0,1,2,0,1,2};

/*
// indicator with common anode
const char font[]=
{
 0x28,
 0xaf,
 0x98,
 0x8a,
 0x0f,
 0x4a,
 0x48,
 0xae,
 0x08,
 0x0a,
 0xff,
 0xdf
};

const char byte znk[]={0x08,0x04,0x02,0x01};
*/


// ******* indicator with common cathode **********
const char font[]= 
{
 0xd7,  // 0
 0x50,  // 1
 0x67,  // 2
 0x75,  // 3
 0xf0,  // 4
 0xb5,  // 5
 0xb7,  // 6
 0x51,  // 7
 0xf7,  // 8
 0xf5,  // 9
 0x00,  // 10 all segments off
 0x20   // 11 this: -
};
const char znk[]={0xf7,0xfb,0xfd,0xfe};

ISR(TIMER0_COMPA_vect) // output to indicator
{
 word shift;
 byte i;
 shift=znk[cnt_ind];
 shift<<=8;
 shift|=font[seg[cnt_ind]];
 if (point[cnt_ind]) shift|=0x08;
 cnt_ind++;
 cnt_ind&=0x03;
 if (!led_3) shift&=0x7fff;    
 else shift|=0x8000;
 if (!led_2) shift&=0xbfff;  
 else shift|=0x4000;
 if (!led_0) shift&=0xefff;
 else shift|=0x1000;
 if (!led_1) shift&=0xdfff;
 else shift|=0x2000;
 for (i=0;i<16;i++)
 {
  if (shift&0x8000)
  {
   SER_1;
  } 
  else
  {
   SER_0;
  } 
  SCK_0;
  SCK_1;
  shift<<=1;
 }
 RCK_0;
 RCK_1;
}
// ******************************


ISR(TIMER1_COMPA_vect) // countdown, interrupt 500ms
{
 if (mode==7)  
 {
  ++cnt_sec;
  cnt_sec&=0x01;
  if (cnt_sec)
  {
   if (cnt_time_down)
   {
    ++cnt_time_up;
    --cnt_time_down;
    if (!flg_loop)
    {
     if (!cnt_time_down)
     {
      if (!flg_pause)
      {
       flg_pause=1;
       MainSetTotal();
       if (!cnt_time_down)
       {
        MainEndCnt();
       }
       else
       {
        REL1_OFF;
        REL2_ON;
		LED2_ON;
       }
      }
      else
      {
       MainEndCnt();
      }
     }
    }
    else
    {
     if (!cnt_time_down)
     {
      flg_pause++;
      flg_pause&=0x01;
      MainSetTotal();
      if (!cnt_time_down)
      {
       MainEndCnt();
      }
      else
      {
       if (!flg_pause)
       {
        REL1_ON;
        REL2_OFF;
        LED2_OFF;
       }
       else
       {
        REL1_OFF;
        REL2_ON;
		LED2_ON;
       }
      }
     }
    }
   }
  }
 }
 else cnt_sec=1;
 MainDis();
}

void MainEncIncDcr(byte mx,byte flg) // parameter value change
{
 if (flg)
 {
  if (++mem[mode-1]>mx) mem[mode-1]=0;
 }
 else
 {
  if (--mem[mode-1]>mx) mem[mode-1]=mx;
 }
 seg[mode-1]=mem[mode-1]/10;
 seg[mode]=mem[mode-1]%10;
 MainDis();
}

void MainEncSwi(byte f1) // Encoder rotation actions
{
 switch(mode)
 {
  case 0:
  case 7:
  {
   if (f1)
   {
    flg_loop++;
	flg_loop&=0x01;
	if (flg_loop) LED0_ON;
	else LED0_OFF;    
   }
   else
   {
    flg_dir++;
    flg_dir&=0x01;
    if (!flg_dir) LED1_OFF;
    else LED1_ON;
   }
   MainDis();
   break;
  }
  case 1:
  case 4:
  {
   MainEncIncDcr(MAX_HR,f1);
   break;
  }
  case 2:
  case 3:
  case 5:
  case 6:
  {
   MainEncIncDcr(MAX,f1);
   break;
  }
 }
}


void MainEncStep(void) // encoder state
{
 byte sum;
 if (flg_button) return;
 state=((PIND&0x0c)>>2); // состояние энкодера
 sum=(m_state<<2)|state;
 if ((sum==0x02)||(sum==0x23)||(sum==0x31)||(sum==0x10)) MainEncSwi(1);
 else
 {
  if ((sum==0x01)||(sum==0x20)||(sum==0x32)||(sum==0x13)) MainEncSwi(0);
 }
 m_state=state;
}

void MainSetTotal(void) 
{ // time in seconds
 cnt_time_up=0;
 cnt_time_down=((word)mem[0+flg_pause*3]*3600+((word)mem[1+flg_pause*3]*60)+mem[2+flg_pause*3]);
}

void MainOffSeg(void)
{
 byte i;
 for(i=0;i<4;i++)
 {
  if (mode<4) seg[i]=10;
  else  seg[i]=11;
  point[i]=0;
 }
}

void MainDis(void)
{ //preparation for display
 byte i,ns;
 if((mode)&&(mode<7))
 {
  MainOffSeg();
  ns=locn[mode-1];
  seg[ns]=mem[mode-1]/10;
  seg[ns+1]=mem[mode-1]%10;
 }
 else
 {
  if (!flg_dir) cnt_time=cnt_time_down;    //account direction
  else cnt_time=cnt_time_up;
  i=cnt_time/3600; // total hours
  if (i)
  {
   seg[0]=i/10; // hours
   seg[1]=i%10;
   i=(cnt_time%3600)/60;
   seg[2]=i/10; // minutes
   seg[3]=i%10;
   flg_h_m=1;
   point[0]=0;
   point[1]=cnt_sec;
   point[2]=0;
   point[3]=cnt_sec;
  }
  else
  {
   i=cnt_time/60;
   seg[0]=i/10;  // минуты
   seg[1]=i%10;
   i=cnt_time%60;
   seg[2]=i/10; // секунды
   seg[3]=i%10;
   flg_h_m=0;
   point[0]=0;
   point[1]=cnt_sec;
   point[2]=0;
   point[3]=0;
  }
 }
}

void MainEndCnt(void)
{
 LED0_OFF;
 LED1_OFF;
 LED2_OFF;
 REL1_OFF;
 REL2_OFF;
 mode=0;
 flg_dir=0;
 flg_pause=0;
 MainSetTotal();
}

unsigned char MainReadEeprom(word adr)
{
 while (EECR&(1<<EEPE));
 EEAR=adr;
 EECR|=(1<<EERE);
 return EEDR;
}

void MainWriteEeprom(word adr,byte dan)
{
 while (EECR&(1<<EEPE));
 EEAR=adr;
 EEDR=dan;
 EECR|=(1<<EEMPE);
 EECR|=(1<<EEPE);
}

void MainCheckParam(void)
{
 byte i;
 if (MainReadEeprom(ADR_HR)>MAX_HR)
 {
  for (i=0;i<6;i++) MainWriteEeprom(ADR_HR+i,0x00);
 }
}

void MainReadParam(void) // reading parameters from  EEPROM
{
 byte i;
 for (i=0;i<6;i++) mem[i]=MainReadEeprom(i+1);
}

void MainWritePar(void) // write parameters to EEPROM
{
 byte i;
 for (i=0;i<6;i++)
 {
  if (MainReadEeprom(i+1)!=mem[i]) MainWriteEeprom(i+1,mem[i]);
 }
}

void MainSwi(void)
{
 mem[mode]=MainReadEeprom(mode+1);
 step=0;
 ++mode;
 flg_button=0xff;
 cnt_but=0;
}

void MainButDown(void)
{
 {
  if (!mode) 
  { //go to parameter setting
   if (++cnt_but>=MAX_CNT)
   {
    MainEndCnt();
    MainSwi();
   }
  }
  else
  {
   if (mode<7)
   { // parameter selection
    if (++cnt_but>=MIN_CNT)
    {
     MainSwi();
     if (mode>6)
     {
      MainWritePar();
      MainEndCnt();
      MainOffSeg();
     }
    }
   }
   else
   {  // timer stop
    if (++cnt_but>=MAX_CNT)
    {
     MainEndCnt();
     flg_button=0xff;
     cnt_but=0;
    }
   }
  }
 }
}


void MainButUp(void) // start timer
{
 if (!mode)
 {
  if (cnt_but<MAX_CNT)   
  {
   if (cnt_but>MIN_CNT)
   {
    MainReadParam(); // reading parameters from EEPROM
    if (cnt_time_down)
    {
     mode=7;   // countdown mode
     REL1_ON;
    }
   }
  }
 }
}

void MainButt_n(void) 
{
 if (!BUTT)
 {
  if (!flg_button) MainButDown(); // long press 
 }
 else
 {
  if (cnt_but)
  {
   MainButUp(); // short press 
   cnt_but=0;
  }
  flg_button=0;
 }
}

void MainIni(void)
{
 SET_SCK;
 SET_SER;
 SET_RCK;
 SET_BUTT;
 SET_REL1;
 SET_REL2;
 REL1_OFF;
 REL2_OFF;
 TCCR0A=0x02;
 OCR0A=0xbb;          // 3 ms 16.0/256 mHz 
 TIMSK0=0x02;
 TIMSK1=0x02;       // interrupt resolution compared to OCR1A
 OCR1A=0x7a11;      //  500 ms 16.0/256 mHz
 sei();              // Global enable interrupts
 TIMER1_ON;         // start timer 0       
 TIMER0_ON;         // start timer 1
 LED0_OFF;
 LED1_OFF;
 LED2_OFF;
 LED3_OFF;
 MainCheckParam(); //
 MainReadParam();
 MainEndCnt();
 _delay_ms(350);
}

int main(void)
{
 MainIni(); // initialization
 for(;;)
 {
  MainButt_n();  // button state
  MainEncStep(); // encoder state
 }
}



