PIC16F57 Digital Clock - timing a minute

MMcLaren

Joined Feb 14, 2010
861
MMcLaren said:
Unfortunately, any write to TMR0, including clearing of bit 7, causes the prescaler to be reset, if you're using it, and also causes TMR0 to pause for 2 instruction cycles before it starts counting again.

Caveat! This may not be a very good method for "correct accuracy".
Hmm. I made one such circuit, a LED display minute counter, based on 32 KHz crystal. This is 8,192 clocks per second.

The prescaler is set to 0x07 (256). So it is 32 clocks per second.

In the main loop I test for 0x20:
Rich (BB code):
if (TMR0==0x20)add_sec();
Then I increment the seconds counter:
Rich (BB code):
void add_sec()
{
    TMR0=0;
    time_secs1++;
    if(time_secs1==10){time_secs1=0;time_secs10++;};
    if(time_secs10==6){time_secs10=0;time_mins1++;};
    if(time_mins1==10){time_mins1=0;time_mins10++;};
    if(time_mins10==6){time_mins10=0;};
}
I don't observe a resetting of the prescaler. Or I misunderstand something! I examined the source right now, it is just some lines C.

Also I don't observe any inaccuracy. I am wondering about this for a while, since it is mentioned in the 16F5X datasheet.
Unfortunately, this example will not produce an accurate timebase. When you clear TMR0 each second you loose two cycles plus any counts accumulated in the prescaler. It may only be six or eight or ten cycles lost each second, but the error is cumulative and measurable.

You can't "observe a resetting of the prescaler" directly because we don't have direct access to the prescaler but you can measure the cumulative error empirically in simulation simply by keeping track of the number of instruction cycles between one second intervals.

I'm sure you don't want to pass on faulty advice to Robin and other forum members, but, without qualification, your advice about clearing TMR0 bit 7, and this new code example which clears TMR0, may not be very good methods for producing an accurate timebase.

Once you understand the problem, it's relatively easy to work around it. For example, all you really had to do in the example code to avoid cumulative error was to leave TMR0 "free running" and test for the recurring one second interval. Perhaps something like this;

Rich (BB code):
  if (TMR0 & 0x20) add_sec();    //
  while(TMR0 & 0x20);            //
Rich (BB code):
void add_sec()
{
    time_secs1++;
    if(time_secs1==10){time_secs1=0;time_secs10++;};
    if(time_secs10==6){time_secs10=0;time_mins1++;};
    if(time_mins1==10){time_mins1=0;time_mins10++;};
    if(time_mins10==6){time_mins10=0;};
}
 
Last edited:

takao21203

Joined Apr 28, 2012
3,702
Unfortunately, this example will not produce an accurate timebase. When you clear TMR0 each second you loose two cycles plus any counts accumulated in the prescaler. It may only be six or eight or ten cycles lost each second, but the error is cumulative and measurable.

You can't "observe a resetting of the prescaler" directly because we don't have direct access to the prescaler but you can measure the cumulative error empirically in simulation simply by keeping track of the number of instruction cycles between one second intervals.

I'm sure you don't want to pass on faulty advice to Robin and other forum members, but, without qualification, your advice about clearing TMR0 bit 7, and this new code example which clears TMR0, may not be very good methods for producing an accurate timebase.

Once you understand the problem, it's relatively easy to work around it. For example, all you really had to do in the example code to avoid cumulative error was to leave TMR0 "free running" and test for the recurring one second interval. Perhaps something like this;

Rich (BB code):
  if (TMR0 & 0x20) add_sec();    //
  while(TMR0 & 0x20);            //
Rich (BB code):
void add_sec()
{
    time_secs1++;
    if(time_secs1==10){time_secs1=0;time_secs10++;};
    if(time_secs10==6){time_secs10=0;time_mins1++;};
    if(time_mins1==10){time_mins1=0;time_mins10++;};
    if(time_mins10==6){time_mins10=0;};
}
I have one small circuit here actually, a minute timer based on a 16F54.
The prescaler isn't resetting. Otherwise the timing would be "off" totally. As I say it is based on 32 KHz crystal (8Khz / 256 = 32 Hz). However there is an observeable deviation after just 5 minutes (if you start a stop watch in parallel).

It should be possible relatively easily simply to correct this in software. If the timer would stop for 2 cycles, simply subtract 2 cycles from the test?

I will try this today. As I say, most of the circuits I have built are based on TMR0 testing without resetting.
 

takao21203

Joined Apr 28, 2012
3,702
Hmm I have actually tried to modify the code.

What I believe not the prescaler as such is reset.
The internal counter register (before the prescaler) is reset!
And of course this will lead to inaccuracy.

