bclose

57 – ARDUINO AND INTERRUPTIONS

Objectives

 

 

    • Introduce the concept of interruption in Arduino.
    • Get to know the types of interruptions.
    • Show the kinds of Interruption triggers.
    • General considerations.

 

Bill of materials

Arduino UNO Arduino UNO or equivalent.
BreadboardJumper wires A Breadboard plus some jumper wires.
330 Ohm resistor A 330Ω resistor.
Push-button switch A push-button switch.

 

 

Interruptions

 

I have never known very well why interruptions make veteran developers shake as if they were rookies.

I remember the time when programmers were focused on microprocessors (when dinosaurs ruled the earth) and began to play with interruptions. There was a percentage of technicians, either electronic or computer science engineers, that even understanding the idea of interruptions, they simply ignored them as if they couldn’t grasp it.

Talk about interruptions to seasoned Arduino enthusiasts suppose, in many cases, to suddenly make them go out to do something else. I’ve never known why this happens, but we will try to remedy it immediately.

What is a hardware interruption?

At a basic level, an interruption is a signal that interrupts, if you’ll excuse the repetition, the normal activity of our microprocessor so that it jumps to attend it. There are three events that can trigger an interruption:

 
  • A hardware event, previously defined.
  • A programmed event or Timer
  • A software call.
 

When an event triggers an interruption, the normal execution of the program is suspended (tidily to be able to come back later) and jumps to perform a special function called Interrupt Service Handler or ISH.

When the ISH finishes, the program returns quietly to the point where it left off and continues with what it was doing as if nothing had happened.

 
  •  The concept of interruption stems from the need to react immediately in response to an electronic event, which admits no delay. Either because of the urgency of the event or because something could be lost irretrievably if we don’t react with sufficient alacrity.

 

But, what is so urgent that it can not wait? Are our Arduinos not fast enough to see if there is an alarm signal every little time? Why do we complicate life with such an outlandish thing?

The answer as always is …, well, it depends. Our Arduino can be busy so it will only read the signal from a pin from time to time. And if the signal that appears vanishes before we check it, we will not even know it, because although Duinos are fast enough, an electronic signal is several million times faster. This is another reason why using delays is very dangerous.

 
  •  In technical jargon, to check from time to time how is the issue, is called Polling.

 

On the other hand they offer a huge advantage when organizing our program. We just define the function that will be executed after receiving a given interruption so that it will be executed when it occurs. We don’t have to check whether or not a given situation happens.

We don’t have to worry about it and it runs solely when the interruption triggers. Smart, isn’t it? (yes, it’s an obsession).

Actually, we use interruptions usually in response to unplanned events that take us out of the usual routine.

Imagine that you are watching your favorite show on TV and you are waiting for your mate, friend or girlfriend.

There are two ways to let him/her come in. One way is to go to the door every, let’s say, two minutes, to check if he/she is astonished at the door waiting for us to open.

The other is to establish an interruption using the doorbell. When your mate arrives, he/she rings the bell. Then you pause the chapter quietly, leave the drink on the table and go to open the door.

When you are back again, you resume the film and pick the soda. Why are interruptions so rare? What do you say about your
phone or WhatsApp? It’s the same idea. And the same happens with your Arduino.

Why should I give up interruptions and devote myself to going to the door every bit? It is absurd. Interruptions have nothing strange or unknowable. Spend a little time and you will find a wonderful tool that will solve more than one problem cleanly.

 

TYPES OF INTERRUPTIONS

 

Of the three events that can trigger an interruption:

 
  • A hardware event, previously defined.
  • A programmed event or Timer
  • A software call.
 

… we find out that Arduino does not support software interruptions.

And then why have we talked about them? Well, because other programming environments accept them and perhaps it will not be uncommon in the future in the Arduino IDE as well.

The programmed events or timers are very interesting and will have a own monographic chapter in the near future. But for now let’s deal with hardware triggered interruptions.

 

