CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Digital clock with 18f4550 not accurate

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
irmanao



Joined: 08 Apr 2015
Posts: 77

View user's profile Send private message

Digital clock with 18f4550 not accurate
PostPosted: Sat Jun 10, 2017 11:22 am     Reply with quote

Hey guys, i've been trying to build a digital clock with the use of a timer but it's not accurate enough. It loses about 10 seconds per 10 minutes. I am using the internal oscillator.(version:5.008)
Code:
#include <18F4550.h>
#fuses INTRC_IO, NOWDT, PUT ,NOBROWNOUT, NOLVP, CPUDIV1
#use delay(clock=8000000)
#include <flex_lcd.h>
void init_all (void);
int counter=100;
int8 seconds=0;
int8 minutes=0;
int8 hours=0;
int1 flag=0;
void main()
{
init_all();
output_high(PIN_E1); 
set_tris_d(0x00);
delay_ms(500);
lcd_init();
while(1)
 {if (flag){lcd_gotoxy(8,1);
printf ( lcd_putc, "%02d:%02d:%02d",hours,minutes,seconds );
   
flag=0;
   }
 }
}

void init_all (void) {
SETUP_TIMER_0(T0_INTERNAL | T0_DIV_16 );
set_timer0(64286);
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);
clear_interrupt(INT_EXT1);
ext_int_edge(1, H_TO_L);
enable_interrupts(INT_EXT1);
}

#INT_EXT1
void ext_int1 (void){

output_toggle(PIN_E1);
delay_ms(10);
}

#INT_TIMER0
void timer0_int(void){
set_timer0(64286);
counter=counter-1;
if (counter==0){
counter=100;
seconds=seconds+1;
flag=1;

if (seconds>59){
seconds=0;
minutes=minutes+1;
}
if (minutes>59)
{minutes=0;
hours=hours+1;
}
if (hours>23)
hours=0;
}
}



I am counting 10ms so the counter is 100 for the seconds. Any tips?
thanks


Last edited by irmanao on Mon Jun 12, 2017 7:59 am; edited 1 time in total
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Sat Jun 10, 2017 11:38 am     Reply with quote

First, the internal oscillator is not designed or guaranteed to be accurate.
Second you are making it worse by setting the timer 'to' a value in the interrupt. By the time the processor gets to this instruction, several counts will have occurred since the interrupt triggered (variable amount if other things are taking place), so this will never be accurate.
Generally you should always let the timers 'free run' for accuracy. In the code library there is an example of how to do an RTC using a clock that is not at some convenient sub-division of the time you want to use. Use code like this, to improve things, but you need a crystal to even hope for accuracy.
temtronic



Joined: 01 Jul 2010
Posts: 9081
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Sat Jun 10, 2017 1:26 pm     Reply with quote

You should look at the software RTC in the 'code library'. It's pretty good but as Mr T points out , you NEED an xtal on any PIC to be accurate over the long haul.
You can get HW RTC based on DS1307 for about 2 Canucks. Battery backed too !!
irmanao



Joined: 08 Apr 2015
Posts: 77

View user's profile Send private message

PostPosted: Mon Jun 12, 2017 7:31 am     Reply with quote

Ok so i got a 20Mhz crystal with two 13pF capacitors but i still have inaccuracy. The crystal works fine (tested with oscilloscope). I also tried the following code from the library here but with the same results.
Code:
#include <18F4550.h>
#fuses HS, NOWDT, PUT ,NOBROWNOUT, NOLVP, CPUDIV1
#use delay(clock=20000000)
#include <flex_lcd.h>
void init_all (void);
int32 counter=5000000;
int8 seconds=0;
int8 minutes=0;
int8 hours=0;
int1 flag=0;
void main()
{



init_all();
output_high(PIN_E1);
delay_ms(500);
output_low(PIN_E1);
set_tris_d(0x00);
delay_ms(500);
lcd_init();
while(1)
 {if (flag){lcd_gotoxy(8,1);
printf ( lcd_putc, "%02d:%02d:%02d",hours,minutes,seconds );
   
flag=0;
   }
 }
}

void init_all (void) {
SETUP_TIMER_1(T1_INTERNAL | T1_DIV_BY_1 );
counter=5000000;
enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);
clear_interrupt(INT_EXT1);
ext_int_edge(1, H_TO_L);
enable_interrupts(INT_EXT1);
}

