Hardware and Software Interruptions in Arduino

One very useful thing I would like to talk about is Interruptions in Arduino. An interruption is a signal of either Hardware or Software that causes our Arduino to exit the main program and execute an indicated function.

Before starting with this, there are a number of important considerations to keep in mind:

  • As I indicated before, the interruptions cut the main program and what is being done at that time, so it is not recommended to abuse them.
  • You cannot overlap interruptions on Arduino, so if there is an interruption in process all others will be ignored.
  • You cannot use functions such as Serial, delay, millis … or others that depend on interruptions or timers.
  • If your program is very dependent on functions such as delay or milis you must be careful, since these counters are paralyzed during the execution of the interruption.
  • Knowing the above, it is important to end the interruption as soon as possible.
  • The function executed in an interrupt does not support arguments or return variables.
  • It is always advisable to define the global variables that we are going to use as volatile, since otherwise strange problems could occur.

What are they for?

You may wonder that if it has so many disadvantages, what can it do for us? The answer as always is what you think it can be for you.

Something for which it is useful is, for example, for an event that cannot wait and needs to react as quickly as possible so that it is not lost. Suppose you are waiting for a button to be pressed. In this case you have the option to scan the pin every three times to detect the pulsation and pray so that the controller is not busy at the time it is pressed, or you can create an interrupt that is activated when pressed.

Types of interruptions

When programming interruptions in Arduino, we have three types of events to which they will react. These events are as follows:

  • Hardware, such as changing the status of a pin.
  • Software (Arduino only supports the Timer library)

Hardware Event

The first trigger or trigger we will talk about will be the Hardware event. As the name implies, it occurs when there is some change in the Hardware, and more specifically, in the state of an Arduino Pin.

You can only configure one type of interruption per pin, or at least when I put two together I ignored the first one.

Of course, not all hardwares are the same, and the pins we will have for interruptions will depend on each Arduino. In the following table we can see a list of the models, the numbers of interruptions, and the pins to which they correspond:

Model INT0 INT1 INT2 INT3 INT4 INT5
UNOPin 2Pin 3
MegaPin 2Pin 3Pin 21Pin 20Pin 19Pin 18
DUEAll
LeonardoPin 3Pin 2Pin 0Pin 1Pin 7

But calm down, do not panic yet thinking about having to learn the table. You can also use the digitalPinToInterrupt function to retrieve the interrupt number corresponding to the pin:

attachInterrupt(digitalPinToInterrupt(pin), ISR, mode)

Once we know what pins we have to make our interruptions, we need to know what conditions will trigger the interruptions, and in the case of pins, we have the following conditions:

  • LOW: The interrupt jumps when the pin is in LOW state
  • HIGH: The interrupt jumps when the pin is in HIGH state (you didn’t expect it, right? :P), unfortunately this trigger is only available in Arduino DUE.
  • CHANGE: The interruption jumps when the pin changes state, either LOW to HIGH, or vice versa.
  • RISING: The interruption jumps when the pin goes from LOW to HIGH.
  • FALLING: This is the inverse of the one above, and therefore jumps when the pin goes from HIGH to LOW.

To make it better understood, there is nothing like a small code example:

int contador = 0;
int n = contador;
double lastpush = 0;

void setup() {
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(3), ServicioBoton, FALLING);
}
void loop() {
  if (n != contador)
  {
    Serial.println(contador);
    n = contador;
  }
}

void ServicioBoton() {
  if (millis() > lastpush+100) {
    contador++;
    lastpush = millis();
  }
}

In the code above, digital pin 2 is configured as interruption (interruption 0), and reacts to the pulse by adding one to the counter. Due to the rebound effect of the button, the interruption is sometimes executed multiple times, and to avoid it we add a delay of 100ms.

I know you will be thinking “but you did not say that you cannot use millions in an interruption“, and so it is, power cannot be used to time or calculate time because it does not advance, but if you can recover the value it has at that time.

Software Interruptions

Other microcontrollers have different types of software interruptions, but in the case of Arduino we only have one: the timer.

This type of interruptions must be used carefully when executed continuously, as they could enter an infinite loop if it took longer to execute than the time between executions. In addition, depending on the timer used, we will affect some functions or others of our microcontroller, so it is very important to be careful. For example, timer1 could affect the control of PWM, AnalogWrite and the control of servos.

Like hardware interruptions, it is recommended that they be as short as possible to avoid affecting the operation of the main program.

You can directly program the internal records to create timers, but to avoid complication we will use the TimerOne library. This library is not available in the Arduino program, but the official website offers us the possibility to download it, just like the plugin manager. To install it we will follow these steps:

  • We enter the Tools menu -> Manage libraries
  • We look for the TimerOne library
  • Press the install button

Once installed we can start using it, and as an example, I leave this code in which you simply turn on and off the led of the placada every half second.

#include <TimerOne.h>

void setup() {
  // Initialize pin 13 as output
  pinMode(LED_BUILTIN, OUTPUT);
  // We make sure that when the LED starts it is off
  digitalWrite(LED_BUILTIN, LOW);
  
  // Initialize Timer1 to run every 0.5 seconds (twice per second)
  Timer1.initialize(500000);
  // Execute for each interruption the function parpadearLED()
  Timer1.attachInterrupt(parpadearLED);
}

void loop() {
}

// We use this global variable to save the status of the led (initially off)
bool estado = false;

void parpadearLED() {
  if (estado) {
    digitalWrite(LED_BUILTIN, LOW);
    estado = false;
  }
  else {
    digitalWrite(LED_BUILTIN, HIGH);
    estado = true;
  }
}

In the code above we create an interruption in the Timer1 that runs every 500,000µs, and we add the LED flashing function. This simply causes the LED to flash every second.

Notas

Finally, a couple of notes about interruptions:

  • We can use the nonInterrupts() and Interrupts() functions to deactivate and reactivate the interruptions
  • If we want to stop using an interrupt, we can deactivate it with the detachInterrupt(num Interrupt) function

As always, I hope it works for you, and if you want to see more information you will go through the official page.

Greetings!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.