Handling Interrupts in Kernel-Based Applications (Part 1)

Part 1: Basic ISR Ingredients

Application developers are not always responsible for the kernel-specific code needed to properly handle interrupts under µC/OS-II and µC/OS-III.

Real-time kernels can be viewed as task managers – software modules that allow application developers to write their code as a collection of different tasks. Most kernel-based applications, though, do not consist of tasks alone. Oftentimes, developers of kernel-based applications mix tasks with interrupt service routines, or ISRs, to meet their systems' requirements.

The proper format for an ISR within a kernel-based application varies somewhat according to the kernel being used. Developers who consult the books describing Micrium's µC/OS-II and µC/OS-III kernels learn that ISRs under these kernels should at least roughly resemble the example shown below. The function called near the midpoint of this routine, App_ISR(), represents the application's portion of the ISR, while the rest of the example's pseudo-code can be considered kernel-related.

ExampleISR:
    Save CPU registers;
    OSIntEnter();
    App_ISR();
    OSIntExit();
    Restore CPU registers;
    Return

Many of the operations depicted in the ISR are there to facilitate preemption – the mechanism by which a relatively high-priority task takes control of the CPU from a relatively low-priority task. The pseudo-code that saves registers at the start of the routine, for example, helps to ensure that, even if the actions of App_ISR() make a new task ready to run, the task that was running prior to interrupt handling will eventually be able to resume its execution. Following the register-saving code, a call to OSIntEnter() increments a counter to notify the kernel that an interrupt is being processed, and, later, a similar call, to OSIntExit(), decrements the counter. If the counter is decremented to zero (meaning that there are no additional levels of nested interrupts to handle), OSIntExit() runs the kernel's scheduler to determine whether the ISR should return to the task that was originally interrupted, or, as a result of activity in App_ISR(), cause the interrupted task to be preempted.

Although this example pseudo-code function could be considered a "typical" ISR under µC/OS-II or µC/OS-III, it is not always the case that application developers have to implement each of the operations that this function performs. The interrupt controllers on certain microcontrollers allow Micrium to easily incorporate code for saving registers, restoring registers, and calling OSIntEnter() and OSIntExit() into BSP or startup functions, leaving only App_ISR() as the responsibility of application developers. Many of ARM's "classic" processors, like those in the ARM7 and ARM9 families, for example, employ a single vector for interrupt requests (IRQs) that facilitates this approach.

Task list with Interrupt

Developers of kernel-based applications often mix tasks with ISRs

To determine what, exactly, you'll need to write into your own ISRs, you can consult Micrium's ample collection of documentation, including the µC/OS-III books, each of which begins with hardware-independent content (that includes a slightly more detailed version of the above pseudo-code handler) and concludes with a look at example projects and low-level code for a particular hardware platform. The book's example projects themselves, along with the other examples available on Micrium's Web site, are also ideal reference sources for interrupt-related information. At a minimum, every example project based on either µC/OS-II or µC/OS-III incorporates one ISR – the tick handler through which the kernels implement time-based services – and many examples incorporate additional ISRs that you can use as templates for your own code.

By following the example established by Micrium's own code, you can help to ensure the proper functionality of your ISRs. However, it's not always the case that your routines must be written exactly like the examples. Under µC/OS-II and µC/OS-III, it's actually possible to completely forgo kernel operations in an ISR, provided that a few key conditions are met. I'll cover this topic in my next post, in which I'll elaborate on the concept of a "non-kernel-aware" ISR.

Tags: , ,

Questions or Comments?

Have a question or a suggestion for a future article?
Don't hesitate to contact us and let us know!
All comments and ideas are welcome.