//Interfaz MiniZ
#include <avr/io.h>

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

#define Bus_Datos PORTC
#define Bus_Control PORTA

#define RS  PORTA5               
#define RW  PORTA6
#define E   PORTA7

#define LED1 PORTL7  //Amarillo
#define LED2 PORTL5  //Rojo
#define LED3 PORTG1  //Verde

//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

void inicializar_puertos(void);
void reseteo(void);
void escribir_valor_decimal(unsigned char registro);

//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(char comando);
char lcd_invertir_bits(char caracter);

//Funciones I2C
void i2c_ST(unsigned char dato);
unsigned char i2c_SR(void);
void i2c_error(void);

//Programas
void programa0(void);
void programa1(void);
void programa2(void);
void programa3(void);

//Variables
volatile unsigned char botones;
volatile unsigned char programa;
volatile unsigned char i2c_enviar;
volatile unsigned char i2c_recibido;
volatile unsigned char boton_libre;
volatile unsigned char comunicaciones;

int main(void)
{
    inicializar_puertos();
    reseteo();
    lcd_inicializar();

    while(1)
    {
        switch(programa)
        {
        case 3:
            programa3();
            break;

        case 2:
            programa2();
            break;

        case 1:
            programa1();
            break;

        case 0:
            programa0();
            break;

        default: break;
        }

//    unsigned char copia_ADCH;

/*        ADMUX  &= ~(1<<MUX0);             //Canal 0.
        _delay_us(600);                     
        copia_ADCH=ADCH;
         posicionar_cursor(3,10);
        escribir_valor_decimal(copia_ADCH);

         posicionar_cursor(4,10);        //Canal 1
        ADMUX  |= (1<<MUX0);    
        _delay_us(600);                     
        copia_ADCH=ADCH;
        escribir_valor_decimal(copia_ADCH);*/
    }
}

void inicializar_puertos(void)              //Configurar hardware del pic.
{
   //Configurar entradas y salidas.
   DDRC=0xFF;     //1111 1111
   PORTC=0x00;
      
   DDRA|=((1<<RS)|(1<<RW)|(1<<E));     //1110 0000
   PORTA &= ~(1<<E);  
    
   DDRL= 0xA0;      //1010 0000
   PORTL=0x00;

   DDRG =0x02;      //0000 0010
   PORTG=0x00;

   //Configurar interrupciones externas.
   PCICR |= (1<<PCINT2);
   PCMSK2 |= 0xFE;
   sei();

   //Configurar ADC
    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.
    ADCSRA |= (1<<ADEN);                          //Encendemos ADC
    ADCSRA|= (1<<ADSC);                              //Comenzar conversiones.

    //Configurar I2C
    TWAR = 0xE2;                //Dirección de esclavo en el bus I2C.
    TWCR |= ((1<<TWEA)|(1<<TWEN)|(1<<TWIE));
}

void lcd_inicializar() //Se mandan los comandos de configuración según el datasheet.
{
    _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 = lcd_invertir_bits(caracter);       
    Bus_Control |= (1<<E);
    _delay_us(100);
    Bus_Control &= ~(1<<E);
    _delay_ms(1);
}

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

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  = lcd_invertir_bits(inicio[i]);       
        Bus_Control |= (1<<E);
        _delay_us(100);
        Bus_Control &= ~(1<<E);
        _delay_ms(1);
    }
}

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);
}

char lcd_invertir_bits(char caracter)
{
    char copia_caracter=caracter;
    char caracter_cambiado=0x00;

    caracter_cambiado |= (((copia_caracter)&(0x80))>>7);

    copia_caracter=caracter;
    caracter_cambiado |= (((copia_caracter)&(0x40))>>5);

    copia_caracter=caracter;
    caracter_cambiado |= (((copia_caracter)&(0x20))>>3);

    copia_caracter=caracter;
    caracter_cambiado |= (((copia_caracter)&(0x10))>>1);

    copia_caracter=caracter;
    caracter_cambiado |= (((copia_caracter)&(0x08))<<1);

    copia_caracter=caracter;
    caracter_cambiado |= (((copia_caracter)&(0x04))<<3);

    copia_caracter=caracter;
    caracter_cambiado |= (((copia_caracter)&(0x02))<<5);

    copia_caracter=caracter;
    caracter_cambiado |= (((copia_caracter)&(0x01))<<7);    
            
    return(caracter_cambiado);
}

