PIC16F73 - Timer0 Interrupt

Papabravo

Joined Feb 24, 2006
22,082
From your example it should be obvious that COUNTER_PERIOD is not an integer multiple of TMR_PERIOD. After adding TMR_PERIOD to my_counter and testing the result for >= to COUNNTER_PERIOD some number of times you will find the comparison to be true. By subtracting COUNTER_PERIOD from my_counter the result will be between zero and TMR_PERIOD. This new initial value will correct for the previous counting of too many TMR_PERIOD's.

100,000 / 512 = 195 Remainder 160
So on the first cycle you will count 196 TMR_PERIOD's
512 * 196 = 100,352
On the second cycle you will count 195 TMR_PERIOD's
352 + 195*512 = 100,192
On the third cycle you will count 195 TMR_PERIOD's
192 + 195*512 = 100,032
On the fourth cycle you will count 196 TMR_PERIOD's
32 + 196*512 =100,384
and so on...

Does that help?
 

Thread Starter

Sadlercomfort

Joined Sep 14, 2015
11
Thanks Guys I'm understanding this better now. I'm using 4Mhz so if my TMR0 overflows after 256 and I count 16 overflows I can create a delay of 1 second. Using this I can create different delays.

I'm on mobile atm so can't show you my code but it runs great

I created a counter
Code:
volatile char counter = 0;
And set the timer at the beginning of the program, clearing the timer and counter at the end of the interrupt.

Code:
TMR0=12;
 

jayanthd

Joined Jul 4, 2015
945
Code to blink PORTA LEDs once a second.

C:
#define _XTAL_FREQ 4000000

#include <pic16f73.h>
#include <htc.h>

__CONFIG(FOSC_XT & WDTE_OFF & PWRTE_OFF & CP_ON & BOREN_OFF);

char counter = 0;

//Timer0
//Prescaler 1:256; TMR0 Preload = 61; Actual Interrupt Time : 49.92 ms
 //Place/Copy this part in declaration section
void InitTimer0() {
  OPTION_REG = 0x87;
  TMR0 = 61;
  INTCON = 0xA0;
}

void interrupt isr(void) {
    if(TMR0IF) { 
        TMR0IF = 0;
        TMR0 = 61;
       //Enter your code here
        if(++counter == 20) {
            PORTA = ~PORTA;
        }   
    }
}

void main(void) {

    ADCON1 = 0x07; 

    TRISA = 0x00;
    TRISB = 0x00; 
    TRISC = 0x00;   

    PORTA = 0x00;
    PORTB = 0x80;   

    InitTimer0();

    while(1) {
   
    }
}
 

dannyf

Joined Sep 13, 2015
2,197
Code:
        TMR0 =61;
That basically negated the reason to go down this path.

You are taking the manual reloading approach -> not that it wouldn't work.
 

jayanthd

Joined Jul 4, 2015
945
Code:
        TMR0 =61;
That basically negated the reason to go down this path.

You are taking the manual reloading approach -> not that it wouldn't work.
Sorry, I didn't understand what you are telling. English is not my native language. Please tell in simple words. I have tested this code in hardware and it works fine.
 

jayanthd

Joined Jul 4, 2015
945
Fixed code

C:
void interrupt isr(void) {
   if(TMR0IF) {
        TMR0IF = 0;
        TMR0 = 61;
       //Enter your code here
       if(++counter == 20) {
            PORTA = ~PORTA;
            counter = 0;
       }
   }
}
 

dannyf

Joined Sep 13, 2015
2,197
Code:
TMR0 =61;
The issue was discussed earlier in the thread.

Essentially when TMR0 rolls over to 0, an interrupt is trigger. While the mcu services that interrupt (ie., "interrupt latency"), TMR0 continues to advance. Interrupt latency varies based on a lot of factors but generally anywhere from 10 - 20+ ticks could be passed before the first "user instructions" in the ISR are executed.

So by the time you get to "TMR0 =61;", TMR0 has contained a value (10 - 20+, depending on how the code is structured). And that value is destroyed by your assignment statement. This will cause the interrupt intervals to be longer than expected: in your case, 61 = -195. ie, it should have been 195 ticks long.

That's why you see that in the two proposals posted earlier, rather than "TMR0 = 61;", "TMR0 += 61;" is used, to ***more*** accurately reproduced a 195 tick interval.

Now, even that approach has issues but it would have greatly reduced the errors introduced by a simple assignment statement.

You can clearly see this effect by simulating the code and stepping through the ISR while watch TMR0's value changes.
 

dannyf

Joined Sep 13, 2015
2,197
Here is a simulation that shows the issue.

I reconfigured the tmr0 to run at 1:1 prescaler - one of the posters earlier correctly pointed out that writing to TMR0 will clear the prescaler which is not viewable. So to make the problem more visible, I have reconfigured the timer0 to run at 1:1 so TMR0 will serve as a way to measure the latency.

As you can see, before the first user instruction is executed, TMR0 contains a value of 16 -> under XC free mode.

Using "TMR0 = 61;"
TMR0 value before the first isr instruction: 16 @ XC free, 12 @ XC pro;
TMR0 value before the assignment statement: 21 @ XC free, 15 @ XC pro;
TMR0 value after the assignment statement: 61 @ XC free, 61 @ XC pro;
Total interrupt interval: 220 @ XC free, 214 @ XC pro.
vs. Expected interrupt interval: 195 -> timing error of 27 / 19;

Using "TMR0+= 61;"
TMR0 value before the first isr instruction: 16 @ XC free, 12 @ XC pro;
TMR0 value before the assignment statement: 21 @ XC free, 15 @ XC pro;
TMR0 value after the assignment statement: 85 @ XC free, 77 @ XC pro.
Total interrupt interval: 198 @ XC free, 198 @ XC pro.
vs. Expected interrupt interval: 195 -> timing error of 3 / 3.

The story here really is that if you don't have to write to TMR0, try not to write to TMR0. If you have to write to TMR0, try to preserve its existing value.
 

Attachments

ErnieM

Joined Apr 24, 2011
8,415
Often I put a timer to work to create an accurate 1ms period ISR. It is exactly as accurate as the clock on PIC.

Rather brainlessly simple if you just use timer 2 for this task.
 
Top