Home Tutorials Handling Overflows in Timers and Counters for a PIC Microcontroller

Handling Overflows in Timers and Counters for a PIC Microcontroller

by shedboy71

Timers are crucial in PIC microcontroller programming for delay generation, frequency measurement, and periodic event triggering. However, because timers are limited in size (8-bit, 16-bit), they overflow when they exceed their maximum value.

Handling timer overflows correctly is essential to ensure accurate timing and event scheduling.

This tutorial covers:

  1. Understanding Timer Overflow
  2. Configuring Timer0 for Overflow Interrupts
  3. Handling Timer1 Overflow (16-bit Timer)
  4. Calculating Timer Overflow Period
  5. Using Timer Interrupt Service Routines (ISRs)
  6. Practical Examples in Embedded C (MPLAB XC8)

Understanding Timer Overflow in PIC

Timers in PIC microcontrollers increment every instruction cycle and overflow when reaching their maximum value.

Timer Overflow Behavior

Timer Type Bit-Size Max Value Overflow After
Timer0 8-bit 255 Overflows at 256 counts
Timer1 16-bit 65,535 Overflows at 65,536 counts
Timer2 8-bit 255 Overflows at 256 counts (used for PWM)

Timer Overflow Calculation

The time taken for an overflow is:

Toverflow=(MaxTimerValue+1−InitialValue)×PrescalerFosc/4T_{overflow} = \frac{(Max Timer Value + 1 – Initial Value) \times Prescaler}{F_{osc}/4}

Where:

  • Max Timer Value = 255 for Timer0, 65535 for Timer1
  • Initial Value = Preloaded Timer Value
  • Prescaler = Divides the timer clock
  • Fosc = Oscillator Frequency

Example:
For PIC16F877A, Timer0 with 8MHz crystal and a prescaler of 256, the overflow time is:

Toverflow=(256)×2568MHz/4=32.768msT_{overflow} = \frac{(256) \times 256}{8MHz/4} = 32.768ms

This means Timer0 overflows every 32.768ms.

Configuring Timer0 Overflow Interrupt (8-bit Timer)

Timer0 is an 8-bit timer, meaning it overflows after 256 counts.

Steps to Configure Timer0 for Overflow

  1. Set up Timer0 (TMR0).
  2. Enable Timer0 interrupt.
  3. Write an ISR to handle overflow events.

Example: Using Timer0 Overflow to Blink LED Every Second

#include <xc.h>

// CONFIGURATION BITS
#pragma config FOSC = XT, WDTE = OFF, PWRTE = OFF, BOREN = ON, LVP = OFF

#define _XTAL_FREQ 8000000  // Define CPU frequency (8MHz)
#define TIMER_PRELOAD 60     // Preload for 1s delay

void __interrupt() timer0_isr(void) {
    if (T0IF) {  // Check if Timer0 overflowed
        TMR0 = TIMER_PRELOAD;  // Reload timer
        T0IF = 0;  // Clear overflow flag
        PORTB ^= (1 << 0);  // Toggle LED on RB0
    }
}

void timer0_init() {
    OPTION_REG = 0x07;  // Prescaler 1:256
    TMR0 = TIMER_PRELOAD;  // Load initial value
    T0IE = 1;  // Enable Timer0 interrupt
    GIE = 1;   // Enable global interrupts
}

void main() {
    TRISB0 = 0;  // Set RB0 as output
    timer0_init();  // Initialize Timer0

    while (1) {
        // Main loop does nothing, Timer ISR handles LED blinking
    }
}

How It Works

  • Timer0 overflows every 32.768ms.
  • Interrupt Service Routine (ISR) reloads the timer and toggles the LED.

Handling Timer1 Overflow (16-bit Timer)

Timer1 is 16-bit, allowing a wider range of timing delays.

Example: Using Timer1 Overflow for 1-Second Delay

#include <xc.h>

// CONFIGURATION BITS
#pragma config FOSC = XT, WDTE = OFF, PWRTE = OFF, BOREN = ON, LVP = OFF

#define _XTAL_FREQ 8000000  // 8MHz Oscillator
#define TIMER1_PRELOAD 3036  // Preload for 1-second delay

void __interrupt() timer1_isr(void) {
    if (TMR1IF) {  // Check Timer1 overflow
        TMR1 = TIMER1_PRELOAD;  // Reload Timer1
        TMR1IF = 0;  // Clear overflow flag
        PORTB ^= (1 << 0);  // Toggle LED
    }
}

void timer1_init() {
    T1CON = 0x31;  // Enable Timer1, prescaler 1:8
    TMR1 = TIMER1_PRELOAD;  // Preload Timer1
    TMR1IE = 1;  // Enable Timer1 overflow interrupt
    GIE = 1;  // Enable global interrupts
}

void main() {
    TRISB0 = 0;  // Set RB0 as output
    timer1_init();  // Initialize Timer1

    while (1);
}

How It Works

  • Timer1 counts up to 65535.
  • It overflows every second, toggling the LED.

Reloading Timer to Adjust Overflow Time

Since timers count continuously, we can preload the timer register to reduce overflow time.

Example: Adjusting Timer0 for 10ms Overflow

TMR0=256−10ms×8MHz4×256TMR0 = 256 – \frac{10ms \times 8MHz}{4 \times 256}

Setting TMR0 = 60 ensures 10ms timer overflow.

#define TIMER0_PRELOAD 60

void __interrupt() timer0_isr(void) {
    if (T0IF) {
        TMR0 = TIMER0_PRELOAD;  // Reload Timer0
        T0IF = 0;
    }
}

Practical Applications of Timer Overflow Handling

  1. Generating Delays Without Blocking Code
    • Using ISR-based timers allows continuous execution without using __delay_ms().
  2. Measuring Time Intervals
    • Use Timer1 overflow to measure event duration.
  3. Creating Real-Time Clock (RTC) Features
    • Timer overflow can be used for seconds, minutes, and hours counting.

Best Practices

Always reload timer in ISR to prevent timing drift.
Use prescalers to extend overflow time and reduce CPU usage.
Keep ISRs short to avoid missing other interrupts.
Disable interrupts when modifying timer registers to avoid glitches.
Test with simulation tools like MPLAB X and Proteus.

Summary Table

Timer Size Max Value Common Uses
Timer0 8-bit 255 Short delays, LED blinking
Timer1 16-bit 65535 Long delays, frequency measurement
Timer2 8-bit 255 PWM generation

Handling timer overflows is essential for accurate timing and event scheduling in PIC microcontrollers.

By correctly configuring Timer0 and Timer1, and using interrupts, you can achieve precise time delays without blocking the main code.

Share

You may also like

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More