#include <avr/io.h>

#define F_CPU 16000000UL
#include <util/delay.h>
#include <avr/interrupt.h>
#include <util/delay_basic.h>

#define Bus_Datos PORTK
#define Bus_Control PORTF
#define RS  PORTF5
#define RW  PORTF6
#define E   PORTF7

//Inicializar
void inicializar();
void inicializar_timer4(void);
void inicializar_timer0(void);
void inicializar_adc(void);

//Comandos para situar el cursor en inicio de líneas.
#define DDRA_LINEA1 0x80    //1000 0000
#define DDRA_LINEA2 0xC0    //1100 0000
#define DDRA_LINEA3 0x94    //1001 0100
#define DDRA_LINEA4 0xD4    //1101 0100



//Funciones para escribir en el LCD
void lcd_inicializar();
void lcd_escribir_c(char caracter);
void lcd_escribir(char *cadena);
void posicionar_cursor(unsigned char x, unsigned char y);
void lcd_comando(unsigned char comando);

void escribir_valor_decimal(unsigned int registro);
void escribir_espacio(unsigned long int registro);
void escribir_direccion(unsigned char valorl, unsigned char valorh);


/***************** VARIABLES ***********************/
volatile unsigned int cuenta;
volatile unsigned int cuenta_old;
volatile unsigned char contador;
volatile unsigned int suma_cuenta;
volatile unsigned long int espacio;
volatile unsigned char direccionl;
volatile unsigned char direccionh;
volatile unsigned char contador2;


int main(void)
{
    inicializar();
    lcd_inicializar();
    inicializar_timer4();
    inicializar_timer0();
    inicializar_adc();
    ADCSRA|= (1<<ADSC);    //Comenzar conversiones.

    char linea1[]="www.jmnlab.com";  //Poner en la Flash de programa las cadenas.
    char linea2[]="Contar los pulsos";
    char linea3[]="de un encoder";
    char linea4[]="Pulsos/seg:";
    char linea5[]="Velocidad:";
    char linea6[]="Espacio:";
    char linea7[]="V. Direccion:";

       posicionar_cursor(1,4);
    lcd_escribir(linea1);         
    posicionar_cursor(3,3);   
    lcd_escribir(linea2);
    posicionar_cursor(4,4);
    lcd_escribir(linea3);
    
    _delay_ms(3000);
    lcd_comando(0x01);

    posicionar_cursor(1,1);
    lcd_escribir(linea7);
    posicionar_cursor(2,1);
    lcd_escribir(linea4);
    posicionar_cursor(3,1);
    lcd_escribir(linea5);
    posicionar_cursor(4,1);
    lcd_escribir(linea6);
    posicionar_cursor(3,17);
    lcd_escribir_c('m');
    lcd_escribir_c('m');
    lcd_escribir_c('/');
    lcd_escribir_c('s');
    posicionar_cursor(4,17);
    lcd_escribir_c('m');

    unsigned int suma_cuenta_local=0;
    unsigned int velocidad=0;
    unsigned char direccion_locall=0;
    unsigned char direccion_localh=0;


    while(1)
    {

    if(contador>62)
    {
        suma_cuenta_local=suma_cuenta;
        contador=0;
        suma_cuenta=0;

        posicionar_cursor(2,13);
        escribir_valor_decimal(suma_cuenta_local);

        velocidad= suma_cuenta_local*12;
        posicionar_cursor(3,12);
        escribir_valor_decimal(velocidad);

        espacio= espacio + (unsigned long int)suma_cuenta_local*12036;
        posicionar_cursor(4,10);
        escribir_espacio(espacio);
    }   
    
    if(contador2>10)
    {
        direccion_locall=direccionl;
        direccion_localh=direccionh;
        posicionar_cursor(1,15);
        escribir_direccion(direccion_locall, direccion_localh);
        contador2=0;
    }

    }
}

void inicializar()              //Configurar hardware del micro.
{
    DDRK=0xFF;     //1111 1111
    PORTK=0x00;    
    DDRF|=((1<<RS)|(1<<RW)|(1<<E));     //1110 0000
    PORTF &= ~(1<<E);  
}

void inicializar_adc(void)
{
    ADMUX |=(1<<REFS0);                           //Tensión de referencia 5 voltios.
    ADMUX |=(1<<ADLAR);                                //8 bits de resolución.
                                                  //Canal ADC por defecto ADC0, no tenemos que cambiarlo.
    ADCSRA |=(1<<ADATE);                          //Habilitamos el Free Running Mode, por defecto seleccionado
    ADCSRA |=((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0));  //Prescaler 128, Fadc 125 KHz @ 16MHz.
    DIDR0 |= ((1<<ADC0D)|(1<<ADC1D));             //Apagamos la parte digital del puerto.
}

