All Interrupts Vector to a Common Location
Even though an interrupt controller is present in most designs, some CPUs still vector to a common interrupt handler, and the ISR queries the interrupt controller to determine the source of the interrupt. At first glance, this might seem silly since most interrupt controllers are able to force the CPU to jump directly to the proper interrupt handler. It turns out, however, that with µC/OS-III, it’s easy to have the interrupt controller vector to a single ISR handler. Listing 9-4 describes the sequence of events to be performed when the interrupt controller forces the CPU to vector to a single location.
An interrupt occurs; (1) The CPU vectors to a common location; (2) The ISR code performs the "ISR prologue" (3) The C handler performs the following: (4) while (there are still interrupts to process) { (5) Get vector address from interrupt controller; Call interrupt handler; } The "ISR epilogue" is executed; (6)
(1) An interrupt occurs from any device. The interrupt controller activates the interrupt pin on the CPU. If there are other interrupts that occur after the first one, the interrupt controller will latch them and properly prioritize the interrupts.
(2) The CPU vectors to a single interrupt handler address. In other words, all interrupts are to be handled by this one interrupt handler.
(3) The ISR executes the “ISR prologue” code needed by µC/OS-III. as previously described. This ensures that all ISRs will be able to make µC/OS-III “post” calls.
(4) You call a µC/OS-III C handler, which will continue processing the ISR. This makes the code easier to write (and read). Notice that interrupts are not re-enabled.
(5) The µC/OS-III C handler then interrogates the interrupt controller and asks it: “Who caused the interrupt?” The interrupt controller will either respond with a number (0
to N-1
) or with the address of the interrupt handler for the highest priority interrupting device. Of course, the µC/OS-III C handler will know how to handle the specific interrupt controller since the C handler is written specifically for that controller.
If the interrupt controller provides a number between 0 and N-1, the C handler simply uses this number as an index into a table (in ROM or RAM) containing the address of the interrupt service routine associated with the interrupting device. A RAM table is handy to change interrupt handlers at run-time. For many embedded systems, however, the table may also reside in ROM.
If the interrupt controller responds with the address of the interrupt service routine, the C handler only needs to call this function.
In both of the above cases, all interrupt handlers need to be declared as follows:
void MyISRHandler (void);
There is one such handler for each possible interrupt source (obviously, each having a unique name).
The “while
” loop terminates when there are no other interrupting devices to service.
(6) The µC/OS-III “ISR epilogue” is executed to see if it is necessary to return to the interrupted task, or switch to a more important one.
A couple of interesting points to note:
- If another device caused an interrupt before the C handler had a chance to query the interrupt controller, most likely the interrupt controller will capture that interrupt. In fact, if that second device happens to be a higher-priority interrupting device, it will most likely be serviced first, as the interrupt controller will prioritize the interrupts.
- The loop will not terminate until all pending interrupts are serviced. This is similar to allowing nested interrupts, but better, since it is not necessary to redo the ISR prologue and epilogue.
The disadvantage of this method is that a high priority interrupt that occurs after the servicing of another interrupt that has already started must wait for that interrupt to complete before it will be serviced. So, the latency of any interrupt, regardless of priority, can be as long as it takes to process the longest interrupt. One more reason to keep your ISRs short.