You are on page 1of 7

© Gooligum Electronics 2015 www.gooligum.com.

au

Introduction to PIC Programming


Programming Enhanced Mid-Range PICs in C

by David Meiklejohn, Gooligum Electronics

Lesson 13: Using Timer2

The last lesson introduced Timer1, a 16-bit timer with a number of advanced features, including a low-power
(32.768 kHz crystal) oscillator, operation during sleep mode, and external and timer gating. Those features
are useful, but often only a simple 8-bit timer is needed, as we saw in lesson 4, which showed how the 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 fact, it is so useful that many enhanced mid-range PICs provide several of these 8-bit “Timer2” modules,
named Timer2, Timer4, Timer6 and so on, all identical, but independent of each other.
In summary, this lesson covers:
 Introduction to the Timer2-type modules
 Using the Timer2 postscaler to drive an interrupt at a reduced rate
 Using the Timer2 period register to generate a specific time-base
with an example implemented using XC8 (running in “Free mode”).

Timer2/4/6 Modules
The PIC16F1824 includes three identical ‘Timer2’ modules: Timer2, Timer4 and Timer6, which we will
refer to generically as Timer2/4/61.
The current value of each timer is held in a single 8-bit register: TMRx, where ‘x’ is ‘2’, ‘4’ or ‘6’.
Associated with each TMRx register is an 8-bit period register: PRx, where ‘x’ is ‘2’, ‘4’ or ‘6’.
For example, the value of Timer2 is held in TMR2 and its period register is PR2, while Timer4 consists of
timer register TMR4 and period register PR4, and so on.

Unlike the other timers, these timers don’t simply rollover to 0 after they reach 255. Instead, TMRx is reset
to 0 (on the next increment) after it reaches the value held in PRx.
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.

1
Some enhanced mid-range PICs have more of these ‘Timer2’ modules; the PIC16F1526 has five, named Timer2,
Timer4, Timer6, Timer8 and Timer10, or generically Timer2/4/6/8/10.

Enhanced Mid-Range PIC C, Lesson 13: Using Timer2 Page 1


© Gooligum Electronics 2015 www.gooligum.com.au

Thus, each timer’s period is equal to the value stored in PRx, plus one.

Note that Timer2/4/6 can only be driven by the instruction clock (FOSC/4).
If the processor is clocked at the default 500 kHz, each Timer2/4/6 module will be clocked at 125 kHz.
Thus, the Timer2/4/6 modules can only be used as timers; they cannot be used to count external events.

Each Timer2/4/6 module is configured via a dedicated control register, TxCON (where ‘x’ is ‘2’, ‘4’ or ‘6’):

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

TxCON – TxOUTPS<3:0> TMRxON TxCKPS<1:0>

Like Timer1, each Timer2/4/6 module can be turned on or off, using its TMRxON bit: setting it to ‘1’ to
enables the timer, and clearing it to ‘0’ stops it.

Prescaler
Like the other timers, the Timer2/4/6 modules include a prescaler, so that the timer does not have to
increment on every clock cycle.
However, the Timer2/4/6 prescaler provides a choice of only four prescale ratios.
The prescale ratio is set by the TxCKPS<1:0> bits, as shown in the following table:
TxCKPS<1:0> = ‘00’ means that no prescaling will occur, and
TxCKPS<1:0> Timer2/4/6
TMRx will increment at the instruction cycle rate.
bit value prescale ratio
TxCKPS<1:0> = ‘11’ selects the maximum prescale ratio of 1:64,
00 1:1
meaning that TMRx will increment every 64 instruction cycles.
01 1:4 Given a 125 kHz instruction cycle rate, the timer would increment
every 64 × 8 µs = 512 µs.
10 1 : 16
11 1 : 64

Postscaler and Timer2/4/6 interrupts


Unlike the other timers, the Timer2/4/6 modules also include a postscaler.
A postscaler does not affect how quickly the timer increments.
Instead, it affects how often the timer’s interrupt flag is set.

Each Timer2/4/6 module has a separate interrupt flag, TMRxIF (where ‘x’ is ‘2’, ‘4’ or ‘6’), located in one
of the peripheral interrupt registers:
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0

PIR1 TMR1GIF ADIF RCIF TXIF SSP1IF CCP1IF TMR2IF TMR1IF

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

PIR3 – – CCP4IF CCP3IF TMR6IF – TMR4IF –

Enhanced Mid-Range PIC C, Lesson 13: Using Timer2 Page 2


