INTRO
The whole point of a timer with overflow interrupt is that the interrupt will trigger on a precisely timed interval - so long as the code executed in the interrupt doesn't take longer than the timer interval.
THE PROBLEM
On a PIC16LF1823, I have run into the following behavior and I don't understand it. In short, one of my routines was being called from within the interrupt and was causing the interrupt to delay firing the next time. Its as though the routine somehow causes Timer0 to pause while its running. I've reduced the problem code as much as I can.
STARTING WITH WHAT WORKS
First, the basic code with no frills sets the processor to 2 MHz, starts Timer0 with no pre-scaler, and in the interrupt routine the test pin (C3) is toggled to verify everything is working. (Incidentally, I'm using mikroC). This code works as expected:
void main() {
//2MHz
SPLLEN_bit = 0;
SCS1_bit = 1;
SCS0_bit = 0;
IRCF3_bit = 1;
IRCF2_bit = 1;
IRCF1_bit = 0;
IRCF0_bit = 1;
//Oscilloscope probe on C3
ANSC3_bit = 0;
TRISC3_bit = 0;
LATC3_bit = 1;
//Start Timer0
TMR0CS_bit = 0; //Internal clock
PSA_bit = 1; //No prescaler assigned to Timer0
//Enable timer interrups
TMR0IE_bit = 1;
//Enable general interupt
PEIE_bit = 0;
GIE_bit = 1;
LATC0_bit = ~LATC0_bit;
}
void interrupt(void){
if (TMR0IF_bit) {
//The interrupt fires every 132uS
TMR0 = 255 - 121;
TMR0IF_bit = 0;
LATC3_bit = ~LATC3_bit;
//Delay_us(100); //<<<<Uncommenting this widens the pulse, but the pulses are still 132uS apart
LATC3_bit = ~LATC3_bit;
}
}
I have verified this works. Pin C3 toggles once every 132 µs with or without the delay.
HOW TO BREAK IT
However, in the following code, the interrupt now fires every 136 µs, instead of 132 µs. The only difference is the addition of two function (routine1 and routine2), and the declaration of an unsigned short. As I'm asking this question, just removing the initialized value ('=0') fixes the problem.
void main() {
//2MHz
SPLLEN_bit = 0;
SCS1_bit = 1;
SCS0_bit = 0;
IRCF3_bit = 1;
IRCF2_bit = 1;
IRCF1_bit = 0;
IRCF0_bit = 1;
//Oscilloscope probe on C3
ANSC3_bit = 0;
TRISC3_bit = 0;
LATC3_bit = 1;
//Start Timer0
TMR0CS_bit = 0; //Internal clock
PSA_bit = 1; //No prescaler assigned to Timer0
//Enable timer interrups
TMR0IE_bit = 1;
//Enable general interupt
PEIE_bit = 0;
GIE_bit = 1;
LATC0_bit = ~LATC0_bit;
}
unsigned short mode=0; //<<<<<<<<<<Removing the initiator ('=0') fixes the problem!!!
void routine1(void){
unsigned short result;
mode = 0;
switch(mode) {
case 99:
//result = someFunction();
if (result == 0xFF) {
//Do something
}
break;
}
}
void routine2(void){
unsigned int result;
if (result == 0xFF) {
//Do something
}
}
void interrupt(void){
int tmp;
if (TMR0IF_bit) {
//The interrupt fires every 132uS
TMR0 = 255 - 121;
TMR0IF_bit = 0;
LATC3_bit = ~LATC3_bit;
//Delay_us(100);
LATC3_bit = ~LATC3_bit;
routine2();
}
}
Can anyone explain this?
EDIT1:
CORRECTION, the clock is set to 4MHz, so the clock is running at 1uS (1 / instruction clock). The code comment above is wrong.
Bruce's and m.Alin's comment about the magic number got me to thinking. I should be able to hard code 132. And then it occurred to me in the original code:
void interrupt(void){
if (TMR0IF_bit) {
TMR0 = 255 - 121; //Produces 132uS, but proves unpredictable
TMR0IF_bit = 0;
LATC3_bit = ~LATC3_bit;
LATC3_bit = ~LATC3_bit;
}
}
the line:
TMR0 = 255 - 121;
is an attempt to get the timing right, having taken into account anything that happens after the interrupt fires but before I get a chance to reset the timer- thus the magic fudge factor of 11. In other words, I'm trying to predict what the value of the timer is at the time I preset it.
The fact is I know the value- its held in TMR0. So I tried the following line next:
TMR0 = TMR0 - 132; //Predictable but off by 3uS
This produced 135uS pulses, because of the if statement, and the subtraction. So, I subtracted 3 from 132:
TMR0 = TMR0 - 132 - 3; //This produces 142uS pulses (132uS + 10uS for the extra operation)
But of course, that didn't quite work because while reducing the time by 3, I also added a couple of operations which took even more time. So I tried the following...
TMR0 = TMR0 - 129; //This produces 132uS pulses (132 - 3)
This seems to have fixed the problem. This must mean the interrupt isn't firing in exactly the same way every time. Something must be holding the interrupt off at times. So even though this is a fix, it leaves me not know exactly how the timer interrupt mechanism works.
Any ideas?
while(1)
at the end of your main function. It's likely your code is simply resetting the uC every time it hits the end of that function. Be careful about the watchdog too. \$\endgroup\$