PIC18F452 clock not inaccurate

Thread Starter

ecka333

Joined Oct 1, 2009
76
Made project with pic18f452 microcontroller. Timer1 module used as a real time clock with 32768kHz crystal and 33pF caps connected to microcontroller's Timer1 oscillator pins. Problem is that clock is not accurate, it makes about 4sec/day delay. Changed crystal, but this didnt helped. Tried to reroute PCB, this also not helped. I suspect maybe my clock routine is wrong? Here it is:
Rich (BB code):
if(PIR1.TMR1IF==1)
        {
        TMR1H=252;
        if(FourthHertz<3){FourthHertz++; Blinker=1;} //count to four, that is to 1 second and invert variable state for text blinking
        else
            {
            FourthHertz=0; Blinker=0;
                
            if(Seconds<59){Seconds++;}
            else
                {
                Seconds=0; 
                if(Minutes<59){Minutes++;}
                else
                    {
                    Minutes=0;
                    if(Hours<23){Hours++;}
                    else{Hours=0; if(Day<6){Day++;} else {Day=0;}}
                    }
                }   
            }
        PIR1.TMR1IF=0; //clear timer1 interrupt flag
        }
Can clock inaccuracy occur due to permanent writing to TMR1h register? (TMR1L register is never changed.) Any ideas and suggestions?
 
Last edited:

tshuck

Joined Oct 18, 2012
3,534
Well, the fact that you are using C will incur timing errors. Since the pic is a risc processor, each instruction is executed in a predetermined time... With the exception of branching statements. Unfortunately, a simple line of code in C will not necessarily correlate to a single risc instruction.

I'd recommend anything requiring a very precise timing to be in assembly.

Would it be so bad to simply compensate for the drift , our does it have to be as accurate as possible?
 

tshuck

Joined Oct 18, 2012
3,534
...also, writing to the timer registers causes it to skip a few clock cycles, though I don't think that could account for a 4 sec/day offset...
 

JohnInTX

Joined Jun 26, 2012
4,787
4 sec/day is a .0046% error. Is your XTAL / osc running at the exact speed? It doesn't have to be too far off to accumulate this amount of error over 24 hours.

Are you sure that the compiler runs timer 1 in the 8 bit write mode and is not writing 16 bits? T1CON bit 7 controls that..

Instead of writing to timer 1 each time, I would set it up using CCP1 using the special event trigger to reset TMR1 each time it matches the CCP count. (Instead of TMR1IF you'll use CCP1IF). That way you don't have any worries about problems writing to your time base and you get much more time to service the IRQ.

Finally, be sure to check the PICs errata to see if it has any issues with TMR1. Lots do and the '452 is an old chip.

TMR1 Errata for the '452
 
Last edited:

MrChips

Joined Oct 2, 2009
34,807
4sec/day is about 40 parts per 1,000,000.
You should be able to achieve about 1-5 parts per million with a crystal.
Replace the capacitor at T1OSI with a 56pF variable trim capacitor.
 

t06afre

Joined May 11, 2009
5,934
Here is a the ISR I made for a clock once. The trick is to start timer 1 at 0x8000. Then using a clock crystal. That is done by writing 0x80 to TMR1H in the ISR. But you are writing 252 to the register. That I do not understand

Rich (BB code):
static void interrupt
isr(void)   // Here is interrupt function - the name is
    // unimportant.
{
 if(TMR1IF) 
  {// Was this a timer overflow?
      TMR1IF=0;//Clear interrupt flag, ready for next
   TMR1H=0x80;
        //If we set TMR1 to start at 0x8000 (32768), the TMR1 will overflow every 1 second
     timer_tick=1;
  }//we are done here
}
 

THE_RB

Joined Feb 11, 2008
5,438
You should not need to write to TMR1. A 32768 Hz xtal means the timer overflows at 65536 ticks which is exactly 2 seconds.

If you need a 1 second event, just check for when bit7 of TMR1H changes, that change (toggle) occurs exactly every second.

If you write to TMR1 you incur a delay, so the time will not be accurate. You need to leave TMR1 free running and just read it.

Is there any reason you must use TMR1 and a 32768 Hz xtal for the clock? Why not just derive the clock from the main PIC xtal?
 

Thread Starter

ecka333

Joined Oct 1, 2009
76
My timer1 register is configured once: T1CON=0b0011111111; that means prescaler is 1:8; register Read/Write of Timer1 in two 8-bit operations. With these settings i get 4Hz frequency. It is used for time counting (seconds, minutes, hours) and numbers blinking in LCD, when they are edited.
Yesterday changed Timer1 configuration: prescaler set to 1, deleted TMR1H register preload in ISR; T1CON=0b00001111. So now interrupt occurs at 0,5Hz, or every two seconds. But result remained the same: clock is slow. Today i will buy new crystal and will try it.
 

t06afre

Joined May 11, 2009
5,934
You should not need to write to TMR1. A 32768 Hz xtal means the timer overflows at 65536 ticks which is exactly 2 seconds.

If you need a 1 second event, just check for when bit7 of TMR1H changes, that change (toggle) occurs exactly every second.

If you write to TMR1 you incur a delay, so the time will not be accurate. You need to leave TMR1 free running and just read it.

Is there any reason you must use TMR1 and a 32768 Hz xtal for the clock? Why not just derive the clock from the main PIC xtal?
In a typical clock application like this . You can write to the TMR1H register before TMR1L roll over with no loss of accuracy. But in general it is kind of a dodgy approach I shall admit that
 

THE_RB

Joined Feb 11, 2008
5,438
I don't believe that is right T06afre, on which PIC? From what I remember writing to TMR1 means it does not increment during that one instruction (because it is being written instead of incrementing when the instruction takes place).

If TMR1 is prescaled at 1:1 than each instruction is 1 TMR1 count, so writing to TMR1 once every 65536 counts means it will take 65536+1 counts to roll, giving a time error of 1 part in 65536.

If I remebered that right your code writes to the TMR1 every 32768 ticks so will produce a total period of 32768+1. That would make a real time clock error of about 2.6 seconds a day.
 

t06afre

Joined May 11, 2009
5,934
I don't believe that is right T06afre, on which PIC? From what I remember writing to TMR1 means it does not increment during that one instruction (because it is being written instead of incrementing when the instruction takes place).

If TMR1 is prescaled at 1:1 than each instruction is 1 TMR1 count, so writing to TMR1 once every 65536 counts means it will take 65536+1 counts to roll, giving a time error of 1 part in 65536.

If I remebered that right your code writes to the TMR1 every 32768 ticks so will produce a total period of 32768+1. That would make a real time clock error of about 2.6 seconds a day.
I think I used the 16f690. And set it up to work in Asynchronous Counter Mode. The external clock input is not synchronized. The timer increments asynchronously to the internal phase clocks. The system used the internal oscillator set to 4MHz. So it was not driven by the watch crystal.
 

THE_RB

Joined Feb 11, 2008
5,438
I still call that one. :) I'll try to get some time tomorrow to do tests and get back to you.

