Continue to Site

# Accurate 1-second periods with any xtal 2011-02-13

Here are some easy C code examples to generate 1-second (or any period) from any xtal value and any interrupt period.

The code is performed in an interrupt, and only needs one variable to generate the desired period.

A simple example is a PIC running at 4MHz xtal, which is 1MHz on Timer0 (set to 1:1 prescale). In this example the interrupt occurs every 256 counts, and a second would be 1 million counts;

Code:
	// C code for a 1 second period with a 1MHz timer (4MHz xtal);
// uses 1 variable; unsigned long bres
// gets here every TMR0 int (every 256 ticks)

bres += 256;	// add 256 ticks to bresenham total

if(bres >= 1000000)	// if reached 1 second!
{
bres -= 1000000;	// subtract 1 second, retain error
do_1sec_event();	// update clock, etc
}

In each interrupt it just adds another 256 ticks, because the interrupt occurs every 256 ticks.
Then when the total count > 1 million, it generates a 1-second event (like update a clock etc).

Each second there will be a small error, but that error is always fixed the following second so it is a "zero accumulated error" system and will keep perfect time for clocks etc.

This system can be "tuned" to give more accuracy, it is typical to find that even with a decent xtal your clock might gain or lose a few seconds a day.

Because it uses a long constant for the "second" you can just trim this value to give a very fine adjustment of your clock.

If your clock runs 3 seconds a day fast;
newvalue = 1000000 / 1day * (1day+3seconds)
newvalue = 1000000 / 86400 * 86403
newvalue = 1000035

So this is the new code, "trimmed" exactly for your xtal;

Code:
	// C code for a 1 second period with a 1MHz timer (4MHz xtal);
// uses 1 variable; unsigned long bres
// gets here every TMR0 int (every 256 ticks)

bres += 256;	// add 256 ticks to bresenham total

if(bres >= 1000035)	// if reached 1 second!
{
bres -= 1000035;	// subtract 1 second, retain error
do_1sec_event();	// update clock, etc
}

Obviously you can use any value xtal, and just change that long constant to suit;
4MHz (/4) = 1000000
10MHz (/4) = 2500000
20MHz (/4) = 5000000
8.86MHz (/4) = 2215000 (cheap TV xtal)

So you can use any junkbox xtal and still get a 1 second accurate clock.

Making it faster and simpler; this is basically the same code, but refined so it uses a unsigned int for the second instead of a long, this uses less RAM and makes for faster code;

Code:
	// Optimised C code for a 1 second period with a 1MHz timer (4MHz xtal);
// uses 1 variable; unsigned int bres
// gets here every TMR0 int (every 256 ticks)

bres += 16;	// add (256/16) ticks to bresenham total

if(bres >= 62500)	// if reached (1000000/16) 1 second!
{
bres -= 62500;	// subtract 1 second, retain error
do_1sec_event();	// update clock, etc
}

This code is faster and is easier to convert to assembler, but not as finely "tuneable".

For more variations on this system, including PIC assembler examples and more advanced code like frequency conversion, see this page;
http://www.romanblack.com/one_sec.htm
Author
Mr RB
Views
1,296
First release
Last update
Rating
0 ratings