#include <avr/pgmspace.h>


// ***** LCD *****
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
char LCD_Row_1[17];
char LCD_Row_2[17];


// ***** Stepper Motor *****
//#define Motor_X_Step_Per_Revolution          2500
//#define Motor_Y_Step_Per_Revolution          2500
#define MotorPort                    PORTL                    // celý port je přidělen pro motory "L",
#define MotorInitialization()        DDRL=B11111111           // nakonfigurován jako výstup
#define Motor_X_SetPulse()           MotorPort &= ~(1<<0)     // Pin49 1
#define Motor_X_RemovePulse()        MotorPort |= (1<<0)      // Pin49 0
#define Motor_Y_SetPulse()           MotorPort &= ~(1<<1)     // Pin48 1
#define Motor_Y_RemovePulse()        MotorPort |= (1<<1)      // Pin48 0
#define Motor_X_Forward()            MotorPort |= (1<<2)      // Pin47 0
#define Motor_X_Reverse()            MotorPort &= ~(1<<2)     // Pin47 1
#define Motor_Y_Forward()            MotorPort |= (1<<3)      // Pin46 0
#define Motor_Y_Reverse()            MotorPort &= ~(1<<3)     // Pin46 1
#define Motor_X_Enable()             MotorPort |= (1<<4)      // Pin45 0
#define Motor_X_Disable()            MotorPort &= ~(1<<4)     // Pin45 1
#define Motor_Y_Enable()             MotorPort |= (1<<5)      // Pin44 0
#define Motor_Y_Disable()            MotorPort &= ~(1<<5)     // Pin44 1
boolean Step_On_flag=false;                                   // Vlajka povolit / zakázat účet před "krokem"
boolean Mode_On_flag=false;                                   // Režim Zapnutí / Vypnutí vlajky


// ***** Taho *****
#define TahoPort                      PORTL
#define TahoSetPulse()                TahoPort |= (1<<6)             // Pin43 1
#define TahoRemovePulse()             TahoPort &= ~(1<<6)            // Pin43 0


// ***** Encoder *****
#define Enc_Line_per_Revolution       200                           // Počet řádků kodéru
#define Enc_Line                      Enc_Line_per_Revolution*2      // Pracovní počet klíšťat
#define EncoderPort                   PORTD
#define EncoderInitialization()       DDRD=B00000000                 // Celý port "D" je přiřazen kodéru, je konfigurován pro vstup
#define Enc_Read()                    (PIND & B00000010)
volatile int Enc_Pos=0;                                              // Počítadlo polohy snímače
volatile byte Ks_Count=0;                                            // Počítadlo pro "Feed", "Závit" celou část
volatile int Km_Count=0;                                             // Počítadlo pro zlomek "Feed", "Thread"
byte Ks_Divisor=0;                                                   // Oddělovač pro "Krmení", "Závit" celou část
int Km_Divisor=0;                                                    // Oddělovač pro dílčí část "Krmivo", "Závit"
int Enc_Pos_tmp=0;
long Spindle_Angle=0;


//***** Sensor *****
#define SensorPort                     PORTD
#define Sensor                         PIND
#define Sensor_Left_Mask B00001000
#define Sensor_Right_Mask B00000100
char Sensor_Mask = B00000000;


enum Pressed_Key
{
  Key_None,
  Key_Right,
  Key_Up,
  Key_Down,
  Key_Left,
  Key_Select
};
byte Pressed_Key=Key_None;
boolean key_flag=false;                                              // příznak stisknutého / stisknutého tlačítka

// ***** Mode *****
enum Mode
{
  Mode_Thread_Left=1,
  Mode_Feed_Left,
  Mode_Divider,
  Mode_Feed_Right,
  Mode_Thread_Right
};
byte Mode = Mode_Divider;


// ***** Feeds *****
// Enc_Line/(Step_Per_Revolution/Feed_Screw*Feed_mm)
#define Total_Feeds                   400                  // Počet inningů
typedef struct
{
  byte  s_Divisor;                                        // Oddělovač pro krmení celé části
  char  Text[7];
}
FEED_INFO;
FEED_INFO Feed_Info[Total_Feeds] =
{
   { 113, "0.02mm" },
   { 56,  "0.04mm" },
   { 38,  "0.06mm" },
   { 28,  "0.08mm" },
   { 23,  "0.10mm" },
   { 19,  "0.12mm" },
   { 16,  "0.14mm" },
   { 14,  "0.16mm" },
   { 13,  "0.18mm" },
   { 11,  "0.20mm" },
};
byte Feed_Step = 3;                                      // výběr z výchozího pole (0,08 mm)

