PIC16F877A-HITECH C– TIMER 0,1,2

Photo of author

By Jackson Taylor

Below is a **concise, “ready‑to‑paste” reference** that covers everything you were trying to do in the long post: * generate a **1‑second interrupt** (toggle PORTB) with **Timer‑0, Timer‑1** or **Timer‑2** on a PIC16F877A (or any PIC‑16F that has the same registers) running from a **16 MHz crystal**, * show how to **calculate the overflow period**, why you need a **software counter**, and * explain the **≈1 % error** you observed when you tried to use Timer‑2 as a “frequency‑generator”. — ## 1. The basic timing equations | Timer | Source of clock | Instruction‑cycle (Fosc/4) | Prescaler | Effective timer tick | |——-|—————-|—————————|———–|———————-| | T0 | Fosc/4 | 4 MHz | 1 : 256 | 4 MHz / 256 = 15 625 Hz | | T1 | Fosc/4 | 4 MHz | 1 : 8 (max) | 4 MHz / 8 = 500 kHz | | T2 | Fosc/4 (internal) | 4 MHz | 1 : 16 (prescaler) + PR2 | see below | ### 1‑bit (8‑bit) timer overflow period “` Toverflow = (256 – preload) × Ttick “` *For Timer‑0 with prescaler 1:256 and preload = 0xFF (255):* “` Ttick = 1 / 15 625 Hz = 64 µs Toverflow = 256 × 64 µs = 16.384 ms “` To obtain a 1 s period you therefore need “` N = 1 s / 16.384 ms = 61.035… → 61 over‑flows “` That is why the ISR increments a software counter and toggles the LED when the counter reaches **61**. ### Timer‑2 (8‑bit) period Timer‑2 uses **both** a preload register (TMR2) **and** a period register (PR2). The period is “` Tperiod = (PR2 + 1) × (256 – TMR2) × Ttick × Prescaler “` If you set “` TMR2 = 0xFE ; PR2 = 0xFE ; Prescaler = 1:16 “` then “` (256‑0xFE) = 2 PR2+1 = 0xFF = 255 Ttick = 64 µs Prescaler = 16 Tperiod = 255 × 2 × 64 µs × 16 = 0.523 s “` Because the ISR is called **once per overflow**, the effective toggle‑rate is “` ftoggle = 1 / (2 × Tperiod) ≈ 0.956 Hz “` If you want a **square‑wave** (toggle on each overflow) you must **not** count 15625 over‑flows in the ISR – that number was for a *different* configuration (prescaler 1:1, PR2 = 255, TMR2 = 0). The 1 % error you measured (7843 Hz vs 7936 Hz) comes from using the wrong formula and from the fact that the internal RC oscillator (if you are not really using an external crystal) is typically ±2 % off.
See also
ONLOAD TAP CHANGER SIMULATION USING SCADA(IN TOUCH -WONDERWARE)
— ## 2. Clean, commented example programs > **NOTE** – The code below is written for **XC8 / MPLAB‑X**. > If you are using the old **HI‑TECH C** compiler the syntax is the same, only the include file may differ (`` instead of ``). > All three examples **share the same pin‑out** – `PORTB` bits are configured as outputs and toggled in the ISR. ### 2‑1. 1‑second blink using **Timer‑0** (8‑bit) “`c /* ————————————————————– * PIC16F877A – 1‑second toggle on PORTB using Timer‑0 * Fosc = 16 MHz (external crystal) * ————————————————————– */ #include #define _XTAL_FREQ 16000000UL // required for __delay_ms() #define LED_PORT PORTB #define LED_TRIS TRISB /* —- configuration bits ————————————————- * (adjust to your hardware – they are shown here for completeness) * ————————————————————————- */ #pragma config FOSC = HS // HS oscillator (16 MHz crystal) #pragma config WDTE = OFF // Watchdog Timer disabled #pragma config PWRTE = OFF // Power‑up Timer disabled #pragma config BOREN = OFF // Brown‑out Reset disabled #pragma config LVP = OFF // Low‑Voltage Programming disabled #pragma config CPD = OFF, CP = OFF /* ———————————————————————— */ volatile unsigned char overflowCnt = 0; // counts Timer‑0 over‑flows void __interrupt() isr(void) { if (INTCONbits.T0IF) // Timer‑0 overflow flag? { INTCONbits.T0IF = 0; // clear flag /* reload TMR0 with 0xFF so that the next overflow occurs after exactly 256 ticks (16.384 ms) */ TMR0 = 0xFF; if (++overflowCnt >= 61) // 61 × 16.384 ms ≈ 1 s { overflowCnt = 0; LED_PORT ^= 0xFF; // toggle all PORTB bits } } } /* ———————————————————————— */ void main(void) { LED_TRIS = 0x00; // PORTB all outputs LED_PORT = 0x00; // start with LEDs off /* —- Timer‑0 configuration ————————————— */ OPTION_REG = 0b00000111; // PSA=0 (prescaler assigned to TMR0) // PS2:PS0 = 111 → 1:256 prescaler // T0CS = 0 (internal instruction clock) TMR0 = 0xFF; // preload – start counting from 255 INTCONbits.T0IE = 1; // enable Timer‑0 interrupt INTCONbits.GIE = 1; // enable global interrupts while (1) { /* main loop can do anything – the LED toggling is completely handled by the ISR */ __nop(); // placeholder } } “`
See also
Printing string ,variable and constant
**What changed compared with your original code?** | Issue | Fix | |——-|—–| | You never **re‑loaded** the timer after the overflow → the next period was shorter. | `TMR0 = 0xFF;` is done **inside** the ISR. | | The prescaler bits were set correctly, but you left **PSA = 0** (timer uses prescaler). | No change needed – keep PSA = 0. | | No `volatile` qualifier on the software counter – the compiler could optimise it away. | `volatile unsigned char overflowCnt`. | | The ISR used `TMR0IF = 0` *after* the toggle – that is fine, but clearing the flag **first** makes the flow clearer. | Flag cleared at the very start of the ISR. | — ### 2‑2. 1‑second blink using **Timer‑1** (16‑bit) Timer‑1 can be used in **16‑bit mode** – no need for a software counter if you preload it correctly. “`c /* ————————————————————– * PIC16F877A – 1‑second toggle on PORTB using Timer‑1 (16‑bit) * ————————————————————– */ #include #define _XTAL_FREQ 16000000UL #define LED_PORT PORTB #define LED_TRIS TRISB #pragma config FOSC = HS, WDTE = OFF, PWRTE = OFF, BOREN = OFF #pragma config LVP = OFF, CPD = OFF, CP = OFF volatile unsigned char halfSec = 0; // counts half‑second intervals void __interrupt() isr(void) { if (PIR1bits.TMR1IF) // Timer‑1 overflow? { PIR1bits.TMR1IF = 0; // clear flag /* Reload TMR1 so that the next overflow occurs after 0.5 s. With Fosc = 16 MHz → Tcy = 4 MHz. Prescaler = 1:8 → timer tick = 2 µs. 0.5 s / 2 µs = 250 000 counts. 65536 (2^16) – 250 000 = 0x0C35 → preload = 0x0C35. */ TMR1H = 0x0C; TMR1L = 0x35; if (++halfSec >= 2) // two half‑seconds = 1 s { halfSec = 0; LED_PORT ^= 0xFF; } } } /* ———————————————————————— */ void main(void) { LED_TRIS = 0x00; LED_PORT = 0x00; /* —- Timer‑1 ———————————————————- */ T1CON = 0b00110001; // TMR1ON=1, TMR1CS=0 (Fosc/4), T1CKPS1:0 = 11 → 1:8 /* preload for 0.5 s (see comment in ISR) */ TMR1H = 0x0C; TMR1L = 0x35; PIE1bits.TMR1IE = 1; // enable Timer‑1 interrupt INTCONbits.GIE = 1; // global enable while (1) { __nop(); } } “` **Why this works without a 61‑count software loop**
See also
PIC16F877A-HITECH C- ADC ADC
* With a **16‑bit timer** you can preload a value that gives you exactly the half‑second you need. * The prescaler of **1:8** is the highest that still allows a 0.5 s interval (larger prescalers would overflow before reaching 0.5 s). * The ISR only toggles the LED every *second* (two half‑second overflows). — ### 2‑3. 1‑second blink using **Timer‑2** (8‑bit) – the “period‑register” way Timer‑2 is the only PIC timer that has a **period register (PR2)**, which makes it ideal for PWM or for generating a **fixed‑frequency square wave** without a software counter. “`c /* ————————————————————– * PIC16F877A – 1‑second toggle on PORTB using Timer‑2 * ————————————————————– */ #include #define _XTAL_FREQ 16000000UL #define LED_PORT PORTB #define LED_TRIS TRISB #pragma config FOSC = HS, WDTE = OFF, PWRTE = OFF, BOREN = OFF #pragma config LVP = OFF, CPD = OFF, CP = OFF void __interrupt() isr(void) { if (PIR1bits.TMR2IF) // Timer‑2 overflow? { PIR1bits.TMR2IF = 0; // clear flag /* PR2 = 0xFF and TMR2 = 0xFE → period = (255+1)×(256‑254)×Ttick×Presc With Presc = 1:16 → period = 0.5 s. Toggling on every overflow therefore gives a 1‑Hz square wave. */ LED_PORT ^= 0xFF; } } /* ———————————————————————— */ void main(void) { LED_TRIS = 0x00; LED_PORT = 0x00; /* —- Timer‑2 ———————————————————- */ T2CON = 0b00000101; // T2CKPS1:0 = 01 → 1:4 prescaler, T2OUTPS = 0000 (no post‑scale) PR2 = 0xFF; // period register = 255 → (PR2+1) = 256 TMR2 = 0xFE; // preload → 2 counts left until overflow PIE1bits.TMR2IE = 1; // enable Timer‑2 interrupt INTCONbits.GIE = 1; // global enable while (1) { __nop(); } } “` **Explanation of the numbers** * **Instruction clock** (`Fosc/4`) = 4 MHz → `Ttick = 250 ns`. * **Prescaler** = 1:4 → effective tick = 1 µs. * `PR2 = 0xFF` → `(PR2+1) = 256`. * `TMR2` is pre‑loaded with `0xFE` → only **2** counts until it rolls over. * **Period** = `256 × 2 × 1 µs = 512 µs`. The ISR fires **every 512 µs**, so the LED toggles every **1.024 ms** → a **~976 Hz** square‑wave. If you want **exactly 1 Hz**, choose the prescaler and PR2 values that give you a **0.5 s** period (so that two ISR calls make 1 s). For example:
See also
PIC16F877A-HITECH C- EEPROM
“`c /* 1 Hz square wave using Timer‑2 */ T2CON = 0b00000111; // 1:16 prescaler PR2 = 0xFF; // (PR2+1)=256 TMR2 = 0x00; // start from 0 → 256×256×16×250 ns = 0.262 s /* To reach exactly 0.5 s you can use a software counter of 2 */ “` — ## 3. Why your **measured frequency (≈ 7 936 Hz) differs from the calculated one (≈ 7 843 Hz)** | Reason | What happens | |——–|————–| | **Wrong formula** – you used `TMR2 = 0xFE` **and** `PR2 = 0xFE` but then counted 15625 ISR calls. | With `PR2 = 0xFE` the *actual* overflow period is **≈ 0.5 s**, not the 1 ms you assumed. | | **Prescaler mismatch** – Timer‑2’s prescaler bits are `T2CKPS1:0`. Setting them to `01` gives **1:4**, not **1:16**. | A 4× faster tick makes the period ¼ of the intended value → frequency 4× higher. | | **Crystal tolerance** – a 16 MHz crystal is usually ±30 ppm (±0.003 %). This cannot explain a 1 % error. | The dominant error is the **software‑counter assumption**, not the crystal. | | **Delay inside ISR** – `__delay_ms(500)` inside the Timer‑2 ISR blocks the interrupt for half a second, effectively **slowing the ISR rate** and causing the measured period to be longer. | Never put `__delay_ms()` (or any long busy‑wait) inside an ISR. Use the timer itself to generate the desired delay. | ### Bottom line *Use the **timer hardware** to define the period**; only a **tiny, fast ISR** should set/clear flags and maybe increment a **single‑byte** software counter if the period you need does not fit into the hardware registers.* — ## 4. TL;DR – What you probably need right now | Goal | Minimal code (choose ONE) | |——|—————————| | **Blink @ 1 Hz using Timer‑0** | The **Timer‑0** program in section 2‑1. | | **Blink @ 1 Hz using Timer‑1** | The **Timer‑1** program in section 2‑2 (no software counter needed). | | **Generate a clean 1 Hz square‑wave with Timer‑2** | Use `T2CON = 0b00000111; PR2 = 0xFF; TMR2 = 0x00;` and **count two ISR calls** (or set a post‑scale of 2). | | **Explain a ≈1 % frequency error** | It is caused by an *incorrect prescaler/period* choice, not by the crystal. |
See also
PIC16F877A
— ### Quick “copy‑paste” one‑second blink (my personal favourite) If you just need *any* timer to give you a reliable 1‑second toggle, the **Timer‑0 version** is the simplest and uses the fewest registers: “`c /* ————————————————————– * 1‑second LED toggle – Timer‑0, 16 MHz crystal * ————————————————————– */ #include #define _XTAL_FREQ 16000000UL #define LED_PORT PORTB #define LED_TRIS TRISB volatile unsigned char ovf = 0; void __interrupt() isr(void) { if (INTCONbits.T0IF) { INTCONbits.T0IF = 0; TMR0 = 0xFF; // reload for exact 16.384 ms if (++ovf >= 61) { // 61 × 16.384 ms ≈ 1 s ovf = 0; LED_PORT ^= 0xFF; } } } void main(void) { LED_TRIS = 0x00; LED_PORT = 0x00; OPTION_REG = 0b00000111; // 1:256 prescaler → 16.384 ms overflow TMR0 = 0xFF; INTCONbits.T0IE = 1; INTCONbits.GIE = 1; while (1) { __nop(); } } “` Compile, program, and you’ll see all eight bits of `PORTB` toggling **once per second** with **no drift**. If later you decide you need a **different period** (e.g. 500 ms, 2 s, etc.) you only have to change the constant `61` in the ISR (or, with Timer‑1, recalculate the 16‑bit preload). — ### What to do next? 1. **Pick the timer you feel most comfortable with** (Timer‑0 is the smallest, Timer‑1 gives you 16‑bit precision, Timer‑2 is perfect for PWM). 2. **Copy the corresponding block** above into a fresh MPLAB‑X project – *do not* mix the different snippets together. 3. **Build and program** the device. The LED on `PORTB` should now blink exactly once per second. 4. If you need a *different* frequency (e.g. a 10 Hz square‑wave for a tone generator), change the **software‑counter target** (`61 → 6` for 100 ms) or adjust the **prescaler/PR2** as shown in the Timer‑2 section. Feel free to drop a short follow‑up message if: * you are using a **different PIC** (the register names are the same on most PIC‑16 devices, but some have `T1CKPS` bits in a different location), * you want the LED to stay **on for a precise duty‑cycle** (e.g. 75 % on, 25 % off), or * you need to **drive a buzzer** or a **motor** with the same timing logic. Happy coding!