Professional Documents
Culture Documents
Osmeoisis 2022-09-06 15-33-19PIC - Enh - C - 13
Osmeoisis 2022-09-06 15-33-19PIC - Enh - C - 13
au
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.
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’):
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
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
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.
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.
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.
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...
// 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
}
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 *
* *
************************************************************************/
#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 = 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
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.