µC/OS-III requires that interrupt service routines be written in assembly language. However, if a C compiler supports in-line assembly language, the ISR code can be placed directly into a C source file. The pseudo-code for a typical ISR when using µC/OS-III is shown in Listing 9-1.
Anchor | ||||
---|---|---|---|---|
|
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
MyISR: (1)
Disable all kernel aware interrupts; (2)
Save the CPU registers; (3)
OSIntNestingCtr++; (4)
if (OSIntNestingCtr == 1) { (5)
OSTCBCurPtr->StkPtr = Current task's CPU stack pointer register value;
}
Clear interrupting device; (6)
Re-enable kernel aware interrupts (optional); (7)
Call user ISR; (8)
OSIntExit(); (9)
Restore the CPU registers; (10)
Return from interrupt; (11) |
Panel | ||
---|---|---|
| ||
(1) As mentioned above, an ISR is typically written in assembly language. (2) It is important that all ‘kernel aware’ interrupts are disabled before going any further. Some processors have interrupts disabled whenever an interrupt handler starts. Others require the user to explicitly disable interrupts as shown here. This step may be tricky if a processor supports different interrupt priority levels. However, there is always a way to solve the problem. (3) The first thing the interrupt handler must do is save the context of the CPU onto the interrupted task’s stack. On some processors, this occurs automatically. However, on most processors it is important to know how to save the CPU registers onto the task’s stack. You should save the full “context” of the CPU, which may also include Floating-Point Unit (FPU) registers if the CPU used is equipped with an FPU. Certain CPUs also automatically switch to a special stack just to process interrupts (i.e., an interrupt stack). This is generally beneficial as it avoids using up valuable task stack space. However, for µC/OS-III, the context of the interrupted task needs to be saved onto that task’s stack. If the processor does not have a dedicated stack pointer to handle ISRs then it is possible to implement one in software. Specifically, upon entering the ISR, simply save the current task stack, switch to a dedicated ISR stack, and when done with the ISR switch back to the task stack. Of course, this means that there is additional code to write, however the benefits are enormous since it is not necessary to allocate extra space on the task stacks to accommodate for worst case interrupt stack usage including interrupt nesting. (4) Next, either call (5) If this is the first nested interrupt, you need to save the current value of the stack pointer of the interrupted task into its (6) At this point, you need to clear the interrupting device so that it does not generate the same interrupt. However, most people defer the clearing of the source and prefer to perform the action within the user ISR handler in “C.” (7) At this point, it is safe to re-enable kernel aware interrupts if you want to support nested interrupts. This step is optional. (8) At this point, further processing can be deferred to a C function called from assembly language. This is especially useful if there is a large amount of processing to do in the ISR handler. However, as a general rule, keep the ISRs as short as possible. In fact, it is best to simply signal or send a message to a task and let the task handle the details of servicing the interrupting device. The ISR must call one of the following functions: (9) When the ISR completes, you must call (10) If the ISR signaled or sent a message to a lower-priority task than the interrupted task, (11) The ISR performs a return from interrupts and so resumes the interrupted task. NOTE: From this point on, (1) to (6) will be referred to as the ISR Prologue and (9) to (11) as the ISR Epilogue. |