08 Timers

Your Ad Here

Timers

Timers are exactly what they sound like. They keep track of time, and send you a signal when the time is up. Sounds pretty simple right?

The PIC24 has a variety of way to use the timers. On the “F” series, there are usually 5 timers, one 16 bit timers, and 4 more timers that can be used in two modes: as 4 individual 16 bit timers, or as 2 linked 32 bit timers. On the PIC24H series, there are usually just 3 (only two timers that can be used as two 16 bit timers, or one linked 32 bit timer).

Most Obvious Use: Delays

So why do we need timers? Well, let’s say you need a delay in your program. The easiest and most obvious way to create the delay is to write a delay function that loops around with a bunch of increments and decrements, and maybe a few no-ops depending on the compiler:


//loop nops for delay
void DelayuSec(unsigned int N)
{
   unsigned int j;
   while(N > 0)
   {
      for(j=0;j < 10; j++);
         N–;
   }
}

However, you’re never really sure how many instructions the loops will take. In other words, your ability to control the accuracy of your delay is limited since it is impossible to figure out just how the compiler will turn the code into assembly. There are rough ways to estimate these things, but as the word implies, it is merely an estimate.

A programmer can circumvent this clumsy, but frequently used solution by using timers. Again, if you’ve read the oscillator and timing section of the tutorial, you’d know that microcontrollers do not keep track of absolute time. They keep track of relative time with the use of a clock signal, whether it is externally generated or created internally. Therefore, the concept of a timer isn’t so much a counting clock, but more of a counter of clock cycles. This is a concept that might be a little strange at first. If you want an exact 1 second delay, you’re going to have to do some math because depending on the frequency clocking your controller, you’ll need to count to a certain number (hence, the timers count “relative time”).

The simplest way to imagine the timer is to think of it as a counter. You put in the number you want to count to, and it starts to count the clock cycles. Once you’ve reached the desired number, you get a nudge on the shoulder to tell you that the desired number of clock cycles has been counted.

Using Timers

Even though there are two types of timers on the PIC24s, they both work the same way. I will only show examples of 16 bit timer, because it is used most often, and because once you know how it works, it’s just a matter of turning one or two bits on or off to change the linked timers into a 32 bit timer. There are several registers to be concerned with. The most notable is TxCON, where “x” is the number of the timer. This register controls all aspects of the timer except its interrupt abilities. If you need a refresher course on interrupts, here is the link to the previous section of the tutorial.

timer-diagram.jpg

The general procedure of using a timer is something like the following: First, set the preset time using the PRx register. This register is the number to count to, before the timer issues and interrupt signal. Another way of saying this would be, if I set my oscillator clock to 1000 Hz, and my preset PRx register to 1000, then I will get an interrupt every 1 second. Makes sense right?