© Gooligum Electronics 2015 www.gooligum.com.au

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

If the postscaler is active (TxOUTPS<3:0> ≠ ‘0000’), this match has to occur some number of times before
TMRxIF is set.
The postscale ratio is equal to the value of TxOUTPS<3:0> plus one.
Thus, the postscale ratio ranges from 1:1 (TxOUTPS<3:0> = ‘0000’) to 1:16 (TxOUTPS<3:0> = ‘1111’).

For example, if T4OUTPS<3:0> = ‘1001’ (binary) = 9, the postscale ratio for Timer4 will be 1:10, and the
match between TMR4 and PR4 will have to occur 10 times before TMR4IF is set.

As always, an interrupt can only be triggered if it is enabled.


Each Timer2/4/6 module has a separate interrupt enable flag, TMRxIE (where ‘x’ is ‘2’, ‘4’ or ‘6’), located
in one of the peripheral interrupt enable registers:
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0

PIE1 TMR1GIE ADIE RCIE TXIE SSP1IE CCP1IE TMR2IE TMR1IE

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

PIE3 – – CCP4IE CCP3IE TMR6IE – TMR4IE –

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

The postscaler effectively slows the interrupt rate, allowing each Timer2/4/6 module to generate a longer
time-base.
Given the default 500 kHz processor clock, and the maximum prescale ratio of 1:64, TMRx will increment
every 512 µs.
If we load the value 255 into PRx2, the timer’s period will be 256 increments, and TMRx will match PRx
every 256 × 512 µs = 131 ms.
If we also use the maximum postscale ratio of 1:16, TMRxIF will be set on every 16th match, or every 16 ×
131 ms = 2.097 s.
Thus, the longest period that any of the Timer2/4/6 modules can generate directly from the default 500 kHz
processor clock is a little more than two seconds.

An example will help to illustrate these concepts…

2
This is the power-on default, so strictly speaking there is no need to load a value of 255 into PRx, 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.

Enhanced Mid-Range PIC C, Lesson 13: Using Timer2 Page 3


© Gooligum Electronics 2015 www.gooligum.com.au

Example 1: Flash an LED at exactly 1 Hz