// ***** Threads *****
// Enc_Line/(Step_Per_Revolution/Feed_Screw*Thread_mm)
#define Total_Threads                 36                // Počet vláken
typedef struct
{
  byte s_Divisor;                                       // Oddělovač pro "vyřezávání" celé části
  int  m_Divisor;                                       // Dělič pro "řezbářskou" část
  char Text[7];
}
THREAD_INFO;
THREAD_INFO Thread_Info[Total_Threads] =
{
   { 11, 2500, "0.20mm" },
   { 9,  0,    "0.25mm" },
   { 7,  5000, "0.30mm" },
   { 6,  4286, "0.35mm" },
   { 5,  6250, "0.40mm" },
   { 4,  5000, "0.50mm" },
   { 3,  7500, "0.60mm" },
   { 3,  2143, "0.70mm" },
   { 3,  0,    "0.75mm" },
   { 2,  8125, "0.80mm" },
   { 2,  2500, "1.00mm" },
   { 1,  8000, "1.25mm" },
   { 1,  5000, "1.50mm" },
   { 1,  2857, "1.75mm" },
   { 1,  1250, "2.00mm" },
   { 7,  866,  "80tpi " },
   { 6,  3780, "72tpi " },
   { 5,  6693, "64tpi " },
   { 5,  3150, "60tpi " },
   { 4,  9606, "56tpi " },
   { 4,  2520, "48tpi " },
   { 3,  8976, "44tpi " },
   { 3,  5433, "40tpi " },
   { 3,  1890, "36tpi " },
   { 2,  8347, "32tpi " },
   { 2,  4803, "28tpi " },
   { 2,  3917, "27tpi " },
   { 2,  3032, "26tpi " },
   { 2,  1260, "24tpi " },
   { 1,  9488, "22tpi " },
   { 1,  7717, "20tpi " },
   { 1,  6831, "19tpi " },
   { 1,  5945, "18tpi " },
   { 1,  4173, "16tpi " },
   { 1,  2402, "14tpi " },
   { 1,  0630, "12tpi " },
};
byte Thread_Step = 10;                      // výběr z výchozího pole (1,0 mm)

// ***** Interrupts *****
#define EnableINT0()          EIMSK |= (1 << INT0)
#define DisableINT0()         EIMSK &= ~(1 << INT0)
#define Init_INT0_Any()       EICRA = B00000001


//*********************************************************
void setup()
{
  TIMSK0=0;                                 // Vypněte časovač! (dělá něco v pozadí)
  
  EncoderInitialization();
  PORTD=B00001111;                          // подтяжка PIN_21, 20, 19, 18
  
  MotorInitialization();
  Init_INT0_Any();
  EnableINT0();
  
  lcd.begin(16, 2);
}


//**********************************************************
void loop()
{
  Enc_Pos_tmp = Enc_Pos;                         // в "void Divider" Přečtěte si znovu a porovnejte 

  if ((Mode == Mode_Divider) || !Mode_On_flag)
  {
    Motor_X_Disable();
//    Motor_Y_Disable();
  }
  else
  {
    Motor_X_Enable();
//    Motor_Y_Enable();
  }
  
  menu();
  Sensors();
}


// ********** Funkce zpracování události v hlavní nabídce **********
void menu()
{
  int ADC_value = analogRead(A0);
  if (ADC_value < 65) Pressed_Key=Key_Right;
  else if (ADC_value < 220) Pressed_Key=Key_Up;
  else if (ADC_value < 390) Pressed_Key=Key_Down;
  else if (ADC_value < 600) Pressed_Key=Key_Left;
  else if (ADC_value < 870) Pressed_Key=Key_Select;
  else Pressed_Key = Key_None;
  
  if (!key_flag)
  {
      switch (Pressed_Key)
      {
        case Key_Left:
          MenuKeyLeftPressed();
          break;
        case Key_Right:
          MenuKeyRightPressed();
          break;
        case Key_Up:
          MenuKeyUpPressed();
          break;
        case Key_Down:
          MenuKeyDownPressed();
          break;       
        case Key_Select:
          MenuKeySelectPressed();    
          break;
      }
  }
  if (Pressed_Key == Key_None) key_flag = false;

  SelectWorkMode();                                // zvolte zvolený provozní režim
}

// ********** Ovládací tlačítko pro klepnutí na tlačítko Select **********
void MenuKeySelectPressed()
{
  switch (Mode) 
  {
      case Mode_Thread_Left:
      case Mode_Thread_Right:
      case Mode_Feed_Left:
      case Mode_Feed_Right:
        Step_On_flag = false;
        Mode_On_flag = !Mode_On_flag;              // změníme hodnotu naopak
        Ks_Count = 0;
        Km_Count = 0;
        break;
      case Mode_Divider:
        Enc_Pos=0;
        break;
   }
   key_flag = true;
}