void reseteo(void)
{

    PORTL |= ((1<<LED1)|(1<<LED2));
    PORTG |= (1<<LED3);
    _delay_ms(500);
    PORTL &= ~((1<<LED1)|(1<<LED2));
    PORTG &= ~(1<<LED3);
    _delay_ms(500);
    PORTL |= ((1<<LED1)|(1<<LED2));
    PORTG |= (1<<LED3);
    _delay_ms(500);
    PORTL &= ~((1<<LED1)|(1<<LED2));
    PORTG &= ~(1<<LED3);
    _delay_ms(500);
    PORTL |= ((1<<LED1)|(1<<LED2));
    PORTG |= (1<<LED3);
    _delay_ms(500);
    PORTL &= ~((1<<LED1)|(1<<LED2));
    PORTG &= ~(1<<LED3);
}

ISR(PCINT2_vect)
{

    if ((PINK&(1<<PINK1))==0)
        {
            botones=7;
            boton_libre=0;
        }

    else if ((PINK&(1<<PINK2))==0)
        {
            botones=6;
            boton_libre=0;
        }


    else if ((PINK&(1<<PINK3))==0)
        {
            botones=5;
            boton_libre=0;
        }


    else if ((PINK&(1<<PINK4))==0)
        {
            botones=4;
            boton_libre=0;
        }


    else if ((PINK&(1<<PINK5))==0)
        {
            botones=3;
            boton_libre=0;
        }


    else if ((PINK&(1<<PINK6))==0)
        {
            botones=2;
            boton_libre=0;
        }


    else if ((PINK&(1<<PINK7))==0)
        {
            botones=1;
            boton_libre=0;
        }

    PCIFR  |=(1<<PCIF2);
}

void escribir_valor_decimal(unsigned char registro) //Escribir binario a decimal.
{
    char centenas = 0;
    char decenas = 0;
    char unidades = 0;
    while(registro>=100)
    {
        registro= registro -100;
        centenas++;
    }
    while(registro>=10)
    {
        registro=registro -10;
        decenas++;
    }
    while(registro>0)
    {
        registro=registro-1;
        unidades++;
    }   
    lcd_escribir_c(centenas | 0b00110000);
    lcd_escribir_c(decenas | 0b00110000);
    lcd_escribir_c(unidades | 0b00110000);
}
/************************************************************************************/
void programa0()
{
    char linea1[]="Interfaz MiniZ";  
    char linea2[]="con Arduino MeGa";
    char linea3[]="www.jmnlab.com";

    PORTL &= ~((1<<LED1)|(1<<LED2));
    PORTG &= ~(1<<LED3);

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

    while(programa==0)
    {
        switch(botones)
        {

        case 7:
            break;

        case 6:
            break;

        case 5:
            break;

        case 4:
            programa=0;
            break;

        case 3:
            programa=3;
            break;

        case 2:
            programa=2;
            break;

        case 1:
            programa=1;
            break;

        default: break;
        }

    PORTG |= (1<<LED3);
    PORTL |= (1<<LED1);
    PORTL |= (1<<LED2);
    _delay_ms(400);

    PORTL &= ~((1<<LED1)|(1<<LED2));
    PORTG &= ~(1<<LED3);
    _delay_ms(400);    
    }
}

