#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>

//port a bity pro enkoder:
volatile uint8_t &pinENK = PIND;
const uint8_t bitCK = 5;
const uint8_t bitDT = 4;
const uint8_t bitBT = 3;
//perioda a stred:
const uint8_t max = 6;
const uint8_t stred = 3;

//globalni promenne:
volatile uint16_t kmitocet = 0;
volatile bool novykmitocet = false;
volatile bool preteklkmitocet = false;
volatile uint16_t perioda = 0;
volatile uint16_t pospulz = 0;
volatile bool novaperioda = false;
volatile bool preteklaperioda = false;

void inicializace()
{
  DDRD = (1 << 2);  //PD2 vystup
  PORTD = (1 << 2); //OC4B povolen

  PCICR =(1 << PCIE2);  //Pin Change PD povolen
  //PD5 (PCINT21), PD3 (PCINT19) povoleny:
  PCMSK2 =(1 << bitCK) | (1 << bitBT);

  //timer0: CTC, D = 256:
  TCCR0A = (1 << WGM01);
  TCCR0B = (1 << CS02);
  OCR0A = 249; //4 ms
  TIMSK0 = (1 << OCIE0A);  //povoleno CTC preruseni

  //timer1: normalni rezim, D = 1, zachyt sestupnou hranou ICP1:
  TCCR1A = 0;
  TCCR1B = (0 << ICES1) | (1 << WGM10);
  TIMSK1 = (1 << ICIE1);  //povoleno ICP1 preruseni

  //timer3: normalni rezim, casovac, STOP:
  TCCR3A = 0;
  TCCR3B = 0;

  //timer4: PWM (f+k+ICR4), neinv. rezim OC4B, D = 1024:
  TCCR4A = (1 << COM4B1);
  TCCR4B = (1 << WGM43) | (1 << CS42) | (1 << CS40);
  ICR4 = max;
  OCR4B = stred;
}

uint8_t getbit(volatile uint8_t reg, uint8_t bit)
{
  return (reg & (1 << bit)) >> bit;
}

int main(void)
{
  inicializace();
  sei();  //preruseni povoleno

  printf("Mereni kmitoctu, sirky impulzu a periody OC4B:\n");

  while (1)
  {
    if(novykmitocet)
    {
      novykmitocet = false;
      printf("ENK = %u, ", OCR4B);
      
      if(preteklkmitocet)  //test preteceni
        printf("citac T3 pretekl, ");
      else
        printf("f = %u Hz, ", kmitocet);
        
      if(novaperioda)
      {
        novaperioda = false;
        
        if(preteklaperioda)
          printf("citac T1 pretekl");
        else
          printf("t+ = %u us, T = %u us", 
            uint16_t(pospulz * 0.0625), uint16_t(perioda * 0.0625));
      }
      else
      {
        if(getbit(PINB, 0))
          printf("log. 1 trvale");
        else
          printf("log. 0 trvale");
      }
      
      printf("\n");
    }
  }
}

//obsluha CTC preruseni pro timer0:
ISR(TIMER0_COMPA_vect)
{
  static uint8_t poc = 0;
  static bool poprve = true;
  
  poc++;
  if(poc == 250)
  {
    //dokonci predchozi mereni:
    TCCR3B = 0;
    
    preteklkmitocet = getbit(TIFR3, TOV3);
    kmitocet = TCNT3;     //ulozi vysledek
    if(!poprve)
      novykmitocet = true;  //oznami data k dispozici

    poprve = false;
    poc = 0;  //priprava na novy odmer 1000 ms
    
    //start noveho mereni:
    TIFR3 = (1 << TOV3);  //nulovani priznaku preteceni
    TCNT3 = 0;  //nulovani obsahu
    //cita nabezne hrany T3:
    TCCR3B = (1 << CS32) | (1 << CS31) | (1 << CS30);
  }
}

//obsluha ICP1 preruseni (timer1):
ISR(TIMER1_CAPT_vect)
{
  static uint8_t hrana = 0;
  static uint16_t t0 = 0;
  
  hrana++;
  switch(hrana)
  {
    case 1: //prvni sestupna hrana - priprava:
      TIFR1 = (1 << TOV1);  //nulovani TOV1
      TCNT1 = 0;
      break;
    case 2: //prvni nabezna hrana:
      t0 = ICR1;  //zacatek mereni;
      break;
    case 3: //druha sestupna hrana - urceni t+
      pospulz = ICR1 - t0;  //zmereny t+
      break;
    case 4: //druha nabezna hrana - urceni T
      perioda = ICR1 - t0;
      preteklaperioda = getbit(TIFR1, TOV1);
      novaperioda = true;
      hrana = 0;
      break;
  }

  TCCR1B = TCCR1B ^ (1 << ICES1);  //zmena spousteci hrany
}

//obsluha preruseni enkoderu:
ISR(PCINT2_vect)
{
  uint8_t stav = ~pinENK;

  if(getbit(stav, bitCK))  //detekce otaceni
  {
    if(getbit(stav, bitDT))  //detekce smeru
    {
      if(OCR4B < max)  //po smeru hod. rucek
      OCR4B++;
    }
    else
    {
      if(OCR4B > 0)    //proti smeru hod. rucek
      OCR4B--;
    }
  }

  if(getbit(stav, bitBT))  //stredove tlacitko
  {
    OCR4B = stred;
  }
}