YET ANOTHER TIMER TUTORIAL

Introduction

For those of us who choose to sit down and learn to program the PIC chips, there are a lot of tutorials to be found on the Internet. Some of them are good, some are bad, and some are mediocre.

I am not sure just what category you might place this one in, but the intent is to produce one which is somewhat more basic and geared to the absolute novice.

Scientific Notation

Most of us had scientific notation in college, but may not remember all the rules. So I will cover it very quickly here. Essentially, it is just a notation for dealing with large numbers, which makes it easier to do the calculations we will need to do.

A typical crystal frequency for a PIC is 20 MHZ, or 20 million cycles per second. If we write it as a number, we would write 20,000,000 cycles per second or, even worse, we might write it as 20000000 cycles per second.

This is kind of hard to deal with, so we use scientific notation and write it as 20 x 106 which simply means that we have a 20 followed by 6 zeroes. 20 x 106 = 20,000,000. The "x" of course denotes multiplication.

More often, we have to deal with milliseconds, microseconds and nanoseconds. A millisecond is a thousandth of a second, or 1/1000 seconds. A microsecond is a millionth of a second, or 1/1,000,000 seconds. A nanosecond is a billionth of a second, or 1/1,000,000,000 seconds. Notice that we multiply by 1000 each time. A thousand is 1,000. A million is 1,000,000. A billion is 1,000,000,000.

Here again, it is easier to deal with this if we use scientific notation. A millisecond can be written as 1/(1 x 103). A microsecond can be written as 1/(1 x 106) and a nanosecond can be written as 1/(1 x 109).

The Rules of Exponents

The rules for doing arithmetic with scientific notation are quite simple. Since for the purpose of this paper we shall only use 10 (or sometimes 1) as the base, we can simply state the following:

    1. When we multiply we add the exponents.
    2. When we divide we subtract the exponents.
    3. To move an exponent from the numerator to the denominator of a fraction, or vice-versa, we merely change the sign of the exponent.

Example 1: Multiplication: 103 x 102 = 10(3+2) = 105

Example 2: Multiplication: 105 x 10-2 = 10(5-2) = 103

Example 3: Division: 105 / 103 = 10(5-3) = 102

Example 4: Division: 103 / 105 = 10(3-5) = 10-2

Example 5: Switching: 103 = 1/10-3

Example 6: Switching: 400/103 = 400 x 10-3 = 4 x 102 x 10-3 = 4 x 10(2-3) = 4 x 10-1

