Work in progress

PIC 16F628A Engine Timer

The idea behind this circuit is that I have no idea of how many hours my Volvo Penta is running during a season. With this circuit, I can monitor the hours, and change the motor oil according to the timer.

The program write run time to the PIC EEPROM, so I have good control of how long the motor has run.

The schematic

This is in the making, the circuit is breadboarded, and now getting transferred to eaglCAD and int o a proper schematic

The code

The code is pretty much done, but needs some more comments and fine tuning.

// INCLUDES
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// CONFIG
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator: High-speed crystal/resonator on RA6/OSC2/CLKOUT and RA7/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON      // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

#pragma warning disable 359     // Disable warning 359 - illegal conv between pointer types
#pragma warning disable 373     // Disable warning 373 - signed to unsigned conversion

// DEFINITIONS
#define _XTAL_FREQ 16000000     // refenrence freq for the compiler  
#define Rs PORTAbits.RA0        // Rs is connected to RA0
#define En PORTAbits.RA1        // En is connected to RA1

#define lcd_port TRISB          // lcd port b
#define lcdLine1 0x00           // line one begins here
#define lcdLine2 0x40           // line two begins here

// VARIABLES
volatile char counter,total_run_counter, buffer[3];
volatile int
        seconds     = 0,
        minutes     = 0,
        hours       = 0,
        tot_days    = 0,
        tot_hours   = 0,
        tot_minutes = 0;
unsigned int len, i, total_run_hours;
unsigned char days_adr      = 0x00;
unsigned char hours_adr     = 0x10;
unsigned char minutes_adr   = 0x20;

// FUNCTION PROTOTYPE
void interrupt ISR();
void check_for_erase();
unsigned char eeprom_read_data(unsigned char address);
void eeprom_write_data();
void eeprom_writestr(unsigned char msg[], unsigned char address);
void lcd_cmd(unsigned char cmd);
void lcd_data(unsigned char dat);
void lcd_init(void);
void lcd_line1(void);
void lcd_line2(void);
void lcd_setcursor(unsigned char loc);
int lcd_strobe(void);
void lcd_write(unsigned char dat);
void lcd_writestr(const unsigned char *c);
void read_total_run_time();
void show_time();
void show_total_run_hours();
void show_total_time();
void show_welcome();
void timer_init();
void update_time();
void write_time_to_eeprom();

// function to pulse the En pin high
int lcd_strobe(void)
{
    En = 1;
    __delay_us(1);
    En = 0;
}

// function to write to the lcd port
void lcd_write(unsigned char dat)
{
    lcd_port &= 0x0f;           // get current port and clear upper bits
    lcd_port |= (dat & 0xf0);   // combine upper and lower, leave lower 
    lcd_strobe();

    lcd_port &= 0x0f;
    lcd_port |= ((dat << 4) & (0xf0));  // combine upper and lower
    lcd_strobe();
    __delay_ms(2);                      // little delay for lcd to process
}

// function to set lcd in command mode
void lcd_cmd(unsigned char cmd)
{
    Rs = 0;             // rs low for cmd mode
    lcd_write(cmd);
}

// functionto set lcd in data mode
void lcd_data (unsigned char dat)
{
    Rs = 1;             // rs high for data mode
    lcd_write(dat);
}

// function to initialize the lcd
void lcd_init(void)
{
    lcd_port &= 0x0f;   // clear upper bits
    lcd_port |= 0x30;   // send data to d7-4
    Rs = 0;
    lcd_strobe();
    __delay_ms(10);
    lcd_strobe();;
    __delay_ms(10);
    lcd_strobe();
    __delay_ms(10);

    lcd_port &= 0x0f;   // clear upper bits
    lcd_port |= 0x30;   // send data to d7-4
    lcd_strobe();
    __delay_ms(50);

    lcd_cmd(0x28);  // 4-bit, 2-line, 5x7 font
    lcd_cmd(0x0e);  // display on, cursor on, blink off
    lcd_cmd(0x06);  // auto increment, no shift
    lcd_cmd(0x80);  // address ddram with 0 offset 80h
}

