|
|
View previous topic :: View next topic |
Author |
Message |
irmanao
Joined: 08 Apr 2015 Posts: 77
|
Digital clock with 18f4550 not accurate |
Posted: Sat Jun 10, 2017 11:22 am |
|
|
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: 19546
|
|
Posted: Sat Jun 10, 2017 11:38 am |
|
|
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: 9245 Location: Greensville,Ontario
|
|
Posted: Sat Jun 10, 2017 1:26 pm |
|
|
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
|
|
Posted: Mon Jun 12, 2017 7:31 am |
|
|
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
|
|
Posted: Mon Jun 12, 2017 8:12 am |
|
|
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: 19546
|
|
Posted: Mon Jun 12, 2017 8:13 am |
|
|
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
|
|
Posted: Mon Jun 12, 2017 8:37 am |
|
|
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: 566 Location: Ottawa, Ontario, Canada
|
|
Posted: Tue Jun 13, 2017 5:07 am |
|
|
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 |
|
|
|
|
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
|