/*************************************************************************/
void lcd_inicializar() //Se mandan los comandos de configuración según el dibujo.
{
    _delay_ms(100);
                       
    lcd_comando(0x3C);    //Function Set RS0 RW0 0011NF** 0011 1100
    lcd_comando(0x3C);    //Function Set
    lcd_comando(0x0C);    //Display ON/OFF Control RS0 RW0 00001DCB 0000 1100
    lcd_comando(0x01);    //Display Clear RS0 RW0 00000001
    lcd_comando(0x06);    //Entry Mode Set RS0 RW0 000001I/DS 0000 0110
}

void lcd_escribir_c(char caracter)
{
    Bus_Control |= (1<<RS);        //RS1    Dato
    Bus_Control &= ~(1<<RW);       //RS0
    Bus_Datos = caracter;       
    Bus_Control |= (1<<E);
    _delay_us(100);
    Bus_Control &= ~(1<<E);
    _delay_ms(10);
}

void lcd_comando(unsigned char comando)
{
    Bus_Control &= ~(1<<RS);    //RS0   Comando
    Bus_Control &= ~(1<<RW);   //RW0
    Bus_Datos  = comando;           //Comando
    Bus_Control |= (1<<E);
    _delay_us(100);
    Bus_Control &= ~(1<<E);
    _delay_ms(10);
}

void lcd_escribir(char *cadena)
{
    char *inicio = cadena;
    unsigned char i=0;

    for(i=0;((inicio[i]!='\0')&&(i<20));i++)
    {
        Bus_Control |= (1<<RS);    //RS0 RW0 caracter
        Bus_Control &= ~(1<<RW);
        Bus_Datos  = inicio[i];       
        Bus_Control |= (1<<E);
        _delay_us(100);
        Bus_Control &= ~(1<<E);
        _delay_ms(3);
    }
}

void posicionar_cursor(unsigned char x, unsigned char y) //línea x 1 a 4, carácter y de 1 a 20.
{                                                                                               
    unsigned char comando=DDRA_LINEA1;
    switch(x)
    {
        case 1:
        {
            comando=DDRA_LINEA1+y-1;
        }break;
        case 2:
        {
            comando=DDRA_LINEA2+y-1;
        }break;
        case 3:
        {
            comando=DDRA_LINEA3+y-1;
        }break;
        case 4:
        {
            comando=DDRA_LINEA4+y-1;
        }break;
        default:break;
    }
        lcd_comando(comando);
}

/****************************************************************************************************/
void inicializar_timer4(void) //Configura el timer y la interrupción.
{
    DDRA |= (1<<PA7);    //Puerto A7 pin osciloscopio
//    OCR4A= 0x0F9F;    //16 ms
    OCR4A= 0x0534;        //5.33 ms
    TCCR4B |=((1<<WGM42)|(1<<CS41)|(1<<CS40));    //Los bits que no se tocan a 0 por defecto
    TIMSK4 |= (1<<OCIE4A);
    sei();
}

void inicializar_timer0(void)
{
    TCCR0B |=((1<<CS02)|(1<<CS01)|(1<<CS00));     //Normal Mode y reloj externo flanco de subida
}

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

