Millisecond clock with a PIC

One of the first things I tried to do when I learned Assembly back in middle school was to make an ultra precise timer.  Much to my surprise this is very difficult!  Computer processors aren’t very good at keeping track of time.  The problem is that they’re always being interrupted (literally, so I guess this doesn’t count as a pun).  Even making a precise delay is difficult.  You can’t rely on the time it takes for a CPU to execute a certain number of commands because inevitably some other process will steal away precious clock cycles.

Enter the PIC microprocessor…

I’m working on a project with a 16F887 PIC and wanted some sort of “system clock” with a 1ms resolution that I could query to find the elapsed time of a particular code loop.  The PIC has a built-in module, Timer0, that increments an 8-bit variable every instruction cycle.  When it gets to 1111 1111, it resets to 0 and triggers an overflow interrupt flag.  This means you can generate an interrupt every 256 instructions.  You can also add a prescaler so that it increments every 2, 4, 8, .. 256 instructions.  The clever part of the code below is that when Timer0 overflows, the interrupt routine resets the value to 6 instead of 0, which results in an interrupt every (250 * Prescaler) instructions.

Some math:

Time between interrupts in microseconds =

(1,000,000 us / 1 sec) * ( 1 second / x cycles) * (  4 cycles / instruction) * ( p  instructions/ timer increment ) * (250 timer increments/interrupt)

Note that the second term is the clock speed of the chip (ex 4000000 hz = 4MHz).  The third term means the PIC takes 4 cycles to execute each instruction, the fourther term is the prescaler, and the fifth term is the number of times Timer0 is incremented before it overflows and calls the interrupt.

Canceling the terms and simplifying we get:

(1000000 us / x)  * p  * 1000 * ( 1 / interrupt)

If we specify cycles in M MHz instead, we get this:

Time per interrupt = 1000*P/M microseconds  (or P/M milliseconds)

So choosing a prescaler equal to the chip MHz will give us an interrupt every 1 ms.  Okay, now you’re ready for the code:

#define TMR0_RESET (0xFF - 250 + 1)	//want 250 cycles before overflow, subtract more for overhead if necessary
					// +1 to compensate for rollover - afterall a value of 0xFF would still result in 1 cycle before reset
unsigned int    msCounter = 0;

void main(void)
{
	OPTION &= 0b11000000;	//set 8-bit Timer0 module to increment every instruction cycle
				//assign prescaler to Timer0
				//reset interrupt flag
	OPTION |= 0b0000001;	//set prescaler (see datasheet for OPTION register)
	T0IE = 1;		//enable Timer0 interrupt on overflow
	GIE = 1;		//enable global interrupts
	while(1)
	{
		// do something
	}
}

void interrupt isr(void)
{
	if (T0IF)
	{
		TMR0 = TMR0_RESET;
		T0IF = 0;
		msCounter++;
	}
}

Dennis

A mathematician, hacker, fabricator, and all around do-it-yourselfer. I like building my own tools, learning new technologies, and breaking stuff.

One thought on “Millisecond clock with a PIC

Leave a Reply

Your email address will not be published. Required fields are marked *