So in the datasheet they write "the prescaler is reset", but this does not mean
the prescaler divider preset will be reset or changed.

I have to change the code to include a flip-flop logic.

OK I have changed the code. See the attachment. I am running a test now to see if there is still any deviation.

Thank you for pointing out this inaccuracy. I will record the observations in a video!

Rich (BB code):
 // flip-flop logic to increment seconds once every 0x20 timer cycles.
        if (TMR0&0x10){if(run_add_sec==0){run_add_sec++;}}else run_add_sec=0;
        if (run_add_sec&0x01)add_sec(); // add a second
Rich (BB code):
void add_sec()
{
    run_add_sec|=0x02; // increment from 0x1 to 0x2 once, is reset when (TMR0 & 0x10)
    run_add_sec&=0x02; // becomes 0x00 again.
    time_secs1++;
http://www.youtube.com/watch?v=-jag6rgKPWQ (Incorrect code)
http://www.youtube.com/watch?v=wQAPwXvsGSc (new code)
 

Attachments

Last edited:

JohnInTX

Joined Jun 26, 2012
4,787
From DS41213D 16F5x Datasheet Section 7.2 Prescaler:
When assigned to the Timer0 module, all instructions
writing to the TMR0 register (e.g., CLRF 1, MOVWF 1,
BSF 1, x, etc.) will clear the prescaler. When assigned
to WDT, a CLRWDT instruction will clear the prescaler
along with the WDT. The prescaler is neither readable
nor writable. On a Reset, the prescaler contains all ‘0’s.
And as Fig 7-2 and 7-3 indicates, any write to TMR0 also adds a fixed 2Tcyc before the first count is racked up.
 

takao21203

Joined Apr 28, 2012
3,702
From DS41213D 16F5x Datasheet Section 7.2 Prescaler:
And as Fig 7-2 and 7-3 indicates, any write to TMR0 also adds a fixed 2Tcyc before the first count is racked up.
Yes the prescaler counter register is cleared, but the prescaler divider preset remains unchanged.
 

MMcLaren

Joined Feb 14, 2010
861
Rich (BB code):
 // flip-flop logic to increment seconds once every 0x20 timer cycles.
        if (TMR0&0x10){if(run_add_sec==0){run_add_sec++;}}else run_add_sec=0;
        if (run_add_sec&0x01)add_sec(); // add a second
Rich (BB code):
void add_sec()
{
    run_add_sec|=0x02; // increment from 0x1 to 0x2 once, is reset when (TMR0 & 0x10)
    run_add_sec&=0x02; // becomes 0x00 again.
    time_secs1++;
I'm happy you figured everything out. Your "flip flop" logic seems a bit complicated when spread across functions like that. Just curious how others might tackle the same thing (initiating an "add_sec()" action every 32 TMR0 'ticks')? Here's one solution I came up with;

Rich (BB code):
      if(tmr0.6 ^ latch.6)       // if "new" 1-second interval
      { latch.6 ^= 1;            // update latch, then
        add_sec();               // bump RTC variables
      }                          //
Regards, Mike
 

takao21203

Joined Apr 28, 2012
3,702
I'm happy you figured everything out. Your "flip flop" logic seems a bit complicated when spread across functions like that. Just curious how others might tackle the same thing (initiating an "add_sec()" action every 32 TMR0 'ticks')? Here's one solution I came up with;

Rich (BB code):
      if(tmr0.6 ^ latch.6)       // if "new" 1-second interval
      { latch.6 ^= 1;            // update latch, then
        add_sec();               // bump RTC variables
      }                          //
Regards, Mike
Hmm.

1. 0 / 0
2. 1 / 0
2.1 1 / 1
3. 1 / 1

4. 0 / 1
4.1 0 / 0

The XOR gets triggered 2 times as much.
Also when it is set, and the timer bit returns to 0.

If you need many such flip-flops, it is a good idea to wrap the processing into a helper function.
 
Last edited:

MMcLaren

Joined Feb 14, 2010
861
Oops! My mistake! Using bit 6 results in updates every 16384 instruction cycles (once every 2 seconds). Instead, the routine needs to use TMR0 bit 5 which changes state once every 8192 instruction cycles (once per second) when using a 32768 Hz crystal and 1:256 prescaler setting. Please check it out in the Simulator...

Rich (BB code):
      if(tmr0.5 ^ latch.5)       // if "new" 1-second interval
      { latch.5 ^= 1;            // update latch, then
        add_sec();               // bump RTC variables
      }                          //
I'd still love to see how others might produce a simple elegant (less complicated) solution...

Regards, Mike
 
Last edited:
Top