Let’s look at that last example a little closer. 400/103 means 400/1000. If we use our basic knowledge of arithmetic to cancel the two zeroes we have 4/10 (cancel two zeroes from the numerator with two corresponding zeroes from the denominator. But 4/10 can also be written as .4 can it not? Of course it can. As can 4 x 10-1.

The rule to remember when you are dealing with a number like 43.333 x 10-3 is this:

"To move the decimal place to the left, add to the exponent. To move it to the right, subtract from the exponent."

Example 1: 3.455 x 103 = .3455 x 104 = 34.55 x 102 = .003455 x 106

Cycles

The 18F452 PIC can be run at several speeds, by using different crystals. For example, we can run it at 20 MHz by using a 20 MHz crystal. Or at 10 MHz by using a 10 MHz crystal. Or, we take advantage of the fact that an internal multiply-by-four PLL (phase locked loop) can be ued and run I at 40 MHz using a 10 MHz crystal. The point is that the PIC can be run at different clock speeds. The common ones are 4 MHz, 10 MHz, 20 MHz and 40 MHz, and we shall use those for this paper.

Although the clock speed is the basic driving force of the PIC, it actually takes several clock cycles to execute an instruction. Four of them, to be exact. While I am not familiar with the exact architecture, generally we have to (a) fetch the next instruction, (b) decode the op-code, (c) fetch the operand, and (d) execute the resulting instruction. So it takes four clock cycles to execute each instruction, both in my example and for the PIC.

Therefore, the instructions are executed at ¼ the speed of the clock. For a 40 MHz clock, we will be executing 10 million instructions per second, or 10 MIPs. For a 10 MHz clock, we will be executing 2.5 million instructions per second, or 2.5 MIPS.

To calculate how long one instruction takes, we can divide the number of instructions per second into 1. This would give us the speed, in seconds, of each instruction. So, for example, with a 40 MHz clock, executing 10 MIPs, we would have:

(1 second)/(10 x 106) = .1 x 10-6 = 100 x 10-9 = 100 nanoseconds per instruction. (Of course, we could equally well say 0.1 microseconds per instruction, but that is not done).

Timer0

On the PIC 18F452 chip there are four timers, referred to as Timer0, Timer1, Timer2 and Timer3. This discussion shall deal only with Timer0 of the 18F452. Since they all work pretty much the same, however, if you understand this tutorial thoroughly then you should not have any particular problem adapting it to other PIC’s and other timers.

To simplify matters, let us specify that Timer0 of the 18F452 has two counters. One of these consists of 8 bits, and can count from 0 to 255. Decimal, of course. When it reaches 255, all the bits are 1’s, and the next time it counts the counter turns over to zero, much as the speedometer on your car would. When it turns over, or "overflows," it sets a bit in a register, which can be checked by the program. Thus we can tell when the counter overflows.

To make life even rosier, this counter can be incremented once for each instruction. If we have a 40 MHz clock, then it is incremented one every hundred nanoseconds, since it takes that long for each instruction to execute. If we wanted to know exactly how long it takes to overflow the counter we merely multiply 256 x 100 x 10-9 = 25600 x 10-9 or 25.6 x 10-6 or 25.6 microseconds. So if we wanted to have a delay of exactly 25.6 microseconds we merely have to set the timer, wait for it to overflow, and voila!

Now, the problem is that a delay of 25.6 microseconds, although perhaps not unheard of, is not exactly your common period. People use delays for things like blinking LEDs on and off, stuff like that. So delays of maybe 1 second, 100 milliseconds, 10 milliseconds, or even 1 millisecond tend to be more common. Which means the 8 bit counter might not be too useful.

However, the 18F452 also has a 16 bit counter, and we could do the same calculation with it. A 16 bit counter overflows after it reaches 65535, so if we want to know how long it takes to overflow (using the 40 MHz clock again), then we merely multiply 65536 x 100 x 10-9 = 6,553,600 x 10-9 or 6.5536 x 10-3 or 6.5 milliseconds.

This is not too bad, but still won’t get us the delay of 1 second, 100 milliseconds, or even 10 milliseconds. Oh, we could get it, but we would have to let the counter overflow more than once, and that introduces a certain amount of hassle.

However, the 18F452 also has a feature called "prescaling," which lets us slow down the clock which is driving the increment. We can divide the clock by 2, by 4, 8, 16, 32, 64, 128 or 256. Dividing the clock results in a multiplication of the of the increment time. If we divide the clock by 2, we mutiple the increment time by 2, and so forth.

So let us do the exact same calculation with the 40 MHz clock and the 16 bit counter, but this time let’s prescale by 4. So we multiply 65536 x 100 x 4 x 10-9 = 26,214,400 x 10-9 = 26.2 x 10-3 or 26.2 milliseconds.

This still does not get us to the 100 milliseconds, or the 1 second, but using the 256 prescaler with the 16-bit counter will take care of both of those.

Toward this end, the following tables may prove useful. The discrepancies in the numbers are primarily from rounding (or not rounding – I was not consistent), but they are close enough to give you a good idea as to which variables you want to select.

The tables show the time it takes to overflow the counter, starting with the counter at an initial value of zero. The first line of each table is for the 8-bit counter. The second line of each table is for the 16-bit counter.

Table 1 – 4 MHz
1 MIPS, each instruction executes in 1 microsecond.
1:1 1:2 1:4 1:8 1:16 1:32 1:64 1:128 1:256
255 us 512 us 1 ms 2 ms 4 ms 8 ms 16 ms 32 ms 65 ms
65 ms 131 ms 262 ms 524 ms 1 sec 2 sec 4 sec 8 sec 16 sec

Table 2 – 10 MHz
2.5 MIPS, each instruction executes in 400 nanoseconds.
1:1 1:2 1:4 1:8 1:16 1:32 1:64 1:128 1:256
102 us 204 us 409 us 819 us 1.6 ms 3.2 ms 6.5 ms 13 ms 26 ms
26 ms 52 ms 105 ms 209 ms 419 ms 839 ms 1.6 sec 3.3 sec 6.7 sec

Table 3 – 20 Mhz
5 MIPS, each instruction executes in 200 nanoseconds.
1:1 1:2 1:4 1:8 1:16 1:32 1:64 1:128 1:256
51 us 102 us 204 us 409 us 819 us 1.6 ms 3.2 ms 6.5 ms 13 ms
13 ms 26 ms 52 ms 105 ms 209 ms 419 ms 839 ms 1.6 sec 3.3 sec

Table 4 – 40 MHz
10 MIPS, each instruction executes in 100 nanoseconds.
1:1 1:2 1:4 1:8 1:16 1:32 1:64 1:128 1:256
25 us 51 us 102 us 204 us 409 us 819 us 1.6 ms 3.2 ms 6.5 ms
6.5 ms 13 ms 26 ms 52 ms 105 ms 209 ms 419 ms 839 ms 1.6 sec

Using the Tables

Using the above tables, we can easily decide the variables we want to use in our timing routine. For example, suppose we are programming a PIC with a 20 Mhz clock. We would use Table 3 in that case. Further suppose that we want to delay for 9 milliseconds. Looking at Table 3, we see that we can use the 8-bit counter with a 1:256 prescaler, since this allows us to time for 13 milliseconds. We could also use the 16-bit tables, of course, since any prescaler would give us more than 9 milliseconds. However, using the 16-bit counter presents a little bit more of a hassle, so no use to use it if we do not need it. The 8-bit counter will do the job just fine.

Just to reiterate: we use our crystal speed to select the proper table, and then choose the smallest value which is as large as, or greater than, the delay value we want. And we always choose an 8-bit counter if possible.

However, since we do not have an entry for exactly 9 milliseconds, we have to do some further calculation. If the 8-bit counter begins at zero, with a 1:256 prescaler, it will overflow in 13 milliseconds. To make it overflow in 9 milliseconds, then we have to begin the count at a number greater than zero. The PIC timer allows us to load any number we wish into the counter and then start the timer, so the only problem is to calculate the beginning number.

We know from Table 3 that with a 20 MHz crystal, each instruction executes in 200 nanoseconds. But since we are going to use the 1:256 prescaler, we have to multiply that by 256. So the counter will increment every 256 x 200 x 10-9 seconds, or every 51,200 x 10-9 seconds, or every 51.2 x 10-6 seconds, or every 51.2 microseconds.

Since 13 – 9 = 4 milliseconds, we have to preset the counter with the corresponding number for 4. To phrase it differently, if the counter began at zero, what would be the value after 4 milliseconds? It would be (4 x 10-3)/(51.2 x 10-6) = (4/51.2) x 103 = 78.125, which we will round to 78.

Lets check that. If we have calculated correctly, then (256 – 78) x 51.2 microseconds should equal 9 milliseconds. So 178 x 51.2 x 10-6 = 9113.6 x 10-6 = 9.1 milliseconds. This is fairly close. But why is it not exact?

The answer is that we have lost some accuracy in all the rounding and non-rounding that we have did in calculating the tables. Which is to say, the number given in the table is an approximation, in order to give us a good choice of which counter to use and which prescaler to use. Since the table gives a figure of 13 milliseconds, and we want to delay for 9, then there should be plenty of room for fitting in there. (If the table gives 13 milliseconds and we need a delay of 12.88889 milliseconds, then we should probably go to a larger counter. We would use a 16-bit counter with 1:2 prescaling, in that case).

So let us calculate the exact numbers we need. Table 3 tells us that with a 20 MHz crystal, the counter will be incremented every 200 nanoseconds. With the prescaler of 1:256, however, it will only be incremented every (200 ns) x 256 = 51200 nanoseconds, or every 51.2 microseconds. Since the 8-bit counter increments 256 times before it overflows, then it will overflow after (51.2 us) x 256 = 13107200 nanoseconds, or a total of 13.1072 milliseconds.

Since 13.1072 – 9 = 4.1072, our calculation is now (4.1072 x 10-3)/(51.2 x 10-6) = 0.08021875 x 103 = 80. So for maximum precision, we preset the counter to 80.

And (256 – 80) x 51.2 us = 9011.2 microseconds, or 9.0 milliseconds. Obviously closer, if you need that sort of precision. And if you are willing to assume that the crystal is exactly 20,000,000 cycles per second rather than a little more or a little less. J

An Assembly Language Example

Okay. Let me put these ideas into practice. The following is a general delay routine, written in assembly language for the 18F452. It has been verified on the MPLAB Simulator, and the timing looks good.

 
;  General delay routine.  Written specifically for the 18F452 chip, running at 40Mhz.
;  Any changes in the speed will require changes to the constants used.
;
;   We are using Timer0 for the delay, loading a constant value into the counter which
;   the timer increments once each instruction cycle.  Therefore, the only things that
;   need to be changed are the constants.  Since the delay routine offers a choice of
;   1ms, 10ms, 100ms or 1 second delays, there are four constants defined.  Each constant
;   represents (65535 minus the number of instruction cycles to be performed until the
; specific time value is reached). 
;
;                                       Explanation of the constants
;
;   Let’s examine how the 1 second delay works first.  We can then expand that.
;
;   Since we are dividing our actual clock speed (10 MIPS) by 256, the effective
;   clock speed (for the purpose of incrementing the counter) will be 39.0625 KHz.
;                  
;          (10 * 106 )/256 = 39062.5 instructions per second
;
;   Since 39062.5 instructions execute in one second, then what we need to do is to set our
;   counter to 65535 - 39063 (rounding up) = 26472.  If it starts there, and we
;   then activate the timer, it will do an additional 39063 increments and set the overflow
;   bit.
;
;   This makes it easy to set the constants for 100 ms, 10 ms and 1 ms, since we just have
;   to divide by ten.  
;
;         100 ms = .1 seconds, 10 ms = .01 seconds, and 1 ms = .001 seconds.
;
;	To get the figure for 100 ms, we have 65535 - 3906 = 61629
;	For 10 ms it is 65535 – 390 =  65144
;	For 1 ms it is 65535 – 39 = 65496
;      
;
.

        #include <p18f452.inc>
        global   d_1_ms, d_10_ms, d_100_ms, d_1_sec

		radix dec

d_1ms_h          equ     65496/256			
d_1ms_l	equ      65496 % 256
d_10ms_h 	equ      65144/256
d_10ms_l 	equ      65144 % 256		
d_100ms_h 	equ      61629/256
d_100ms_l 	equ      61629 % 256
d_1sec_h 	equ      26472/256
d_1sec_l 	equ      26472 % 256

d_code       	code
d_1_ms	movlw	d_1ms_h
		movwf	TMR0H
		movlw	d_1ms_l
		bra	delay

d_10_ms	movlw	d_10ms_h
		movwf	TMR0H
		movlw	d_10ms_l
		bra	delay

d_100_ms	movlw	d_100ms_h
		movwf	TMR0H
		movlw	d_100ms_l
		bra	delay

d_1_sec	movlw	d_1sec_h
		movwf	TMR0H
		movlw	d_1sec_l
delay		movwf	TMR0L				; saved a few instructions :-)

;
;     Set the necessary bits for the timer
;     (Some of the instructions are commented out, since it is not necessary to
;      execute them because we start by clearing the entire byte.)
;
		clrf	T0CON			; start with a virgin
;		bcf	T0CON,T0CS			; use instruction clock
;		bcf	T0CON,PSA			; select prescaler
		bsf	T0CON,T0PS0			
		bsf	T0CON,T0PS1		; 1:256 prescale
		bsf	T0CON,T0PS2
;		bcf	T0CON,T08BIT		; 16 bit counter

    		bcf	INTCON,TMR0IF		; clear the overflow bit
tlp1		btfsc	INTCON,TMR0IF
		bra	tlp1				; make sure overflow bit is clear
		bsf	T0CON,TMR0ON		; enable the timer
;
;	Now, just hang out, looking cool, and wait for the bit to be set
;

tlp2		btfss	INTCON,TMR0IF
		bra	tlp2
		bcf	INTCON,TMR0IF		; clear it
		bcf	T0CON,TMR0ON		; and clear timer enable

		return
		end

  John W. Nall John_nall@hotmail.com 5-17-2003

Interested:

Questions:

Comments: