I was recently involved in a discussion about suitable operations for interrupt handlers. Interrupts can be an intimidating and/or tricky area of embedded systems development, so I want to share some guidelines with you.
Generally you will want to keep your interrupt handlers as simple as possible. The normal flow of our program has been interrupted, so we need to handle the interruption as quickly as possible and return to normal operational flow (or go back to sleep).
Here are some general rules of thumb for operations to avoid:
- Don't declare any non-static variables inside the handler
- Avoid blocking function calls
- Avoid non-reentrant function calls
- Avoid any processing that takes non-trivial time
- Avoid operations with locks as you can deadlock your program in an ISR
- Avoid operations that involve dynamic memory allocations
- The implementation may require a lock
- The allocation typically takes a non-determinate amount of time
Depending on your architecture and operational model, your interrupt handler may utilize the stack of the interrupted thread or a common "interrupt stack". Regardless of the model, I advise you to be parsimonious and to avoid stack allocations inside your interrupt handler.
In most cases, there should be little-to-no processing inside of the handler. If I am using an RTOS on my embedded system, my interrupt handlers set an event flag (or semaphore) and clear the interrupt state. The event flag will be tied to a processing thread which wakes up and handles the event in user space.
These guidelines have their exceptions, of course. If you are not using your RTOS, it is likely that your program flow is either a run loop or entirely interrupt driven. It is highly likely that locks and dynamic memory allocation aren't utilized on those systems, reducing deadlock risk. For both of these cases, you may need to use heavier operations inside of the interrupt handler.
Do you have any useful rules of thumb for interrupt handlers? Leave a comment!