#INT_EXT1
void ext_int1 (void){

output_toggle(PIN_E1);
delay_ms(10);
}

#INT_TIMER1
void timer1_int(void){
counter-=65536;

if (counter<65536){
counter+=5000000;
seconds=seconds+1;
flag=1;

if (seconds>59){
seconds=0;
minutes=minutes+1;
}
if (minutes>59)
{minutes=0;
hours=hours+1;
}
if (hours>23)
hours=0;
}



}


Ttelmah, i tried removing the line where i set the timer inside the interrupt (on my original code) but it made the code very slow. I figured i could do that since it is also set at init_all. Any ideas? I will try rtc hardware later on but would like to make this work relatively accurate first.
thanks
irmanao



Joined: 08 Apr 2015
Posts: 77

View user's profile Send private message

PostPosted: Mon Jun 12, 2017 8:12 am     Reply with quote

With the external crystal it drifts ten seconds per hour, with my original code. Similar accuracy is displayed with the library code...

Last edited by irmanao on Mon Jun 12, 2017 8:19 am; edited 1 time in total
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Mon Jun 12, 2017 8:13 am     Reply with quote

You are introducing huge errors by having a delay inside an interrupt.

Do NOT do this.

You are also counting wrong.
Remember when you reset the counter on each level, is when it _has_ wrapped.

So 0,1,.......59, then needs to go back to 0 next time. You are doing it one count early....

Code:

//parts only
   enable_interrupts(INT_TIMER1);
   enable_interrupts(GLOBAL);
   ext_int_edge(1, H_TO_L);
   clear_interrupt(INT_EXT1);
   //you need to clear _after_ you change the edge. It is the act
   //of setting the edge that can cause an incorrect trigger
   enable_interrupts(INT_EXT1);

#INT_EXT1
void ext_int1 (void)
{
   output_toggle(PIN_E1);
   //delay_ms(10);
   //Avoid delays in interrupts
}

#INT_TIMER1
void timer1_int(void)
{
   counter-=65536;

   if (counter<65536)
   {
       counter+=5000000;
       seconds=seconds+1;
       flag=1;

       if (seconds>59)
       {
           seconds=0;
           minutes=minutes+1;
           if (minutes>59)
           {
              minutes=0;
              hours=hours+1;
              if (hours>23) //you got this one right...
                 hours=0;
          }
       }   
   }
}
irmanao



Joined: 08 Apr 2015
Posts: 77

View user's profile Send private message

PostPosted: Mon Jun 12, 2017 8:37 am     Reply with quote

I will remove the delay inside the interrupt. The library code has now been running for 13 hours with 4 sec drift.
benoitstjean



Joined: 30 Oct 2007
Posts: 542
Location: Ottawa, Ontario, Canada

View user's profile Send private message

PostPosted: Tue Jun 13, 2017 5:07 am     Reply with quote

If I may comment, my son wanted to do a similar project this past winter and I thought it was a good way to teach him programming.

So I took an old circuit I had kicking around built many years ago and it was already mounted with a PIC18F4620, an LCD display and a few buttons. I didn't have the luxury of adding components like an RTC chip because it was a PCB I had manufactured.

I replaced the crystal, if I remember correctly, with a 16.384MHz frequency so that it could be perfectly divisible in order to count with interrupts.

And you know what? I ran into the same issues as you. As much as all the calculations for the timers gave perfect numbers and on paper, everything should have worked perfectly, it turned-out that after so many hours (at first it was minutes!), the clock was either delayed by so many seconds or in advance by so many seconds.

So what I had to do was compare over 10 minute periods with my phone's timer and start both at the same time and compare after 10 minutes. I had to add a delay of a few microseconds after each second and in the end, after a full 24-hour, the seconds of the phone's timer were almost on-par with the circuit's time display (off by not even half a second - they were changing about at the same time).

We knew that if after 24 hours they were still synched, then it was good enough.

Quite frankly, it took a little while to have them synched because we eventually wanted to wait 1 hour for each test to see how the synchronization was. And given that we worked on this at night after homework and given he goes to be early, this dragged for a little while before we eventually got to the sweet spot.

As I am wrinting this, I think its been up and running for weeks and it's off by like 1 second or so.

We added the date on it as well and all the settings are changed via a scroll-wheel / button and a red and green button.

It's pretty cool. He likes it a lot and has it in his room like a little trophy.

Ben
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group