Tuesday, July 20, 2010

Timers on PIC16F877

The timer service is one of the basic services offered by a microcontroller. Although CCS compiler provides some simple macros that implement delay routines, in order to determine time elapsed and to maximize use of MCU (see Simple Scheduler section), understanding of the timer functionality is necessary.
To introduce delays in an application, the CCS macro delay_ms and delay_us can be used. These macros provide an ability to block the MCU until the specified delay has elapsed.
For tasks that require the ability to measure time (for instance, how much time has elapsed since a given pin has transitioned to low), it is possible to write code that uses the microcontroller timers.
There are three timers on the PIC16F877: Timer0, Timer1 and Timer2 (there is also a watchdog timer, but it is not typically used to drive application based behavior).
Timer0, Timer1 and Timer2 are essentially counters that increment based on the clock cycle and the timer prescaler. An application can monitor these counters to determine how much time has elapsed. On PIC16F877, Timer0 is an 8-bit counter whereas Timer1 and Timer2 are 16-bit counters. Individual timer counters can be set to an arbitrary value using CCS macro set_timer0, set_timer1 or set_timer2. When the counter reaches its limit (255 for 8-bit and 65535 for 16-bit counters), it wraps around to 0. Interrupts can be generated when wrap around occurs. Timer2 is normally used for PWM or capture and compare functions.
Each timer can be configured with a different source (internal or external) and prescaler. The prescaler determines the timer granularity. A timer with a prescaler of 1 increments its counter every 4 clock cycles (1,000,000 times a second if using a 4 MHz clock). A timer with a prescaler of 8 increments its counter every 32 clock cycles.
Given that Timer1 is set up with a prescaler of 8 on a MCU clocked at 20 MHz. If Timer1 is reset to 0 and the application performs a certain task and if the value of Timer1 read at the end of the task is 6 250, the application can assume that a delay of 10 ms has taken place. To determine the delay, use the following formula:
delay (in ms) = (# ticks) * 4 * prescaler * 1000 / (clock frequency)
or in our example:
delay (in ms) = (# ticks) * 4 * 8 * 1000 / 20 000 000
or
delay (in ms) = (# ticks) / 625
This translates in our example to:
delay (in ms) = 6 250 / 625 = 10 ms
Here is some sample code that shows how to initialize and use the timer to determine the length of a task:
long delay;
/* Set Timer1 prescaler to 8. */
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8);
/* Reset Timer1 to 0. */
set_timer1(0);

/* Perform some task. */
printf("Hello, world!");

/* Calculate delay in ms. */
delay = get_timer1() / 625;

In the above example, it is important that the task performed does not exceed the maximum delay a timer can sustain before wrapping around, otherwise, the calculations may be incorrect.
Here is another code sample that shows how to create a delay of 50 ms before resuming execution (alternative to delay_ms):
/* Set Timer1 prescaler to 8. */
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8);
/* Reset Timer1 to 0. */
set_timer1(0);

for ( ; get_timer1() < 3125; ) ;

/* 50 ms has elapsed. */

No comments:

Post a Comment