// ********** Ovládací prvek tlačítka Nahoru **********
void MenuKeyUpPressed()
{
  switch (Mode)
  {
      case Mode_Thread_Left:
      case Mode_Thread_Right:
        if (Thread_Step < Total_Threads-1)
        {
          Mode_On_flag = false;
          Step_On_flag = false;
          Ks_Count=0;
          Km_Count=0;
          Thread_Step++;
        }
        break;
      case Mode_Feed_Left:
      case Mode_Feed_Right:
        if (Feed_Step < Total_Feeds-1)
        {
          Ks_Count=0;
          Feed_Step++;
        }
        break;
      }
      key_flag = true;
}

// ********** Ovládací tlačítko dolů **********
void MenuKeyDownPressed()
{
   switch (Mode)
   { 
      case Mode_Thread_Left:
      case Mode_Thread_Right:
        if (Thread_Step > 0)
        {
          Mode_On_flag = false;
          Step_On_flag = false;
          Ks_Count=0;
          Km_Count=0;
          Thread_Step--;
        }
        break;
      case Mode_Feed_Left:
      case Mode_Feed_Right:
        if (Feed_Step > 0)
        {
          Ks_Count=0;
          Feed_Step--;
        }
        break;
   }
   key_flag = true;
}

// ********** Psovod pro stisknutí levého tlačítka **********
void MenuKeyLeftPressed()
{
      switch (Mode)
      {
        case Mode_Feed_Left:
        case Mode_Divider:
        case Mode_Feed_Right:
        case Mode_Thread_Right:
          Mode_On_flag = false;
          Step_On_flag = false;
          Ks_Count=0;
          Km_Count=0;
          Mode--;
          break;
      }
      
      key_flag = true;
}

// ********** Spouštěč pravého tlačítka **********
void MenuKeyRightPressed()
{
      switch (Mode)
      {
        case Mode_Thread_Left:
        case Mode_Feed_Left:
        case Mode_Divider:
        case Mode_Feed_Right:
          Mode_On_flag = false;
          Step_On_flag = false;
          Ks_Count=0;
          Km_Count=0;
          Mode++;
          break;
      }
      
      key_flag = true;
}

// ********** Výběr provozního režimu **********
void SelectWorkMode()
{
   switch (Mode)
   {
    case Mode_Thread_Left:
        Thread_Left();
        break;
    case Mode_Feed_Left:
        Motor_X_Reverse();
        Feed_Left();
        break;
    case Mode_Divider:
        Divider();
        break;
    case Mode_Feed_Right:
        Motor_X_Forward();
        Feed_Right();
        break;
    case Mode_Thread_Right:
        Thread_Right();
  }
}


//***************************************
void Thread_Left()
{
  Sensor_Mask = Sensor_Left_Mask;
  Ks_Divisor=Thread_Info[Thread_Step].s_Divisor;
  Km_Divisor=Thread_Info[Thread_Step].m_Divisor;
  snprintf(LCD_Row_1, 17, (Mode_On_flag==true) ? "Mode:  ON       " : "Mode:  OFF      ");
  snprintf(LCD_Row_2, 17, "Thread <= %s", Thread_Info[Thread_Step].Text);
  Print();
}

void Feed_Left()
{
  Sensor_Mask = Sensor_Left_Mask;
  Ks_Divisor=Feed_Info[Feed_Step].s_Divisor;
  snprintf(LCD_Row_1, 17, (Mode_On_flag==true) ? "Mode:  ON       " : "Mode:  OFF      ");
  snprintf(LCD_Row_2, 17, "Feed   <= %s", Feed_Info[Feed_Step].Text);
  Print();
}

void Divider()
{
  if (Enc_Pos == Enc_Pos_tmp)
  {
    Spindle_Angle=(Enc_Pos*360000/(Enc_Line));
  }
  snprintf(LCD_Row_1, 17, "Mode:  Divider  ");
  snprintf(LCD_Row_2, 17, "Angle: %3ld.%03ld  ", Spindle_Angle/1000, Spindle_Angle%1000);
  Print();
}

void Feed_Right()
{
  Sensor_Mask = Sensor_Right_Mask;
  Ks_Divisor=Feed_Info[Feed_Step].s_Divisor;
  snprintf(LCD_Row_1, 17, (Mode_On_flag==true) ? "Mode:  ON       " : "Mode:  OFF      ");
  snprintf(LCD_Row_2, 17, "Feed   => %s", Feed_Info[Feed_Step].Text);
  Print();
}

void Thread_Right()
{
  Sensor_Mask = Sensor_Right_Mask;
  Ks_Divisor=Thread_Info[Thread_Step].s_Divisor;
  Km_Divisor=Thread_Info[Thread_Step].m_Divisor;
  snprintf(LCD_Row_1, 17, (Mode_On_flag==true) ? "Mode:  ON       " : "Mode:  OFF      ");
  snprintf(LCD_Row_2, 17, "Thread => %s", Thread_Info[Thread_Step].Text);
  Print();
}


