Arduino microcontrollers are many and varied in terms of their specifications and capabilities. But what is common across the range is that these microcontrollers provide a means to deal with external events as they occur, this being achieved via the use of interrupts. An external event can be anything. For example, the activation of a PIR, the change of state of a switch, etc.
The Arduino development environment and software libraries provide a means to design and define into end user code routines (Interrupt Service Routines (ISRs)) that can be called whenever an external interrupt occurs. When an external interrupt is triggered the microcontroller will cease the operation of the running end user code and pass control to an associated ISR which has been defined as a part of the end user application.
If a solution is required to deal with several external interrupts then each will necessitate a specific ISR to be defined to deal with an associated triggered event - ISRs will need to be designed to deal with particular and individual requirements. Furthermore, ISRs can suffer from spurious triggers, for example, ‘dirty’ switching. This is often referred to as ‘bounce’ and is something ISR design needs to cater for. The challenge is therefore to design solutions that can take account of interrupt processing as a part of the overall solution recognising the varied set up requirements of interrupts to deal with such external events.
The number of external interrupts supported varies depending on the particular Arduino microcontroller. The choice of a microcontroller for a project will therefore be influenced by the number of external interrupts an end user solution requires.
This framework provides a ‘container’ that can be configured to meet any specific set up requirements for external interrupts and to which end user code can be easily added.
The method will allow external interrupts to be processed in their order of priority (when triggered concurrently and in real time), but at such time that the main processing code dictates (asynchronously). That is, the method allows for external interrupts to be captured, as triggered, but actioned outside and away from their associated Interrupt Service Routines (ISRs).
The solution has been developed around the ARDUINO MEGA 2560 microcontroller, but it may be modified for any board in a straight forward way for any number of external interrupts up to the maximum limit set by the specific microcontroller and this framework.
The MEGA 2560 supports up to six external interrupts that are readily accessible to end user developers and which are mapped to digital input pins. However, the method is easily scaled to use one, two, three,..., or all six external interrupts concurrently, making it an ideal framework on which to base solutions requiring multiple external interrupt processes and processing. For microcontrollers that support more than six interrupts an extension to the underlying framework would be needed. However, it is considered that a limit of six interrupts should not be a limiting factor for most applications likely to be developed. If more are required then the method could be extended by increasing the number of generic ISRs and associated data support structures.
Asynchronous processing is achieved through the design and implementation of generic interrupt handlers and supported by an interrupt queue management concept which provides the means to separate the event of an interrupt from its ultimate processing.
When an external interrupt is triggered, it is presented the nominated (generic) ISR mapped and established within the setup() process. ISRs will then do little more than insert the interrupt event into the interrupt queue with no further process taking place at this point. However, to avoid ‘bounce’ from spurious/dirty interrupts, each ISR incorporates a debounce mechanism which is variable through configuration parameters. Interrupt ‘bounce’ values may defined differently for each mapped external interrupt, as necessary, to reflect the ‘cleanliness’ of the triggering circuitry.
If several external interrupts are triggered at the same time then the ARDUINO microcontroller will ‘fire’ their respective ISRs in the defined priority order as determined by the microcontroller specifications.
The interrupt queue is operated and managed on a first in, first out basis (FIFO). The approach therefore preserves the order in which external interrupts are triggered in the real world.
However, such a method as asynchronous interrupt processing does come with some overheads, as may be expected. The method is not promoted for applications with absolute and critical processing needs, where timing constraints are paramount; it is a method that would be applicable to many non-critical solutions.
In designing this framework a number of objectives were set, these being to arrive at a solution for external interrupt handling that:
- disassociates the event of an external interrupt occurring from the processing of its occurrence, providing freedom and control to the overlying end user application code. That is, a solution that will provide independent processing from the ‘capture’ of received interrupts and their eventual processing
- a container that can be ‘plugged’ into without extensive modification
- is scalable, providing a framework to handle multiple external interrupts
- is highly configurable for each possible external interrupt. For example, the need to configure each interrupt differently if needed
- can support unique or nonunique external interrupt event capture/recoding prior to onward processing
- takes account of spurious interrupts. That is, provides debounce for ‘dirty’ switching.
The functionality of the framework is largely as described above. However, one interesting feature is in its ability to allow external interrupts to be ‘unique’ or ‘nonunique’. No more is said about this feature here, rather to refer those interested in the framework to the User Guide where a fuller description can be found (see User Guide, Scalable Asynchronous Processing of External Interrupts).