ISR(TIMER4_COMPA_vect)
{

    unsigned int cuenta_actual=TCNT0;
    static char cont_int = 0;
    static unsigned char seleccion = 0;
    static unsigned int velocidad = 2000;
    static unsigned int posicion = 2000;
    unsigned int copia_ADCH=0;

    cont_int++;

    switch(cont_int)
    {
        case 1:
        {

           //Se lee el potenciometro y se asigna a posición o velocidad.
           ADMUX  &= ~(1<<MUX0);             //Canal 0.
           ADMUX  |= (1<<ADLAR);             //8 bits.
           ADCSRA |= (1<<ADEN);                 //Encendemos ADC
           ADCSRA |= (1<<ADSC);
           _delay_us(350);                     //Captura y conversión de la señal.
           copia_ADCH=ADCH;
           ADCSRA &= ~(1<<ADEN);             //Apagamos ADC
            
           //Asignar velocidad y dirección.
           if((PIND & (1<<PIND2))==0)
                   seleccion=0;
           else if((PIND & (1<<PIND3))==0)
                    seleccion=1;

           if(seleccion==0)
                   posicion=copia_ADCH*16;
            else
                   velocidad=copia_ADCH*16;
                           
           //Señal de control MiniZ
              PORTA |=(1<<PORTA7);
             _delay_us(400);
            PORTA &= ~(1<<PORTA7);
           _delay_loop_2(2400+posicion);

              PORTA |=(1<<PORTA7);
             _delay_us(400);
            PORTA &= ~(1<<PORTA7);
           _delay_loop_2(2400+velocidad);

              PORTA |=(1<<PORTA7);
             _delay_us(400);
            PORTA &= ~(1<<PORTA7);

        }break;

        case 2:
        {
               //Código del micro (leer enconders)
                if (cuenta_actual>cuenta_old)
                {
                    cuenta=cuenta_actual-cuenta_old;
                    suma_cuenta=suma_cuenta+cuenta;
                 }
                
                 else if(cuenta_actual<cuenta_old)
                 {
                cuenta=256+cuenta_actual-cuenta_old;
                suma_cuenta=suma_cuenta+cuenta;
              }

              cuenta_old=TCNT0;
              contador++;
        }break;

        case 3:
        {
            //Código micro (se lee la realimentación)
            cont_int=0;

            ADMUX  |= (1<<MUX0);                            //Seleccionamos ADC1 y 5V
            ADMUX  &= ~(1<<ADLAR);                          //10 bits
            ADCSRA |= (1<<ADEN);                           //Encendemos ADC
            ADCSRA |= (1<<ADSC);
            _delay_us(350);
            direccionl = ADCL;
            direccionh = ADCH;
            ADCSRA &= ~(1<<ADEN);                          //Apagamos ADC

            contador2++;
        }break;

        default:break;
    }
    

    TIFR4 |= (1<<OCF4A);
}

/************************* Escribir registros *****************************************************/

void escribir_valor_decimal(unsigned int registro) //Escribir binario a decimal.
{
    char miles=0;
    char centenas = 0;
    char decenas = 0;
    char unidades = 0;

    while(registro>=1000)
    {
        registro= registro -1000;
        miles++;
    }

    while(registro>=100)
    {
        registro= registro -100;
        centenas++;
    }
    while(registro>=10)
    {
        registro=registro -10;
        decenas++;
    }
    while(registro>0)
    {
        registro=registro-1;
        unidades++;
    }   
    lcd_escribir_c(miles | 0b00110000);
    lcd_escribir_c(centenas | 0b00110000);
    lcd_escribir_c(decenas | 0b00110000);
    lcd_escribir_c(unidades | 0b00110000);
}

void escribir_espacio(unsigned long int registro)
{


    char miles=0;
    char centenas = 0;
    char decenas = 0;
    char unidades = 0;
    char decena_cm=0;

    while(registro>=1000000000)
    {
        registro= registro -1000000000;
        miles++;
    }

    while(registro>=100000000)
    {
        registro= registro -100000000;
        centenas++;
    }

    while(registro>=10000000)
    {
        registro= registro -10000000;
        decenas++;
    }

    while(registro>=1000000)
    {
        registro= registro -1000000;
        unidades++;
    }

    while(registro>=100000)
    {
        registro= registro -100000;
        decena_cm++;
    }

 
    lcd_escribir_c(miles | 0b00110000);
    lcd_escribir_c(centenas | 0b00110000);
    lcd_escribir_c(decenas | 0b00110000);
    lcd_escribir_c(unidades | 0b00110000);
    lcd_escribir_c('.');
    lcd_escribir_c(decena_cm | 0b00110000);
}

void escribir_direccion(unsigned char valorl, unsigned char valorh)
{

    unsigned int registro=0;
    registro= (int)valorh*256+valorl;


    char miles=0;
    char centenas = 0;
    char decenas = 0;
    char unidades = 0;


    while(registro>=1000)
    {
        registro= registro -1000;
        miles++;
    }

    while(registro>=100)
    {
        registro= registro -100;
        centenas++;
    }

    while(registro>=10)
    {
        registro= registro -10;
        decenas++;
    }

    while(registro>=1)
    {
        registro= registro -1;
        unidades++;
    }


 
    lcd_escribir_c(miles | 0b00110000);
    lcd_escribir_c(centenas | 0b00110000);
    lcd_escribir_c(decenas | 0b00110000);
    lcd_escribir_c(unidades | 0b00110000);
}