Part 2: Non-Kernel-Aware ISRs
Applications written around µC/OS-II and µC/OS-III can often use non-kernel-aware ISRs for rapid responses to certain events.
In the previous post in this series, I showed the format of what could be called a "typical" ISR for Micriµm's µC/OS-II and µC/OS-III kernels. Here, I'll review the format for an alternate type of ISR – one sometimes referred to as "non-kernel-aware." This format involves less overhead than the approach from the first post, but it is not applicable to all ISRs.
The non-kernel-aware format is intended for ISRs that don't require the use of kernel services – routines that, for example, don't place a message in a queue or signal a task using a semaphore. The assumption behind the non-kernel-aware format is that such routines will never result in preemption. In other words, because they don't involve any of the kernel's services, they will never make another task ready to run. ISRs of this type can forgo invoking the kernel routines that would otherwise be needed to facilitate a post-ISR switch to a new task.
A key concern in the development of non-kernel-aware ISRs is nesting. In general, µC/OS-II and µC/OS-III support the nesting of interrupts. However, developers working with a combination of kernel-aware and non-kernel-aware ISRs should take pains to ensure that the former cannot interrupt the latter. For such developers, a prioritization scheme like the one shown below, in which non-kernel-aware routines are given the highest priorities, is ideal.
The example ISR provided in my previous post included two kernel function calls – to
OSIntExit() – both of which are unnecessary in non-kernel-aware ISRs. Thus, the format of such ISRs should resemble what is shown below. It should be noted that the Save CPU registers line here would not necessarily be identical to that in the previous post's ISR, since the below function might not be required to save all of the registers involved in a context switch. Additionally, developers of non-kernel-aware ISRs should keep in mind that, while the ISR format from the previous post can be appended with code to re-enable interrupts and, thus, facilitate nesting, such code, as noted above, can be problematic in non-kernel-aware routines.
NonKernelAwareISR: Save CPU registers; App_ISR(); Restore CPU registers; Return
The different formats that can be observed in interrupt routines for µC/OS-II and µC/OS-III may make the job of writing these routines seem challenging. However, based on the information provided above and in my previous post, it's possible to summarize the decisions involved in ISR development with two basic rules:
The interrupt-related code that must be written by application developers varies somewhat according to interrupt controller, so you should check Micriµm's documentation and example projects to determine what is required of a "typical" ISR on your platform.
If your ISR will not use any kernel services and will not be interrupted by routines that rely on such services, you can use the non-kernel-aware ISR format to reduce the amount of overhead associated with your routine.
Keeping these rules in mind, you can begin writing robust applications that use both tasks and interrupts to take full advantage of the capabilities of your embedded microcontroller. Additional documentation is also available in our books and extensive online documentation: µC/OS-II Documentation or µC/OS-III Documentation.