HARDWARE INTERRUPTIONS

 

These hardware interruptions, were designed by the need to react fast enough, in unimaginable short time, to electronic regular working times to which even the software was not able to react.

The idea that we must bear in mind is that we are going to define a function that will run asynchronously, without planning, when a given electronic event happens.

In order to define a interruption we need three things:

 
  • An Arduino pin that will receive the trigger signal.
  • A trigger condition.
  • A function that will be executed when the interruption is triggered (called call back function)..
 

First, we have to select the Arduino pin to which the trigger signal will be connected. According to the Arduino board we will have several possibilities:

Arduino board Int 0 Int 1 Int 2 Int 3 Int 4 Int 5
UNO Pin 2 Pin 3
MEGA 2 3 21 20 19 18
DUE All DUE pins can be used to handle interruptions.
Leonardo 3 2 0 1 7

 

This means that Arduino UNO can define 2 hardware interruptions called 0 and 1, connected to pins 2 and 3 (For it not to be easy).

The Mega, as usual, accepts no less than 6 different interruptions. And DUE very show-off, exhibits its power.

The trigger condition can be:

 
  • LOW, the interruption triggers when the pin is LOW.
  • CHANGE, the interruption triggers when it turns from HIGH to LOW or vice versa.
  • RISING, the interruption triggers in the rising edge (when it turns from LOW to HIGH).
  • FALLING, the interruption triggers in the falling edge (when it turns from HIGH to LOW).
  • And only one for the DUE: HIGH, the interruption triggers when the pin is HIGH.
 

If our call back function is called Function1(), to activate the interruption we will use the following statement:

attachInterrupt(interrupt, ISR, mode)

Where Interrupt is the interruption number, ISR will be Function1() and mode is one of the conditions shown above. So in Arduino UNO we could use:

attachInterrupt(0, Function1, RISING) ;

Providing that we have connected the interruption signal to Arduino pin 2, let’s see some examples of interruptions.

 

CIRCUIT WIRING DIAGRAM

 

It is a kind of tradition in Arduino, to use a push-switch button to illustrate the concept of interruption, so we will submit to it. Let’s use a typical circuit to read a button using a pull-up resistor.

Chapter 57, schematic diagram

 

So far we would have written the sketch to read it this way:

void setup()
   {   pinMode(2, INPUT);
       Serial.begin(9600);
   }

void loop()
   {   bool p = digitalRead(2);
       Serial.println(p);
   }

The result would be normally 1, because of the pull up, and the reading would drop to 0 when pressing the button. There is nothing new in this.

But we will rewrite the program to set an interruption on pin 2 (Interrupt 0) .The sketch will be more or less like this:

Sketch 57.1
int counter = 0;
int n = counter;

void setup()
   {   
       Serial.begin(9600);
       attachInterrupt( 0, ButtonHandler, FALLING);
   }
void loop()
   {
       if (n != counter)
          {     
             Serial.println(counter);
             n = counter;
          }
   }

void ButtonHandler () 
   {    
       counter++ ;
   }

First, note that we have eliminated the definition of pin 2 as an input, because we will not use it as an input strictly. To define the interruption is enough.

Second, we use the attachInterrupt() function passing as parameters the interruption 0, which is the pin 2 of the Arduino UNO board. If it had been the interruption 1, we would have connected it to the pin 3.

We pass the name of the call back function ButtonHandler() function, which is easy as ABC. The global variable counter, keeps the number of keystrokes. The only thing that makes the ButtonHandler function is to increase the counter by one each time you press and then comes back.

And finally the kind of trigger is FALLING because its state is normally HIGH and it turns to LOW when pressing the button. We will use the trigger with the falling edge.

The loop checks whether the number of keystrokes has changed and if so print it, but it can devote itself to doing anything else, because we will not lose any keystroke.

It may seem an extravagant way of doing things but do not tell me that is not elegant. In fact, all modern high-level languages for Windows, Mac or Linux use event-driven programming, which is basically something like this (relatively speaking, of course).

