In this tutorial, we’ll take a brief look at the timer module and use it to write a delay function. This versatile module can be used in lots of creative ways, like generating pauses, counting events, and scheduling tasks. When we learn how to use interrupts in a future tutorial, we’ll learn how to set up an event that occurs at regular intervals, which can be used to update graphics at a regular frame rate or periodically sample a signal for performing a FFT.
If you haven’t already gone through the ADC tutorial, make sure you do so before this one, as we’ll draw from our knowledge of the microcontroller datasheet.
Reading the datasheet
From the datasheet’s table of contents, we see that our MCU has 3 timers. We’ll use the simplest one for now, timer 0, so jump to that section and take a few minutes to read the first few pages before we go through it together.
Timer/counter modules are essentially just a register (or registers) that stores a count. The module has an input, which is some digital signal, and whenever the digital signal transitions, the register increments. For example, if the input signal is a clock signal, then our register increments at the frequency of that clock. When the register value overflows (or reaches some specified value), the module’s output goes high.
A quick side note: we’re talking about the module being used as a timer, but we can also input an asynchronous signal (we won’t be in this tutorial). For example, our input signal could be a pushbutton, and our register would increment every time the button is pressed, in which case we’d be using the module as a counter. If we wanted to count button presses, we could also use a plain ol’ GPIO module, but the timer module would increment automatically, making our lives a lot easier.
For timer 0, the registers that store the count are TMR0L and TMR0H. In 16-bit mode, both registers store the count, but we’ll use the module in 8-bit mode. The datasheet’s section on 8-bit mode (section 25.1.2, page 270) tells use that:
- TMR0L is the register that stores the count and increments
- TMR0L is compared to TMR0H to determine whether the module’s output goes high or low
- When the values of TMR0L and TMR0H match, TMR0L is reset to 0x00 after the output goes high (and then the output goes low again)
- By default, TMR0H is set to 0xFF in 8-bit mode
- TMR0IF (the “timer 0 interrupt flag”) is set to 1
We have 2 different ways to determine if the timer output has gone high: by checking the module’s output bit (which is in one of the SFRs) or by checking the value of TMR0IF. We’ll be using TMR0IF, which is an interrupt flag — we haven’t covered interrupts yet, but that’s ok — all we have to know is that it has to be cleared manually, in software (i.e. after the timer output goes high and TMR0IF sets set to 0b1, we’ll write TMR0IF = 0b0;).
Hopping down to the register definitions section, we set that register T0CON1 contains a bitfield that allows us to select the input clock for our timer. We’ll set it to FOSC/4. Looking at the settings of our configuration bits, we see that FOSC (the frequency of our system oscillator) is 32MHz — so the input to our timer module will be an 8MHz clock signal. This is fast, but we can slow it down: timer 0 includes a prescaler and postscaler, which are circuits in the module that divide the frequency of the clock signal. T0CON1 contains the prescaler, which allows us to divide our clock by as much as 32768 times, and T0CON0 contains the postscaler, which allows us to divide our clock by as much as 16 times.
Ok, so let’s set up our timer to output once a second, and use it to blink a LED. How do we set up ? Well, in 8-bit mode, TMR0L will increment every time our scaled-down clock ticks, and we can make the output go high when TMR0L reaches whatever value we set TMR0H to. For example, if our clock was (after scaling) 100Hz, TMR0L would increment 100 times a second. If we then set TMR0H to 100, every second, TMR0L would count up to 100 and a match would occur. Then TMR0IF would go high, which would tell us to toggle the LED, and TMR0L would reset to 0.
All that’s left before writing the code is to calculate values for our prescaler and postscaler. We need TMR0L to count up to some value that’s less than 256, since it’s an 8-bit register, so we have to try different prescale and postscale factors that are available (based on the register definitions for T0CON0 and T0CON1) that give us a clock signal with a frequency less than 256Hz. For example, if we try setting the prescaler to 4096x, and the postscaler to 16x, we get a clock frequency of:
(FOSC/4) / prescale / postscale
= 8,000,000 / 4096 / 16
= 122.070
That’ll work! Now we just have to set up our module, and make sure to set TMR0H to 122 so our match will occur every second.
//configure the TIMER0 module to match once a second
//(FOSC/4) / 16 / 4096 = 122.07
void setupTimer0()
{
T0CON0bits.T016BIT = 0b0; //use 8-bit mode
T0CON0bits.T0OUTPS = 0b1111; //set the postscaler to 1:16
T0CON1bits.T0CS = 0b010; //use FOSC/4 as the timer input
T0CON1bits.T0CKPS = 0b1100; //set the prescaler to 1:4096 pre
TMR0H = 122; //we want the timer match to occur every time
//TMR0 reaches 122, which happens every second
T0CON0bits.T0EN = 0b1; //enable the module
}
Then our main() just has to wait until the TMR0IF is set, indicating that the timer match occurred, toggle the LED, and clear TMR0IF (if we don’t, how will we know when the next match happened?)
void main(void)
{
TRISCbits.TRISC0 = 0b0; //set RC0 as an output
setupTimer0(); //set up timer 0 to match once a second
while(1)
{
while(TMR0IF == 0b0); //wait until TMR0L matches TMR0H and
//TMR0IF is set
LATCbits.LATC0 = ~LATCbits.LATC0; //toggle the LED
TMR0IF = 0b0; //clear the bit so we know when the
//next match occurs
}
}
If everything is correct, the LED should blink, and it should be on for 1 second and off for 1 second.
Delay
This is great, but what if we want some code that’s more flexible and easier to reuse? Let’s write a function that pauses for a given number of milliseconds. If we set our prescaler to 1:64 and our postscaler to 1:5, we get a clock frequency of 8,000,000 / 64 / 5 = 25kHz, which means that a millisecond will elapse exactly every 25 ticks. After changing our setupTimer0() function accordingly, we can write a delay function.
void delayMs(unsigned long n)
{
for(; n>0; n--)
{
while(TMR0IF == 0b0); //wait until TMR0L matches TMR0H and
TMR0IF = 0b0;
}
}
Delays aren’t as flashy as LEDs blinking and motors kicking on, but they’re an indispensable tool in our arsenal as firmware developers. It’s obvious that lots of practical programs will require us to pause for a given amount of time.
Continue to the next article on PWM, which we’ll use to simulate an analog output signal for dimming LEDs and controlling the speed of motors.
Going further
The delay we wrote is called a blocking delay — we can’t run other code while the delay function is running. But what if we wanted to blink a LED and do other things simultaneously? The timer 0 module can control an output pin, which means that the module can blink the LED without us having to do anything. Read through the datasheet’s “Timer0 Output” section and see if you can make the LED blink with the following main() function:
void main(void) { TRISCbits.TRISC0 = 0b0; setupTimer0(); while(1); }
Troubleshooting
If your LED isn’t lighting up at all, review the code from the previous GPIO output tutorial. Run it and make sure that the LED can light in the first place — maybe something got disconnected.
If it’s blinking, but not at the correct rate, check your configuration bits and see if the oscillator frequency is actually set to 32MHz. If not, either set it to 32MHz or recalculate your timer prescaler/postscaler/TMR0H values accordingly.
Also, it’s possible that the LED is on solidly, which may convince you that the timer module isn’t working — but in fact, the light might just be blinking so fast that you can’t see it blink! If that’s the case, it’ll be dimmer than usually — which means you need to slow down your timer by checking your
prescaler/postscaler/TMR0H values.