Why the place where I clear TMR0IF changes the behavior of the program?

The basic problem is that your interrupt code is taking longer to execute than the time between interrupts. This probably due to one of two problems:

  1. The interrupt period isn't what you think it is. The interrupt condition is actually being triggered more often than intended.

  2. You are taking too many cycles in the interrupt routine.

The reason it appears to work when you clear the interrupt condition at the end of the ISR is that there is then some time for the foreground code to run before the next interrupt. However, that is NOT a solution to the problem. When you move the clearing of the interrupt condition to the start of the ISR where it should be, the ISR takes so long that the next interrupt is taken immediately when the ISR ends. No, this does not overflow the stack as another answer claims. However, it does lock out the foreground code from getting cycles.

Always clear the interrupt condition right at the beginning of the interrupt routine. If using timer 0 for the interrupt period DO NOT set TMR0 to a fixed value each interrupt. Instead, add the appropriate quantity each interrupt. That way you don't lose time based on interrupt jitter latency.

Also, don't use the prescaler when adding a offset into the timer. If using timer 0, either set add a offset each interrupt to get a specific period with the prescaler 1, or use only the resulting free-running period with non-unity prescaler.

If you want more arbitrary interrupt periods, use timer 2. That's what it's for.

I notice that you wrote your interrupt routine in C. I've seen some pretty horrible code bloat due to the C compiler not knowing what really needed to be saved, and therefore saving lots of stuff. Personally, I write PIC 16 interrupt routines in assembler. It also makes it easier to figure out what is going on in cases like this, because there is no compiler between you and the hardware.

I just scanned your ISR code. You are doing divides in the ISR! No wonder it's taking longer than intended.

This is a great example of why on small microcontrollers like this, compilers should never be used as a substitute for understanding the machine at the instruction level. The compiler must only be a shortcut for creating a bunch of code for you, but you still need to have a basic understanding of what that code is and what the machine has to do to implement what you ask.

Put another way, you have to architect the code while thinking of the machine's raw capabilities. Once you do that, using a compiler to generate some of the actual code for you can be a legitimate shortcut. Even then, the ISR is the one routine you want to most consider writing in assembler.

Go write this interrupt routine in assembler. That's not because it necessarily needs to be in assembler, but because you need to learn the machine. You have no business being here without understanding the instruction set and the other low level capabilities of the hardware. You never would have tried to do division in a ISR if you really understood the instruction set.


I don't think you should ever put TMR0IF=0 (clear the interrupt flag) at the beginning of your ISR.

I think what is happening is your ISR routine is taking more than 800 us or your TMR0 is triggering faster than you think. By putting TMR0IF=0 in front, you are clearing the interrupt and allowing for another TMR0 interrupt to interrupt the interrupt. The MCU pushes the present info into the stack, and calls the same interrupt again. Eventually your stack will overflow and your system will crash.

  1. Keep TMR0IF=0 as the last instruction
  2. Time your ISR to see if it is shorter than 800 us.
  3. Verify if TMR0 is running at the rate you think.

I time my routines by driving a GPIO high at the beginning and driving same GPIO low before exiting the routine. Use a o'scope on that GPIO and you can verify a) if your ISR is really less than 800 us, b) if your TMR0 is really calling at the rate you think.

The pulse width will tell you how long your ISR is taking. The period will tell you how often your ISR is being called. If the period of the o'scope signal is erratic, then your ISR is taking longer than TMR0 timer periods (i.e. is skipping TMR0 periods)