To show stopwatch timing on LCD

Thread Starter

PicUser

Joined Jun 15, 2011
5
Hi, i am a newbie in programming.

I am working to start a timer0 and show it's timing on LCD. I would like to make the timing like a stopwatch.

Please help me :( I have tried to write out and troubleshoot the coding for the past few days. Yet i got no result from it.

The LCD is working out fine (i have tried working only on the LCD).

Please advise me on my errors. Thank you so much.

These are my coding:

#include <p18f4620.h>
#include <stdio.h>
#include <delays.h>
#include <init_testing.h>
#include <timers.h>
#include <init_timers.h>
#pragma config LVP = OFF, PWRT = ON, WDT = OFF
#pragma config PBADEN = OFF
#pragma config DEBUG = OFF

void delay_ms(unsigned int);
void delay_20us(unsigned int);
void functionSet(void);
void entryMode(void);
void displayOn(void);
void displayClear(void);
void sendChar(char c);
void enable (void);
void sendString(far rom char *str);
void moveTo2nd(void);

unsigned long int tick_count, hour2, minute2, second2, milisecond2;
unsigned long int rec_hour, rec_minute, rec_second, rec_milisecond;
unsigned char hour, minute, second, milisecond;
char outchar; // character to send to the LCD
char int_2_char (unsigned char int1);

void main(void)
{
tick_count, hour2, minute2, second2, milisecond2 = 0;
rec_hour, rec_minute, rec_second, rec_milisecond = 0;
milisecond = 99;
second = 60;
minute = 60;
hour = 10;

// init timer0
RCONbits.IPEN = 1; // Enable Priority Features
INTCON2bits.TMR0IP = 1; // TMR0 Overflow Interrupt Priority bit = HIGH
INTCONbits.TMR0IF = 0; // Clear timer0 interrupt flag
INTCONbits.GIE = 1; // Enable global interrupt
INTCONbits.PEIE = 0; // disables all low priority peripheral interrupts
INTCONbits.TMR0IE = 1; // Enables the TMR0 overflow interrupt


// init LCD
TRISD = 0; // define Port D as output
displayClear();
Delay10TCYx(200); // delay for 200us
functionSet();
entryMode();
displayOn();

while(1);

}

#pragma code
#pragma interrupt T0ISR // Timer0 -- interrupt service routine
void T0ISR(void)
{
INTCONbits.GIE = 0; // disable global interrupt
INTCONbits.TMR0IF = 0; // Clear timer0 interrupt flag
INTCONbits.TMR0IE = 0; // Disables the TMR0 overflow interrupt

for(tick_count = 0; tick_count<milisecond; tick_count++) //count til 98
{
milisecond2++;
rec_milisecond = milisecond2; // recorded

if (rec_milisecond >= 99) // 1second is up
{
tick_count = 0; // reset
milisecond2 = 0;
for(tick_count = 0; tick_count<second; tick_count++) //count til 59
{
second2++;
rec_second = second2; // recorded

if (rec_second >= 59) // 1minute is up
{
tick_count = 0; // reset
second2 = 0;

for(tick_count = 0; tick_count<minute; tick_count++) // count til 59
{
minute2++;
rec_minute = minute2; //recorded

if (rec_minute >= 59) // 1hour is up
{
tick_count = 0; // reset
minute2 = 0;

for(tick_count = 0; tick_count<hour; tick_count++) // count til 9
{
hour2++;
rec_hour = hour2; // recorded
if (rec_hour >= 9)
{
tick_count = 0; // reset
hour2 = 0;
}
}

}
}
}
}
}

// send "time = mm:ss" to LCD display
sendString(" Lap 1");
moveTo2nd();
sendChar('T');
sendChar('i');
sendChar('m');
sendChar('e');
sendChar(' ');
sendChar('=');
sendChar(' ');

outchar = int_2_char (rec_minute);
sendChar(outchar);
sendChar(':');
outchar = int_2_char (rec_second);
sendChar(outchar);
sendChar('.');
outchar = int_2_char (rec_milisecond);
sendChar(outchar);
}

return;
}

// function to convert unsigned char to char (for display)
char int_2_char (unsigned char int1)
{
char char1;
switch (int1)
{
case 0 : char1 = '0' ; break;
case 1 : char1 = '1' ; break;
case 2 : char1 = '2' ; break;
case 3 : char1 = '3' ; break;
case 4 : char1 = '4' ; break;
case 5 : char1 = '5' ; break;
case 6 : char1 = '6' ; break;
case 7 : char1 = '7' ; break;
case 8 : char1 = '8' ; break;
case 9 : char1 = '9' ; break;
default : char1 = '?' ;
}
return(char1);
}

void functionSet(void) // 0010 1010 - 4 bit mode, 2 lines, 5 X 8.
{
RS= 0;
DB7 = 0; // Upper nibbles
DB6 = 0;
DB5 = 1;
DB4 = 0;
enable();
Delay10TCYx(200);
enable();
DB7 = 1; // Lower nibbles
enable();
Delay10TCYx(200);
}
void enable(void)
{
E = 1;
Delay10TCYx(1);
E = 0;
}
void entryMode(void) //0000 0110, increment with no shift
{
RS = 0;
DB7 = 0; // Upper nibbles
DB6 = 0;
DB5 = 0;
DB4 = 0;
enable();
DB6 = 1; // Lower nibbles
DB5 = 1;
enable();
Delay10TCYx(80);
}
void displayOn(void) // display on, cursor off, blink off
{
RS = 0;
DB7 = 0; // Upper nibbles
DB6 = 0;
DB5 = 0;
DB4 = 0;
enable();
DB7 = 1; // Lower nibbles
DB6 = 1;
DB5 = 0;
DB4 = 0;
enable();
Delay10TCYx(80);
}
void displayClear(void) // display clear
{
RS = 0;
DB7 = 0; // Upper nibbles
DB6 = 0;
DB5 = 0;
DB4 = 0;
enable();
DB7 = 0; // Lower nibbles
DB6 = 0;
DB5 = 0;
DB4 = 1;
enable();
Delay10TCYx(80);
}
void sendString(far rom char *str)
{
int index = 0;
while (str[index] != '\0')
{
sendChar(str[index]);
index++;
}
}
void sendChar(char c) // send character to LCD
{
RS = 1;
DB7 = (c >> 7) & 1;
DB6 = (c >> 6) & 1;
DB5 = (c >> 5) & 1;
DB4 = (c >> 4) & 1;
enable();
DB7 = (c >> 3) & 1;
DB6 = (c >> 2) & 1;
DB5 = (c >> 1) & 1;
DB4 = (c & 1);
enable();
Delay10TCYx(80);
}
void moveTo2nd(void) //1100 0000 - move cursor to 2nd line
{
RS = 0;
DB7 = 1; // Upper nibbles
DB6 = 1;
DB5 = 0;
DB4 = 0;
enable();
DB7 = 0; // Lowr nibbles
DB6 = 0;
enable();
Delay10TCYx(80);
}
 

DumboFixer

Joined Feb 10, 2009
217
Your biggest mistake is that there's far too much code in your interrupt routine. The interrrupt handler needs to be very short - use it to increment a counter and to reset the interrupt ready for the next one.

The sending of data to the LCD should definately not be done in the ISR.

When including code for the form you should use the CODE tag (#)
 

Thread Starter

PicUser

Joined Jun 15, 2011
5
Hi DumboFixer, thanks for your reply. I will do that amendment.
May i ask if my counting algorithm is correct?
I would like to count every 98ms = 1s, every 59s will be 1 minute.

Please advise.

Thanks
 

Thread Starter

PicUser

Joined Jun 15, 2011
5
I am able to show something on the LCD :) but the LCD shows
" Lap 1"
" Time = ?:?.?"

It is not counting, i do not think it has been to the ISR.
I have set the timer0 to prescale 1:2 and using 16bits. If i did not calculate wrongly, it should go to ISR every 131.07ms ??

Rich (BB code):
void main(void)
{
  
 // init timer0
 
 T0CON = 0x10000000;       // timer0 on, Prescale 1:2
 RCONbits.IPEN = 1;              // Enable Priority Features 
 INTCON2bits.TMR0IP = 1;         // TMR0 Overflow Interrupt Priority bit = HIGH
    INTCONbits.TMR0IF = 0;          // Clear timer0 interrupt flag
    INTCONbits.GIE = 1;             // Enable global interrupt
    INTCONbits.PEIE = 0;            // disables all low priority peripheral interrupts
    INTCONbits.TMR0IE = 1;          // Enables the TMR0 overflow interrupt
 

 // init LCD
 TRISD = 0;      // define Port D as output   
 displayClear();  
 Delay10TCYx(200);    // delay for 200us
 functionSet();
   entryMode();
 displayOn();
  
 while(1)
 {
  // send "time = mm:ss" to LCD display
  sendString("   Lap 1");
    moveTo2nd();
  sendString("   Time = ");
  outchar = int_2_char (rec_minute);
  sendChar(outchar);
  sendChar(':');
  outchar = int_2_char (rec_second);
  sendChar(outchar);
  sendChar('.');
  outchar = int_2_char (rec_milisecond);
  sendChar(outchar);
 }
 
}

#pragma code
#pragma interrupt T0ISR        // Timer0 -- interrupt service routine
void T0ISR(void)
{
     
 INTCONbits.GIE = 0;             // disable global interrupt    
    INTCONbits.TMR0IF = 0;          // Clear timer0 interrupt flag   
    INTCONbits.TMR0IE = 0;          // Disables the TMR0 overflow interrupt
 milisecond = 99;
 second = 60;
 minute = 60;
 
 
  for(milisecond_count = 0; milisecond_count<milisecond; milisecond_count++)   //count til 98
  {  
     rec_milisecond = milisecond_count;    
  
   if (rec_milisecond >= 99)      // 1second is up
   {
    milisecond_count = 0;             // reset 
  
    for(second_count = 0; second_count<second; second_count++)  //count til 59
     {
        rec_second = second_count;    // recorded
  
     if (rec_second >= 59)          // 1minute is up
         {
      second_count = 0;                // reset 
            
      for(minute_count = 0; minute_count<minute; minute_count++)  // count til 59
       {
             rec_minute = minute;    //recorded
       
       if (rec_minute >= 59)          // 1hour is up
           {
        minute_count = 0;                // reset 
              
          }
      }
     }
    }
 
         
  }
 }
 
}
 

Thread Starter

PicUser

Joined Jun 15, 2011
5
Hi, i have did something on to my code.
This time the lcd flickered in a fast speed which i was not able to determine what it was showing and then it stopped at this result "0:000:000:000"

Can someone please advise what is my mistake?
And what is the solution to it?
I am going to be nuts.........
Please advise :(

Rich (BB code):
#include <p18f4620.h> 
#include <stdio.h>
#include <delays.h>
#include <init_testing.h>
#include <timers.h>
#include <init_timers.h>
 
#pragma config LVP = OFF, PWRT = ON, WDT = OFF
#pragma config PBADEN = OFF
#pragma config DEBUG = OFF
#define pulse 16036
 
void delay_ms(unsigned int);
void functionSet(void);
void entryMode(void);
void displayOn(void);
void displayClear(void);
void sendChar(char c);
void enable (void);
void sendString(far rom char *str);
void moveTo2nd(void);
void timer_init(void);

unsigned char minute, second;
unsigned int milisecond;
char outchar; //    character to send to the LCD
char int_2_char (unsigned char int1);
 
void main(void)
{
 int i;
 
 timer_init(); 
 milisecond = 0;
 second = 0;
 minute = 0;
 
 // init LCD
 TRISD = 0;      // define Port D as output   
 displayClear();  
 Delay10TCYx(200);    // delay for 200us
 functionSet();
   entryMode();
 displayOn();
  
 // send "time = mm:ss" to LCD display
 sendString("   Lap 1");
   moveTo2nd();
 sendString("Time =     ");
 
 for(;;)
 {
   
  outchar = int_2_char (minute);
  sendChar(outchar);
 
  sendChar(':');

  outchar = int_2_char (second);
  sendChar(outchar);
 
  sendChar('.');

  outchar = int_2_char (milisecond);
   sendChar(outchar);
 }
 
}

#pragma code
#pragma interrupt T0ISR        // Timer0 -- interrupt service routine
void T0ISR(void)
{
 if(INTCONbits.TMR0IF==1)
 {       
     INTCONbits.TMR0IF = 0;          // Clear timer0 interrupt flag   
  milisecond++;
  if(milisecond>=pulse)   // count til 98ms
  {
   milisecond = 0;
   second++;
  }
  if(second>=60)     // count til 59s
  {
   second = 0;
   minute++;
  }
  
  }  
}
 
void timer_init(void)
{
 OpenTimer0( TIMER_INT_OFF &
    T0_16BIT &
    T0_SOURCE_INT &
    T0_PS_1_2 );
 
 RCONbits.IPEN = 1;              // Enable Priority Features 
 INTCON2bits.TMR0IP = 1;         // TMR0 Overflow Interrupt Priority bit = HIGH
 INTCONbits.TMR0IF = 0;          // Clear timer0 interrupt flag
 INTCONbits.GIE = 1;             // Enable global interrupt
 INTCONbits.PEIE = 1;            // Enables all low priority peripheral interrupts
 INTCONbits.TMR0IE = 1;          // Enables the TMR0 overflow interrupt
 TMR0L = 0;
 TMR0H = 0;
}

 
// function to convert unsigned char to char (for display)
char int_2_char (unsigned char int1)
{
 char char1;
 switch (int1)
 {
 case 0 : char1 = '0' ; break;
 case 1 : char1 = '1' ; break;
 case 2 : char1 = '2' ; break;
 case 3 : char1 = '3' ; break;
 case 4 : char1 = '4' ; break;
 case 5 : char1 = '5' ; break;
 case 6 : char1 = '6' ; break;
 case 7 : char1 = '7' ; break;
 case 8 : char1 = '8' ; break;
 case 9 : char1 = '9' ; break;
  default : char1 = '?' ;
 }
 return(char1);
}
 
void functionSet(void)   // 0010 1010 - 4 bit mode, 2 lines, 5 X 8.
{
  RS= 0;
  DB7 = 0;      // Upper nibbles
  DB6 = 0;
  DB5 = 1;
  DB4 = 0;
  enable();
  Delay10TCYx(200);
  enable();
  DB7 = 1;      // Lower nibbles
  enable();
  Delay10TCYx(200);
}
 
void enable(void)
{
  E = 1;
  Delay10TCYx(1);
  E = 0;
}
 
void entryMode(void)    //0000 0110, increment with no shift
{
  RS = 0;
  DB7 = 0;      // Upper nibbles
  DB6 = 0;
  DB5 = 0;
  DB4 = 0;
  enable();
  DB6 = 1;      // Lower nibbles
  DB5 = 1;
  enable();
  Delay10TCYx(80);
}
 
void displayOn(void)      // display on, cursor off, blink off
{
  RS = 0;
  DB7 = 0;      // Upper nibbles
  DB6 = 0;
  DB5 = 0;
  DB4 = 0;
  enable();
  DB7 = 1;      // Lower nibbles
  DB6 = 1;
  DB5 = 0;
  DB4 = 0;
  enable();
  Delay10TCYx(80);
}
 
void displayClear(void)      // display clear
{
  RS = 0;
  DB7 = 0;      // Upper nibbles
  DB6 = 0;
  DB5 = 0;
  DB4 = 0;
  enable();
  DB7 = 0;      // Lower nibbles
  DB6 = 0;
  DB5 = 0;
  DB4 = 1;
  enable();
  Delay10TCYx(80);
}
 
void sendString(far rom char *str)
{
  int index = 0;
  while (str[index] != '\0')
  {
     sendChar(str[index]);
     index++;
  }
}
 
void sendChar(char c)                // send character to LCD
{
  RS = 1;
  DB7 = (c >> 7) & 1;
  DB6 = (c >> 6) & 1;
  DB5 = (c >> 5) & 1;
  DB4 = (c >> 4) & 1;
  enable();
  DB7 = (c >> 3) & 1;
  DB6 = (c >> 2) & 1;
  DB5 = (c >> 1) & 1;
  DB4 = (c & 1);
  enable();
  Delay10TCYx(80);
}
 
void moveTo2nd(void)       //1100 0000 - move cursor to 2nd line
{
  RS =  0;
  DB7 = 1;      // Upper nibbles
  DB6 = 1;
  DB5 = 0;
  DB4 = 0;
  enable();
  DB7 = 0;      // Lowr nibbles
  DB6 = 0;
  enable();
   Delay10TCYx(80);
}
 
void delay_ms(unsigned int duration) // delay in miliseconds for 4.0MHZ crystal
{
 unsigned int i;
 for(;duration!=0;duration--)
 {
  for(i=0;i<=50;i++)
  {
   _asm
    nop
    nop
    nop
   _endasm
  }
  _asm
   nop
   nop
  _endasm
 }
}
 

Thread Starter

PicUser

Joined Jun 15, 2011
5
Can someone please please advise if the method of calculation is correct.

As i would need 98ms to become 1 second, so the number of ticks which i need is (98*1000) / 2 = 49000 ticks
then timer0 is able to support 16 bits so 2 power of 16bits is 65536.

use 65536 - 49000 = 16536 ticks

I will use 16536 ticks as my 98ms.

I found this calculation method from 1 of PIC tutorial.
 

THE_RB

Joined Feb 11, 2008
5,438
I think your basic method os very flawed and you should use a different method!

If you use a 10MHz xtal in PLL mode that means your PIC 18F timers will run at 10 MIPS.

So each timer "tick" is 0.1uS.

Then use TMR2 prescaled 4:1 with a PR2 value of (250-1) so your TMR2 interrupt will occur at exactly 1000 ticks, which is ALWAYS at exactly 100uS (or 0.1mS).

Then in your interrupt you can just add 0.1mS to a counter every interrupt, and use that counter to generate all the times exactly.
 
Top