Using crystals with PICs and their accuracy ..#2

Thread Starter

cmartinez

Joined Jan 17, 2007
7,422
Hello, @camerart. I was about to start a new thread so as to ask a question about the use of crystals in PIC MCU's when I happened to stumble upon yours. The reason I'm making my post here is because I think that it would not only make things easier for me, but also because I believe I can make a valuable contribution to this thread. If you feel that I'm somehow intruding or hijacking your thread, please let me know and I'll be more than glad to start a new one.


Here's my conundrum:

I've been working on a project that requires fairly precise timekeeping because the MCU (a PIC16LF1823) has the task of logging the moment an event (the event in question being when one of its input pins goes low) takes place.
For this purpose, I acquired several 32.7680KHz crystals (CFS-20632768HZFB) that boast an accuracy of ±5ppm. This should translate into an error of about ±7 seconds per year, if I'm not mistaken. So what I did is install said crystal in the MCU's circuitry so as to make it work as an asynchronous timing source for Timer1. And then I enabled Timer1's interrupt in such a way so as to never ever miss a single tick (one second) to be counted in its logic. I've gone through the code many times and so far I haven't found a fault within it.

Things worked rather nice at first, and everything seemed ok when I assembled the PCB and finished the code. I then tested it by leaving it counting seconds overnight (I had previously synched it with my PC's clock via a RS232-UART interface). You can imagine my disappointment when I checked how things were going the next morning and confirmed that my circuit's clock was 5 seconds behind my PC's after 12 straight hours of timekeeping.

I've repeated the same test several times, and the result has always been the same: about 5 to 7 seconds are lost every twelve hours in my circuit's timekeeping count, even though I'm using one of the best crystals available out there. This sort of error is a monstrosity that seriously jeopardizes my project, and it's imperative that it gets fixed.

My question to the members of this forum is, how can I make sure that the crystal oscillator is producing a nice and stable square wave and is not wandering around?

@MrChips mentioned that layout is critical. Well, here's a copy of my circuit's layout:

1605246179529.png

Dimensions are in inches, for reference. The crystal is shown outlined in red (upper left). The crystal's pins are soldered to the two orifices immediately below it. The 220k resistor is 0805 size, and the two tiny 22pF caps are 0402

I based my decision to use 22pF caps and a 220K resistor on the recommendations stated in Microchip's TB097 application note. But I suspect that that's only a ballpark sort of thing and one has to go into the details to land a definite design.

This is the application note's sample circuit that I copied into mine:

1605246936698.png

Following common-sense recommendations, I placed all of the elements as close as possible, but I must confess on two things:
  • My PCB is single-sided and neither the crystal nor the caps are surrounded by a ground plane
  • I glued the crystal to the PCB using LocTite 495, which is a rigid type sort of adhesive. The crystal's datasheet states that soft glue should be used. I don't know if that would affect things as much so as to produce such a notorious effect on my circuit. My goal at this moment is to give as much info as possible to see if someone with more experience than me can help me wit this.

Any thoughts or suggestions on this matter?
 
Last edited:

camerart

Joined Feb 25, 2013
2,678
Hello, @camerart. I was about to start a new thread so as to ask a question about the use of crystals in PIC MCU's when I happened to stumble upon yours. The reason I'm making my post here is because I think that it would not only make things easier for me, but also because I believe I can make a valuable contribution to this thread. If you feel that I'm somehow intruding or hijacking your thread, please let me know and I'll be more than glad to start a new one.


Here's my conundrum:

I've been working on a project that requires fairly precise timekeeping because the MCU (a PIC16LF1823) has the task of logging the moment an event (the event in question being when one of its input pins goes low) takes place.
For this purpose, I acquired several 32.7680KHz crystals (CFS-20632768HZFB) that boasts an accuracy of ±5ppm. This should translate into an error of about ±7 seconds per year, if I'm not mistaken. So what I did is install said crystal in the MCU's circuitry so as to make it work as an asynchronous timing source for Timer1. And then I enabled Timer1's interrupt in such a way so as to never ever miss a single tick (one second) to be counted in its logic. I've gone through the code many times and so far I haven't found a fault within it.

Things worked rather nice at first, and everything seemed ok when I assembled the PCB and finished the code. I then tested it by leaving it counting seconds overnight (I had previously synched it with my PC's clock via a RS232-UART interface). You can imagine my disappointment when I checked how things were going the next morning and confirmed that my circuit's clock was 5 seconds behind my PC's after 12 straight hours of timekeeping.

I've repeated the same test several times, and the result has always been the same: about 5 to 7 seconds are lost every twelve hours in my circuit's timekeeping count, even though I'm using one of the best crystals available out there. This sort of error is a monstrosity that seriously jeopardizes my project, and it's imperative that it gets fixed.

My question to the members of this forum is, how can I make sure that the crystal oscillator is producing a nice and stable square wave and is not wandering around?

@MrChips mentioned that layout is critical. Well, here's a copy of my circuit's layout:


Dimensions are in inches, for reference. The crystal is shown outlined in red (upper left). The crystal's pins are soldered to the two orifices immediately below it. The 220k resistor is 0805 size, and the two tiny 22pF caps are 0402

I based my decision to use 22pF caps and a 220K resistor on the recommendations stated in Microchip's TB097 application note. But I suspect that that's only a ballpark sort of thing and one has to go into the details to land a definite design.

This is the application note's sample circuit that I copied into mine:


Following common-sense recommendations, I placed all of the elements as close as possible, but I must confess on two things:
  • My PCB is single-sided and neither the crystal nor the caps are surrounded by a ground plane
  • I glued the crystal to the PCB using LocTite 495, which is a rigid type sort of adhesive. The crystal's datasheet states that soft glue should be used. I don't know if that would affect things as much so as to produce such a notorious effect on my circuit. My goal at this moment is to give as much info as possible to see if someone with more experience than me can help me wit this.

Any thoughts or suggestions on this matter?
Hi C2,
Your welcome. I've changed the title to suit.
I'm not as skilled as some of the answers so far, so good luck.
C.
 

MrChips

Joined Oct 2, 2009
24,233
I believe that there is nothing wrong with the crystal. What is wrong is your expectation of the accuracy of the crystal.
You are faced with two issues:

1) Short term crystal accuracy
2) Long term crystal stability

