Interrupts
The PIC allows interrupts to occur on the basis of a lot of different triggering events. I've only tried out a few of them.
The most useful is a timed interrupt which can be used as a servo routine to control motors and read sensors on a regular schedule, independent of the logic of your program.
Any of the four timers can be used to create regular interrupts, but we'll use Timer 2 which is the most flexible. We'll set up Timer 2 to overflow/restart at 16KHz (which is used for PWM timing) and to call an interrupt every 16th overflow, so that we have repeating interrupts at 1mS intervals:
setup_timer_2(T2_DIV_BY_4, 78, 16);
To enable interrupts we need these two statements:
enable_interrupts(INT_TIMER2); enable_interrupts(GLOBAL);
Then, at 1mS intervals, the interrupt service routine (ISR) labeled #INT_TIMER2 will be invoked. As shown in the sample code all that happens in the interrupt service routine is that pin D7 is flashed high for 10uS (to watch it on a scope) and the 32-bit integer msecs is incremented to keep track of time.
We could do a lot more in the ISR, but it's a good idea to keep it as brief as possible, especially for a rapidly repeating ISR like this one. Keep in mind that an interrupt may occur between any two instructions in your main program. So, a variable like msecs that is written by the ISR can change unexpectedly.
You can also trigger an interrupt on a high-to-low or low-to-high transition of a digital input pin (INT0, INT1, or INT2). See InterruptExternal.c
WARNING:
USING THE SAME VARIABLE IN BOTH THE INTERRUPT AND THE MAIN CODE WILL GENERATE ELUSIVE BUGS
If a variable, X, is being updated in your interrupt routine, you cannot reliably use that variable in your main code. Not even for seemingly benign operations such as conditionals (i.e. if (X<3){...} ). You can work around this problem by disabling interrupts before referencing the variable and then re-enabling them afterwards.
//INTERRUPT ROUTINE #INT_TIMER2 void Timer2isr() { u16_X += 3; //add 3 to X every time the interrupt is tripped ... } //MAIN ROUTINE void main(void) { while(TRUE){ disable_interrupts(GLOBAL); u16_X_MAIN = u16_X; //access the interrupt variable only when interrupts are disabled enable_interrupts(GLOBAL); ... if (u16_X_MAIN>20) //the copy of the variable for the main code can be accessed at any time {u8_y = 1;} end ... } }
One disadvantage is that sometimes an interrupt will be missed because the timer trips while interrupts are disabled. The consequences of this behavior depends on your application.