Using Interrupts
Electronics use many different communication protocols and most of them have distinct master and slave roles in the communication sequence. Very often, a slave cannot initiate a communication sequence. This makes it difficult for a slave device to notify the master device of a condition that requires action. In this case, the master must constantly check the slave to see if the event has occurred. This wastes the master device’s resources and the timing of the action in response to the slave event will be inaccurate. This inaccuracy is the delay between when the event happens at the slave device and when the master device checks to see if the event has occurred.
This problem is commonly solved by having a dedicated alert/interrupt signal line between the slave device and the master device. The slave device monitors its own information for an alert condition and when the condition is met, the slave device asserts a signal on the dedicated interrupt line. The master can monitor the state of the interrupt line to determine if the condition has been met, rather than performing a cumbersome exchange with the slave device. This saves resources.
This process can happen even more efficiently with the use of a software interrupt. Software interrupts immediately stop what the processor is currently doing and execute ‘interrupt handling’ code to respond to the interrupt condition. Interrupts can be triggered by a number of different events including timers or the state change of an I/O pin.
The Trēo system was designed to have a dedicated interrupt line between devices to allow for the use of software interrupts. A capable module can be set to generate the alert signal when a certain event occurs. The module then asserts the interrupt line back to the master device which can trigger a software interrupt in the master. The connections of the interrupt lines on the Trēo connectors are listed on the Trēo adapters boards. The host/master can then be programmed to take action in response to the interrupt signal.
Arduino IDE Example
For this example, let’s consider the Number Pad module (NSE-1142-1), connected to an Arduino Shield adapter (NSE-1120-1), and attached to an Arduino Leonard. The number pad generates an interrupt signal with every key press. So, the program code has to monitor the interrupt line and take actions to read the keypad when the interrupt line is asserted. The example program code show below is structured specifically to be driven on interrupt events. The program blinks the LED a certain number of times, defined by the user. The user interfaces with the unit through the keypad and serial terminal. The user enters the number of times that the LED should blink and confirms the entry by pressing #.
In order to run the example, you must use an Arduino board with a Tre the Number Pad module must be connected to an I2C connector in the left column. Be sure that the INT selector switch is in the A position. This will connect the interrupt pin of the connectors in the left column to Arduino pin D2.
#include
#define BLINK_FREQUENCY 2
#define HALF_BLINK_PERIOD (500/BLINK_FREQUENCY)
volatile int newCharacterFlag = 0;
const int ledPin = 13, keypadIntPin = 2;
int ledOn = 0, blinkCount = 0;
unsigned long lastBlink = 0;
String input = "";
// Define NumberPad object
NightShade_Treo_NumberPad keypad(1);
void setup() {
Serial.begin(115200); // Initialize Serial
while (!Serial); // Wait for serial connection
pinMode(ledPin, OUTPUT); // Initialize LED
pinMode(keypadIntPin, INPUT_PULLUP); // Set interrupt pins to inputs with pullup
keypad.begin(); // Initialize keypad
// Attach ISR to keypad interrupt pin
attachInterrupt(digitalPinToInterrupt(keypadIntPin), readKeypressISR, FALLING);
Serial.println("Type the number of times you would like the LED to blink.");
Serial.println("Press # to confirm your entry and * to clear.");
}
void loop() {
// Read new keypad entry and handle interface
if (newCharacterFlag == 1) {
char character = keypad.read(); // Read key pressed on keypad
if (character == '#') { // If # parse character string and set blinkCount
blinkCount = input.toInt();
Serial.print("\nBlinks = ");
Serial.println(blinkCount);
input = "";
} else if (character == '*') { // If * clear the input string
Serial.println("\nInput Cleared.");
input = "";
} else if (character > 0) { // If a valid character (not -1, '#', or '*')
Serial.write(character);
input.concat(character);
}
newCharacterFlag = 0; // Clear newCharacterFlag
}
// Blink LED
if (millis() >= lastBlink + HALF_BLINK_PERIOD) {
if (blinkCount > 0) {
if (!ledOn) {
digitalWrite(ledPin, HIGH);
ledOn = 1;
} else {
digitalWrite(ledPin, LOW);
ledOn = 0;
if (--blinkCount == 0) Serial.println("Blinks completed.");
}
lastBlink = millis();
}
}
}
void readKeypressISR() {
newCharacterFlag = 1; // Set newCharacterFlag
}
The main loop of the program has two operations enclosed by if statements. If the newCharacterFlag has been set by the keypad interrupt, the keypad is read and the entry is handled. The other if statement handles the LED blinking based on the interrupt driven timer count returned by the millis() function. The main loop executes VERY quickly, checking for each of these conditions and, when executed, the if statements also take a very small amount of time to execute. This makes the program respond very quickly to these events.
Modules that have a capability to an interrupt signal will have functions to do so listed in their datasheets. Below are some of the platform specific instructions on using software interrupts.
- Arduino IDE
- GPIO Interrupt from Module: attachInterrupt() function
- Timer Interrupts: TimerInterrupt Library by Khoi Hoang
- STM32 Nucleo with Arduino IDE
- GPIO Interrupt from Module: attachInterrupt() function
- Timer Interrupts: STM32_TimerInterrupt Library by Khoi Hoang
- Raspberry Pi
- The Raspberry Pi does not operate in “real-time” because of delays introduced by the operating system.
- C++: GPIO Interrupt with Wiring Pi
- Python 3: GPIO Interrupts