You are on page 1of 7

© Gooligum Electronics 2012 www.gooligum.com.

au

Introduction to PIC Programming


Mid-Range Architecture and Assembly Language

by David Meiklejohn, Gooligum Electronics

Lesson 16: Using Timer2

The last lesson introduced Timer1, a 16-bit timer with a number of advanced features, including integration
with a low-power (32.768 kHz crystal) oscillator, operation during sleep mode, and external gating. Those
features are useful, but often only a simple 8-bit timer is needed, as we saw in lesson 4, which showed how
an 8-bit Timer0 can be used to time events, create delays, and count external pulses.
This lesson describes Timer2, another 8-bit timer – even simpler in some ways than Timer0. For example, it
cannot be externally driven, so cannot be used as a counter. Nevertheless, Timer2 is very useful as a basic
timer, and can be used with a period register to easily generate a specific time-base, as we shall see.
In summary, this lesson covers:
 Introduction to the Timer2 module
 Using the Timer2 postscaler to drive an interrupt at a reduced rate
 Using the Timer2 period register to generate a specific time-base

Timer2 Module
The current value of Timer2 is held in a single 8-bit register: TMR2.
Associated with TMR2 is an 8-bit period register: PR2.
Unlike the other timers, Timer2 doesn’t simply rollover to 0 after it reaches 255. Instead, TMR2 is reset to 0
(on the next increment) after it reaches the value held in PR2.
For example, if you store the value 99 in PR2, TMR2 will repeatedly count from 0 to 99, resetting to 0 after
counting 100 times.
Thus, Timer2’s period is equal to the value stored in PR2, plus one.

Note that Timer2 can only be driven by the instruction clock (FOSC/4).
If the processor is clocked at 4 MHz, the Timer2 module will be clocked at 1 MHz.
Thus, Timer2 can only be used as a timer; it cannot be used to count external events.

Timer2 is configured using a dedicated control register, T2CON:


Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0

T2CON – TOUTPS3 TOUTPS2 TOUTPS1 TOUTPS0 TMR2ON T2CKPS1 T2CKPS0

Mid-range PIC Assembler, Lesson 16: Using Timer2 Page 1


© Gooligum Electronics 2012 www.gooligum.com.au

Like Timer1, Timer2 can be turned on or off, using the TMR2ON bit: setting it to ‘1’ to enables Timer2, and
clearing it to ‘0’ stops it.

Prescaler
Like the other timers, Timer2 includes a prescaler, so that the timer does not have to increment on every
clock cycle.
However, the Timer2 prescaler provides a choice of only three prescale ratios.
The prescale ratio is set by the T2CKPS<1:0> bits, as shown in the following table:
T2CKPS<1:0> = ‘00’ means that no prescaling will occur, and
T2CKPS<1:0> Timer2
TMR2 will increment at the instruction cycle rate.
bit value prescale ratio
T2CKPS1 = ‘1’ (regardless of the value of T2CKPS0) selects the
00 1:1
maximum prescale ratio of 1:16, meaning that TMR2 will
01 1:4 increment every 16 instruction cycles. Given a 1 MHz instruction
cycle rate, the timer would increment every 16 µs.
10 or 11 1 : 16

Postscaler and Timer2 interrupts


Unlike the other timers, Timer2 also includes a postscaler.
A postscaler does not affect how quickly the timer increments.
Instead, it affects how often the Timer2 interrupt flag, TMR2IF, flag in the PIR1 register, is set:
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0

PIR1 EEIF ADIF CCP1IF C2IF C1IF OSFIF TMR2IF TMR1IF

This is similar to the other timers’ interrupt flags, except that, instead of indicating a timer overflow, the
TMR2IF flag indicates that a match between TMR2 and PR2 has occurred. I.e. it indicates that TMR2 has
reached its maximum value, and will overflow on the next increment.

If the postscaler is active (TOUTPS<3:0> ≠ ‘0000’), this match has to occur some number of times before
TMR2IF is set.
The postscale ratio is equal to the value of TOUTPS<3:0> plus one.
Thus, the postscale ratio ranges from 1:1 (TOUTPS<3:0> = ‘0000’) to 1:16 (TOUTPS<3:0> = ‘1111’).
So, if TOUTPS<3:0> = ‘1001’ (binary) = 9, the postscale ratio will be 1:10, and the match between TMR2
and PR2 will have to occur 10 times before TMR2IF is set.

As usual, an interrupt will only be triggered by TMR2IF, if the Timer2 interrupt is enabled.
The Timer2 interrupt is enabled by setting the TMR2IE enable bit is in the PIE1 register:
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0

PIE1 EEIE ADIE CCP1IE C2IE C1IE OSFIE TMR2IE TMR1IE

And of course, being a peripheral interrupt, to enable Timer2 interrupts you must also set the peripheral and
global interrupt enable bits (PEIE and GIE) in the INTCON register.

Mid-range PIC Assembler, Lesson 16: Using Timer2 Page 2


© Gooligum Electronics 2012 www.gooligum.com.au