//******************************************************************
void Print()
{
lcd.setCursor(0, 0);
lcd.print(LCD_Row_1);
lcd.setCursor(0, 1);
lcd.print(LCD_Row_2);
}


//******************************************************************
void Sensors()
{
   if (!(Sensor & Sensor_Mask) )
   {
      Mode_On_flag = false;
   }
}


//******************************************************************
ISR(INT0_vect)
{
   TahoRemovePulse();
   Motor_X_RemovePulse();

   if (!Enc_Read())                                     // Otáčení vřetena doprava
   {
      Enc_Pos++;
      if (Enc_Pos == Enc_Line)                          // plný obrat
      {                                           
         Enc_Pos = 0;
         TahoSetPulse();                                // při průchodu 0 generujeme signál Taho
         Step_On_flag = (Mode_On_flag == true);         //Zkontrolujte pouze režim ON / OFF! po průchodu 0 vyřešíme účet do divize
      }
      
                 
      if (Step_On_flag)
      {
         if (!(Sensor & Sensor_Mask) )
         {
            Step_On_flag = false;
            return;
         }
        
         if (Mode == Mode_Feed_Left)
         {
               if (Ks_Count == Ks_Divisor)
               {
                  Motor_X_SetPulse();
                  Ks_Count = 0;
               }
               Ks_Count++;
         }
         
         else if (Mode == Mode_Feed_Right)
         {
               if (Ks_Count == Ks_Divisor)
               {
                  Motor_X_SetPulse();
                  Ks_Count = 0;
               }
               Ks_Count++;
         }
         
         else if (Mode == Mode_Thread_Left)
         {
               if (Ks_Count > Ks_Divisor)
               {
                  Motor_X_Reverse();
                  Motor_X_SetPulse();
                  Km_Count = Km_Count + Km_Divisor;
                  if (Km_Count > Km_Divisor)
                  {
                     Km_Count = Km_Count - 10000;
                     Ks_Count = 0;
                  }
                  else
                  {
                  Ks_Count = 1;
                  }
               }
               Ks_Count++;
         }
         
         else if (Mode == Mode_Thread_Right)
         {
               if (Ks_Count > Ks_Divisor)
               {
                  Motor_X_Forward();
                  Motor_X_SetPulse();
                  Km_Count = Km_Count + Km_Divisor;
                  if (Km_Count > Km_Divisor)
                  {
                     Km_Count = Km_Count - 10000;
                     Ks_Count = 0;
                  }
                  else
                  {
                  Ks_Count = 1;
                  }
               }
               Ks_Count++;
         }
      }   
   }


   else
   {                                                 // Otáčením vřetena doleva
      Enc_Pos--;
      if (Enc_Pos < 0)
      {
         Enc_Pos = Enc_Line - 1;
         TahoSetPulse();
         Step_On_flag = (Mode_On_flag == true);     // Zkontrolujte pouze režim ON / OFF! po průchodu 0 vyřešíme účet do divize
      }

      if (Step_On_flag)
      {
         if (!(Sensor & Sensor_Mask) )
         {
            Step_On_flag = false;
            return;
         }
         
         if (Mode == Mode_Feed_Left)
         {
               if (Ks_Count == 0)
               {
                  Motor_X_SetPulse();
                  Ks_Count = Ks_Divisor;
               }
               Ks_Count--;
         }
         
         if (Mode == Mode_Feed_Right)
         {
               if (Ks_Count == 0)
               {
                  Motor_X_SetPulse();
                  Ks_Count = Ks_Divisor;
               }
               Ks_Count--;
         }
         
         if (Mode == Mode_Thread_Left)
         {
               if (Ks_Count == 0)
               {
                  Motor_X_Forward();
                  Motor_X_SetPulse();
                  Km_Count = Km_Count - Km_Divisor;
                  if (Km_Count < 1)
                  {
                     Km_Count = Km_Count + 10000;
                     Ks_Count = Ks_Divisor + 1;
                  }
                  else
                  {
                     Ks_Count = Ks_Divisor;
                  }
               }
               Ks_Count--;
         }
         
         if (Mode == Mode_Thread_Right)
         {
               if (Ks_Count == 0)
               {
                  Motor_X_Reverse();
                  Motor_X_SetPulse();
                  Km_Count = Km_Count - Km_Divisor;
                  if (Km_Count < 1)
                  {
                     Km_Count = Km_Count + 10000;
                     Ks_Count = Ks_Divisor + 1;
                  }
                  else
                  {
                     Ks_Count = Ks_Divisor;
                  }
               }
               Ks_Count--;
         }
      }
   }
}

//*******************************************************************