// function to write a strng to the lcd
void lcd_writestr(const unsigned char *c)
{
    while (*c != '\0')
    {
        lcd_data(*c);
        c++;
    }
}

// function to place the cursor on the lcd
void lcd_setcursor(unsigned char loc)
{
    lcd_cmd(loc | 0x80);
}

// function to move cursor to 1st place on line 1
void lcd_line1(void)
{
    lcd_setcursor(lcdLine1);
}

// function to move cursor to 1st place on line 2
void lcd_line2(void)
{
    lcd_setcursor(lcdLine2);
}

// Finction to initiate the timer
void timer_init()
{
    TMR1H = TMR1L = 0;
    T1CONbits.T1CKPS = 1;
    T1CONbits.T1CKPS0 = 0;
    PIE1bits.TMR1IE = 1;
    T1CONbits.TMR1ON = 1;
    INTCONbits.PEIE = 1;
    INTCONbits.GIE = 1;
    T1CONbits.TMR1CS = 0;
}

// Function that shows a welcome message
void show_welcome()
{
    lcd_writestr("  ENGINE TIMER  ");
    lcd_line2();
    lcd_writestr("PIC16F628a v0.1a");
    __delay_ms(2000);    
}

// Function that shows the current trip time 
void show_time()
{
    lcd_setcursor(0);
    lcd_writestr("Trip: ");
    itoa(buffer, hours, 10);
    if (hours < 10)
    {
        lcd_writestr("0");
    }
    lcd_writestr(buffer);
    lcd_writestr(":");
    itoa(buffer, minutes, 10);
    if (minutes < 10)
    {
        lcd_writestr("0");
    }
    lcd_writestr(buffer);
    lcd_writestr(":");
    itoa(buffer, seconds, 10);
    if (seconds < 10)
    {
        lcd_writestr("0");
    }            
    lcd_writestr(buffer);
    lcd_writestr("   ");
    if (seconds < 30)
    {
        show_total_time();
    }
    else
    {
        show_total_run_hours();
    }
}

// Function that shows the total run time
void show_total_time()
{
    lcd_setcursor(0x40);
    lcd_writestr("Tot: ");

    itoa(buffer, tot_days, 10);
    lcd_writestr(buffer);
    lcd_writestr("d ");

    itoa(buffer, tot_hours, 10);
    lcd_writestr(buffer);
    lcd_writestr("h ");

    itoa(buffer, tot_minutes, 10);
    lcd_writestr(buffer);
    lcd_writestr("m   ");
}

// Function that calculates all run time to hours, and shows it
void show_total_run_hours()
{
    total_run_hours = (tot_days * 24) + tot_hours;
    lcd_setcursor(0x40);
    lcd_writestr("Tot: ");
    itoa(buffer, total_run_hours, 10);
    lcd_writestr(buffer);
    lcd_writestr(" hrs     ");
}

// Function that updates the time
void update_time()
{
    seconds++;
    if (seconds == 60)
    {
        minutes += 1;
        tot_minutes += 1;
        seconds = 0;
    }
    if (minutes == 60)
    {
        hours += 1;
        minutes = 0;
    }
    if (hours == 24)
    {
        hours = 0;
    }
    if (tot_minutes == 60)
    {
        tot_hours += 1;
        tot_minutes = 0;
    }
    if (tot_hours == 24)
    {
        tot_days += 1;
        tot_hours = 0;
    }
}

// Function to handle interrupts
// Counter = Fosc / instruction cycle * prescaler * timer1 resolution
// Counter = 16000000 / (4 * 1 * (2^16))
// Counter = 61.035
void interrupt ISR()
{
    if (PIR1bits.TMR1IF == 1)
    {
        counter++;  
        if (counter == 61)
        {
            update_time();
            show_time();
//            show_total_run_hours();                
//            show_total_time();                
            write_time_to_eeprom();
            counter = 0;
        }
        PIR1bits.TMR1IF = 0;
    }
    if (PORTBbits.RB1 == 1)
    {
        lcd_setcursor(0);
        check_for_erase();
    }
}

