Just a quick design question. If you have various interrupts within a system, would having a dedicated task with the highest priority be the best approach for handling these and pushing them to queues to clients who have subscribed to them.
What professional advice would you give for managing interrupts within a large system. To note, I’m only working on a hobby project but I want to apply the best practices possible.
No surprises when I say there is no single piece of advice that covers all use cases - if you have a high frequency interrupt, or one that requires very precise timing, or similar type of requirements then performing the processing in the interrupt handler is normally going to be the right thing.
Deferred interrupt processing, which is what you are describing, is useful in many ways though - I think these are discussed in the book though so I won’t repeat them here.
One thing that is not discussed in the book but is more and more important is security - you cannot process an interrupt without being in a privileged mode and if you don’t want application code to run with privileges you can have the operating system install interrupt handlers that do not much else but lower privilege before running the application provided interrupt handling code - and a portable way of doing that is to defer the processing to a non-privileged task.
There is an API function that enables you to do this: https://www.freertos.org/xTimerPendFunctionCallFromISR.html
Well, as usual it depends
Often I have dedicated tasks per (HW) interface with its associated ISR(s). Also because this kind of component usually implements a high level function maybe with a certain (task) priority. In short I strive for functional partitioning/encapsulation.
Just my 2ct
Thanks for that. Certainly gives me something to think about. The scenario you’ve just described, how would handle the situation where a single interrupt vector is responsible for handling interrupts from different sources.
For example a single interrupt vector is shared between various gpio lines such a button presses, interrupts from external hardware like sensors.
I will experiment between various designs to see which suites and if I can live with any tradeoffs that may arise. The dedicated task for interrupts would increase latency between interrupts and processing data, but I think I may offer one advantage to the situation I previously described.
In fact I had an application with a rotary encoder and additional buttons I handled in a keypad task on a STM32F4 board. It covered all GPIOs and EXTI ISRs needed. Even with multiple GPIO lines handled by 1 EXTI ISR it’s easily possible to decode the actual GPIO events signalled to the deferred processing task. The task in turn mapped the HW events to application level keypad events signalled to the main application task.
Just as a real world example.
Remember that in principle you can prioritize the ISRs according to your HW constraints independent from the post-processing task, which prio can be choosen to match your (functional) task priority requirements of your application.
As has been noted before, there is no universal solution to your question.
Consider the application and in particular the requirements of the task(s).
Then look for the tools and techniques that can achieve those objectives.
The point of an interrupt is to get quick control of the processor. Creating a scheduler in the interrupt routine is a curious notion, as it increases the complexity and service time.
That said, I have created operating systems that were rooted in the interrupts service routines. Each interrupt, thread, had specified responsibilities with constraints, such as must complete in 2.5 ms and can not use more than 10% of the CPU averaged over one second.
Given the above, another lower priority interrupts, thread, can be created with looser service times, say try to complete in 10 ms, most of the time and use less than 90% of the CPU.
And the lowest level priority that makes due with the leftover CPU time or simply goes to sleep.
At each level, there could be multiple service routines. For the top-level things like UART and timer ticks might be services. In the middle-level data transport, compression and encryption would make sense. At the lower priority, things like system maintenance and watchdog service.
These are relatively loose ideas.
Might you offer some more details on your intended desing?
I find that I can divide most of my interrupt handlers into a few classes. The simplest take the interrupt, maybe do some very simple processing and post an event to a task (Direct-To-Task, Event-Group, or a Semaphore). This is common for GPIO pins. The task then does the processing based on the notification.
The second class gets a bit of data from the device, and builds up a message for a task to processes. This is common for things like Serial Ports, or things like I2C or SPI bus controllers. These might use a Queue to send the message, or a special buffer and an event to signal that the data is ready.
The last class of interrupts is for something like USB or Ethernet, where the interrupt indicates a ‘packet’ of data has bee transferred, and this sort of device is one where the transfer to a task can make sense, as you often have some significant work to do to complete the interrupt processing.
The key factor is if the processing that needs to be done is of the same order (or less) than the pair of scheduler calls needed to defer the interrupt to a task, it is probably better to just do the work in the ISR. If it is going to take significantly more work than that, it make sense to pend to the task.