When we see the output in the console we will have an unexpected surprise:

Chapter 57, Sketch 1

When you press the button, the displayed number does not increase one by one but abruptly. Why?

Well, as we said in a previous chapter, it is due to the push-button switch’s bounces. We said in the chapter “Conditionals and buttons”, that in order to eliminate the bounces we have to do the debouncing so we used a delay of 250 ms.

But we will have a problem. We can’t use a delay within an interruption. It does not work. I beg your pardon?

There are several considerations to take into account when using interruptions:

 
  • Do what you want but do not delay. Finish as soon as possible and get out of there.
  • There are things that do not work, as the delay() functions, millis() and anything that relies on interruptions or timers.
  • Do not even think about writing a Serial function within an interruption, they are very slow (and they do not work because they also depend on interruptions).
  • You must understand that an interruption is like a state of emergency, which can be used without hesitation, but understanding that the job must be done and get out as soon as possible.
  • In fact, an ISR or call back function can not return parameters nor receive them.
 

Besides, when we define a global variable, as counter, which depends on an ISR function, it is recommended to define it as volatile and not as a normal global variable.

 
  • Strictly speaking, volatile is not a variable, but a directive to the compiler.
  • This means that the variable in question, must be stored in a certain way to avoid some rare problems that can arise when a variable can be changed by the ISR or the program (which does not happen in our example).
  • Under certain circumstances it may arise a conflict and using volatile avoids it, therefore it is always recommended doing this way.
 

If you need a delay for something, it is always better to check the elapsed time and decide whether or not an action should be taken. Here’s another example:

Sketch 57.2

 

volatile int counter = 0;   // We are of the most obedient
int n = counter;
long T0 = 0 ;               // global variabel for time

void setup()
   {    
        pinMode(2, INPUT);
        Serial.begin(9600); 
        attachInterrupt( 0, ButtonHandler, LOW);
   } 
void loop()
   {   
        if (n != counter)
           {   Serial.println(counter);
               n = counter;
           }
   }
void ButtonHandler()
   {
       if ( millis() > T0  + 250)
          {   counter++ ;
              T0 = millis();
          }
    }

First, we define counter as volatile, as medical prescription, and define another global variable to store the time, T0, from which we will count.

In the ISR, the ButtonHandler() function, the difference is that we check if the current value of millis() is greater in 250 ms to the last time we swept across the interruption. If not, we consider it a bounce and ignore it. On the contrary, if a
reasonable time has passed we increase the counter.

 

Chapter 57, Sketch 2

The advantage of this system is that it does not freeze the processor with a delay, but we let it continue its work, attending other interruptions, for example.

But…, wait a moment! Did not we say that millis() does not work within interruptions?
So it is. While an interruption is active, millis() is frozen and its value will not change, but it can still be read.

 
  • While you are inside an interruption, all other interruptions are ignored, so nothing that depends on other interruptions works.
  • That is why it is so important to leave soon, to ensure that we do not miss anything of interest.
  • While an interrupt is active, millis() and micros() are frozen. That means that if you have a few thousand interrupts per second (as if you were measuring the frequency of a wave audio) the time measured with millis() or micros() can be distorted.
 

Finally you should know that there are some other instructions related to interruptions:

 
  • noInterrupts(), disable the execution of interruptions until new order
  • Interrupts(), restarts the interruptions defined with attachInterrupt().
  • detachInterrupt( num Interrupt), cancels the indicated interruption
 

 

Summary

 

 

    • We have known the reason and the concept of interruptions.
    • We have started by studying hardware interruptions, leaving timers for further sessions.
    • We have seen that Arduino UNO only have 2 interruptions, called 0 and 1, on pins 2 and 3 respectively.
    • We have seen the possible kind of triggers for an interruption: by rising edge, by falling edge or LOW.

 

 

 

No Comments

Give a Reply