Lesson 5 included an example where an LED was flashed at exactly 1 Hz (toggled every 500 ms, 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 2 ms – adding more overhead than we might like.
In this example, we will see that it is easier to achieve the
same result using Timer23.

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


on a PIC16F1824 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 PIC16F1824;
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 500
ms.
Given the default 500 kHz processor clock, we’ll need to
toggle the LED every 62,500 instruction cycles.
The largest value, less than or equal to 256, that divides exactly into 62,500 is 250.
So, our first step is to load the value 249 (= 250 – 1) into PR2:
PR2 = 249; // Timer2 period = 250 clocks (PR2 = period-1)

If we set the prescaler to 1:1, TMR2 increments every 8 µs, and will match PR2 every 250 × 8 µs = 2 ms.
That means that, if we do not use the postscaler (T2OUTPS<3:0> = ‘0000’), the Timer2 interrupt would be
triggered every 2 ms.
That’s ok, but to reduce the time spent servicing interrupts (the interrupt handler overhead), giving our main
code more time to perform other tasks, we can use the postscaler so that the interrupt is triggered less often.
If we set the postscaler to 1:10, the interrupt will be triggered only on every 10th match, i.e. after 20 ms.
So to configure Timer2, we have:
T2CONbits.T2CKPS = 0b00; // prescale = 1
T2CONbits.T2OUTPS = 9; // postscale = 10 (T2OUTPS = postscale-1)
// -> increment TMR2 every 8 us,
// match PR2 every = 2 ms (8 us x 250)
// set TMR2IF every 20 ms (2 ms x 10)
T2CONbits.TMR2ON = 1; // enable Timer2

3
or Timer4 or Timer6...

Enhanced Mid-Range PIC C, Lesson 13: Using Timer2 Page 4


© Gooligum Electronics 2015 www.gooligum.com.au

If we then enable the Timer2 interrupt:


PIE1bits.TMR2IE = 1; // enable Timer2 interrupt

// enable interrupts
INTCONbits.PEIE = 1; // enable peripheral
ei(); // and global interrupts

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

As we did in lesson 5, we can use a static variable, defined at the start of the interrupt function, to count these
20 ms periods:
static uint8_t cnt_20ms = 0; // counts 20 ms periods

The ISR can then increment this variable each time it is called, toggling the LED after 500 ms (when the
count = 25):
// toggle LED every 500 ms
++cnt_20ms; // increment 20 ms period count
if (cnt_20ms == FlashMS/20) // if we've counted for 500 ms,
{
cnt_20ms = 0; // reset count
F_LED = ~F_LED; // toggle flashing LED
}

where the constant FlashMS is defined by:


#define FlashMS 500 // LED flash toggle time in milliseconds

The main loop is left with nothing to do:


/*** Main loop ***/
for (;;)
{
; // (do nothing)
}

Complete program
This is how these pieces fit together; the code is very similar to the equivalent example in lesson 5:
/************************************************************************
* Description: Lesson 13, example 1 *
* *
* Demonstrates use of Timer2 to 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 *
* *
************************************************************************/

Enhanced Mid-Range PIC C, Lesson 13: Using Timer2 Page 5


© Gooligum Electronics 2015 www.gooligum.com.au

#include <xc.h>
#include <stdint.h>

/***** CONFIGURATION *****/


// ext reset, internal oscillator (no clock out), 4xPLL off
#pragma config MCLRE = ON, FOSC = INTOSC, CLKOUTEN = OFF, PLLEN = OFF
// no watchdog timer, brownout resets enabled, low brownout voltage
#pragma config WDTE = OFF, BOREN = ON, BORV = LO
// no power-up timer, no failsafe clock monitor, two-speed start-up disabled
#pragma config PWRTE = OFF, FCMEN = OFF, IESO = OFF
// no code or data protect, no write protection
#pragma config CP = OFF, CPD = OFF, WRT = OFF
// stack resets on, high-voltage programming
#pragma config STVREN = ON, LVP = OFF

// Pin assignments
#define F_LED LATCbits.LATC0 // flashing LED on RC0

/***** CONSTANTS *****/


#define FlashMS 500 // LED flash toggle time in milliseconds

/***** MAIN PROGRAM *****/


void main()
{
/*** Initialisation ***/

// configure ports
LATC = 0; // start with all output pins low (LED off)
TRISC = 0b111110; // configure only led pin (RC0) as an output

// configure oscillator
OSCCONbits.SCS1 = 1; // select internal clock
OSCCONbits.IRCF = 0b0111; // internal oscillator = 500 kHz

// configure Timer2
PR2 = 249; // Timer2 period = 250 clocks (PR2 = period-1)
T2CONbits.T2CKPS = 0b00; // prescale = 1
T2CONbits.T2OUTPS = 9; // postscale = 10 (T2OUTPS = postscale-1)
// -> increment TMR2 every 8 us,
// match PR2 every = 2 ms (8 us x 250)
// set TMR2IF every 20 ms (2 ms x 10)
T2CONbits.TMR2ON = 1; // enable Timer2
PIE1bits.TMR2IE = 1; // enable Timer2 interrupt

// enable interrupts
INTCONbits.PEIE = 1; // enable peripheral
ei(); // and global interrupts

/*** Main loop ***/


for (;;)
{
; // (do nothing)
}
}

Enhanced Mid-Range PIC C, Lesson 13: Using Timer2 Page 6


© Gooligum Electronics 2015 www.gooligum.com.au

/***** INTERRUPT SERVICE ROUTINE *****/


void interrupt isr(void)
{
static uint8_t cnt_20ms = 0; // counts 20 ms periods

// *** Service Timer2 interrupt


//
// Runs every 20 ms
// (every 10th TMR2/PR2 match;
// TMR2 matches PR2 every 250 x 8 us = 2000 us)
//
// Flashes LED at 1 Hz
// by toggling on every 25th interrupt (every 500 ms)
//
// (only Timer2 interrupts are enabled)
//
PIR1bits.TMR2IF = 0; // clear interrupt flag

// toggle LED every 500 ms


++cnt_20ms; // increment 20 ms period count
if (cnt_20ms == FlashMS/20) // if we've counted for 500 ms,
{
cnt_20ms = 0; // reset count
F_LED = ~F_LED; // toggle flashing LED
}
}

Conclusion
As arguably the simplest of the enhanced mid-range PIC timers, a single example is all we need to
demonstrate Timer2 (or Timer4 or Timer6...).
But despite its simplicity, hopefully this lesson has shown that the Timer2/4/6 modules are useful, and easy-
to-use, timers.

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

Enhanced Mid-Range PIC C, Lesson 13: Using Timer2 Page 7

You might also like