/*
 * TMAP Sensor Fault Detection for ESD Assignment
 * PIC16F877A, 20MHz Crystal, 8-bit LCD on PORTB
 * A0 = MAP Signal, A1 = Temp Signal
 * Thresholds: <0.2V = Open, >4.8V = Short to Power
 */
 
#define _XTAL_FREQ 20000000
#include <xc.h>
 
// CONFIG bits for 20MHz HS oscillator
#pragma config FOSC = HS        // High speed crystal
#pragma config WDTE = OFF       // Watchdog off
#pragma config PWRTE = ON       // Power-up timer on
#pragma config BOREN = ON       // Brown-out reset on
#pragma config LVP = OFF        // Low voltage programming off
#pragma config CPD = OFF        // Data EEPROM protection off
#pragma config WRT = OFF        // Flash write protection off
#pragma config CP = OFF         // Code protection off
 
// LCD connections - 8 bit mode on PORTB
#define LCD_PORT PORTB
#define LCD_TRIS TRISB
#define RS RB0  // Register Select
#define EN RB1  // Enable
#define RW RB2  // Read/Write - tied low in hardware
 
// Function prototypes
void LCD_Init(void);
void LCD_Command(unsigned char cmd);
void LCD_Char(unsigned char data);
void LCD_String(const char *str);
void LCD_SetCursor(unsigned char row, unsigned char col);
void ADC_Init(void);
unsigned int ADC_Read(unsigned char channel);
void Delay_ms(unsigned int ms);
 
void main(void) {
    unsigned int adc_map, adc_temp;
    float volt_map, volt_temp;
    
    // Set ports: A0,A1 analog input, rest digital
    ADCON1 = 0x80;  // Right justified, AN0-AN1 analog, rest digital
    TRISA = 0x03;   // RA0, RA1 input. Rest output
    TRISB = 0x00;   // PORTB all output for LCD
    PORTB = 0x00;
    
    LCD_Init();
    ADC_Init();
    
    LCD_SetCursor(1,1);
    LCD_String("TMAP Sensor Test");
    Delay_ms(2000);
    LCD_Command(0x01); // Clear
    
    while(1) {
        // Read ADC: 0-1023 for 0-5V
        adc_map = ADC_Read(0);   // AN0 = MAP
        adc_temp = ADC_Read(1);  // AN1 = Temp
        
        // Convert to voltage: ADC * 5 / 1023
        volt_map = adc_map * 0.004887;
        volt_temp = adc_temp * 0.004887;
        
        LCD_SetCursor(1,1);
        
        // Priority: E01 Connector Open > E02/E03 Short
        // E01: Both signals < 0.2V = connector open
        if(volt_map < 0.20 && volt_temp < 0.20) {
            LCD_String("E01: OPEN CKT   ");
            LCD_SetCursor(2,1);
            LCD_String("Check Connector ");
        }
        // E02: MAP > 4.8V = Short to power
        else if(volt_map > 4.80) {
            LCD_String("E02: MAP SHORT  ");
            LCD_SetCursor(2,1);
            LCD_String("MAP=5V ");
            LCD_Command(0xC7); // Line 2, pos 7
            LCD_Char((adc_map/1000)%10 + '0');
            LCD_Char((adc_map/100)%10 + '0');
            LCD_Char((adc_map/10)%10 + '0');
            LCD_Char(adc_map%10 + '0');
        }
        // E03: Temp > 4.8V = Short to power  
        else if(volt_temp > 4.80) {
            LCD_String("E03: TEMP SHORT ");
            LCD_SetCursor(2,1);
            LCD_String("TMP=5V ");
            LCD_Command(0xC7);
            LCD_Char((adc_temp/1000)%10 + '0');
            LCD_Char((adc_temp/100)%10 + '0');
            LCD_Char((adc_temp/10)%10 + '0');
            LCD_Char(adc_temp%10 + '0');
        }
        // Normal condition
        else {
            LCD_String("System OK       ");
            LCD_SetCursor(2,1);
            LCD_String("M:");
            LCD_Char((adc_map/1000)%10 + '0');
            LCD_Char('.');
            LCD_Char(((adc_map*5)/102)%10 + '0');
            LCD_String("V T:");
            LCD_Char((adc_temp/1000)%10 + '0');
            LCD_Char('.');
            LCD_Char(((adc_temp*5)/102)%10 + '0');
            LCD_String("V  ");
        }
        Delay_ms(300);
    }
}
 
void ADC_Init(void) {
    ADCON0 = 0x41; // ADC ON, Fosc/16, Channel 0
    ADCON1 = 0x80; // Right justified, Vref = Vdd/Vss
}
 
unsigned int ADC_Read(unsigned char channel) {
    ADCON0 &= 0xC5; // Clear channel bits
    ADCON0 |= channel << 3; // Set channel
    Delay_ms(1);    // Acquisition time
    GO_nDONE = 1;   // Start conversion
    while(GO_nDONE); // Wait
    return ((ADRESH << 8) + ADRESL); // Return 10-bit result
}
 
void LCD_Init(void) {
    LCD_TRIS = 0x00; // PORTB output
    Delay_ms(20);
    LCD_Command(0x38); // 8-bit, 2 line, 5x7
    LCD_Command(0x0C); // Display on, cursor off
    LCD_Command(0x06); // Auto increment
    LCD_Command(0x01); // Clear display
    Delay_ms(2);
}
 
void LCD_Command(unsigned char cmd) {
    RS = 0; // Command mode
    RW = 0; // Write
    LCD_PORT = (LCD_PORT & 0x03) | (cmd & 0xFC); // Keep RS,EN, put data
    if(cmd & 0x02) RB1 = 1; else RB1 = 0; // D1
    if(cmd & 0x01) RB0 = 1; else RB0 = 0; // D0
    EN = 1; Delay_ms(1); EN = 0; Delay_ms(2);
    LCD_PORT = cmd; // Send full byte
    EN = 1; Delay_ms(1); EN = 0; Delay_ms(2);
}
 
void LCD_Char(unsigned char data) {
    RS = 1; // Data mode
    RW = 0;
    LCD_PORT = data;
    EN = 1; Delay_ms(1); EN = 0; Delay_ms(2);
}
 
void LCD_String(const char *str) {
    while(*str) LCD_Char(*str++);
}
 
void LCD_SetCursor(unsigned char row, unsigned char col) {
    if(row == 1) LCD_Command(0x80 + (col-1));
    else LCD_Command(0xC0 + (col-1));
}
 
void Delay_ms(unsigned int ms) {
    while(ms--) __delay_ms(1);
}
 