void programa1()
{
//    unsigned int tdir=0;
//    unsigned int tvel=0;
//    unsigned int direccion=0;
//    unsigned char velocidad=0;
//    unsigned int sensores=0;

    lcd_comando(0x01);
    PORTL &= ~((1<<LED1)|(1<<LED2));
    PORTG |= (1<<LED3);

    lcd_escribir_c('D');
    lcd_escribir_c('.');
    posicionar_cursor(1,10);
    lcd_escribir_c('m');
    lcd_escribir_c('s');
    lcd_escribir_c(' ');
    lcd_escribir_c('R');
    lcd_escribir_c(':');

    posicionar_cursor(2,1);
    lcd_escribir_c('V');
    lcd_escribir_c('.');
    posicionar_cursor(2,10);
    lcd_escribir_c('m');
    lcd_escribir_c('s');

    posicionar_cursor(3,1);
    lcd_escribir_c('E');
    lcd_escribir_c('.');
    lcd_escribir_c('D');
    lcd_escribir_c(':');

    posicionar_cursor(3,10);
    lcd_escribir_c('E');
    lcd_escribir_c('.');
    lcd_escribir_c('I');
    lcd_escribir_c(':');

    posicionar_cursor(4,1);
    lcd_escribir_c('S');
    lcd_escribir_c(':');
    posicionar_cursor(4,16);
    lcd_escribir_c('B');
    lcd_escribir_c(':');
    

    while(programa==1)
    {
        switch(botones)
        {

        case 7:
            break;

        case 6:
            break;

        case 5:
            break;

        case 4:
            programa=0;
            break;

        case 3:
            programa=3;
            break;

        case 2:
            programa=2;
            break;

        case 1:
            programa=1;
            break;

        default: break;
        }    
    }
}


void programa2()
{

    static unsigned char copia_i2c_enviar=0;
    static unsigned char copia_i2c_recibido=0;

    lcd_comando(0x01);
    PORTL &= ~(1<<LED2);
    PORTG &= ~(1<<LED3);
    PORTL |= (1<<LED1);

    lcd_escribir("Prueba conexion I2C.");
    posicionar_cursor(2,1);
    lcd_escribir("Led V I:     C:");
    posicionar_cursor(3,1);
    lcd_escribir("Led A I:     C:");
    posicionar_cursor(4,1);
    lcd_escribir("Led R I:     C:");

    while(programa==2)
    {
        switch(botones)
        {

        case 7:
            if(boton_libre==0)
            {
                boton_libre=1;    
                i2c_enviar ^=0x02;
            }
            break;

        case 6:
            if(boton_libre==0)
            {
                boton_libre=1;    
                i2c_enviar ^=0x01;
            }
            break;

        case 5:
            if(boton_libre==0)
            {
                boton_libre=1;    
                i2c_enviar ^=0x04;
            }
            break;

        case 4:
            programa=0;
            break;

        case 3:
            programa=3;
            break;

        case 2:
            programa=2;
            break;

        case 1:
            programa=1;
            break;

        default: break;
        }

        copia_i2c_enviar=i2c_enviar;
        copia_i2c_recibido=i2c_recibido;
        
    
        if(((copia_i2c_enviar)&(0x01))!=0)
        {
            posicionar_cursor(2,10);
            lcd_escribir_c('O');
            lcd_escribir_c('N');
            lcd_escribir_c(' ');
        }
        else
        {
            posicionar_cursor(2,10);
            lcd_escribir_c('O');
            lcd_escribir_c('F');
            lcd_escribir_c('F');
        }    

        copia_i2c_enviar=i2c_enviar;
        if(((copia_i2c_enviar)&(0x02))!=0)
        {
            posicionar_cursor(3,10);
            lcd_escribir_c('O');
            lcd_escribir_c('N');
            lcd_escribir_c(' ');
        }
        else
        {
            posicionar_cursor(3,10);
            lcd_escribir_c('O');
            lcd_escribir_c('F');
            lcd_escribir_c('F');
        }

        copia_i2c_enviar=i2c_enviar;
        if(((copia_i2c_enviar)&(0x04))!=0)
        {
            posicionar_cursor(4,10);
            lcd_escribir_c('O');
            lcd_escribir_c('N');
            lcd_escribir_c(' ');
        }
        else
        {
            posicionar_cursor(4,10);
            lcd_escribir_c('O');
            lcd_escribir_c('F');
            lcd_escribir_c('F');
        }

        if(((copia_i2c_recibido)&(0x01))!=0)
        {
            posicionar_cursor(2,17);
            lcd_escribir_c('O');
            lcd_escribir_c('N');
            lcd_escribir_c(' ');
        }
        else
        {
            posicionar_cursor(2,17);
            lcd_escribir_c('O');
            lcd_escribir_c('F');
            lcd_escribir_c('F');
        }    

        copia_i2c_recibido=i2c_recibido;
        if(((copia_i2c_recibido)&(0x02))!=0)
        {
            posicionar_cursor(3,17);
            lcd_escribir_c('O');
            lcd_escribir_c('N');
            lcd_escribir_c(' ');
        }
        else
        {
            posicionar_cursor(3,17);
            lcd_escribir_c('O');
            lcd_escribir_c('F');
            lcd_escribir_c('F');
        }

        copia_i2c_recibido=i2c_recibido;
        if(((copia_i2c_recibido)&(0x04))!=0)
        {
            posicionar_cursor(4,17);
            lcd_escribir_c('O');
            lcd_escribir_c('N');
            lcd_escribir_c(' ');
        }
        else
        {
            posicionar_cursor(4,17);
            lcd_escribir_c('O');
            lcd_escribir_c('F');
            lcd_escribir_c('F');
        }
    }
}


