//
// Encoder module common operations library
// Homelab library
//
// Department of Mechatronics
// Tallinn University of Technology
// ITT Group
// © Copyrights 2014
//

//
// Take use of pin and timer operations
//
#include "pin.h"
#include "module/encoders.h"
#include <avr/interrupt.h>

#include "timer.h"

//
// Data types
//
typedef struct
{
    unsigned short num_pulses;
}
encoder_data;

void encoder_pulse(unsigned char index);

#if defined (__AVR_ATxmega128A1U__)
static encoder_data encoder[5];

ISR(PORTC_INT0_vect)
{
    encoder_pulse(1);
}

ISR(PORTC_INT1_vect)
{
    encoder_pulse(2);
}

ISR(PORTD_INT0_vect)
{
    encoder_pulse(3);
}

ISR(PORTD_INT1_vect)
{
    encoder_pulse(4);
}

void encoder_init(unsigned char index)
{
    switch (index)
    {
    case 1:
        //ioport_configure_pin(ENC1_GPIO,IOPORT_DIR_INPUT|IOPORT_RISING|IOPORT_PULL_UP);
        PORTC.DIRCLR = (1<<4);
        PORTC.PIN4CTRL |= (3<<3)|(1);
        PORTC.INTCTRL |= (2<<0);	// Interrupt level Medium
        PORTC.INT0MASK |= (1<<4);	// INT0 mask
        encoder_reset_pulses(1);
        break;

    case 2:
        PORTC.DIRCLR = (1<<5);
        PORTC.PIN5CTRL |= (3<<3)|(1);

        PORTC.INTCTRL |= (2<<2);
        PORTC.INT1MASK |= (1<<5);	//INT1 mask
        encoder_reset_pulses(2);
        break;

    case 3:
        PORTD.DIRCLR = (1<<0);
        PORTD.PIN0CTRL |= (3<<3)|(1);

        PORTD.INTCTRL |= (2<<0);
        PORTD.INT0MASK |= (1<<0);
        encoder_reset_pulses(3);
        break;

    case 4:
        PORTD.DIRCLR = (1<<1);
        PORTD.PIN1CTRL |= (3<<3)|(1);

        PORTD.INTCTRL |= (2<<2);
        PORTD.INT1MASK |= (1<<1);
        encoder_reset_pulses(4);
        break;
    }
    PMIC.CTRL |= PMIC_MEDLVLEN_bm; //Enable Medium level Interuupts
    sei();
}
#else
//
// Encoders count
//
#define NUM_ENCODERS 2

//
// Encoder counter ticks per second
//
#define ENCODER_TICKS (F_CPU / 8 / 256)

//
// Pin configuration
//
static pin encoder_pins[NUM_ENCODERS] =
{
    PIN(E, 6),
    PIN(E, 7)
};

//
// Local variables
//
//static unsigned short encoder_ticks = 0;
static encoder_data encoder[NUM_ENCODERS];

//
// External interrupt.
//
ISR(INT6_vect)
{
    encoder_pulse(0);
}

//
// External interrupt.
//
ISR(INT7_vect)
{
    encoder_pulse(1);
}

//
// Specific encoder initialization
//
void encoder_init(unsigned char index)
{
    // Set pin as input
    pin_setup_input_with_pullup(encoder_pins[index]);

    // Enable external interrupts on falling edge.
    switch (index)
    {
    case 0:
        bit_set(EICRB, ISC61);
        bit_clear(EICRB, ISC60);
        bit_set(EIMSK, INT6);
        break;

    case 1:
        bit_set(EICRB, ISC71);
        bit_clear(EICRB, ISC70);
        bit_set(EIMSK, INT7);
        break;
    }

    // Reset data
    encoder[index].num_pulses = 0;
}

#endif

//
// Encoder pulse interrupt handler.
//
void encoder_pulse(unsigned char index)
{
    encoder[index].num_pulses++;
}

//
// Reset specific encoder number of pulses
//
void encoder_reset_pulses(unsigned char index)
{
    // Interrupt-safe resetting
    cli();
    encoder[index].num_pulses = 0;
    sei();
}

//
// Get specific encoder number of pulses
//
unsigned short encoder_get_pulses(unsigned char index)
{
    // Return number of pulses without disabling interrupts.
    return encoder[index].num_pulses;
}