Reacting to real-time events using Interrupt in Arduino

Reacting to real-time events using Interrupt in Arduino

Good day, today I decided to put some old push buttons I rescued from an old project to good use. Today, I will be going into using interrupt in Arduino. Trust me, it is a very cool practice to employ.

An interrupt is a signal to the processor/microcontroller emitted by hardware or software indicating an event that needs immediate attention. An interrupt alerts the processor/microcontroller to a high-priority condition requiring the interruption of the current code the processor is executing. The processor responds by suspending its current activities, saving its state, and executing a function called an interrupt handler (or an interrupt service routine, ISR) to deal with the event. This interruption is temporary, and, after the interrupt handler finishes, the processor resumes normal activities. There are two types of interrupts: hardware interrupts and software interrupts:

See Also:

Hardware interrupts are used by devices to communicate that they require attention from the operating system. Internally, hardware interrupts are implemented using electronic alerting signals that are sent to the processor from an external device, which is either a part of the computer itself.. For example, pressing a key on the keyboard or moving the mouse triggers hardware interrupts that cause the processor to read the keystroke or mouse position. Unlike the software type (described below), hardware interrupts are asynchronous and can occur in the middle of instruction execution, requiring additional care in programming.

A software interrupt is caused either by an exceptional condition in the processor itself, or a special instruction in the instruction set which causes an interrupt when it is executed. The former is often called a trap or exception and is used for errors or events occurring during program execution that are exceptional enough that they cannot be handled within the program itself. For example, a divide-by-zero exception will be thrown if the processor’s arithmetic logic unit is commanded to divide a number by zero as this instruction is an error and impossible.

Each interrupt has its own interrupt handler. The number of hardware interrupts is limited by the number of interrupt request (IRQ) lines to the processor, but there may be hundreds of different software interrupts. Interrupts are a commonly used technique for computer multitasking, especially in real-time computing. Such a system is said to be interrupt-driven.

Interrupt is a great mechanism built into all Arduinos that is ideal for monitoring kinds of real-time events.

What’s awesome about this is that it structures your system to react quickly and efficiently to important events that aren’t easy to anticipate in software. Best of all, it frees up your processor for doing other stuff while it’s waiting on an event to show up.

 

Example

We will use a two push buttons to control the switching ON and OFF of an LED and Relay inside the loop() function. Materials for this tutorial includes:

 

Create an empty sketch and paste these lines of codes inside:

int relayPin = 4; //attach relay to pin D4
int ledPin = 5; //attach relay to pin D5
int btn1Pin = 2; //attach relay to pin D2 (interrupt 0)
int btn2Pin = 3; //attach relay to pin D2 (interrupt 1)

int btnValue1 = 0;
int btnValue2 = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(relayPin, OUTPUT); //initalize relay pin as output
  pinMode(ledPin, OUTPUT);  //initalize ledPin pin as output
  pinMode(btn1Pin, INPUT);  //initalize button1 pin as input
  pinMode(btn2Pin, INPUT);  //initalize button2 pin as input
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  btnValue1 = digitalRead(btn1Pin); //read value from button 1
  btnValue2 = digitalRead(btn2Pin); //read value from button 2

  Serial.println(btnValue1);

  if (btnValue1 == HIGH){ //if value is high, switch LED on 
    digitalWrite(ledPin, HIGH);
  }else{
    digitalWrite(ledPin, LOW);
  }

  if (btnValue2 == HIGH){ //if value is high, switch relay on
    digitalWrite(relayPin, HIGH);
  }else{
    digitalWrite(relayPin, LOW);
  }
}

The code is pretty straight forward and boring. It runs inside the loop(), whenever any of the buttons is pressed the corresponding pin (either LED pin or relay pin) goes high. Let us see the interrupt version:

 

int relayPin = 4; //attach relay to pin D4
int ledPin = 5; //attach relay to pin D5
int btn1Pin = 2; //attach relay to pin D2 (interrupt 0)
int btn2Pin = 3; //attach relay to pin D2 (interrupt 1)

volatile int btnValue1 = 0; //button state for button 1
volatile int btnValue2 = 0; //button state for button 2

void setup() {
  // put your setup code here, to run once:
  pinMode(relayPin, OUTPUT); //initalize relay pin as output
  pinMode(ledPin, OUTPUT);  //initalize ledPin pin as output
  pinMode(btn1Pin, INPUT);  //initalize button1 pin as input
  pinMode(btn2Pin, INPUT);  //initalize button2 pin as input
  Serial.begin(9600);

  //attach interrupt for the various pins
  attachInterrupt(digitalPinToInterrupt(btn1Pin), pinEventLED, CHANGE);
  attachInterrupt(digitalPinToInterrupt(btn2Pin), pinEventRelay, CHANGE);
}

void loop() {
  // put your main code here, to run repeatedly:
 
}

void pinEventLED(){
  btnValue1 = digitalRead(btn1Pin); //read value from button 1
  digitalWrite(ledPin, btnValue1);
  
}

void pinEventRelay(){
   btnValue2 = digitalRead(btn2Pin); //read value from button 2
   Serial.println(btnValue2);
   digitalWrite(relayPin, btnValue2);
 
}

 

Download code from GitHub

You’ll notice a few changes here. The first, and most obvious of these, is that `loop()` doesn’t contain any instructions! We can get away with this because all of the work that was previously done by an `if/else` statement is now handled by a new functions, pinEventLED() and pinEventRelay() . These two functions are an example of an interrupt service routine – its job is to run quickly and handle the interrupt and let the processor get back to the main program (i.e. the contents of `loop()`). There are a few important things to consider when writing an interrupt service routine, which you can see reflected in the code above:

  • ISRs should be short and sweet. You don’t want to derail the main loop for too long!
  • There are no input variables or returned values. All changes have to be made on global variables.

You’re probably wondering – how do we know when an interrupt gets run? What triggers it? The third function in the `setup()` routine is what sets up the interrupt for the whole system. This function, `attachInterrupt()`, takes three arguments:

1. The interrupt vector, which determines what pin can generate an interrupt. This isn’t the number of the pin itself – it’s actually a reference to where in memory the Arduino processor has to look to see if an interrupt occurs. A given space in that vector corresponds to a specific external pin, and not all pins can generate an interrupt! On the Arduino Mega, pins 2, 3,  21, 20, 19 and 18 are capable of generating interrupts, and they correspond to interrupt vectors 0 to 5 respectively. For a list of what pins are available as interrupt pins, check out the Arduino documentation on `attachInterrupt()`.

2. The function name of the interrupt service routine – this determines the code that gets run when the interrupt condition is met.

3. The interrupt mode, which determines what pin action triggers an interrupt. The Arduino Mega supports four interrupt modes:

  •  `RISING`, which activates an interrupt on a rising edge of the interrupt pin,
  •   `FALLING`, which activates on a falling edge,
  •   `CHANGE`, which responds to any change in the interrupt pin’s value,
  •   `LOW`, which triggers any time the pin is a digital low.

One more quick thing to point out – our ISR(s) uses the variables btnValue1 and btnValue2 to store pin states. volatile is a C keyword not only applicable in C language applied to variables. It means that the value of that variable is not entirely within a program’s control. It reflects that the values of btnValue1 and btnValue2 could change, and change on something that the program itself can’t predict – in this case, user input.

 

Leave a Reply

Your email address will not be published. Required fields are marked *