The postscaler effectively slows the interrupt rate, allowing Timer2 to generate a longer time-base.
Given a 4 MHz processor clock, and the maximum prescale ratio of 1:16, TMR2 will increment every 16 µs.
If we load the value 255 into PR21, the timer’s period will be 256 increments, and TMR2 will match PR2
every 256 × 16 µs = 4096 µs.
If we also use the maximum postscale ratio of 1:16, TMR2IF will be set on every 16th match, or every 16 ×
4096 µs = 65.536 ms.
Thus, the longest period that Timer2 can generate directly, with a 4 MHz processor clock, is 65.5 ms.

An example will help to illustrate these concepts…

Example 1: Flash an LED at exactly 1 Hz


Lesson 6 included an example where an LED was flashed at exactly 1 Hz (assuming an accurate processor
clock), using a Timer0 interrupt. To do so, the interrupt handler had to add an appropriate offset to the timer,
and, to ensure accuracy, the prescaler could not be used. With no prescaler, the interrupt had to be called
every 250 µs – adding more overhead than we might like.
In this example, we will see that it is much simpler to
achieve the same result using Timer2.

We will use the simple circuit shown on the right, based


on a PIC16F684 with an LED on RC0.
If you have the Gooligum training board, close jumper
JP16 to enable the LED on RC0.
As usual, if you are using Microchip’s Low Pin Count
Demo Board, you only need to plug in your PIC16F684;
the LED on RC0 is labelled DS1.

Using Timer2’s period register, we can have TMR2 reset


automatically, with a period that divides evenly into 1 sec.
Given the default 4 MHz processor clock, we’ll need to
toggle the LED every 500,000 instruction cycles.
The largest value, less than or equal to 256, that divides
exactly into 500,000 is 250.
So, our first step is to load the value 249 (= 250 – 1) into PR2:
banksel PR2 ; Timer2 period = 250 clocks
movlw .249 ; (PR2 = period-1 = 249)
movwf PR2

If we set the prescaler to 1:4, TMR2 increments every 4 µs, and will match PR2 every 250 × 4 µs = 1 ms.

1
This is the power-on default, so strictly speaking there is no need to load a value of 255 into PR2, but it’s good
practice to either explicitly do so, or to note in a comment that you’re relying on the power-on default value.

Mid-range PIC Assembler, Lesson 16: Using Timer2 Page 3


© Gooligum Electronics 2012 www.gooligum.com.au

That means that, if we do not use the postscaler (TOUTPS<3:0> = ‘0000’), the Timer2 interrupt (if enabled)
would be triggered every 1 ms.
That’s not ideal; the ISR would have to toggle the LED every 500th time it is run, but we can’t use a single 8-
bit register to count to 500. We’d need to use two registers, as was done in lesson 6. That’s ok, but we can
make our lives easier by using the postscaler.
If we set the postscaler to 1:10, the interrupt will be triggered only on every 10th match, i.e. after 10 ms.
So to configure Timer2, we have:
movlw b'01001101' ; configure Timer2:
; -1001--- postscale = 10 (TOUTPS = 1001)
; -----1-- enable Timer2 (TMR2ON = 0)
; ------01 prescale = 4 (T2CKPS = 01)
banksel T2CON ; -> increment TMR2 every 4 us,
movwf T2CON ; match PR2 every = 1 ms (4 us x 250)
; set TMR2IF every 10 ms (1 ms x 10)

If we then enable the Timer2 interrupt:


banksel PIE1 ; enable Timer2 interrupt
bsf PIE1,TMR2IE

; enable interrupts
bsf INTCON,PEIE ; enable peripheral
bsf INTCON,GIE ; and global interrupts

The ISR will be triggered every 10 ms, and hence has to toggle the LED every 50th time it runs.

As we did in lesson 6, we can use a variable to count these 10 ms periods:


GENVAR UDATA_SHR ; general variables
cnt_10ms res 1 ; counts 10 ms periods
; (decremented by ISR every 10 ms)

This is initialised by:


; initialise variables
banksel cnt_10ms ; initial count for 500 ms toggle time
movlw FlashMS/10
movwf cnt_10ms

where the constant FlashMS is defined by:


constant FlashMS=500 ; LED flash toggle time in milliseconds

The ISR can then decrement this variable each time it is called, toggling the LED after 500 ms (when the
count = 0).
; toggle LED every 500 ms
banksel cnt_10ms
decfsz cnt_10ms,f ; decrement 10 ms period count
goto isr_end ; when count = 0 (every 50 times = 500 ms)
movlw FlashMS/10 ; reload count
movwf cnt_10ms

movf sPORTC,w
xorlw 1<<nF_LED ; toggle flashing LED
movwf sPORTC ; using shadow register

Mid-range PIC Assembler, Lesson 16: Using Timer2 Page 4


© Gooligum Electronics 2012 www.gooligum.com.au

As we’ve done before, a shadow register is used when toggling a single port bit, to avoid potential read-
modify-write issues.
That means that all the main loop has to do is copy this shadow register to the port:
main_loop
; copy shadow register to port
banksel PORTC
movf sPORTC,w
movwf PORTC

; repeat forever
goto main_loop

