/*
23  PB4 ( OC2A/PCINT4 ) Digital pin 10 (PWM)
24  PB5 ( OC1A/PCINT5 ) Digital pin 11 (PWM)
25  PB6 ( OC1B/PCINT6 ) Digital pin 12 (PWM)
26  PB7 ( OC0A/OC1C/PCINT7 )  Digital pin 13 (PWM)
*/

//*******************************  Библиотеки  *******************************//
#include "Arduino.h"

//#include "LedControl.h"

#include <util/delay.h>
//*******************************  /Библиотеки  ******************************//

//********************** Объявление переменных, констант *********************//
//LedControl ledLineX = LedControl(23,27,25,1);

// Переменная в которой храним последнее значение порта E.
volatile uint8_t lastValuePortB = 0;

// Кол-во импульсов линейки X.
volatile int32_t lineEncoderX = 0;
int32_t oldLineEncoderX = 0;

// Кол-во импульсов линейки Y.
volatile int32_t lineEncoderY = 0;
int32_t oldLineEncoderY = 0;

// Кол-во импульсов линейки Z.
volatile int32_t lineEncoderZ = 0;
int32_t oldLineEncoderZ = 0;
//********************* /Объявление переменных, констант *********************//

void setup() 
{
  // Настройка Serial.
  Serial.begin(9600);
  // Создаем заголовок в Excel.
  Serial.println("CLEARDATA");          // Очистка листа excel
  Serial.println("LABEL,Time,Pulse. X,Pulse. Y,Pulse. Z,X,Y,Z");  // Заголовки столбцов.
  
  // Настройка прерывания по изменению состояния группы выводов 0.
  SetupPortInterrupt0();

//  ledLineX.shutdown(0,false);  /* Normal functioning of device device with address 0 */
//  ledLineX.setIntensity(0,8);  /* Define medium(8) intensity of display device with address 0 */
//  ledLineX.clearDisplay(0);    /* Clear display device with address 0 */ 

  // Немного подождем, вдруг, что-то не успело инициализировать.
  _delay_ms(1000);
}

void loop() {
  if (lineEncoderX != oldLineEncoderX
    || lineEncoderY != oldLineEncoderY
    || lineEncoderZ != oldLineEncoderZ)
  {
//    ledLineX.setDigit(0,0,0,false);
//    ledLineX.setDigit(0,1,2,false);
//    ledLineX.setDigit(0,2,4,false);
//    ledLineX.setDigit(0,3,6,false);
//    ledLineX.setChar(0,4,'F',false); 
//    ledLineX.setChar(0,5,'P',false);
//    ledLineX.setChar(0,6,'c',false);
//    ledLineX.setChar(0,7,'A',false);

    lineEncoderY = lineEncoderX+50;
    lineEncoderZ = lineEncoderX+100;
    
    Serial.print("DATA,TIME,");      // Запись в excel текущей даты и времени.
    Serial.print(lineEncoderX);      // Запись количество импульсов энкодера Х.
    Serial.print(",");
    Serial.print(lineEncoderY);      // Запись количество импульсов энкодера Y.
    Serial.print(",");
    Serial.print(lineEncoderZ);      // Запись количество импульсов энкодера Z.
    Serial.print(",");
    Serial.print((float)lineEncoderX * (float)0.005, 3); 
    Serial.print(",");
    Serial.print((float)lineEncoderY * (float)0.005, 3); 
    Serial.print(",");
    Serial.println((float)lineEncoderZ * (float)0.005, 3); 
    
    oldLineEncoderX = lineEncoderX; 
    oldLineEncoderY = lineEncoderY;
    oldLineEncoderZ = lineEncoderZ;
  }
}

/// <summary>
/// Настройка прерывания по изменению состояния группы выводов 0.
/// </summary>
void SetupPortInterrupt0()
{
  pinMode(13, INPUT);
  pinMode(12, INPUT);
  digitalWrite(13, HIGH);
  digitalWrite(12, HIGH);
  
  // PCICR (Pin Change Interrupt Enable) - регистр разрешающий прерывания по изменению состояния группы выводов.
  // PCIEn - биты которые разрешают прерывания по изменению состояния группы выводов. Когда бит PCIEn и бит I регистра SREG установлены в 1 - прерывания по изменению состояния на группе выводов PCIEn разрешены.
  // Очищаем регистр разрещающий прерывания по изменению состояния группы выводов.
  PCICR |= (0 << PCIE0);

  // PCMSKn (Pin Change Mask Register) - регистр включающий выводы конкретной группы портов на прерываний по изменению состояния.
  PCMSK0 |= (1 << PCINT7) | (1 << PCINT6) | (0 << PCINT5) | (0 << PCINT4) | (0 << PCINT3) | (0 << PCINT2) | (0 << PCINT1) | (0 << PCINT0);

  // PCIFR (Pin Change Interrupt Flag Register) - регистр флагов прерываний по изменению состояния группы выводов. Указывает от какой группы поступило прерывание.
  // Очищаем регистр флагов прерываний по изменению состояния группы выводов.
  PCIFR = (1 << PCIF0);

  // PCICR (Pin Change Interrupt Enable) - регистр разрешающий прерывания по изменению состояния группы выводов.
  // PCIEn - биты которые разрешают прерывания по изменению состояния группы выводов. Когда бит PCIEn и бит I регистра SREG установлены в 1 - прерывания по изменению состояния на группе выводов PCIEn разрешены.
  PCICR |= (1 << PCIE0);

}

/// <summary>
/// Прерывание по изменению состояния группы выводов 0.
/// </summary>
ISR(PCINT0_vect)
{
  // Получаем измененные биты порта B.
  uint8_t changedBits = PINB & B11000000 ^ lastValuePortB;

  // Проверка линейки на X-су.
  if (changedBits & (1 << PB7) && lastValuePortB & (1 << PB7))
  {
      // Состояние вывода изменилось с высокого уровня на низкий.
      if (PINB & (1 << PB6))
      {
        // Состояние вывода изменилось с низкого уровня на высокий.
        lineEncoderX++;
      }
      else
      {
        // Состояние вывода изменилось с высокого уровня на низкий.
        lineEncoderX--;
      }
  }

  // Сохраняем последнее значение порта B.
  lastValuePortB = PINB;
}

