Professional Documents
Culture Documents
au
This lesson describes the type of Timer2 module included in most enhanced mid-range PICs, although as we
will see it is very similar to the mid-range PIC Timer2 module, as described in mid-range C lesson 10.
A new feature of the enhanced mid-range architecture is that many devices now provide several of these
“Timer2” modules, named Timer2, Timer4, Timer6 and so on, all identical, but independent of each other.
In summary, this lesson covers:
Introduction to the enhanced mid-range 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.
These 16F1824 Timer2/4/6 modules are almost identical to the 16F684 Timer2 module, described in mid-
range C lesson 10, so we won’t describe them here in as much detail.
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.
Each TMRx register 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 PR4, TMR4 will repeatedly count from 0 to 99, resetting to 0 after
counting 100 times.
Thus, each timer’s period is equal to the value stored in PRx, plus one.
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.
Note that, as was true for Timer2 in mid-range devices, Timer2/4/6 can only be driven by the instruction
clock (FOSC/4); these modules can only be used as timers and 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’):
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
Unlike the Timer2 module present in mid-range PICs, the enhanced mid-range Timer2/4/6 prescaler provides
a choice of four prescale ratios.
The prescale ratio is set by the TxCKPS<1:0> bits, as shown in the following table:
The first two prescale ratios are the same as we have seen before.
TxCKPS<1:0> Timer2/4/6
bit value prescale ratio In mid-range PICs, T2CKPS = ‘10’ or ‘11’ both select a prescale
ratio of 1:16.
00 1:1
However, in enhanced mid-range PICs, TxCKPS = ‘10’ selects
01 1:4 the previously-available 1:16 ratio, while TxCKPS = ‘11’ selects
10 1 : 16 the new maximum prescale ratio of 1:64.
11 1 : 64
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:
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.
For example, if T6OUTPS<3:0> = ‘1001’ (binary) = 9, the postscale ratio for Timer6 will be 1:10, and the
match between TMR6 and PR6 will have to occur 10 times before TMR6IF is set.
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:
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.
As we saw in mid-range C lesson 10, 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.
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.
We can use Timer4’s period register to specify a period that divides evenly into 500 ms.
Given a 4 MHz processor clock, and hence a 1 MHz instruction 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 PR4:
PR4 = 249; // Timer4 period = 250 clocks (PR4 = period-1)
If we set the prescaler to 1:16, TMR4 increments every 16 µs, and matches PR4 every 250 × 16 µs = 4 ms.
That means that, if we do not use the postscaler (T4OUTPS<3:0> = ‘0000’), the Timer4 interrupt would be
triggered every 4 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:5, the interrupt will be triggered only on every 5th match, i.e. after 20 ms.
So to configure Timer4, we have:
T4CONbits.T4CKPS = 0b10; // prescale = 16
T4CONbits.T4OUTPS = 4; // postscale = 5 (T4OUTPS = postscale-1)
// -> increment TMR4 every 16 us,
// match PR4 every = 4 ms (16 us x 250)
// set TMR4IF every 20 ms (4 ms x 5)
T4CONbits.TMR4ON = 1; // enable Timer4
// 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’ve done before, 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
}
Complete program
This is how these pieces fit together; the code is very similar to the example in mid-range C lesson 10:
/************************************************************************
* *
* Description: Migration lesson 8, example 1 *
* *
* Demonstrates use of Timer4 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 *
* *
************************************************************************/
#include <xc.h>
#include <stdint.h>
// Pin assignments
#define F_LED LATCbits.LATC0 // flashing LED on RC0
// 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 = 0b1101; // internal oscillator = 4 MHz
// -> 1 us / instruction cycle
// configure Timer4
PR4 = 249; // Timer4 period = 250 clocks (PR4 = period-1)
T4CONbits.T4CKPS = 0b10; // prescale = 16
T4CONbits.T4OUTPS = 4; // postscale = 5 (T4OUTPS = postscale-1)
// -> increment TMR4 every 16 us,
// match PR4 every = 4 ms (16 us x 250)
// set TMR4IF every 20 ms (4 ms x 5)
T4CONbits.TMR4ON = 1; // enable Timer4
PIE3bits.TMR4IE = 1; // enable Timer4 interrupt
// enable interrupts
INTCONbits.PEIE = 1; // enable peripheral
ei(); // and global interrupts
Conclusion
We’ve seen in this lesson that, other that providing an additional prescale ratio, the enhanced Timer2/4/6
module is essentially the same as the mid-range Timer2 module that we’re already familiar with.
More significantly, we’ve seen that many enhanced mid-range PICs, including the 16F1824, provide a
number of these ‘Timer2’ modules, and that these modules operate identically, while being independent of
each other.
Similarly, the PIC16F1824 includes multiple Capture/Compare/PWM (CCP) modules, including a full-
bridge enhanced CCP (ECCP) module similar to that described in mid-range C lessons 11 and 12, along with
two standard CCP modules and a half-bridge ECCP module.
Although most of the CCP/ECCP functionality is very similar to that described in the mid-range lessons,
we’ll briefly revisit these modules in the next lesson, highlighting the (minor) differences and, more
importantly, the availability of multiples of each type of module.