/*
 * Universidad Rural de Guatemala - Electronica Digital FE731
 * Practica final, version PIC16F1829: contador binario con pantalla LCD
 * SUMAR suma 1 y BORRAR regresa a 0. La cuenta, de 0 a 15, se ve en
 * cuatro LEDs y en la LCD (I2C), en decimal y en binario.
 * Compilador: MPLAB XC8, en un proyecto de MPLAB X para el PIC16F1829.
 */
#include <xc.h>
#include <stdint.h>

#pragma config FOSC = INTOSC    // oscilador interno: no lleva cristal
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = ON       // terminal 4: 10 kohm a +5 V
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
#pragma config WRT = OFF
#pragma config PLLEN = OFF
#pragma config STVREN = ON
#pragma config BORV = LO
#pragma config LVP = ON         // permite grabarlo tambien con un MPLAB Snap

#define _XTAL_FREQ 4000000      // 4 MHz, lo usa __delay_ms()

// Terminales: los mismos del protoboard de la version PICAXE-20M2
#define SUMAR   PORTCbits.RC5   // terminal 5, boton
#define BORRAR  PORTCbits.RC2   // terminal 14, puente a +5 V
#define LED0    LATBbits.LATB7  // terminal 10, bit 0 (derecha)
#define LED1    LATCbits.LATC7  // terminal 9, bit 1
#define LED2    LATCbits.LATC6  // terminal 8, bit 2
#define LED3    LATCbits.LATC3  // terminal 7, bit 3 (izquierda)

// Direccion de 8 bits: 0x27 por 2 = 0x4E.
// Si la LCD solo muestra cuadros, use 0x7E (0x3F por 2).
#define DIR_LCD 0x4E

uint8_t cuenta = 0;

// ---- Bus I2C con el modulo MSSP: SDA en el terminal 13, SCL en el 11 ----

void i2cEspera(void) {
    while ((SSP1CON2 & 0x1F) || (SSP1STAT & 0x04)) {
    }
}

void i2cInicio(void) {
    i2cEspera();
    SSP1CON2bits.SEN = 1;
}

void i2cFin(void) {
    i2cEspera();
    SSP1CON2bits.PEN = 1;
}

void i2cEscribir(uint8_t dato) {
    i2cEspera();
    SSP1BUF = dato;
}

// ---- Manejo de la LCD por I2C (modulo PCF8574) ----
// Cada byte del PCF8574 lleva: D7 D6 D5 D4 | luz | EN | RW | RS.
// La LCD recibe cada byte en dos mitades de 4 bits; toma cada mitad
// cuando EN pasa de 1 a 0. modo: 0 = orden para la LCD, 1 = letra.

void enviarMitad(uint8_t mitad, uint8_t modo) {
    i2cInicio();
    i2cEscribir(DIR_LCD);
    i2cEscribir(mitad | 0x0C | modo);   // luz encendida, EN = 1
    i2cEscribir(mitad | 0x08 | modo);   // luz encendida, EN = 0
    i2cFin();
}

void enviarByte(uint8_t dato, uint8_t modo) {
    enviarMitad(dato & 0xF0, modo);
    enviarMitad((uint8_t)(dato << 4), modo);
}

void orden(uint8_t o) {
    enviarByte(o, 0);
    if (o <= 0x02) {
        __delay_ms(2);                  // borrar la pantalla tarda mas
    }
}

void escribir(const char *texto) {
    while (*texto) {
        enviarByte((uint8_t)*texto, 1);
        texto++;
    }
}

void iniciarLCD(void) {
    __delay_ms(50);
    enviarMitad(0x30, 0); __delay_ms(5);
    enviarMitad(0x30, 0); __delay_ms(1);
    enviarMitad(0x30, 0); __delay_ms(1);
    enviarMitad(0x20, 0); __delay_ms(1);  // modo de 4 bits
    orden(0x28);                        // 2 lineas
    orden(0x0C);                        // pantalla encendida, sin cursor
    orden(0x06);                        // el cursor avanza a la derecha
    orden(0x01);                        // borrar
}

// ---- El contador ----

void ponerLEDs(void) {
    LED0 = cuenta & 1;
    LED1 = (cuenta >> 1) & 1;
    LED2 = (cuenta >> 2) & 1;
    LED3 = (cuenta >> 3) & 1;
}

void mostrar(void) {
    ponerLEDs();
    orden(0x89);                        // linea 1, columna 9
    enviarByte('0' + cuenta / 10, 1);   // decenas
    enviarByte('0' + cuenta % 10, 1);   // unidades
    orden(0xC9);                        // linea 2, columna 9
    for (int8_t b = 3; b >= 0; b--) {
        enviarByte('0' + ((cuenta >> b) & 1), 1);
    }
}

void main(void) {
    OSCCON = 0x68;                      // oscilador interno a 4 MHz

    ANSELA = 0;                         // todos los terminales digitales
    ANSELB = 0;
    ANSELC = 0;
    LATA = 0;
    LATB = 0;
    LATC = 0;
    TRISA = 0b00001000;                 // RA3 (terminal 4) es MCLR
    TRISB = 0b01010000;                 // RB4 y RB6: SDA y SCL del I2C
    TRISC = 0b00100100;                 // RC5 SUMAR y RC2 BORRAR: entradas

    SSP1ADD = 9;                        // 4 MHz / (4 x 10) = 100 kHz
    SSP1STAT = 0x80;
    SSP1CON1 = 0x28;                    // MSSP como maestro I2C

    iniciarLCD();
    orden(0x80);                        // linea 1
    escribir("URURAL  FE731");
    orden(0xC0);                        // linea 2
    escribir("Contador binario");

    // Prueba de los LEDs: la luz camina del bit 0 al bit 3
    for (uint8_t k = 0; k < 4; k++) {
        cuenta = (uint8_t)(1 << k);
        ponerLEDs();
        __delay_ms(400);
    }

    cuenta = 0;
    orden(0x01);                        // borrar la pantalla
    orden(0x80);
    escribir("Decimal:");
    orden(0xC0);
    escribir("Binario:");
    mostrar();

    while (1) {
        if (SUMAR == 1) {
            cuenta = cuenta + 1;
            if (cuenta > 15) {
                cuenta = 0;
            }
            mostrar();
            __delay_ms(20);             // deja pasar el rebote
            while (SUMAR == 1) {        // espera a que se suelte
            }
            __delay_ms(20);
        }

        if (BORRAR == 1) {
            cuenta = 0;
            mostrar();
            __delay_ms(20);
            while (BORRAR == 1) {
            }
            __delay_ms(20);
        }
    }
}