I have a strong memory that writing to TMR1H inhibits TMR1 for that clock cycle, even on internal synchronous clock.
 

MMcLaren

Joined Feb 14, 2010
861
Setting bit 15 (TMR1H.7) on a free running TMR1 module shortly after a TMR1 overflow, when using the Timer 1 oscillator and 32768 Hz crystal, is a time tested method for generating a one Hertz TMR1 overflow flag.

I haven't tried this yet on an 18F device and so I'm guessing that you would need to clear the RD16 control bit in T1CON to allow updating TMR1H without touching TMR1L.

Regards...
 

t06afre

Joined May 11, 2009
5,934
I still call that one. :) I'll try to get some time tomorrow to do tests and get back to you.

I have a strong memory that writing to TMR1H inhibits TMR1 for that clock cycle, even on internal synchronous clock.
I can not find any sign of that in section 12 in this document(Mid-Range MCU Family
Reference Manual) http://ww1.microchip.com/downloads/en/devicedoc/33023a.pdf
The method of changing TMR1H is also suggest as a solution in AN580. And for sure used in AN649. And the latter manual state
You should never write to Timer1, where that could cause the loss of time. In most cases that means you should not write to the TMR1L register, but if the conditions are ok, you may write to the TMR1H register. Normally you write to the TMR1H register if you want the Timer1 overflow interrupt to be sooner then the full 16-bit time-out.
 

MMcLaren

Joined Feb 14, 2010
861
A couple thoughts, if I may...

(1) The 33 pf caps may be a bit high. I use 22 pf caps with good results.
(2) Writing either timer register while using prescaler other than 1:1 is a "no no".

Regards...
 

THE_RB

Joined Feb 11, 2008
5,438
Thank you T06afre and MMcLaren for correcting me, it's appreciated (and saved me doing a silly test). ;) As a general rule I don't write to timers to generate periods.

As T06afre said why set TMR1H to 252? That gives 4 rolls of TMR1L before the next int, which gives an int period of 1024 TMR1 ticks or 1024/32768 seconds, which is 0.03125 seconds per interrupt.

That is 8 times faster than the OP's desired "quarter second" and would imply he is using a TMR1 prescaler of 8:1?

I think MMcLaren hit it on the head, it's error caused by writing to TMR1 when the TMR1 prescaler is selected... (Another good reason not to be in the habit of writing to timers).
 

MMcLaren

Joined Feb 14, 2010
861
Well, it would be easy enough to poll TMR1H in a loop in order to extract "one time" bit changes for "one time" operations. For example, the following code uses a state latch and simple logic to refresh 'delta' with "one time" bit changes each loop;

Rich (BB code):
        delta = tmr1h ^ latch;     // changes, hi or lo
        latch ^= delta;            // update state latch
Since 'delta' is refreshed each time through the loop, delta.7 will be a '1' only during one loop at one second intervals. Simply test delta.7 each loop and update your RTC variables when the bit is a '1'. Use delta.6 for 1/2 second (2 Hz) "one time" intervals, or delta.5 for 1/4 second (4 Hz) "one time" intervals. You could, for example, use delta.6 to toggle the colon LEDs on a display twice per second to flash the colon LEDs at a 1 Hz (one second) rate.

Rich (BB code):
        if(delta.6)                // if new 1/2 second interval
          colon ^= 1;              // toggle colon LEDs
Of course the 'latch' variable reflects the actual "real time" state of TMR1H and so 'latch' bits could be used directly for "real time" operations instead of "one time" operations. For example, flashing the colon LEDs once per second, as in the previous example, could also be accomplished by simply copying the 1/2 second latch.6 bit to the colon LED pin during each loop;

Rich (BB code):
    /*
     *  flash colon LEDs once per second
     */
        colon = latch.6;           // real-time 1/2 second bit
Anyway, as Roman suggested, there are plenty of ways to get the intervals you may need without writing timer registers, if writing timer registers is an issue.

Cheerful regards, Mike
 
Last edited:
Top