In this article I present a method for configuring any number of switches linked to a single (common) interrupt service routine (ISR). The method is fully software driven and does not rely on any physical wiring of switches to interrupt pins.
The article originates from previous work in developing a library to efficiently handle switches of any type and wired in differing schemes - the ez_switch_lib
.
The ez_switch_lib
offers some interesting and unique features, in summary:
- any switch type may be configured, eg button, toggle, etc
- common switch wiring schemes are supported
- multiple switches of different types and wiring schemes can be configured concurrently
- switch debounce is automatically taken care of and this can also be user configured
- the status of switches may be tested at any time, eg testing if a switch is in transition, on or off, etc
- switches may be automatically linked to other digital output pins so that when switching occurs the linked output pin is also triggered (set or cleared)
It is the latter feature, linking switches to other digital output pins, that this article highlights and on which it is based.
Linking Switches to InterruptsThis article shows how we are able to link (associate) any number of switches, of any type and wired in different schemes, to interrupts with very little effort or any additional wiring - there are no wires connecting the switches to interrupts!
To keep the example simple, we will use a single ISR to handle all switches, but this is variable - similarly, we can use any number of ISRs and external interrupt pins as we wish (up to the microcontroller design limits, of course).
So, how do we accomplish this?
As alluded to above, the ez_switch_lib
library provides a particular feature that allows a switch to be linked to a digital output pin such that when the switch is actuated the associated output pin is automatically set/cleared. This function is called link_switch_to_output
and has just three parameters:
- The id of the switch to which the digital output pin is to be linked
- the pin number of the digital output pin to be linked
- the initial setting for the linked output pin (i.e. LOW or HIGH)
Once a switch is declared/defined it may then be associated with a digital output pin using the above function. This is the essence of the method - all we then need to do is to define an ISR using the attachInterrupt
function to handle the switch event on any suitable external interrupt digital pin.
We will see how we are able to declare a number of switches, of different types and wired in different ways, but such that each automatically triggers a defined and common ISR when actuated. What this ISR does to process switches is not within scope of this article, but the reader will see where the various 'hooks' exist in the ISR and sketch to add specific end user code to process each possible switch interrupt event type.
Let's look at the example sketch and the switches it is configured for.
The example sketch will use seven digital pins, six for switches and one for the common interrupt pin, assigned and wired as per the table below:
(Wire up the components as per table above and the Schematics, below, using the breadboard.)
To note is:
- we use digital pin 2 as the common pin assigned to the ISR, observe that this pin has nothing physically connected to it
- '
circuit_C1
' and 'circuit_C2
' are reserved words in theez_switch_lib
library and each represents a circuit with an external 10k ohm pull down resistor and a basic circuit (no additional components other than the switch itself), respectively
we assign digital pins 3-8 to switches as follows:
- 3 x toggle switches wired with a pulldown 10k ohm resistor - '
circuit_C1
' (see Schematics below)
- 3 x button switches wired directly, '
circuit_C2
' (see Schematics below)
- all switches are software linked to a common digital pin 2 (ISR pin) using the
link_switch_to_output
function after each switch is created (add_switch
function)
The above switch configuration is arbitrary and used to demonstrate the flexibility of the ez_switch_lib
library and switches may be linked to a single ISR, but any combination switch types, switch circuit wiring and multiple ISRs may be used.
The sketch has six principal parts:
ez_switch_lib
declaration - the declaration of theez_switch_lib
library (i.e.#include "ez_switch_lib.h"
). To run the sketch you will need copy over theez_switch_lib
library files (.cpp,.h and.txt) to the Arduino libraries directory into a directory named 'ez_switch_lib
', ie../Arduino/libraries/ez_switch_lib
(see theez_switch_lib
article to access and download these).
- create
ez_switch_lib
instance - creates and establishes theez_switch_lib
library instance of size equal to the number of switches we wish to configure (here six). The sketch assigns the name 'my_switches
' for the instance which we use to prefix all of ourez_switch_lib
functions and resources, eg 'my_switches.add_switch(..)
', etc.
- define switch configuration data - data defining our switch configurations. At the heart of the sketch lies the switch configuration data which is held in a two dimensional array called '
my_switch_data
' with each row defining the data associated with each switch, as follows:
column[0] - represents the switch type ('button_switch
' or 'toggle_switch
'). (Again, 'button_switch
' or 'toggle_switch
' areez_switch_lib
library reserved words and define the switch type being considered.)
column[1] - this is the digital pin assigned to the switch
column[2] - represents the switch wiring scheme ( reserved words 'circuit_C1
' or 'circuit_C2
')
- the setup function - you will notice that this is where we declare each of our switches to the library using the
add_switch
function and also assign the common interrupt pin to each switch (thelink_switch_to_output
function).
- main loop processing. On examination the main loop does not appear to do much at all - it simply continuously polls each switch in turn using the
read_switch
function, looking for state changes. This is because the method is fully software driven and therefore needs to continually look for state changes. When a state change occurs, two things happen:
1. the switch's associated linked output pin (the common interrupt pin) is automatically raised high (RISING), thus triggering the ISR which will then process the switch event for the actuating switch, and
2. following the completion of ISR processing, theread_switch
function will return a value of 'switched
' (a reservedez_switch_lib
library word also) which can then be further processed in the main loop, if required - two bites of the same cherry!.
- an interrupt service routine (ISR) - a fairly simple ISR, designed to recognise the different switch types and their associated characteristics using the
ez_switch_lib
available variables. You will note that the ISR does nothing more than report to the serial monitor which switch event has been triggered. In ordinary circumstances the use of serial print I/O is not to be recommended, it is used here just to provide visual confirmation that the sketch is doing what it should be doing. Add your own end user code at each of the 'hooks' as appropriate to your project's needs.
Okay, how does it work?
The ez_switch_lib
removes from our considerations any concerns about switch bounce or how switches are wired. It can also provides us with very useful status information about our switches, for example, switch type, if a switch is transitioning or transitioned or otherwise, whether a toggle switch is presently on or off, etc. But, most significantly, it provides us with the ability to automatically link switches to another digital output pin that we can use to trigger an ISR when the associated switch is actuated.
Now, in our example sketch, each declared switch triggers the same ISR, so the question is how can the ISR determine which switch has actuated, i.e. which switch triggered the ISR? Again, the ez_switch_lib
caters for this. It provides the switch id of the last switch that actuates. It is this feature that provides the ISR with the means to process the correct switch. The specific library variable is called 'last_switched_id
'.
With this knowledge the ISR can process the specific requirements for the actuated switch.
All good? Yes, providing you recognise that button and toggle style switches each have their own characteristics which the ISR must also cater for. Specifically:
button style switches - an actuation cycle goes from off-on and back to off before it can be said to have been fully switched.
toggle style switches - toggle switches have two actuation cycles - they can transition from off-on or from on-off.
Again, the ez_switch_lib
allows for these specific switch characteristics, giving the end user (and the ISR) the means to process each event type appropriately.
When you examine the ISR code, you will see how it handles the above scenarios specifically and simply.
Also, let's not forget that whilst the ISR will process switch actuations, there is an additional point at which further processing may be applied. If you examine the main loop you will see that each switch is polled and read directly and constantly. It is this polling of the switches that allows the triggering of the switches' ISR. However, every time a switch ISR is triggered the switch itself will also be deemed to have been actuated ('switched
'). The answer from the read_switch
function in the polling loop will be given as 'switched
' if the switch has been actuated. It therefore provides an additional opportunity to add further switch processing in the main polling loop should this be needed, or if the switch does not have a linked ISR output pin.
Who says we cant have our cake and eat it?!!
FinallyI hope you have found this article of some interest and worth considering for your own projects. I do recommend that you explore more fully the ez_switch_lib
article and download its User Guide and Crib Sheet for a full appreciation of capabilities.
Comments