void programa3()
{
    lcd_comando(0x01);
    PORTL &= ~(1<<LED1);
    PORTG &= ~(1<<LED3);
    PORTL |= (1<<LED2);
    
    posicionar_cursor(2,3);
    lcd_escribir("No Implementado");

    while(programa==3)
    {
        switch(botones)
        {

        case 7:
            break;

        case 6:
            break;

        case 5:
            break;

        case 4:
            programa=0;
            break;

        case 3:
            programa=3;
            break;

        case 2:
            programa=2;
            break;

        case 1:
            programa=1;
            break;

        default: break;
        }    
    }
}

ISR(TWI_vect)
{
    if(programa==2)
    {
        if((TWSR & 0xF8) == 0x60)
            i2c_recibido=i2c_SR();
        else if((TWSR & 0xF8) == 0xA8)  
            i2c_ST(i2c_enviar);    
        else
            i2c_error();
    }
    TWCR |= (1<<TWINT);
}

void i2c_ST(unsigned char dato)
{

    TWDR=dato;                        //Cargar el dato a enviar.
    TWCR &= ~((1<<TWSTO)|(1<<TWEA));
    TWCR |= (1<<TWINT);                //Enviar dato.

    while (!(TWCR & (1<<TWINT)))    //Esperar a TWINT
    {
    }

    if((TWSR & 0xF8) != 0xC8);        //Dato enviado, no esperamos ACK.
        i2c_error();

    TWCR &= ~((1<<TWSTO)|(1<<TWSTA));//Dejamos el módulo preparado.
    TWCR |= ((1<<TWINT)|(1<<TWEA));
}

unsigned char i2c_SR(void)
{
    unsigned char dato=0;

    TWCR &= ~(1<<TWSTO);                //Preparados para recibir dato y devolver ACK
    TWCR |= ((1<<TWINT)|(1<<TWEA));

    while (!(TWCR & (1<<TWINT)))        //Esperar a TWINT
    {
    }

    if((TWSR & 0xF8) != 0x80);            //Hemos recibido dato y generado ACK.
        i2c_error();
    dato=TWDR;                            //Almacenamos dato recibido.

    TWCR &= ~(1<<TWSTO);                //Preparados para recibir STOP
    TWCR |= ((1<<TWINT)|(1<<TWEA));

    while (!(TWCR & (1<<TWINT)))        //Esperar a TWINT
    {
    }

    if((TWSR & 0xF8) != 0xA0);            //STOP
        i2c_error();

    TWCR &= ~((1<<TWSTO)|(1<<TWSTA));    //Dejamos el módulo preparado.
    TWCR |= ((1<<TWINT)|(1<<TWEA));

    return(dato);
}

void i2c_error(void)
{
}