Problem #2 requires the crystal to be installed in a temperature controlled oven.
Problem #1 requires tuning.

Redo your math.
1ppm = 1 second in 12 days
4ppm error = 1 second in 3 days

For accuracy to 1 sec in 4 months you will need better than 100 ppb stability.

What you can do is replace CG with a ceramic trim capacitor with capacitance range of 4-20pF.
The most difficult part is calibrating the oscillator frequency against a known accurate standard. You cannot measure the crystal frequency directly. You have to find an indirect method of determining the frequency, for example, generating a 1 Hz signal.

Next, you will need a high accuracy frequency counter with accuracy of 30 ppb. Without that you will have to rely on synchronizing your time-keeping function against the internet clock over many days and months.

Even my wristwatch is off by 1 or 2 minutes in a year. I have a NIST traceable stopwatch that keeps time to 1 second per year.

1605275913496.png
 

JohnInTX

Joined Jun 26, 2012
4,559
And then I enabled Timer1's interrupt in such a way so as to never ever miss a single tick (one second) to be counted in its logic. I've gone through the code many times and so far I haven't found a fault within it.
You probably have taken this into account but if you are writing to TIMER 1 to reset the period, you also clear the prescaler which can cause it to lose a little time. I believe you can use the CCP module in compare mode with special event trigger to make a 16bit period register with the XTAL oscillator.

Good luck!
 

sagor

Joined Mar 10, 2019
479
Crystal frequency and stability have factors that affect its accuracy, as MrChips points out. First, the original accuracy may be off by a certain tolerance, even 5ppm, during manufacture. Second, the capacitance of your circuit may be pulling the frequency off a bit from the manufactured value. Third, as the board/circuit runs, it may warm up a bit, causing some thermal drift. Most crystal stability figures assume a fixed range of temperatures and have a drift value as temperature changes.
And how are you counting these timer signals? Is there any compensation required in the code for any code overhead, if required?
 

Thread Starter

cmartinez

Joined Jan 17, 2007
7,422
hi,
I tried a number of MCU internal RTC clocks, their long time accuracy was poor, so I switched to the DS3231 RTC module.
The DS3231 long term accuracy is excellent, a second or so in a month, I use them on a SD Data logger projects.
E
Thanks for the recommendation, Eric. That device looks rather good, but the datasheet says that it draws about 100 µA during standby. And that's too much for my poor power-starved application.
 

Thread Starter

cmartinez

Joined Jan 17, 2007
7,422
You probably have taken this into account but if you are writing to TIMER 1 to reset the period, you also clear the prescaler which can cause it to lose a little time. I believe you can use the CCP module in compare mode with special event trigger to make a 16bit period register with the XTAL oscillator.

Good luck!
Would you mind elaborating a bit? Because I'm not sure I understand.

Here's my basic interrupt code:
Code:
INT_VECTOR:   ORG    0x004                       ;interrupt vector location
               ;only the Timer1 Interrupt will be serviced here

               ;Setting bit 7 of the Timer1 counter immediately after this routine
               ;is accessed will halve its interrupt execution period and set it to 
               ;exactly one second
               banksel TMR1H                     ;bank 0
               bsf TMR1H, 7

               call Increment_Timekeeping  ;update internal seconds counter 
             
               bsf FLAGS, TIMER1_OV_FLAG   ;set the Timer Overflow Flag to signal
                                           ;that the Timer_Servicing routine must be 
                                           ;called. Located in common RAM

               ;clear the Timer1 interrupt flag at PIR1 before leaving
               banksel PIR1
               bcf PIR1, TMR1IF
       retfie ;Return from interrupt. Previous memory Bank will automatically be restablished