// Function that reads an EEPROM address
unsigned char eeprom_read_data(unsigned char address)
{
    volatile unsigned char retval;
    unsigned char save_gie;
    save_gie = INTCONbits.GIE;
    EEADR = address;
    EECON1bits.RD = 1;
    retval = EEDATA;
    return retval;
}

// Function that reads the total run time.
void read_total_run_time()
{
    buffer[0] = eeprom_read_data(0x00);
    buffer[1] = eeprom_read_data(0x01);
    tot_days = atoi(buffer);

    buffer[0] = eeprom_read_data(0x10);
    buffer[1] = eeprom_read_data(0x11);
    tot_hours = atoi(buffer);

    buffer[0] = eeprom_read_data(0x20);
    buffer[1] = eeprom_read_data(0x21);
    tot_minutes = atoi(buffer);    
}

// Function that write EEDATA to EEADR
void eeprom_write_data()
{
    EECON1bits.WREN     = 1;
    INTCONbits.GIE      = 0;
    INTCONbits.PEIE     = 0;
    EECON2              = 0x55;
    EECON2              = 0xAA;
    EECON1bits.WR       = 1;
    INTCONbits.GIE      = 1;
    INTCONbits.PEIE     = 1;
    while (!PIR1bits.EEIF);
    PIR1bits.EEIF       = 0;
}

// Function that writes a string to the EEPROM
void eeprom_writestr(unsigned char msg[], unsigned char address)
{
    len = strlen(msg);
    for (i=0; i<len; i++)
    {
        EEADR = address + i;
        EEDATA = msg[i];
        eeprom_write_data(msg[i]);
    }
    EECON1bits.WREN = 0;   
}

// Function writes the total run time to EEPROM
void write_time_to_eeprom()
{
    itoa(buffer, tot_days, 10);
    eeprom_writestr(buffer, days_adr);
    if (tot_days < 10)
    {
        EEADR = 0x01;
        EEDATA = 0xFF;
        eeprom_write_data();
    }

    itoa(buffer, tot_hours, 10);
    eeprom_writestr(buffer, hours_adr);
    if (tot_hours < 10)
    {
        EEADR = 0x11;
        EEDATA = 0xFF;
        eeprom_write_data();
    }

    itoa(buffer, tot_minutes, 10);
    eeprom_writestr(buffer, minutes_adr);
    if (tot_minutes < 10)
    {
        EEADR = 0x21;
        EEDATA = 0xFF;
        eeprom_write_data();
    }    
}

// Function to erase the EEPROM
void check_for_erase()
{
    T1CONbits.TMR1ON = 0;
    lcd_writestr("   ERASE ALL!   ");
    lcd_setcursor(0x40);
    lcd_writestr("                ");
    EEDATA = 0xFF;
    EEADR = 0x00;
    eeprom_write_data();

    EEADR = 0x01;
    eeprom_write_data();    

    EEADR = 0x10;
    eeprom_write_data();

    EEADR = 0x11;
    eeprom_write_data(); 

    EEADR = 0x20;
    eeprom_write_data();

    EEADR = 0x21;
    eeprom_write_data();
    __delay_ms(1000);
    lcd_setcursor(0x40);
    lcd_writestr("  PRESS RESET.  ");
    while(1);   
}


/* THIS IS THE MAIN PROGRAM LOOP */
void main(void) 
{
    CMCON = 0x07;           // Turn comparators off

    TRISA = 0b00000000;     // All output
    TRISB = 0b00000000;     // All output
    PORTA = 0b00000000;     // All low
    PORTB = 0b00000000;     // All low

    lcd_strobe();    
    lcd_init();    

    show_welcome();

    read_total_run_time();

    timer_init();

while (1)
{
    // Nothing to do, everythong is done with timer1/interrupt.
} // end while

} // end main

This is the whole program listing. It is fairly good commented, so it should be easy to read and understand.