Complete program
This is how these pieces fit together; the code is very similar to the equivalent example in lesson 6:
;************************************************************************
; Description: Lesson 16 example 1 *
; *
; Demonstrates use of Timer2 to perform generate a specific time-base *
; for an interrupt-driven background task *
; *
; Flash an LED at exactly 1 Hz (50% duty cycle). *
; *
;************************************************************************
; Pin assignments: *
; RC0 = flashing LED *
; *
;************************************************************************

list p=16F684
#include <p16F684.inc>

errorlevel -302 ; no "register not in bank 0" warnings


errorlevel -312 ; no "page or bank selection not needed" messages

radix dec

;***** CONFIGURATION
; ext reset, no code or data protect, no brownout detect,
; no watchdog, power-up timer, 4 MHz int clock with I/O,
; no failsafe clock monitor, two-speed start-up disabled
__CONFIG _MCLRE_ON & _CP_OFF & _CPD_OFF & _BOD_OFF & _WDT_OFF & _PWRTE_ON
& _INTOSCIO & _FCMEN_OFF & _IESO_OFF

; pin assignments
constant nF_LED=0 ; flashing LED on RC0

;***** CONSTANTS
constant FlashMS=500 ; LED flash toggle time in milliseconds

;***** VARIABLE DEFINITIONS


CONTEXT UDATA_SHR ; variables used for context saving
cs_W res 1
cs_STATUS res 1

Mid-range PIC Assembler, Lesson 16: Using Timer2 Page 5


© Gooligum Electronics 2012 www.gooligum.com.au

SHADOW UDATA_SHR ; shadow registers


sPORTC res 1 ; PORTC

GENVAR UDATA ; general variables


cnt_10ms res 1 ; counts 10 ms periods
; (decremented by ISR every 10 ms)

;***** RESET VECTOR ****************************************************


RESET CODE 0x0000 ; processor reset vector
pagesel start
goto start

;***** MAIN PROGRAM *****************************************************


MAIN CODE

;***** Initialisation
start
; configure ports
banksel PORTC ; start with PORTC clear
clrf PORTC ; (LED off)
clrf sPORTC ; and update shadow
banksel TRISC ; configure LED pin (only) as an output
movlw ~(1<<nF_LED)
movwf TRISC

; configure timers
banksel PR2 ; Timer2 period = 250 clocks
movlw .249 ; (PR2 = period-1 = 249)
movwf PR2
movlw b'01001101' ; configure Timer2:
; -1001--- postscale = 10 (TOUTPS = 1001)
; -----1-- enable Timer2 (TMR2ON = 0)
; ------01 prescale = 4 (T2CKPS = 01)
banksel T2CON ; -> increment TMR2 every 4 us,
movwf T2CON ; match PR2 every = 1 ms (4 us x 250)
; set TMR2IF every 10 ms (1 ms x 10)
banksel PIE1 ; enable Timer2 interrupt
bsf PIE1,TMR2IE

; initialise variables
banksel cnt_10ms ; initial count for 500 ms toggle time
movlw FlashMS/10
movwf cnt_10ms

; enable interrupts
bsf INTCON,PEIE ; enable peripheral
bsf INTCON,GIE ; and global interrupts

;***** Main loop


main_loop
; copy shadow register to port
banksel PORTC
movf sPORTC,w
movwf PORTC

; repeat forever
goto main_loop

Mid-range PIC Assembler, Lesson 16: Using Timer2 Page 6


© Gooligum Electronics 2012 www.gooligum.com.au

;***** INTERRUPT SERVICE ROUTINE


ISR CODE 0x0004
; *** Save context
movwf cs_W ; save W
movf STATUS,w ; save STATUS
movwf cs_STATUS

; *** Service Timer2 interrupt


;
; Runs every 10 ms
; (every 10th TMR2/PR2 match;
; TMR2 matches PR2 every 250 x 4 clocks = 1000 us)
;
; Flashes LED at 1 Hz
; by toggling on every 50th interrupt (every 500 ms)
;
; (only Timer2 interrupts are enabled)
;
banksel PIR1 ; clear interrupt flag
bcf PIR1,TMR2IF

; toggle LED every 500 ms


banksel cnt_10ms
decfsz cnt_10ms,f ; decrement 10 ms period count
goto isr_end ; when count = 0 (every 50 times = 500 ms)
movlw FlashMS/10 ; reload count
movwf cnt_10ms

movf sPORTC,w
xorlw 1<<nF_LED ; toggle flashing LED
movwf sPORTC ; using shadow register

isr_end ; *** Restore context then return


movf cs_STATUS,w ; restore STATUS
movwf STATUS
swapf cs_W,f ; restore W
swapf cs_W,w
retfie

END

Conclusion
As arguably the simplest of the mid-range PIC timers, a single example is all we need to demonstrate
Timer2.
But despite its simplicity, hopefully this lesson has shown that Timer2 is a useful, and easy-to-use, timer.

Now that we’ve seen all the timers, we can turn our attention to one of the most powerful mid-range PIC
peripherals, the Capture/Compare/PWM module, which we’ll introduce in the next lesson, beginning with its
capture and compare modes.

Mid-range PIC Assembler, Lesson 16: Using Timer2 Page 7

You might also like