04 Delays, Counters, and Timers
04 Delays, Counters, and Timers
04 Delays, Counters, and Timers
179
180 Chapter 9
The default state is disabled but there is no rea son why the PWRT timer should
not be en abled for most applica tions.
The OST func tion counts oscillator pulses on the OSC1/CLKIN pin. The counter
starts incrementing after the amplitude of the signal reaches the oscillator input
thresholds. This initial delay allows the crystal oscillator or resonator to stabilize
before the de vice ex its the OST delay. The length of the time-out is a func tion of the
crystal/resonator frequency. For low-frequency crystals, this start-up time can be-
come quite long. That is because the time it takes the low-frequency oscillator to
start oscillating is longer than the power-up timer's delay.
The time from when the power-up timer times out to when the oscillator starts to
oscillate is referred to as dead time. There is no minimum or maximum time for this
dead time because it is dependent on the time required for the oscillator circuitry to
have “good” oscillations.
The PLL can only be en abled when the os cillator con fig u ra tion bits are pro-
grammed for HS mode. In all other modes, the PLL op tion is disabled and the sys tem
clock will come directly from the OSC1 pin. The con figuration bit for HS and PLL
are selected with the following statement:
When the Phase Locked Loop Os cillator Mode is selected, the time-out sequence
following a Power-on Reset is different from the other oscillator modes. In this case,
a portion of the Power-up Timer is used to provide a fixed time-out that is sufficient
for the PLL to lock to the main oscillator frequency. This PLL lock time-out (TPLL)
is typically 2 ms and follows the oscillator start-up time-out (OST).
Delays, Counters, and Timers 181
When the PLL is enabled (HSPLL oscillator mode), the Power-up Timer (PWRT) is
used to keep the de vice in RESET for an extra nominal delay. This additional delay
ensures that the PLL is locked to the crystal frequency.
The hardware of the Watch dog Timer is independent of the PIC's internal clock.
Its time-out period can range from approximately 18 milliseconds to 2.3 seconds,
depending on whether the prescaler is used. According to Microchip, the Watch dog
Timer is not very accurate and in the worst case scenario, the time-out period can
extend to sev eral seconds. When the WDT times out, the TO flag in the STATUS reg-
ister is cleared and the program counter is reset to 0x000 so that the program re-
starts. Applica tions can prevent the re set by issuing the clrwdt instruction before
the time-out period ends. When clrwdt executes, the WDT time-out period restarts.
The clrwdt and sleep instructions clear the WDT and the postscaler (if assigned
to the WDT) and prevent it from timing out and generating a device RESET condi-
tion. The WDT has a postscaler field that can extend the WDT Reset period. The
postscaler is selected by the value written to three bits in the CONFIG2H register
dur ing de vice pro gram ming. When a clrwdr in struc tion is ex e cuted and the
postscaler is assigned to the WDT, the postscaler count will be cleared, but the
postscaler assignment is not changed.
In the remaider of the chapter we discuss the four timer modules available in the
18F452.
bit 7 bit 0
TMR0ON T08BIT T0CS T0SE PSA T0PS2 T0PS1 T0PS0
All bits in the T0CON reg ister are readable and writeable.
The Timer0 module is the first peripheral device discussed in this book. Periph-
eral devices add specific functionality to the microcontroller. Learning to program
the Timer0 module serves as an introduction to programming PIC 18F peripherals,
of which there is a long list. Figure 9.2 is a block diagram of the Timer0 module in 8-
bit mode.
data bus x 8
0 0
OSC/4
Pout PSout 7 6 5 4 3 2 1 0
Sync with
internal
TOCKI pin
clocks TMR0
7 6 5 4 3 2 1 0
1 1
T0PS0-T0PS2
(Prescaler)
TOSE T0IF
Interrupt flag bit
TOCS set on overflow
(Clock source select) PSA
Writing to the high byte of Timer0 must take place through the TMR0H buffer reg-
ister. In this case, Timer0 high byte is updated with the con tents of TMR0H when a
write occurs to TMR0L. This design allows code to update all 16 bits of Timer0 (high
and low bytes) at the same time. When performing a write of TMR0, the carry is held
off during the write of the TMR0L register. Writes to the TMR0H register only mod-
ify the holding latch, not the timer. The operation requires the following steps:
1. Load the TMR0H register.
2. Write to the TMR0L register.
Some instructions (bsf and bcf) are used to read the con tents of a reg ister, make
changes to that content, and write the result back to the register. This sequence is
known as a read-modify-write. With regard to the TMR0L register, the read cycle of
the read-modify-write operation does not update the TMR0H reg ister; there fore the
TMR0H buffer remains unchanged. When the write cycle takes place, then the con-
tents of TMR0H are placed in the high bytes of the Timer0 register.
Delays, Counters, and Timers 185
The sample program Timer0_Delay.asm developed later in this chapter shows the
setup and operation of Timer0 in 16-bit mode as well as reading and writing to the
TMR0H and TMR0L registers. Many re ports of bugs in the Timer0 16-bit mode found
on the Internet are due to pro grams that have not followed the correct read/write
sequence when accessing the Timer0 high byte.
Timer0 can operate in a timer or a counter mode. The timer mode is selected by clear-
ing the T0CS bit in the T0CON register. Without a prescaler, in timer mode the Timer0
module increments on every instruction cycle. If the TMR0 register is written, the in-
crement is inhibited for the following two instruction cycles. Code can work around
this by writing an adjusted value to the TMR0 register.
Counter mode is selected by setting the T0CS bit (T0CON register). In counter
mode, Timer0 increments either on every rising or falling edge of the T0CKI pin. The
edge is determined by the Timer0 Source Edge Select bit T0SE in the T0CON regis-
ter. Clearing the T0SE bit selects the rising edge of the signal.
Timer0 Interrupt
When the interrupt flag bit is set, Timer0 generates an interrupt when the TMR0 regis-
ter over flow. In the 8-bit mode, this takes place when the count goes from 0xff to 0x00.
In the 16-bit mode, the in terrupt is generated when the counter goes from 0xffff to
0x0000.
This interrupt overflow sets the TMR0IF bit in the INTCON register. The interrupt
can be disabled by clearing the TMR0IE bit in the INTCON register. The TMR0IF flag
bit must be cleared in software in the interrupt service routine. The TMR0 interrupt
can not awaken the processor from SLEEP, as the timer is shut off during SLEEP.
When an external clock signal is selected, the Timer0 hardware must ensure that the
clock signal can be synchronized with the internal clock.
When no prescaler is used, the ex ter nal clock in put is used in stead of the
prescaler output. When a prescaler is used, the ex ternal clock input is divided by
the prescaler so that the prescaler output is symmetrical. For the external clock to
meet the sam pling require ment, the rip ple-counter must be taken into ac count.
Therefore, it is necessary for T0CKI to have a period of at least 4TSCLK (and a small
RC delay) divided by the prescaler value. The only requirement on T0CKI high and
low time is that they do not violate the minimum pulse width requirement. Because
the prescaler out put is synchronized with the internal clock, there is a small delay
from the time the external clock edge occurs to the time the Timer0 module is actu-
ally incremented. The actual magnitude of this delay can be obtained from the de-
vices' data sheets.
186 Chapter 9
Timer0 Prescaler
Timer0 contains a prescaler that allows controlling the timer's rate by acting as a cycle
divider. The PSA bit in the T0CON register (Prescaler Assign ment Bit) allows turn ing
the prescaler on and off.
Past errors in some PIC18 data sheets have created confusion regarding the ac-
tion of the PSA bit. For example, the 18F Family Reference Manual (DS39513A)
states on page 13-7 that “Setting the PSA bit will enable the prescaler.” In that same
document, the T0CON register bitmap shows that it is a value of 0 in the PSA bit that
assigns the prescaler to Timer0. Actually, this is the case. Regarding Timer0, the PSA
bit is active low, so a value of 0 turns on the prescaler while a value of 1 turns off the
prescaler assignment.
The rate of the prescaler is determined by bits 0:2 in the T0CON register as shown
in Figure 9.1. The 3-bit field allows selecting eight different prescaler rates: a value
of 0x7 enables a 1:256 prescaler value while a value of 0x0 selects a prescaler rate of
1:2. The prescaler select bits are readable and writable but the prescaler count can-
not be read or written. All instructions that write to the Timer0 register, such as clrf
TMR0, bsf TMR0,x, movwf TMR0, and others) will clear the prescaler count if the
prescaler has been enabled. However, writes to TMR0H do not clear the prescaler
count be cause writing to the latch does not change the con tents of Timer0. The
prescaler is cleared by writing to TMR0L.
Calculating the time taken by each counter itera tion consists of dividing the
clock speed by 4. For example, a 18F452 PIC running on a 4 MHz oscillator clock, in-
crements the counter every 1 MHz. If the prescaler is not used, the counter register
is incremented at a rate of 1 µs. or 1,000,000 times per second. If the prescaler is set
to the maximum divisor value (256), then each increment of the timer takes place at
a rate of 1,000,000/256 µs, which is approximately 3.906 ms. This is the slowest pos-
Delays, Counters, and Timers 187
sible rate of the timer in a machine running at 4 MHz. It is often necessary to employ
supplementary counters in order to achieve larger delays.
Recall that the timer register (TMR0) is both read able and writable. This makes
possible several timing techniques; for example, code can set the timer register to
an initial value and then count up un til a predetermined limit is reached. Suppose
that we define that the difference between the limit and the initial value is 100; then
the routine will count 100 times the timer rate per beat.
As another ex ample, consider a routine in 8-bit mode that allows the timer to
start from zero and count up un restricted. In this case, when the count reaches the
maximum value (0xff), the routine would have introduced a delay of 256 times the
timer beat rate. Now consider the case in which the maximum value (256) was used
in the prescaler and the timer ran at a rate of 1,000,000 beats per second. This means
that each timer beat will take place at a rate of 1,000,000/256, or approximately
3,906 timer beats per second. If now we develop a routine that delays execution un-
til the maximum value has been reached in the counter register, then the delay can
be calculated by dividing the number of beats per second (3,906) by the number of
counts in the delay loop. In this case, 3,906/256 results in a delay of approximately
15.26 iterations of the delay routine per second.
A general formula for calculating the number of timer beats per second is as fol-
lows:
C
T=
4 PR
where T is the number of clock beats per sec ond, C is the system clock speed in Hz, P is
the value stored in the prescaler, and R is the number of iteration, counted in the tmr0
register. The range of both P and R in this formula is from 1 to 256. Also notice that the
reciprocal of T (1/T) gives the time delay, in seconds, per iteration of the delay routine.
In the previous section we saw that even when using the largest possible prescaler and
counting the maximum number of timer beats, the lon gest timer delay that can be ob-
tained in a 4-MHz system is approximately 1/15th second. Consequently, applications
that mea sure time in seconds or in minutes must find ways of keep ing count of large
number of rep etitions of the timer beat.
In implementing counters for larger de lays we must be care ful not to introduce
round-off errors. In the previous example, a timer cycles at the rate of 15.26 times
per second. The closest integer to 15.25 is 15, so if we now set up a seconds counter
that counts 15 iterations, the counter would introduce an er ror of approximately 2%.
Considering that each iteration of the timer contains 256 individual beats, there are
3,906.25 individual timer beats per second at the maximum pre-scaled rate.
188 Chapter 9
This means that if we were to implement a coun ter to keep track of individual
pre-scaled beats, instead of timer iterations, the count would proceed from 0 to
3,906 instead of from 0 to 15. Approximating 3,906.25 by the closest integer (3,906)
introduces a much smaller round-off error than ap proximating 15.26 with 15. In this
same example, we could eliminate the prescaler so that the timer beats at the clock
rate, that is, at 1,000,000 beats per second. In this option a counter that counts from
0 to 1,000,000 would have no intrinsic error due to round-off. Which solution is more
adequate depends on the accuracy required by the application and the acceptable
complexity of the code.
wait:
movf tmr0,w ; Timer value into w
btfss sta tus,z ; Was it zero?
goto wait
; If this point is reached tmr0 has over flowed
But there is a problem: the timer ticks as each instruction executes. Because the goto
instruction takes two machine cycles, it is possible that the timer overflows while the
goto instruction is in progress; therefore the overflow condition would not be de-
tected. One possible solution found in the Microchip documentation is to check for
less than a nominal value by testing the carry flag, as follows:
wait1:
movlw 0x03 ; 3 to w
subwf TMR0,w ; Sub tract w - tmr0
btfsc sta tus,c ; Test carry
goto wait1
One adjustment that is sometimes necessary in free running timers results from
the fact that when the TMR0 register is written, the count is inhibited for the follow-
ing two instruction cycles. Software can usu ally compensate for the skip by writing
an adjusted value to the timer register. If the prescaler is assigned to timer0, then a
write operation to the timer register determines that the timer will not increment
for four clock cycles.
Black–Ammerman Method
A more elegant and accurate solution has been described by Roman Black in a Web ar-
ticle titled Zero-error One Second Timer. Black credits Bob Ammerman with the sug-
gestion of using Bresenham's algorithm for creating accurate PIC timer periods. In the
Black–Ammerman method, the coun ter works in the background, either by polling or
interrupt-driven. In either case, the timer count value is stored in a 3-byte register
which is decremented by the software.
Delays, Counters, and Timers 189
In their inter rupt-driven ver sion, TMR0 gener ates an in ter rupt when ever the
counter register overflows, that is, every 256th timer beat (assuming no prescaler).
The inter rupt han dler routine dec re ments the mid-order reg ister that holds the
3-byte timer count. This is appropriate because every unit in the mid-order register
represents 256 units of the low-order counter, which in this case is the tmr0 register.
If the mid-order reg ister underflows when dec remented, then the high-order one is
dec remented. If the high-order one underflows, then the count has reached zero and
the de lay ends. Because the counter is interrupt-driven, the processor can continue
to do other work in the fore ground.
An even more inge nious option proposed by Black is a background counter that
does not rely on interrupts. This is accomplished by introducing a 1:2 de lay in the
timer by means of the prescaler. Because now the timer beats at one-half the in-
struction rate, 128 timer cycles will be required for one complete iteration at the full
instruction rate. By test ing the high-order bit of the timer coun ter, the routine can
detect when the count has reached 128. At that time, the mid-range and high-range
counter variables are updated (as in the non-interrupt ver sion of the software de -
scribed in the previous paragraph). The high-order bit of the timer is then cleared,
but the low-order bits are not changed. This allows the timer counter not to lose
step in the count, which re mains valid until the next time the high-order bit is again
set. During the pe riod between the up dating of the 3-byte coun ter and the next poll-
ing of the timer register, the program can con tinue to perform other tasks.
Delays with 16-Bit Timer0
In many cases the complica tions mentioned in the previous sections can be avoided
by running Timer0 in the 16-bit mode. For example, if the maximum delay that can be
obtained in 8-bit mode, given a machine running at 4MHz, is 1/15th second (0.0666 sec-
ond), then switching to 16-bit mode makes the maximum delay of ap proximately 17
seconds.
In this section we explore several timer and counter routines of different com -
plexity and requirements. The first one uses the Timer0 module as a counter. Later
we develop a simple delay loop that uses the timer0 register instead of the do-noth-
ing instruc tion count cov ered pre vi ously. We con clude with an inter rupt-driven
timer routine that can be changed to implement different delays.
Programming a Counter
The 18F452 PIC can be pro grammed so that port RA4/TOCKI is used to count events or
pulses by initializing the Timer0 module as a counter. When interrupts are not used,
the process requires the following preparatory steps:
1. Port A, line 4, (RA4/TOCKI) is defined for input.
2. The Timer0 register (TMR0) is cleared.
3. The Watchdog Timer internal register is cleared by means of the clrwdt instruc-
tion.
4. The T0CON register bits PSA and PSO:PS2 are initialized if the prescaler is to be
used.
5. The T0CON register bit TOSE is set so as to increment the count on the
high-to-low transition of the port pin if the port source is active low. Otherwise
the bit is cleared.
6. The T0CON register bit TOCS is set to select action on the RA4/TOCKI pin.
Once the timer is set up as a counter, any pulse received on the RA4/TOCKI pin
that meets the restrictions mentioned earlier is counted in the TMR0L and TMR0H
registers. If Timer0 is set in the 8-bit mode, then the TMR0H register is not used.
Software can read and write to the Timer0 registers in order to obtain or change the
event count. If the timer interrupt is enabled when the timer is defined as a coun ter,
then an interrupt takes place every time the counter overflows, that is, when the
count cycles from 0xff to 0x00 or from 0xffff to 0x0000 according to the active
mode.
Timer0_as_Counter.asm Program
The program named Timer0_as_Counter.asm, listed later in this chapter and con-
tained in this book's online software pack age, uses the circuits mentioned in the previ-
ous paragraph to dem onstrate the pro gramming of the Timer0 module in the counter
mode. The program detects and counts action on DIP switch #3, wired to port
RA4/TOCKI. The value of the count in hex digits in the range 0x00 to 0x0f is displayed
in the seven-segment LED connected to Port B.
The loca tion and use of the code table were discussed in Sections 7.3 and 7.4. The
main() function starts by selecting bank 0, initializing Port A for digital operations,
and trissing Port A for input and Port C for output as in several preced ing programs.
Code first clears the watchdog time and the TMR0L register, and then proceeds as
follows:
;=================================
; Check value in TMR0L and dis play
;=================================
; Ev ery clos ing of DIP switch # 3 (con nected to line
Delays, Counters, and Timers 191
Notice that the program provides no way of detecting when the count exceeds
the displayable range. This means that no display update takes place as the timer cy-
cles from binary 00001111 to binary 11111111.
The program named Timer0_Delay.asm, listed later in this chapter and contained
in this book's online software pack age, uses a timer-based delay loop to flash in se-
quence eight LEDs that display the binary values from 0x00 to 0xff. The delay rou-
tine executes in the fore ground, so that pro cessing is suspended while the count is
in progress. The program executes in the 16-bit mode so the code can demonstrate
the issues related to reading and writing to the 16-bit registers TMR0L and TMR0H.
These issues were discussed in Section 9.6.1.
Setting up Timer0 as a delay counter requires selecting the required bits in the
T0CON register. The following code frag ment shows the program's initialization
routine to set up the timer.
;==============================
; setup Timer0 as de lay timer
;==============================
clrf TMR0H ; Clear high latch
clrf TMR0L ; Write both bytes
clrwdt ; Clear watch dog timer
192 Chapter 9
The previous code snippet starts by clearing both counter registers. This requires
first clearing the buffer register TMR0H and then the low-byte register TMROL. This
last write operation updates both the high and the low byte of the timer simulta-
neously. The bits selected in the T0CON register enable the timer, select the 16-bit
mode, enable the clock source as the internal clock, activate the signal edge in
high-to-low mode, while the prescaler is left unassigned.
The program then pro ceeds to an endless loop that increments the value in the
Port C register by one. Because Port C is wired to eight LEDs on the demo circuit,
the display shows the binary value in the port. The rou tine calls a proce dure that im-
plements a de lay in a do-nothing loop that uses Timer0 overflow. Code is as follows:
;=================================
; end less loop call ing
; de lay routiney
;=================================
; Dis play Port C count on LEDs
showLEDs:
incf PORTC,f ; Add one to reg is ter
call tmr0_de lay ; De lay rou tine
goto showLEDs
;=================================
; Timer0 de lay rou tine
;=================================
tmr0_de lay:
cy cle:
movf TMR0L,w ; Read low byte to latch
; high byte
movf TMR0H,w ; Now read high byte
sublw 0xff ; Sub tract max i mum count
btfss STATUS,Z ; Test zero flag
goto cy cle
; Re set coun ter
clrf TMR0H ; Clear high byte buffer
clrf TMR0L ; Write both low and high
re turn
end
Delays, Counters, and Timers 193
The delay routine is the procedure named tmr0_delay. To make the code more
readable, we have added a second label named cycle at this same address. The code
reads the high byte of the timer, then the low one (this updates both bytes.) The
value 0xff is then sub tracted from the high byte. The subtrac tion returns zero and
sets the zero flag if the value in TMR0H is also 0xff. If the test is true, then the goto
cy cle instruction is skipped, both timer reg isters are cleared, and execution returns
to the caller. If the test is false, then the timer register test loop repeats. In a 4-MHz
test circuit the entire cycle takes approximately 15 seconds.
A Variable Time-Lapse Routine
A variable time-lapse routine can be de signed so that it can be ad justed to produce de -
lays within a certain time range. Such a pro ce dure would be a useful tool in a program-
mer's library. In previous sections we have de veloped de lay routines that do so by
counting timer pulses. This same idea can be used to de velop a routine that can be ad-
justed so as to produce accurate delays within a certain range.
;==============================
; setup Timer0 as coun ter
194 Chapter 9
; 8-bit mode
;==============================
; Prescaler is as signed to Timer0 and initialzed
; to 2:1 rate
; Setup the T0CON reg is ter
; |------------- On/Off con trol
; | 1 = Timer0 en abled
; ||------------ 8/16 bit mode se lect
; || 1 = 8-bit mode
; |||----------- Clock source
; ||| 0 = in ter nal clock
; ||||---------- Source edge se lect
; |||| 1 = high-to-low
; |||||--------- Prescaler as sign ment
; ||||| 0 = prescaler as signed
; ||||||||------ Prescaler se lect
; |||||||| 1:2 rate
movlw b'11010000'
movwf T0CON
; Clear reg is ters
clrf TMR0L
clrwdt ; Clear watch dog timer
The constants that define the time lapse period are entered in #define statements
so they can be easily edited to accommodate other delays and processor speeds. In
a 4-MHz system, a delay of one-half second requires 500,000 timer cycles, while a de-
lay of one-tenth second requires a count of 10,000. Because this delay value must be
entered in three 1-byte variables, the value is converted to hexadecimal so it can be
installed in three constants; for ex ample,
For example, values for one-half second are installed in constants as follows:
Code can read these constants and move them to local variables at the beginning
of each timer cycle, as in the following code fragment
;==============================
; set reg is ter vari ables
;==============================
; Pro ce dure to ini tial ize lo cal vari ables for a
; de lay pe riod de fined in lo cal con stants highCnt,
; midCnt, and lowCnt.
setVars:
movlw highCnt ; From con stants
movwf countH
movlw midCnt
movwf countM
Delays, Counters, and Timers 195
movlw lowCnt
movwf countL
re turn
In implementing this scheme, the TMR0L register provides the low-order level of
the count. Be cause the counter counts up from zero, code must pre-install a value in
the coun ter reg is ter that rep re sents one-half the num ber of timer it er a tions
(prescaler is in 1:2 mode) required to reach a count of 128. For example, if the value
in the low counter variable is 140, then
140/2 = 70
128 - 70 = 58
Because the timer starts counting up from 58, when the count reaches 128,140 timer
beats would have elapsed. The formula for calculating the value to pre-install in the
low-level counter is as fol lows:
Code is as follows:
;==================================
; vari able-lapse de lay pro ce dure
; us ing Timer0
;==================================
; ON ENTRY:
; Vari ables countL, countM, and countH hold
; the low-, mid dle-, and high-or der bytes
; of the de lay pe riod, in timer units
TM0delay:
; For mula:
; Value in TMR0L = 128 - (x/2)
; where x is the num ber of it er a tions in the low-level
; coun ter vari able
; First cal cu late xx/2 by bit shift ing
rrncf countL,f ; Di vide by 2
; now sub tract 128 - (x/2)
movlw d'128'
196 Chapter 9
Interrupt-Driven Timer
Interrupt-driven timers and counters have the advan tage over polled routines that the
time lapse counting takes place in the back ground, which makes it possible for an ap-
plica tion to continue to do other work in the fore ground. De veloping a timer routine
that is interrupt-driven presents no major programming challenges. The initialization
consists of configuring the OPTION and the INTCON register bits for the task at hand.
In the particular case of an interrupt-driven timer, the following are necessary:
1. The external interrupt flag (INTF in the INTCON Register) must be initially
cleared.
2. Global interrupts must be enabled by setting the GIE bit in the INTCON register.
3. The timer0 overflow interrupt must be enabled by setting the TOIE bit in the
INTCON register.
In the present example program, named Timer0_VarInt, the prescaler is not used
with the timer, so the initialization code sets the PSA bit in the OPTION register in
order to have the prescaler assigned to the Watchdog Timer. The following code
fragment is from the Timer0_VarInt program:
main:
; Set BSR for bank 0 op er a tions
movlb 0 ; Bank 0
; Ini tial ize all lines in PORT C for out put
movlw B'00000000' ; 0 = out put
movwf TRISC ; Port C tris reg is ter
movwf PORTC
;==============================
; setup Timer0 as coun ter
; 8-bit mode
;==============================
Delays, Counters, and Timers 197
bcf INTCON,TMR0IE
; Setup the T0CON reg is ter
; |------------- On/Off con trol
; | 1 = Timer0 en abled
; ||------------ 8/16 bit mode se lect
; || 1 = 8-bit mode
; |||----------- Clock source
; ||| 0 = in ter nal clock
; ||||---------- Source edge se lect
; |||| 1 = high-to-low
; |||||--------- Prescaler as sign ment
; ||||| 1 = prescaler not as signed
; |||||||| ----- Prescaler se lect
; |||||||| 1:2 rate
movlw b'11011000'
movwf T0CON
; Clear reg is ters
clrf TMR0L
clrwdt ; Clear watch dog timer
;===============================
; Set up for Timer0 interupt
;===============================
; Dis able in ter rupt pri or ity lev els in the RCON reg is ter
; set ting up the mid range com pat i bil ity mode
bsf RCON,IPEN ; En able in ter rupt pri or i ties
; INTCON reg is ter in i tial ized as fol lows:
; (IPEN bit is clear)
; |------------ high-pri or ity in ter rupts
; ||----------- low-pri or ity pe riph eral
; |||---------- timer0 over flow in ter rupt
; ||||--------- ex ter nal in ter rupt
; |||||-------- port change in ter rupt
; ||||||------- over flow in ter rupt flag
; |||||||------ ex ter nal in ter rupt flag
; ||||||||----- RB4:RB7 in ter rupt flag
movlw b'10100000'
movwf INTCON
; Set INTCON2 for fall ing edge op er a tion
bcf INTCON2,INTEDG0
; Re-en able timer0 in ter rupt
bsf INTCON,TMR0IE ; Ac ti vate Timer0 in ter rupt
bcf INTCON,TMR0IF ; Clear in ter rupt f
;==============================
; set reg is ter vari ables for
; one-half sec ond de lay
;==============================
; Pro ce dure to ini tial ize lo cal vari ables for a
; de lay of one-half sec ond on a 16F84 at 4 MHz.
; Timer is setup for a 500,000 clock beats as
; fol lows: 500,000 = 0x07 0xa1 0x20
198 Chapter 9
The in ter rupt ser vice rou tine in the Timer0_VarInt pro gram receives con trol
when the tmr0 register overflows, that is, when the count goes from 0xff to 0x00.
The service routine then proceeds to decrement the mid-range counter register and
adjust, if necessary, the high-order counter. If the count goes to zero, the han dler
toggles the LED on port B, line 0, and re-initializes the counter variables by calling
the onehalfSec pro ce dure described previously. The interrupt handler is coded as
follows:
;=======================================================
; In ter rupt Ser vice Rou tine
;=======================================================
; Ser vice rou tine re ceives con trol when the timer
; reg is ter tmr0 over flows, that is, when 256 timer beats
; have ellapsed
IntServ:
; First test if source is a timer0 in ter rupt
btfss INTCON,toif ; TOIF is timer0 in ter rupt
goto notTOIF ; Go if not RB0 or i gin
; If so clear the timer in ter rupt flag so that count con tin ues
bcf INTCON,toif ; Clear in ter rupt flag
; Save con text
movwf old_w ; Save w reg is ter
swapf STATUS,w ; STATUS to w
movwf old_sta tus ; Save STATUS
;=========================
; in ter rupt ac tion
;=========================
; Sub tract 256 from beat coun ter by dec re ment ing the
; mid-or der byte
decfsz countM,f
goto exitISR ; Con tinue if mid-byte not zero
; At this point the mid-or der byte has over flowed.
; High-or der byte must be dec re ment ed.
decfsz countH,f
goto exitISR
Delays, Counters, and Timers 199
Notice that one of the initial operations of the service routine is to clear the TOIF
bit in the INTCON register. This action reenables the timer interrupt and prevents
counting cycles from being lost. Because the interrupt is generated every 256 beats
of the timer, there is no risk that by enabling the timer interrupt flag a reentrant in-
terrupt will take place.
1. As a synchronous timer
2. As a synchronous counter
3. As an asynchronous counter
The operating mode is selected by clock select bit, TMR1CS (T1CON register),
and the syn chronization bit, T1SYNC. In the timer mode, Timer1 increments ev ery
instruction cycle. In the counter modes, it increments on every rising edge of the ex-
ternal clock input pin T1OSI. Timer1 is turned on and off by means of the TMR1ON
control bit in the T1CON register.
Timer1 has an internal reset input that can be generated by a CCP module as well
as the capability of operating off an external crystal. When the Timer1 oscillator is
enabled (T1OSCEN is set), the T1OSI and T1OSO pins be come inputs and their cor-
responding TRIS values are ignored. Figure 9.3 shows the bitmap of the Timer1 con-
trol reg ister (T1CON.)
bit 7 bit 0
Timer1 is set in timer mode by clearing the TMR1CS (T1CON register) bit (see Figure
9.3). In the timer mode the input clock to the timer is the processor's main clock at
FOSC/4. In this mode, the synchronize control bit, T1SYNC (T1CON register), has no
effect because the internal clock is always synchronized.
Delays, Counters, and Timers 201
No tice in Fig ure 9.4 that the high byte of Timer1 is not directly read able or
writable in the 16-bit mode. Instead, all reads and writes take place through the
Timer1 high byte buffer register. Also notice that writes to TMR1H do not clear the
Timer1 prescaler.
16-bit Read-Modify-Write
Read-modify-write instructions, such as BSF and BCF, read the con tents of a reg ister,
make the appropriate changes, and then place the result back into the same register.
When Timer1 is configured in 16-bit mode, the read portion of a read-modify-write in-
struction of TMR1L will not update the contents of the TMR1H buffer. The TMR1H
buffer will re main unchanged. When the write of TMR1L portion of the instruction
takes place, the contents of TMR1H are placed into the high byte of Timer1.
Reading and Writing Timer1 in Two 8-bit Operations
When Timer1 is in Asynchronous Counter Mode for 16-bit operations (RD16 = 1), the
hardware ensures a valid read of TMR1H or TMR1L. However, reading the 16-bit timer
in two 8-bit values (RD16 = 0) poses the problem of a possible timer overflow between
the reads. For write operations, the program can stop the timer and write the desired
values. Turning off the timer pre vents a write con tention that could occur when writ-
ing to the timer registers while the register is incrementing. On the other hand, reading
may produce an unpredictable value in the timer register and requires special care in
some cases. This happens because two separate reads are required to read the entire
16-bits.
The following code fragment shows a routine to read the 16-bit timer value with-
out experienc ing the timer overflow issues previously mentioned. This scheme is
useful if the timer cannot be stopped.
; Read ing a 16-bit timer
; Code as sumes the vari ables named tmph and tmpl
; All in ter rupts are dis abled
movf TMR1H,w ; Read high byte
Delays, Counters, and Timers 203
movwf tmph
movf TMR1L,w ; Read low byte
movwf tmpl
movf TMR1H,w ; Read high byte
subwf tmph,w ; Sub tract 1st read and 2nd read
btfsc STATUS,z ; is re sult = 0 ?
goto CONTINUE ; good 16-bit read
; TMR1L may have rolled over be tween the read of the high
; and low bytes. Read ing the high and low bytes now will
; read a good value.
movf TMR1H,w ; Read high byte
movwf TMPH
movf TMR1L, w ; Read low byte
movwf TMPL
CONTINUE:
; Code con tin ues at this la bel
Writing a 16-bit value to the 16-bit TMR1 register is straightforward. First the
TMR1L register is cleared to ensure that there are many Timer1 clock/oscillator cy-
cles before there is a rollover into the TMR1H register. The TMR1H register is then
loaded, and then the TMR1L register, as shown in the following code fragment.
Comparator Posscaler
T2CKPS1:T2CKPS0 1:1 to 1:16
EQ
T2OUTPS3:T2OUTPS0
PR2
In Figure 9.5 note that the postscaler counts the number of times that the TMR2
register matched the PR2 register. This can be useful in reducing the overhead of
the interrupt service routine on the CPU performance. Figure 9.6 is a bitmap of the
T2CON register.
bit 7 bit 0
If the PR2 register is cleared (set to 0x00), the TMR2 register will not increment
and Timer2 will be disabled. Timer2 can also be shut off by clearing the TMR2ON
control bit (T2CON register). When Timer2 is not used by an applica tion it is rec om-
mended to turn it off be cause this minimizes the power consumption of the module.
Prescaler and Postscaler
Four bits serve to select the postscaler. This allows the postscaler rate from 1:1 to
1:16. After the postscaler overflows, the TMR2 interrupt flag bit (TMR2IF) is set to in-
dicate the Timer2 overflow. This is useful in reducing the software overhead of the
Timer2 interrupt service routine, because it will only execute when the postscaler is
matched. The prescaler and postscaler counters are cleared when any of the following
occurs:
1. Aa write to the TMR2 register
2. A write to the T2CON register
3. Any device reset (Power-on Re set, MCLR re set, Watchdog Timer Reset,
Brown-out Reset)
During sleep, TMR2 will not increment. The prescaler will retain the last prescale
count, ready for operation to resume after the device wakes from sleep.
Timer2 Initialization
The following code frag ment shows the initialization of the Timer2 module, including
the prescaler and postscaler:
Synchronized
0
clock input
CLR
TMR3H TMR3L
TMR3 1
TMR3ON
(on/off)
T1SYNC
TT1P 1
Synchronize
Prescaler
1-2-4-8
det
FOSC/4
internal 0
clock
SLEEP input
T3CKPA1:T3CKPS0
TMR3CS
Timer3 increments every instruction cycle while in the timer mode. In counter
mode, it increments on every rising edge of the external clock input. The Timer3 in-
crement can be enabled or disabled by setting or clearing control bit TMR3ON
(T3CON register in Figure 9.8). Timer3 also has an internal “reset input.” This reset
can be generated by a CCP special event trigger (Capture/Compare/PWM) module.
When the Timer1 oscillator is enabled (T1OSCEN, in T1CON, is set), the T1OSCI1
and T1OSO2 pins are con figured as oscillator input and out put, so the cor respond-
ing values in the TRIS register are ignored. The Timer3 module also has a software
programmable prescaler. The operating mode is determined by clock select bit,
TMR3CS (T3CON register), and the synchronization bit, T3SYNC (Figure 9.8).
Delays, Counters, and Timers 207
bit 7 bit 0
During SLEEP mode, Timer3 will not increment even if an external clock is pres-
ent, as the syn chronization circuit is shut off. The prescaler, however, will continue
to increment.
208 Chapter 9
Because the counter can operate in sleep, Timer3 can be used to implement a
true real-time clock. This also explains why in asynchronous counter mode, Timer3
cannot be used as a time base for capture or compare operations.
The high byte of Timer3 is not directly read able or writable in this mode. All
reads and writes must take place through the Timer3 high byte buffer reg ister.
Writes to TMR3H do not clear the Timer3 prescaler. The prescaler is only cleared on
writes to TMR3L.
Delays, Counters, and Timers 209
Regarding write operations it is recommended that code stop the timer and write
the desired values, although a write contention may occur by writing to the timer
registers, while the register is incrementing. In this case an unpredictable value may
result. Microchip provides no information on how to prevent or how to cor rect this
possible error.
Reading the 16-bit value requires some care because two separate reads are re-
quired to read the entire 16-bits. The following code snippet shows reading a 16-bit
timer value in cases when the timer cannot be stopped and while avoiding a timer
rollover error.
To write a 16-bit value to the 16-bit TMR3 register is straightforward. First, the
TMR3L register is cleared to ensure that there are many Timer3 clock/oscillator cy-
cles before there is a rollover into the TMR3H register. The TMR3H register is then
loaded, and finally, the TMR3L register is loaded. The following code snippet shows
the sequence of operations.
210 Chapter 9
The fact that the SLEEP mode does not disable the Timer1 facilitates its use in
keeping real-time. The Timer1 Oscillator is also designed to minimize power con-
sumption, which can be a factor in real-time applications. The Timer1 Oscillator is
enabled by setting the T1OSCEN control bit (T1CON register in Figure 9.8). After
the Timer1 Oscillator is enabled, the user must provide a software time delay to en-
sure its proper start-up.
The following code snippet opens and configures Timer0 to disable interrupts,
en able the 8-bit data mode, use the in ter nal clock source, and se lect the 1:32
prescale.
The C-18 functions for specific timers may con tain support for other hard ware
devices. For example, the arguments in OpenTimer1, OpenTimer2, and OpenTimer3
functions include interaction with CCP modules.
The function takes data from the available timer registers as fol lows:
Timer0: TMR0L,TMR0H
Timer1: TMR1L,TMR1H
Timer2: TMR2
Timer3: TMR3L,TMR3H
212 Chapter 9
Timer4: TMR4
When the ReadTimerx function is used in the 8-bit mode for a timer module that
may be configured in 16-bit mode (for example, timer0, timer1, and Timer3) ), the
read operation does not ensure that the high-order byte will be zero. In this case,
code may cast the result to a char for correct results. For example,
The function places data in the available timer registers as fol lows:
Timer0: TMR0L,TMR0H
Timer1: TMR1L,TMR1H
Timer2: TMR2
Timer3: TMR3L,TMR3H
Timer4: TMR4
For example:
WriteTimer0(32795);
;=========================================================
; def i ni tion and in clude files
;=========================================================
pro ces sor 18F452 ; De fine pro ces sor
#in clude <p18F452.inc>
; ==========================================================
; con fig u ra tion bits
;===========================================================
config OSC = HS ; As sumes high-speed res o na tor
config WDT = OFF ; No watch dog timer
config LVP = OFF ; No low volt age pro tec tion
config DEBUG = OFF ; No back ground debugger
;
; Turn off bank ing er ror mes sages
errorlevel -302
;============================================================
; pro gram
;============================================================
org 0 ; start at ad dress
goto main
; Space for in ter rupt han dlers
;=============================
; in ter rupt in ter cept
;=============================
org 0x008 ; High-pri or ity vec tor
retfie
org 0x018 ; Low-pri or ity vec tor
retfie
;================================
; Ta ble to re turns 7-seg ment
; codes
;================================
org $+2
; Note: Ta ble is placed in low pro gram mem ory at
; an ad dress where PCL = 0x1A. This pro vides space
; for 115 retlw in struc tions (at 2 bytes per
; in struc tion). 18 en tries are ac tu ally used in
; this ex am ple. By know ing the lo ca tion of the
; ta ble (at 0x1A in this case) we make sure that
; a code page bound ary is not strad dled while
; ac cess ing ta ble en tries because the in struc tion:
; addwf PCL,F
; does not up date the PCH reg is ter.
; Ad dresses (PCL value) in cre ment by two for
; each se quen tial in struc tion in the ta ble.
codeTable:
addwf PCL,F ; PCL is pro gram coun ter latch
retlw 0x3f ; 0 code
retlw 0x06 ; 1
retlw 0x5b ; 2
retlw 0x4f ; 3
retlw 0x66 ; 4
retlw 0x6d ; 5
retlw 0x7d ; 6
retlw 0x07 ; 7
retlw 0x7f ; 8
retlw 0x6f ; 9
retlw 0x00 ; Pad ding
214 Chapter 9
;============================================================
; main pro gram en try point
;============================================================
main:
; Set BSR for bank 0 op er a tions
movlb 0 ; Bank 0
; Init Port A for dig i tal op er a tion
clrf PORTA,0
clrf LATA,0
; ADCON1 is the con fig u ra tion reg is ter for the A/D
; func tions in Port A. A value of 0b011x sets all
; lines for dig i tal op er a tion
movlw B'00000110' ; Dig i tal mode
movwf ADCON1,0
; Port A. Set lines 2 to 5 for in put
movlw B'00111100' ; w = 00111100 bi nary
movwf TRISA,0 ; port A (lines 2 to 5) to in put
; Ini tial ize all lines in PORT C for out put
movlw B'00000000' ; 0 = out put
movwf TRISC,0 ; Port C tris reg is ter
;==============================
; setup Timer0 as coun ter
;==============================
clrf TMR0L
clrwdt ; Clear watch dog timer
; Setup the T0CON reg is ter
; |------------- On/Off con trol
; | 1 = Timer0 en abled
; ||------------ 8/16 bit mode se lect
; || 1 = 8-bit mode
; |||----------- Clock source
; ||| 1 = T0CKI pin
; ||||---------- Source edge se lect
; |||| 1 = high-to-low
; |||||--------- Prescaler as sign ment
; ||||| 1 = prescaler NOT assigned
; |||||||| ----- Prescaler se lect
; ||||||||
movlw b'11111000'
movwf T0CON
;=================================
; Check value in TMR0L and dis play
;=================================
; Ev ery clos ing of DIP switch # 3 (con nected to line
; RA4/TOCKI) adds one to the value in the TMR0L reg is ter.
; Loop checks this value, ad justs to the range 0 to 15
; and dis plays the re sult in the seven-seg ment LED on
; port B
checkTmr0:
movf TMR0L,w ; Timer reg is ter to w
; Elimate four high or der bits
andlw b'00001111' ; Mask off high bits
; At this point the w reg is ter con tains a 4-bit value
; in the range 0 to 0xf. Use this value (in w) to
; ob tain seven-seg ment dis play code
call codeTable
movwf PORTC ; Dis play switch bits
goto checkTmr0
end
Delays, Counters, and Timers 215
movwf ADCON1,0
; Port A. Set lines 2 to 5 for in put
movlw B'00111100' ; w = 00111100 bi nary
movwf TRISA,0 ; port A (lines 2 to 5) to in put
; Ini tial ize all lines in PORT C for out put
movlw B'00000000' ; 0 = out put
movwf TRISC,0 ; Port C tris reg is ter
clrf PORTC ; Turn off all LEDs
;==============================
; setup Timer0 as de lay timer
;==============================
clrf TMR0H ; Clear high latch
clrf TMR0L ; Write both bytes
clrwdt ; Clear watch dog timer
; Setup the T0CON reg is ter
; |------------- On/Off con trol
; | 1 = Timer0 en abled
; ||------------ 8/16 bit mode se lect
; || 0 = 16-bit mode
; |||----------- Clock source
; ||| 0 = In ter nal clock
; ||||---------- Source edge se lect
; |||| 1 = high-to-low
; |||||--------- Prescaler as sign ment
; ||||| 1 = prescaler not as signed
; |||||||| ----- No prescaler
; ||||||||
movlw b'10011000'
movwf T0CON
;=================================
; end less loop call ing
; de lay routiney
;=================================
; Dis play Port C count on LEDs
showLEDs:
incf PORTC,f ; Add one to reg is ter
call tmr0_de lay ; De lay rou tine
goto showLEDs
;=================================
; Timer0 de lay rou tine
;=================================
tmr0_de lay:
cy cle:
movf TMR0L,w ; Read low byte to latch
; high byte
movf TMR0H,w ; Now read high byte
sublw 0xff ; Sub tract max i mum count
btfss STATUS,Z ; Test zero flag
goto cy cle
; Re set coun ter
clrf TMR0H ; Clear high byte buffer
clrf TMR0L ; Write both low and high
re turn
end
; Turn on LED
bsf PORTC,0
; Ini tial ize coun ters and de lay
call setVars
call TM0delay
; Turn off LED
bcf PORTC,0
; Re-ini tial ize coun ter and de lay
call setVars
call TM0delay
goto mloop
;==================================
; vari able-lapse de lay pro ce dure
; us ing Timer0
;==================================
; ON ENTRY:
; Vari ables countL, countM, and countH hold
; the low-, mid dle-, and high-or der bytes
; of the de lay pe riod, in timer units
; Rou tine logic:
; The prescaler is as signed to timer0 and setup so
; that the timer runs at 1:2 rate. This means that
; ev ery time the coun ter reaches 128 (0x80) a to tal
; of 256 ma chine cy cles have elapsed. The value 0x80
; is de tected by test ing bit 7 of the coun ter
; reg is ter.
TM0delay:
; Note:
; The TMR0L reg is ter pro vides the low-or der level
; of the count. Because the coun ter counts up from zero,
; code must pre-in stall a value in the coun ter reg is ter
; that rep re sents the one-half the num ber of timer
; it er a tions (pre-scaler is in 1:2 mode) re quired to
; reach a count of 128. For ex am ple: if the value in
; the low coun ter vari able is 140
; then 140/2 = 70. 128 - 70 = 58
; In other words, when the timer coun ter reaches 128,
; 70 * 2 (140) timer beats would have elapsed.
; For mula:
; Value in TMR0L = 128 - (x/2)
; where x is the num ber of it er a tions in the low-level
; coun ter vari able
; First cal cu late xx/2 by bit shift ing
rrncf countL,f ; Di vide by 2
; now sub tract 128 - (x/2)
movlw d'128'
; Clear the bor row bit (mapped to Carry bit)
bcf STATUS,C
subfwb countL,w
; Now w has ad justed re sult. Store in TMR0L
movwf TMR0L
; Rou tine tests timer over flow by test ing bit 7 of
; the TMR0L reg is ter.
cy cle:
btfss TMR0L,7 ; Is bit 7 set?
goto cy cle ; Wait if not set
; At this point TMR0 bit 7 is set
; Clear the bit
bcf TMR0L,7 ; All other bits are pre served
; Sub tract 256 from beat coun ter by dec re ment ing the
220 Chapter 9
;==============================
; set reg is ter vari ables
;==============================
Delays, Counters, and Timers 223
re turn
;=======================================================
; In ter rupt Ser vice Rou tine
;=======================================================
; This is a high-pri or ity in ter rupt so crit i cal reg is ters
; are saved and re store au to mat i cally
; Ser vice rou tine re ceives con trol when the timer
; reg is ter TMR0 over flows, that is, when 256 timer beats
; have elapsed
IntServ:
; First test if source is a timer0 in ter rupt
btfss INTCON,TMR0IF ; T0IF is timer0 in ter rupt
goto notTOIF ; Go if not RB0 or i gin
; If so clear the timer in ter rupt flag so that count con tin ues
bcf INTCON,TMR0IF ; Clear in ter rupt flag
;=========================
; in ter rupt ac tion
;=========================
; Sub tract 256 from beat coun ter by dec re ment ing the
; mid-or der byte
decfsz countM,f
goto exitISR ; Con tinue if mid-byte not zero
; At this point the mid-or der byte has over flowed.
; High-or der byte must be dec re ment ed.
decfsz countH,f
goto exitISR
; At this point count has ex pired so the pro grammed time
; has elapsed. Ser vice rou tine turns the LED on line 0,
; port B on and off at ev ery con clu sion of the count.
; This is done by XORing a mask with a one-bit at the
; port C line 0 po si tion
movlw b'00000001' ; Xoring with a 1-bit pro duces
; the com ple ment
xorwf PORTC,f ; Com ple ment bit 0, port C
; Re set de lay con stants
call setDelay
;=========================
; exit ISR
;=========================
224 Chapter 9
exitISR:
notTOIF:
retfie 0x01
end ; END OF PROGRAM
// Pro to type
void TimerDelay (un signed int);
/***********************************************************
main pro gram
************************************************************/
void main(void)
{
un signed char timerVar = 0;
/* Initalize di rec tion reg is ters */
TRISC = 0;
PORTC = 0;
/* Con fig ure Timer0 to no in ter rupts, 16-bit data,
in ter nal clock source and 1:1 prescaler */
OpenTimer0(
TIMER_INT_OFF &
T0_16BIT &
T0_SOURCE_INT &
T0_PS_1_1 );
while(1) {
TimerDelay(40000);
PORTC = timerVar;
timerVar++;
Delays, Counters, and Timers 225
}
}