Sponsers

PIC Timers

After writing about the PIC hardware PWM module it occurred to me that a little more information about the onboard timers would be in order. Just about every PIC has a timer and many have several. The PIC16F777 that we I used in the hardare PWM article has three of them called TMR0, TMR1, TM2

The three timers are similar to each other in many ways but are different in some respects. Below is a list of each of the timer features from the datasheet.

 

 

TRM0

  • 8-bit timer/counter
  • Readable and writable
  • 8-bit software programmable prescaler
  • Internal or external clock select
  • Interrupt on overflow from FFh to 00h
  • Edge select for external clock

TMR1

  • 16-bit timer/counter which consists of two 8-bit registers
  • Readable and writable (TMR1H:TMR1L)
  • 4-bit software programmable prescaler
  • Internal or external clock select

TMR2

  • 8-bit timer/counter
  • Readable and writable
  • 4-bit software programmable prescalerInternal or external clock select
  • Prescale options include 1:1, 1:4, 1:16
  • Postcale options include 1:1, 1:2, 1:3, 1:16
  • Internal clock only
  • Output (before prescaler) is fed to the SSP module for an optional shift clock.
  • Can be set to automatically compare it to a value you determine.

Each of the timers can be configured separately and can be used for different purposes within your application. One of the nice things about these is that they run on their own in the background while your code is off doing something else. They “tap you on the shoulder” in the form of an interrupt to remind you to go and do something. You can also read them or reset them at any time. These timers are essentially binary counters that start at a value you determine and keep running until you shut them off. If you’re not familiar with a binary counter it looks much like this…

clock pulse 1 00000000
clock pulse 2 00000001
clock pulse 3 00000010
clock pulse 4 00000011
clock pulse 5 00000100
clock pulse 6 00000101

and so on

The counter keeps increasing after every clock pulse until it “overflows”. Overflows means that when the counter is all 1’s and another 1 is added, there is no place for the carry bit to go. This generates and interrupt and the timer is reset to all 0’s again.

Now lets write a little routine that uses one of our timers to blink an LED at 1 second intervals. We’re going to use the CCS compiler for this example and once again use the PIC16F777. We’ll use TMR1 for this task and take I’ll take a moment to point out a couple of things before we get to coding.

– TMR1 counts from 0 to 65535

– On the next count it resets back to zero and sets a bit in the TMR1IF (Timer 1 Interupt Flag) on the PIR1 register.

– If we run the internal clock at 20Mhz we know that the program counter and the pulses that drive the clock are equal to 1/4 of the internal clock speed of each clock count. That means that the TMR1 clock runs a 20Mhz/4=5Mhz.

– A little more math and we find that each clock pulse take 1/5Mhz seconds to comple. That’s .2us for each count of TMR1

– Another piece we need is how long until the timer overflows. From what we know so far it’s .2us x 65536 = 0.0131

– Since the last piece resulted in such a short time until overflow, we could probably use a prescaler to slow it down a bit. Using the 1:8 prescale which divides our clock we get 5Mz/8 = 625KMz which is 1.6us per count of TMR1. With that we have 1.6us x 65536 = 0.104856s wich is how long it takes to overflow the clock.

Now, with that information we know that if it takes 0.104856s to overflow the clock. If we let the clock overflow 10 times we will have taken up 1.04856s. Well that’s pretty close to what we wanted but lets say we had to get that close to 1s. What could we do? Well, remember that TMR one is writable. That means we can preload a value into TMR to get us a little close. I’m not going to show all the math on this part but if you solve this equation …

clock cycles = (0.1s)/((20Mhz)(1/4)(1/8)) = 62500

You’ll see that if we could overflow the clock after 62500 cycles rather than 65536 we would have consumed exactly .10 seconds. So the value we need to preload in is (65335-62500) = 3035.

The code that puts it all together…

#include <16F777.h>
#fuses INTRC,NOWDT,NOPROTECT
#use delay(clock=2000000)
unsigned int counter = 0;
setup_port_b (ALL_DIGITAL);
#int_timer1

void timer1_isr() {
disable_interrupts(INT_TIMER1); //Turn off interrupts
set_timer1(3035); //Load preset
counter = counter +1; //Increment Counter
clear_interrupt(INT_TIMER1);
enable_interrupts(INT_TIMER1); //Enable the TMR1 Interrupt
}

void main(void){
SET_TRIS_B( 0x00 ); //Set Port B to all Outputs
setup_timer_1(T1_INTERNAL|T1_DIV_BY_8); //Timer with internal clock and 1:8 prescaler
enable_interrupts(GLOBAL);
enable_interrupts(INT_TIMER1);

test:
if (counter == 10){
 counter = 0; //Reset the counter
 OUTPUT_TOGGLE(PIN_B1);
}

goto test;

}

We start out telling the CCS compiler which device we’re using and set some of the config bits. We also set port B to all outputs, initalize the variable COUNTER, and declare out timer1 interrupt. The main part of the program sets up the timer with the internal clock and the prescaler we talked about. The interrupt is enabled and then we begin the testing of the variable COUNTER. Remember that the TMR1 clock is running all the time now that we’ve set it up. The test loop checks to see if we’ve overflowed it 10 times. We know how many times it overflowed because we’ve set up an interrupt routine which increments COUNTER each time if overflows. When COUNTER reaches ten, we toggle B1. So with an LED and appropriately sized resister on B1 we would see the LED light up for 1s and then turn off for 1s.

In CCS there are simpler ways of accomplishing this task of lighting the LED for 1s on and 1s off. We could use the delay function build into CCS to simplify our code. We only did it this way to demonstrate how the TMR1 operates. Hopefully I’ve shed a little light into the world of PIC timers using CCS and you can feel comfortable using them in your designs.

Comments are closed.