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.