Dynamically managing interrupts
An interrupt is an event on which the microcontroller reacts by temporarily interrupt the normal program execution and executes a special piece of code whereafter it resumes normal program execution. Such an event can be the finish of a hardware timer, the change of the voltage an a specific pin of the PIC, ... The specific piece of code is called an Interrupt Service Routine (ISR).
Practically this is what happens. One of the hardware modules of the microcontroller sets the interrupt signal of the CPU high. The CPU reacts to this by first pushing the program counter (the return address) on the stack and then jumps to the code on adress 0x008. On this address the first instruction of the ISR has to be. On the end of the ISR an RETFIE (return from interrupt) is executed. This pops the return address from the stack and loads this address into the program counter and resumes normal execution.
If there can happen different types of interrupts within the same program, then the ISR must first check which interrupt occurred and select the correct piece of code. Each possible source of interrupts has a flag that is put high when the interrupt has occurred and is low in the other case. This flag is a bit in the PIR1 - or the PIR2 - register. By checking these flags the ISR can determine which event caused the call of the ISR.
Below you find an example of how to write an ISR for the C18 compiler. Lines 1 to 7 ensure that the ISR start on the address 0x08 by adding a jump on this address to the actual ISR which is located from line 9 to 20. Line 9 ensures that the function InterruptServiceRoutine is recognized as an ISR by the compiler. This has the important consequence that the function has no normal return, but a return from interrupt (RETFIE). In the body of the function we first check what the source of the interrupt is: TIMER1, TIMER3 or both of them. Depending on the cause of the interrupt, the corresponding code is executed.
#pragma code isr_vector=0x08 void isr_vector() { _asm goto InterruptServiceRoutine _endasm } #pragma code #pragma interrupt InterruptServiceRoutine void InterruptServiceRoutine() { if (PIR1bits.TMR1IF == TRUE) { // Code for servicing TIMER1-interrupt } if (PIR2bits.TMR3IF == TRUE) { // Code for servicing TIMER3-interrupt } // ... }
For less complex programs this is good practice, but for more complex program this approach becomes problematic. For more complex programs it is useful the application is split into clearly separated functionalities each written in a library. This partitioning allows the programmer (or team of programmers), to better manage the program complexity. This makes working on the same project with several people much easier and allows the reuse of code in multiple projects.
The fact that interrupts always are handled in one single interrupt routine, makes it normally impossible to split the interrupts into different libraries. The Dwengo library provides a solution for this problem: dynamically managing interrupts.
The library allows up to 5 (default) functions to be registered as ISR by calling the function registerISR. On a later moment the functions can also be unregistered by calling the function deregisterISR. Each time as an interrupt occurs, all the ISRs are executed. For the same example as above, the code with the dwengoInterrupt library is much easier:
#include <dwengoInterrupt.h> void timer1ISR() { if (PIR1bits.TMR1IF == TRUE) { // Code for servicing TIMER1 interrupt } } void timer3ISR() { if (PIR2bits.TMR3IF == TRUE) { // Code for servicing TIMER3 interrupt } } void main() { initInterrupt(); registerISR(timer1ISR); registerISR(timer3ISR); // ... // Every time an interrupt occurs the functions timer1ISR // and timer3ISR are called. // ... deregisterISR(timer1ISR); // ... // Every time an interrupt occurs only the function // timer3ISR is called. // ... }
- Type:

Your shopping cart