nerdegutta.no
PIC 16F690: Engine Timer
31.12.23
Embedded
This is the program running on the PIC 16F690:
// INCLUDING LIBRARIES
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// CONFIGURATION BITS
#pragma config FOSC = XT // Oscillator Selection bits (HS oscillator: High-speed crystal/resonator on RA4/OSC2/CLKOUT and RA5/OSC1/CLKIN)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled and can be enabled by SWDTEN bit of the WDTCON register)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // MCLR Pin Function Select bit (MCLR pin function is MCLR)
#pragma config CP = OFF // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = ON // Brown-out Reset Selection bits (BOR enabled)
#pragma config IESO = ON // Internal External Switchover bit (Internal External Switchover mode is enabled)
#pragma config FCMEN = ON // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is enabled)
#pragma warning disable 373 // disable warning 373
#pragma warning disable 359 // disable warning 359
#pragma warning disable 355 // disable warning 355
// DEFINITIONS
#define _XTAL_FREQ 8000000 // Compiler reference
#define LCD_RS RA2 // LCD Register Select pin
#define LCD_EN RA1 // LCD Enable pin
#define LCD_DATA PORTC // LCD datapins are connected here
#define number 0x30 //
// VARIABLES
volatile char counter, buffer[3];
volatile int
seconds = 0,
minutes = 0,
hours = 0,
tot_days = 0,
tot_hours = 0,
tot_minutes = 0;
unsigned int len, i;
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_clear();
void lcd_goto(unsigned char pos);
void lcd_init();
void lcd_putch(char c);
void lcd_puts(const char* s);
int lcd_strobe(void);
void lcd_write(unsigned char c);
void osc_init();
void read_total_run_time();
void short_welcome();
void show_time();
void timer_init();
void update_time();
void show_total_time();
void write_time_to_eeprom();
// FUNCTIONS
// Function to strobe LCD, "pings" the En-pin
int lcd_strobe(void)
{
LCD_EN = 1;
__delay_us(1);
LCD_EN = 0;
}
// Function to write on the LCD
void lcd_write(unsigned char c)
{
__delay_ms(1);
LCD_DATA = ((c >>4) & 0x0f);
lcd_strobe();
LCD_DATA = (c & 0x0f);
lcd_strobe();
}
// Function to reset/clear the LCD
void lcd_clear(void)
{
LCD_RS = 0;
lcd_write(0x1);
__delay_ms(1);
}
// Function to write a string to the LCD
void lcd_puts(const char *s)
{
LCD_RS = 1;
while(*s)
lcd_write(*s++);
}
// Functionto write one char on the LCD
void lcd_putch(char c)
{
LCD_RS = 1;
lcd_write(c);
}
// FUnction to place cursor on the LCD
void lcd_goto(unsigned char pos)
{
LCD_RS = 0;
lcd_write(0x80+pos);
}
// Function to initialze the LCD
void lcd_init(void)
{
char init_value;
init_value = 0x3;
LCD_RS = 0;
LCD_EN = 0;
__delay_ms(15);
LCD_DATA = init_value;
lcd_strobe(); __delay_ms(10);
lcd_strobe(); __delay_ms(10);
LCD_DATA = 2;
lcd_strobe();
lcd_write(0x0e); // display on, cursor on, blink off
}
// Function to initialize the oscillator
void osc_init(void)
{
OSCCONbits.IRCF = 0b111; // 8MHz
OSCCONbits.OSTS = 1; // Running from Fosc in config/ External clock
OSCCONbits.HTS = 1; // stable
OSCCONbits.SCS = 0; // Clock source def by Fosc
return;
}
// Function to handle timer 1 interrupts
// Counter = Fosc/instruction cycle * prescaler * timer1 resolution
// Counter = 8MHz / (4 intrstructions pr cycle * 1 prescaler value * 2^16resolution)
// Counter = 8000000 / (4 * 1 * (2^16))
// Counter = 30.51
void interrupt ISR()
{
if (PIR1bits.TMR1IF == 1) // Timer1 overflow interrupt flag
{
counter++;
if (counter == 30) // Check if TMR1 have overflown
{
PORTCbits.RC5 ^= 1; // Toggle LED
update_time();
show_time();
show_total_time();
write_time_to_eeprom();
counter = 0; // Reset counter
}
PIR1bits.TMR1IF = 0; // Clear the overflow flag
}
if (PORTCbits.RC4) // Check if RC4 is high
{
lcd_clear();
check_for_erase();
}
}
// Function to initialize the timer
void timer_init()
{
TMR1H = TMR1L = 0; // Clear TMR1H and TMR1L
T1CONbits.T1CKPS1 = 0; // Prescaler 1:1
T1CONbits.T1CKPS0 = 0; // Prescalre 1:1
PIE1bits.TMR1IE = 1; // TMR1 overflow interrupt enable bit
T1CONbits.TMR1ON = 1; // Timer 1 on
INTCONbits.PEIE = 1; // Enable perepherial interrupt
INTCONbits.GIE = 1; // Enable global interrupt
}
// Function to show initial information
void short_welcome(void)
{
lcd_clear();
lcd_goto(0);
lcd_puts(" Engine timer ");
lcd_goto(0x40);
lcd_puts(" ver 0.1a ");
__delay_ms(3000);
lcd_clear();
}
// Function to show the time
void show_time()
{
lcd_goto(0);
lcd_puts("Trip: ");
itoa(buffer, hours,10);
if (hours < 10)
{
lcd_puts("0");
}
lcd_puts(buffer);
lcd_puts(":");
itoa(buffer, minutes, 10);
if (minutes < 10)
{
lcd_puts("0");
}
lcd_puts(buffer);
lcd_puts(":");
itoa(buffer, seconds, 10);
if (seconds < 10)
{
lcd_puts("0");
}
lcd_puts(buffer);
}
// Function to display the total run time
void show_total_time()
{
lcd_goto(0x40);
lcd_puts("Tot: ");
itoa(buffer, tot_days, 10);
lcd_puts(buffer);
lcd_puts("d ");
itoa(buffer, tot_hours, 10);
lcd_puts(buffer);
lcd_puts("h ");
itoa(buffer, tot_minutes, 10);
lcd_puts(buffer);
lcd_puts("m ");
}
// Function to update 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 write 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) // CHeck if buffer is > 10
{
EEADR = 0x01; // If so, write 0xFF
EEDATA = 0xFF; // To the second day_adr
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 write data to EEPROM
void eeprom_write_data()
{
EECON1bits.EEPGD = 0; // Program or data memory access bit. 0 = access data
EECON1bits.WREN = 1; // Write enable bit
INTCONbits.GIE = 0; // Disable gloab interrupt
INTCONbits.PEIE = 0; // Disable perepherial interrupt
EECON2 = 0x55; // Has to be written, read the manual
EECON2 = 0xAA; // Has to be written, read the manual
EECON1bits.WR = 1; // Write control bit. 1 = initiate write cycle
INTCONbits.GIE = 1; // Enable global interrupt
INTCONbits.PEIE = 1; // Eneable perepheral interrupt
while (!PIR2bits.EEIF); // EE Write operation interrupt flag
PIR2bits.EEIF = 0; // Write operation has not completed or has not started
}
// Function to write 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; // EEADR -> Actual memory address
EEDATA = msg[i]; // EEDATA -> Data to be written
eeprom_write_data(msg[i]);
}
EECON1bits.WREN = 0; // Write enable bit
}
// Function to read from teh EEPROM
unsigned char eeprom_read_data(unsigned char address)
{
volatile unsigned char retval;
unsigned char save_gie;
save_gie = INTCONbits.GIE;
EEADR = address; // EEPROM address to be read
EECON1bits.RD = 1; // EEPROM Read control bit. 1 = initiate a memory read
retval = EEDATA; // Read value
return retval;
}
// Function to erase EEPROM data
void check_for_erase()
{
T1CONbits.TMR1ON = 0; // Turn TMR1 off
lcd_puts(" ERASE ALL! ");
EEDATA = 0xFF; // Value to write
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_goto(0x40);
lcd_puts(" PRESS RESET. ");
while(1);
}
// Function to read EEPROM and the total run time, in seconds
void read_total_run_time()
{
buffer[0] = eeprom_read_data(0x00);
buffer[1] = eeprom_read_data(0x01);
tot_days = atoi(buffer); // Convert the data to int so we can do some math
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);
}
// MAIN PROGRAM
void main(void) {
TRISA = 0b00000000; // TRISA output
PORTA = 0b00000000; // PORTA low
TRISB = 0b0000; // RX+TX = in, rest out
PORTB = 0b0000; // PORTB low
TRISC = 0b00010000; // RC4 set to input -> Erase button
PORTC = 0b00000000; // PORTC low
ANSEL = 0; // Disable the input buffer on ANSEL
ANSELH= 0; // Disable the input buffer on ANSELH
CM1CON0 = 0; // Comparator C1 control register 0, disabled
CM2CON0 = 0; // Comparator C2 control register 0, disabled
osc_init(); // Initialize the oscillator
lcd_init(); // Initialize the LCD
short_welcome(); // Show welcome
read_total_run_time(); // Read total run time from eeprom
timer_init(); // Initialize and start the timer
while(1) // Do this forever...
{
// nothing to do, everything is done with timer1/interrupt
}
return;
}
There's two inputs, Erase and Reset.