[\CODE]

What the code does, is set bit 7 of TMR1H so as to shorten the default 2-second interrupt to a 1-second interrput. It also calls a routine that keeps counting the elapsed seconds, and raises a flag that will later be consulted outside of the routine vector.

Are you saying that the [B]bsf TMR1H, 7[/B] instruction could somehow be affecting timekeeping?
 

JohnInTX

Joined Jun 26, 2012
4,559
I see what you are trying to do to get an interrupt every sec but writing to TMR1H will clear any accumulated prescaler counts, too. Also, I don't know about this one but some PIC timers lose a couple of Tcyc to sync up with the system clock when you write them. I haven't looked at this one, though. To see if any of that is a problem could you free run the timer, interrupt every 2 sec (no writing to the timer) and see how your timing error is?
If you still want a 1sec interrupt, I'd use the CCP.

Sorry not to think it through any better, preheating the grill.. mmmm
 

MrChips

Joined Oct 2, 2009
24,233
I don't see your code for updating Timer1 after each interrupt.

In any case, you should never stop the timer or interfere with the timer register. The proper way to use the timer module for an RTC timebase is to use Compare mode as follows:

On every interrupt, add a fixed count value (e.g. 1 second) to the compare register, CCPR1H:CCPR1L.
You do not have to worry about 16-bit overflows. The math works out correctly.
 

Thread Starter

cmartinez

Joined Jan 17, 2007
7,422
preheating the grill.. mmmm
Tanks for the suggestion, I'll look into it. But I don't think that writing to TMR1H would affect the prescaler... though I'm not sure. I've attached the device's datasheet if you feel like looking into it in more detail. In the meantime, I'll do as you've suggested and leave TMR1H alone and let the natural 2 sec interrupt be.... see if that makes a difference.

As for your grilling, please be a responsible member and post pics of your results in the appropriate thread...

Many thanks!
 

Attachments

AlbertHall

Joined Jun 4, 2014
11,506
"The prescale counter is not directly readable or writable;
however, the prescaler counter is cleared upon a write to
TMR1H or TMR1L."

"For writes, it is recommended that the user simply stop
the timer and write the desired values. A write
contention may occur by writing to the timer registers,
while the register is incrementing. This may produce an
unpredictable value in the TMR1H:TMR1L register pair."
 

Thread Starter

cmartinez

Joined Jan 17, 2007
7,422
"The prescale counter is not directly readable or writable;
however, the prescaler counter is cleared upon a write to
TMR1H or TMR1L."

"For writes, it is recommended that the user simply stop
the timer and write the desired values. A write
contention may occur by writing to the timer registers,
while the register is incrementing. This may produce an
unpredictable value in the TMR1H:TMR1L register pair."
There you have it... the above scenario is definitely a possibility of what's happening. I'm going to let the timer run its natural 2 second interrupt and get back here tomorrow with my results.

Thanks for your help, Albert.
 

Thread Starter

cmartinez

Joined Jan 17, 2007
7,422
On every interrupt, add a fixed count value (e.g. 1 second) to the compare register, CCPR1H:CCPR1L.
You do not have to worry about 16-bit overflows. The math works out correctly.
Ok... let me see if I'm getting what you're saying. Taking into account that using a 32,768 KHz crystal to run Timer1 produces an interrupt every two seconds. And considering your suggestion of using Compare Mode to produce said interrupts. Are you suggesting that, for example, on the first interrupt I could set the value of CCPR1H:CCPR1L to 16,384, and on the next one to 0, and on the next one back to 16,384, and so on... Is that how I could get an interrupt every second instead of two without messing with the registers belonging to Timer1?
 

MrChips

Joined Oct 2, 2009
24,233
Ok... let me see if I'm getting what you're saying. Taking into account that using a 32,768 KHz crystal to run Timer1 produces an interrupt every two seconds. And considering your suggestion of using Compare Mode to produce said interrupts. Are you suggesting that, for example, on the first interrupt I could set the value of CCPR1H:CCPR1L to 16,384, and on the next one to 0, and on the next one back to 16,384, and so on... Is that how I could get an interrupt every second instead of two without messing with the registers belonging to Timer1?
Yes, you’ve got it. Almost. You have to add 32768. Hence it will be 0 and 32768.
If you add 16384 the values would be 0, 16384, 32768, 49152.
 

Thread Starter

cmartinez

Joined Jan 17, 2007
7,422
Yes, you’ve got it. Almost. You have to add 32768. Hence it will be 0 and 32768.
There... I got confused with the division by two thingy, and forgot we were talking about a 16-bit register. Right now I'm running Timer1 in its natural 2 second interruption using the aforementioned crystal. And it's been 4 hours and no anomalies so far. I'll be back tomorrow morning to report my results, and if everything looks fine I'll implement the Compare Mode setup you've suggested and see how everything turns out.

Many, many thanks for your help, my friend.
 
Last edited:
Top