Next, using with TxCON register, we can turn the timer modules on and off, as well as configure other settings. I have included a copy of the datasheet page describing the TxCON register of Timer1 of a typical PIC24, so that you can take a look at some of the other settings. Timer1 only allows 16 bit configurations, so the hybrid timers may have some other settings (such as configuring it into a 32 bit timer. Lastly, we will still need to configure the interrupts, in order to interrupt the main program when the preset time has been reached.

timer-register.jpg


Example: Blinking LED

By far, the most frequent college project is to write a program that blinks an LED. This is usually one of the first projects done in a microcontroller class. Usually I like to write my interrupts and timer initiations on separate files, but for clarity, I’ve included them in the main body. So to reiterate again, this program blinks an LED.

Considerations

First of all, there are several considerations before we get to the programming. Number one amongst them is the current drawn by the LED. LEDs usually draw 7 mA to 20 mA for peak brightness. However, care must be taken when trying to power an LED. The LEDs have a very low resistance between the anode and the cathode, so putting 5 volts across the two leads will just burn them out. You need to manually restrict the amount of current going into the device.

Your Ad Here

timer-direct-controller.jpg

The most obvious way to get around this restriction is to put a resistor is series. However, even though a resistor in series will restrict the current through the LED, there is another problem. The amount of current needed to power that LED is significantly large. So large in fact that the PIC might not be able to supply it. The PIC24 can supply 20 mA per digital pin, which is not to say that if it has 10 digital pins, it will supply 200 mA. I find that the total amount that the chip can put out is something like 30-40 mA (so at most two LEDs). To get around this restriction, an LED driver must be created so that the LEDs are powered from an external source. An NPN BJT in an open drain configuration does just that. I’m sure you’ve all dealt with 2N3904’s before (even most mechanical engineers I know have seen that at one point in their lives).

Your Ad Here

timer-open-drain.jpg

Here the circuit above uses the digital outputs as the input to the base. The current drawn by the BJT is in the micro-amps range, several orders of magnitude smaller than the amount required to power the LED. However the current through the LED must still be done through the resistor at the emitter. This ensures that 7 mA goes through the LED, assuming a 1.5 V drop across the diode.

Hence: 5V (Vcc) – 1.5V (Vdiode) / 0.5KOhm = 7mA

Easy math right? Least circuits 101 was good for something.

Anyways, you’ll need to build a driver for each of the LEDs in order to turn them on and off.

Timing

A second consideration is how the timers will be clocked. Typically for precise timing applications a 32.768 kHz clock is used. This frequency is special because it makes for a very easy division to achieve a divided clock of 1 Hz (1 Hz = 32.768 kHz * 2^15). Most alarm clocks, radios clocks and your digital watch probably has one of these inside, in some form or another. If we can use one of these as the primary clock source to our microcontroller, we can easily make an LED blink at 1 Hz. You can find them on digikey for cheap, usually under oscillators.

Programming

In order to blink the LED at 1 Hz, we need to do some divisions. Please review the oscillator section if you are not familiar with some of the terms. First off, our CLK input is at 32.768 kHz. Since the instruction clock runs at half the clock rate, then the Fcy = 16.384 kHz. We want to figure out what our preset PR1 value should be (i.e., how many clock does the timer count before signaling an interrupt?). The answer is simple. To blink at 1 Hz, the timer must interrupt at 2 Hz (half a second on, half a second off, for a total of 1 Hz, but the interrupt occurs ever half a second, or 2Hz).

To get from 16 385 Hz to 2 Hz is a division of 2^13 (2Hz = 16 385Hz / 2^13), or in hex: 0×2000.

Now we have all the calculations we need to start the program. By looking up the timer1 interrupts on the datasheet, I am able to locate which register bits to configure and turn on the timer1 interrupts. I have selected RB9 to be the output to the base of the LED driver. Now, the program:

Example Code


/*
Engscope Tutorial
Timers
April 6
Author: JL
*/

//compiled for a PIC24FJ64GA002, you milage may vary
#include "../h/system.h"

_CONFIG2(0xFAFD);  //use XL external clock, and Fosc/2 (osc/2) at 32.768kHz
_CONFIG1(0x3F7F);  //use in Programming Mode

//prototypes
void TimerInit(void);

int main(void)
{
   OSCCON	=	0x22C0;	 //select Primary Oscillator, External XL
   CLKDIV	=	0x0000;	 //do not divide //Set up I/O Port
   AD1PCFG	=	0xFFFF;	 //set to all digital I/O
   TRISB	=	0xFDFF;	 //configure all PortB as input, RB9 as output
   TimerInit();

   //Main Program Loop, Loop forever
   while(1)
   {
   }

   return(0);
}

//Set up Timer, target 2Hz interrupts
void TimerInit(void)
{
   //set to (2^13), since 32.768kHz / 2^13 = 4, toggles at 2Hz
   PR1 = 0x2000;
   IPC0bits.T1IP = 5;	 //set interrupt priority
   T1CON = 0b1000000000000000;	//turn on the timer
   IFS0bits.T1IF = 0;	 //reset interrupt flag
   IEC0bits.T1IE = 1;	 //turn on the timer1 interrupt
}

//_T1Interrupt() is the T1 interrupt service routine (ISR).
void __attribute__((__interrupt__)) _T1Interrupt(void);
void __attribute__((__interrupt__, auto_psv)) _T1Interrupt(void)
{
   LATBbits.LATB9 = ~LATBbits.LATB9;	//Toggle output to LED
   IFS0bits.T1IF = 0;
}

The main body of the program is relatively simple. I have configured the oscillators to be primary, without PLL, and used OSCON to select these settings.


_CONFIG2(0xFAFD);   //use XL external clock, and Fosc/2 (osc/2) at 32.768kHz
_CONFIG1(0x3F7F);   //use in Programming Mode

//prototypes
void TimerInit(void);

int main(void)
{
   OSCCON	=	0x22C0;	 //select Primary Oscillator, External XL
   CLKDIV	=	0x0000;	 //do not divide

Since this is an interrupt driven program, I don’t need anything in the main loop. The interrupts will signal the controller to execute the ISRs and toggle my LED.

The timer initiation starts by setting the preset PR1 value to the calculated 0×2000. Next I set the timer1 interrupt settings, and turn on the timer to allow the PIC to start receiving interrupts.


//Set up Timer, target 2Hz interrupts
void TimerInit(void)
{
   PR1 = 0x2000;	 //set to (2^13), since 32.768kHz / 2^13 = 2
   IPC0bits.T1IP = 5;	 //set interrupt priority
   T1CON = 0b1000000000000000;	//turn on the timer
   IFS0bits.T1IF = 0;	 //reset interrupt flag
   IEC0bits.T1IE = 1;	 //turn on the timer1 interrupt
}

As you can see here, the interrupt settings for timer1 is just like the settings of the external interrupts of the previous section of the tutorial. Now I can write my ISR for timer1.


void __attribute__((__interrupt__, auto_psv)) _T1Interrupt(void)
{
   LATBbits.LATB9 = ~LATBbits.LATB9;	//Toggle output to LED
   IFS0bits.T1IF = 0;
}

The ISR is as simple as can be. I toggle the input to the base of the open drain LED driver, in this case RB9. Then I turn the interrupt flag back to 0, to reset the interrupt and wait for the next one. If all goes well, my PIC24 will interrupt at 2Hz, and will blink the LED at 1 Hz. Done!

Table of Contents

12 Responses to 08 Timers

  1. PIC_Learner says:

    I’m pretty new at PIC programming and am currently working with a PIC24FJ128GA010. I am trying to light up some LEDs connected to LED drivers but I have no idea how to do so. I am using TI’s TLC5917 drivers if it’s of any help. I was able to program the PIC and light up a LED connected to a resistor and the register pin directly but no such luck when using the LED driver. Any tips or clues on how I should go about this? Thanks for your time.

  2. jliu83 says:

    Well, I just read the datasheet for the driver. I don’t see what’s the problem. If you read and understood the Timer section of the tutorial, this should be a cinch. What seems to be the problem?

    -J

  3. Remco says:

    Could you explain me if this is an error in your code or my wrong vision to this?

    Your code: PR1 = 0×2000; //set to (2^13), since 32.768kHz / 2^13 = 2

    When I write this out I get this:
    0×2000 = 8192
    8192 = 2^13
    but…
    32768 / 8192 = 4
    not 2 or is it ?!

  4. jliu83 says:

    In order to toggle at 2 Hz, you need to interrupt at 4 Hz. For example, if I want to goggle the LED at 1 Hz, I need half a second on, half a second off, which means I need to interrupt at 2 Hz. Make sense?

  5. Remco says:

    Right.. I thought one second on and one second off but as you describe it your way I see it now.
    Your calculation as above was a bit confusing for me.

  6. jliu83 says:

    Noted, I will change the calculations, I should probably say that it equals 4, so to toggle it at 2 Hz.
    -J

  7. Pepe says:

    Again thank you!

  8. kshitij says:

    #include “../h/system.h”
    where do i find this file
    compiler is showing error

  9. jliu83 says:

    Please read the previous section on Interrupts to understand the system.h file.

    -J

  10. Raavindra reddy says:

    nice3 website

  11. qua sar says:

    i want to use my pic24hj chip with all interupts switched off.

    all the examples of using the timers make reference to interupts.

    could you show a little code without interupts involved please. i can get away with just reading
    the timer register value periodically to acheive my goals. thanks

  12. jliu83 says:

    @qua sar. You can definitely get away with just reading the timer register value. Just turn the timer on without turn on the interrupt.

    IEC0bits.T1IE = 1; //turn on the timer1 interrupt
    Turns on the interrupt. Just set it to 0. This should be quite self explanatory (as it is commented in the code).

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>