A PIC timer is a dedicated hardware peripheral inside a Microchip PIC microcontroller that independently counts clock pulses or external events to generate precise delays, measure pulse widths, or trigger interrupts without tying up the main CPU. In a real circuit, leveraging a hardware timer changes your architecture from a blocking, single-task software loop to a concurrent, event-driven system where the MCU can sleep or process sensor data while timing happens in the background. Beginners commonly confuse hardware timers with software delay loops (like __delay_ms()), the Watchdog Timer (WDT) used for brownout reset recovery, or external Real-Time Clock (RTC) I2C modules.
Think of a software delay loop like staring at the microwave countdown until your food is done; a hardware timer is like setting a kitchen alarm and walking away to chop vegetables until it rings. This guide cuts through the datasheet noise and gives you the exact math, register configurations, and decision paths needed to deploy PIC timers effectively.
The Hardware Reality: What a PIC Timer Actually Changes
When you rely on software delays, your instruction cycle is trapped in a while loop, burning milliamps and ignoring external interrupts. A hardware timer shifts this burden to the peripheral bus. On modern PIC16F1xxxx and PIC18Fxxxx families, timers operate synchronously with the system clock or asynchronously via an external crystal, allowing the core to enter Sleep mode while the timer continues counting.
Furthermore, hardware timers provide deterministic jitter. A software loop's execution time changes if a higher-priority interrupt fires mid-loop. A hardware timer's overflow or compare-match event is triggered at the exact clock edge, ensuring microsecond-level precision required for motor commutation and digital power supplies.
Worked Numeric Example: Dialing in a 1-Second Interrupt
Let's configure a precise 1.000-second interrupt using Timer0 on a PIC16F18446 running at its maximum internal oscillator frequency of 32 MHz. Timer0 on this chip can operate in 8-bit or 16-bit mode; we will use 16-bit mode to maximize our resolution.
The Math
- Instruction Clock (Fcy): The PIC architecture executes one instruction every 4 oscillator cycles. Fcy = 32 MHz / 4 = 8 MHz.
- Base Tick Time: 1 / 8 MHz = 125 ns per count.
- Prescaler Selection: In 16-bit mode, the timer rolls over at 65,536. Without a prescaler, max delay is 65,536 × 125 ns = 8.192 ms. We need a prescaler. Setting the prescaler to 1:128 gives an effective tick time of 125 ns × 128 = 16 µs.
- Total Counts Needed: 1.0 second / 16 µs = 62,500 counts.
- Preload Value: Because Timer0 counts up to 65,536 (0xFFFF) to trigger the interrupt, we must preload it with the difference: 65,536 - 62,500 = 3,036. In hexadecimal, 3,036 is 0x0BDC.
Register Configuration (XC8 Syntax)
To implement this, we configure the Timer0 control registers. Note that on the PIC16F18446, Timer0 has a dedicated prescaler separate from the Watchdog Timer, eliminating a common legacy PIC trap.
// T0CON0: Enable Timer0, 16-bit mode, Postscaler 1:1
T0CON0 = 0b10010000;
// T0CON1: Clock source Fosc/4, Prescaler 1:128
T0CON1 = 0b01010111;
// Preload the 16-bit value (Must write Low byte first to latch High byte)
TMR0L = 0xDC;
TMR0H = 0x0B;
// Clear interrupt flag and enable Timer0 interrupt
PIR0bits.TMR0IF = 0;
PIE0bits.TMR0IE = 1;
INTCONbits.PEIE = 1;
INTCONbits.GIE = 1;
TMR0L) before the High byte (TMR0H) when preloading a 16-bit timer. The PIC hardware uses a buffer register; writing the low byte latches the high byte into the actual timer register simultaneously. Reversing this order will result in erratic timing.
Where You Meet PIC Timers in Practice
You will rarely use a PIC timer just to blink an LED. In professional and advanced hobbyist designs, timers are the backbone of real-time control systems.
- Ultrasonic Distance Measurement: Using Timer1's Input Capture module to measure the exact pulse width of the Echo pin on an HC-SR04 sensor. The hardware timestamps the rising and falling edges, eliminating the 10-20µs jitter inherent in software polling.
- Brushless DC (BLDC) Motor Commutation: Using Timer2 to generate hardware PWM signals for the gate drivers. The PR2 (Period Register 2) dictates the exact 20kHz switching frequency, while the CCPR registers handle the duty cycle, entirely independent of CPU load.
- RTOS System Tick: Configuring Timer0 to overflow every 1ms to provide the heartbeat for a Real-Time Operating System (like FreeRTOS), enabling task scheduling and software watchdogs.
- Asynchronous Data Logging: Using Timer1 with a 32.768 kHz watch crystal connected to the T1OSI/T1OSO pins to maintain accurate calendar time while the main 32MHz oscillator is powered down to save energy.
Decision Tree: Picking the Right Timer Peripheral
Modern PIC microcontrollers feature multiple timers (Timer0 through Timer6 on some variants). Choosing the wrong one leads to convoluted workarounds. Use this decision matrix to select the correct peripheral for your task.
| Application Requirement | Recommended Timer | Key Hardware Feature Utilized |
|---|---|---|
| System tick, RTOS heartbeat, general-purpose periodic interrupt | Timer0 | 16-bit mode, flexible prescaler, simple overflow interrupt. |
| Hardware PWM generation for motors, LEDs, or buck converters | Timer2 | 8-bit with PR2 period register, auto-reset, tied to CCP modules. |
| Measuring external pulse widths (Input Capture) or precise 16-bit delays | Timer1 | 16-bit, asynchronous secondary oscillator support, Input Capture gate. |
| High-resolution PWM or multiple independent timebases | Timer4/6 | Similar to Timer2 but often includes enhanced PWM (EPWM) dead-band control. |
The Default Recommendation
If you are starting a new general-purpose project on the PIC16F1xxxx family and need to establish a baseline architecture: Default to Timer0 (16-bit mode) for your 1ms RTOS system tick, and reserve Timer2 exclusively for your first hardware PWM output. This separation prevents PWM frequency adjustments from accidentally destroying your RTOS timing, a common mistake when beginners try to multipurpose a single timer for both periodic interrupts and compare-match outputs.
Common Pitfalls and Register Gotchas
Even with the math dialed in, silicon-level quirks can derail your timing. Watch out for these specific failure modes:
1. The 8-bit vs 16-bit Read Trap
When reading a 16-bit timer value (like TMR0) while it is actively running, you must read the Low byte first. Reading the Low byte latches the current High byte into a temporary buffer. If you read the High byte first, you risk reading a mismatched pair (e.g., the high byte of 0x00FF and the low byte of 0x1000, resulting in a false reading of 0x0000).
2. Interrupt Latency and Jitter
When a timer overflows, it sets an interrupt flag. The PIC core takes a minimum of 3 to 4 instruction cycles to jump to the Interrupt Service Routine (ISR). If you are using software to reload the timer value inside the ISR (rather than using an auto-reload register like PR2), those 4 cycles are lost. For high-precision applications, always account for ISR latency by adding the current timer value to your preload value, rather than blindly overwriting the timer with a static constant.
3. Prescaler Assignment on Legacy Devices
If you are maintaining older code on legacy PIC16F8x or PIC16F6x chips, Timer0 and the Watchdog Timer share the same prescaler. Assigning the prescaler to Timer0 disables the WDT prescaler, potentially causing unexpected MCU resets if your WDT timeout drops below your main loop execution time. Modern PIC16F1xxx and PIC18F families have separated these prescalers, but always verify the block diagram in the specific device datasheet before porting code.






