Processor Interrupts with Arduino


Interrupts!

Interrupts, what are they? They are people that intermittently prevent you from doing your current work. Haha, well maybe... but we really want to know is what are they in the context of embedded electronics and microprocessors.

So, what really is an interrupt in that context? Well, there is a method by which a processor can execute its normal program while continuously monitoring for some kind of event, or interrupt. This event can be triggered by some sort of sensor, or input like a button, or even internally triggered by a timer counting to a particular number.

We see the event, then what?

So what happens when the event, or interrupt happens? The processor takes immediate notice, saves its execution state, runs a small chunk of code often called the interrupt handler or interrupt service routine, and then returns back to whatever it was doing before. How does it know what code to execute? This is set up in the program. The programmer defines where the processor should start executing code if a particular interrupt occurs. In Arduino, we use a function called attachInterrupt() to do this. This function takes three parameters. The first is the number of the interrupt, which tells the microprocessor which pin to monitor.  The second parameter of this function is the location of code we want to execute if this interrupt is triggered.  And the third, tells it what type of trigger to look for, a logic high, a logic low or a transition between the two.  Let's look at a simple coding example to make more sense of this:

 

     
/* 
Simple Interrupt example
by: Jordan McConnell
SparkFun Electronics
created on 10/29/11
license: Beerware- feel free to use this code and maintain
attribution. If we ever meet and you are overcome with gratitude,
feel free to express your feelings via beverage.
*/

int ledPin = 13;  // LED is attached to digital pin 13
int x = 0;  // variable to be updated by the interrupt

void setup() {                
  //enable interrupt 0 (pin 2) which is connected to a button
  //jump to the increment function on falling edge
  attachInterrupt(0, increment, FALLING);
  Serial.begin(9600);  //turn on serial communication
}

void loop() {
  digitalWrite(ledPin, LOW);
  delay(3000); //pretend to be doing something useful
  Serial.println(x, DEC); //print x to serial monitor
}

// Interrupt service routine for interrupt 0
void increment() {
    x++;
    digitalWrite(ledPin, HIGH);
}

Before running this code, make sure to connect a button to pin 2 and the other side of the button to ground. The main loop of this program turns off the LED every 3 seconds. Meanwhile, this program watches digital pin 2 (which corresponds to interrupt 0) for a falling edge. In other words, it looks for a voltage change going from logic high (5V) to logic low (0V), or ground, which happens when the button is pressed. When this happens the function increment is called. The code within this function is executed, variable x is incremented, and the LED is turned on. Then the program returns to where ever it was in the main loop.  If you play around with it you'll notice that the LED stays on for seemingly random amounts of time but never longer than 3 seconds. How long the LED stays on depends on where you interrupted the code in the main loop. For example, if the interrupt was triggered right in the exact middle of the delay function, the LED would remain lit for about 1.5 seconds after you hit the button.

What are the advantages of this method?

At this point you might wonder, why use an interrupt at all? Why not just occasionally use a digitalRead on pin 2 to check its status? Won't that do the same thing? The answer depends on the situation. If you only cared what the status of the pin was at a certain point in your code or time frame, then a digitalRead will probably suffice. If you wanted to continuously monitor the pin, you could poll the pin frequently with digitalReads. However, you could easily miss data in between reads. This missed information could be vital in many real time systems. Not to mention, the more often you're polling for data, the more processor time that is being wasted doing that rather than executing useful code.

Take a car for instance...

Let's take the system that monitors and controls the anti-lock braking of a car as a critical timing example. If a sensor detects the car is about to lose traction, you really don't care about what part of program is currently being executed, because something needs to be done about this situation immediately to assure the car retains traction and hopefully avoids a car wreck or worse. If you were just polling the sensor in this situation, the sensor may be polled too late and the event could be missed entirely. The beauty of interrupts is that they can prompt execution immediately, when it's necessary.

A quick check can solve the matter

One common problem with interrupts is they often can trigger multiple times for a single event. If we run the code above, you'll notice that even if you press the button just once, x will increment many times. To explore why this happens, we have to take a look at the signal itself.  If we took an oscilloscope to monitor the voltage of the pin at the moment we pressed the button, it would look something like this:

 

courtesy of micahcarrick.com

 

While the main transition of the pin is from high to low, during the process, there are several spikes which can cause multiple interrupts. There are several ways to remedy this. Often you can fix it with hardware by adding an appropriate RC filter to smooth the transition or you can fix it in software by temporarily ignoring further interrupts for a small time frame after the first interrupt is triggered. Going back to our old example, lets add in a fix that allows the variable x to only be incremented once each button press instead of multiple times.

 

     
/* 
Simple Interrupt example 2
by: Jordan McConnell
SparkFun Electronics
created on 10/29/11
license: Beerware- feel free to use this code and maintain
attribution. If we ever meet and you are overcome with gratitude,
feel free to express your feelings via beverage.
*/

int ledPin = 13; // LED is attached to digital pin 13
int x = 0; // variable to be updated by the interrupt

//variables to keep track of the timing of recent interrupts
unsigned long button_time = 0;  
unsigned long last_button_time = 0; 

void setup() {                
  //enable interrupt 0 which uses pin 2
  //jump to the increment function on falling edge
  attachInterrupt(0, increment, FALLING);
  Serial.begin(9600);  //turn on serial communication
}

void loop() {
  digitalWrite(ledPin, LOW);
  delay(3000); //pretend to be doing something useful
  Serial.println(x, DEC); //print x to serial monitor
}

// Interrupt service routine for interrupt 0
void increment() {
  button_time = millis();
  //check to see if increment() was called in the last 250 milliseconds
  if (button_time - last_button_time > 250)
  {
    x++;
    digitalWrite(ledPin, HIGH);
    last_button_time = button_time;
  }
}

This fix works because each time the interrupt handler is executed, it compares the current time retrieved by the function millis() with the time the handler was last called. If its within a certain defined window of time, in this case a fourth of a second, the processor immediately goes back to what it was doing. If not, it executes the code within the if statement updating the variable x, turning on the LED and updating the last_button_time variable so the function has a new value to compare to when its triggered in the future.

Straight to the point

Be aware though that it is important to keep the interrupt handler as short as possible because it prevents the processor from running its normal code, prevents other interrupts of lower priority, and could be called quite often depending what input you have connected. It's best to just update status variables and then leave, and let your normal program do the rest of the necessary work.  Also, on that note, the function delay() does not work within interrupt handlers and should not be used.

Special cases

Its possible to reassign interrupts using the attachInterrupt() function again, but its also possible to remove them by calling the function detachInterrupt(). If there is a section of code that is time sensitive, and it's important that an interrupt is not called during that time frame, you can temporarily disable interrupts with the function noInterrupts() and then turn them back on again afterward with the function interrupts(). This definitely comes in handy on occasion.

Thanks for reading

Well, we have learned a bit about the basics of interrupts and hopefully have realized how useful they can be. Consider integrating interrupts into your next project and always feel free to share them with us! You may just get a front page post out of it.

Comments 37 comments

  • Some of you may find this useful in understanding why pin 2 is associated with interrupt 0.

    From the Arduino.cc reference page: “Most Arduino boards have two external interrupts: numbers 0 (on digital pin 2) and 1 (on digital pin 3). The Arduino Mega has an additional four: numbers 2 (pin 21), 3 (pin 20), 4 (pin 19), and 5 (pin 18).”

    • I was really wondering about this too. I didn’t see any setting of pin 2 in the code. Thank you for the information.

    • thanks. i was like, how does the arduino magically know the button is on pin 2??

    • The AVR INT0..5 can trigger interrupts on edge transitions: on low, rising edge, or falling edge, or changing (both rise or fall). Handy. And available as noted above through attachInterrupt().

      Though not natively available from Arduino APIs,the ATmega 48, 88, 168, 328 family (and others I’m sure) provide pin change interrupt capability on other pins, labeled PCINT0..23.

      I found some code here if you want to make use of this capability within the wonderful world of Arduino. :) http://arduino.cc/playground/Main/PcInt (I haven’t tested/tried the code myself)

      HTH

    • Yep, that’s useful. I’m accustomed to ARMs and dsPICs that have interrupts practically everywhere. Interrupts are simple on the ATmega, but you miss some nice features from more complex processors.

  • Another thing to note is that there aren’t interrupt priority levels on AVR’s (and thus arduino). In effect, they are all blocking, as the default ISR behavior in AVR.h is to disable interrupts when entering an ISR and re-enable them afterwards. Meaning it is even more important that the ISR be as fast as possible, especially if you have other things running in the background that use interrupts (like PWM, for example).

    Also, something to note, while your explanation is technically correct as to why interrupts can be superior to polling, you then state that the ISR should simply set status variables and return. While again also technically correct, it implies that you then need to poll these status variables to notice that there is a change, somewhat muddling your point. You might want to clarify that doing it this way will ensure that you never miss the change, even if you don’t process it immediately. Which is appropriate for the majority of cases, assuming that rate of polling the status variables and processing them is faster than the probable frequency of interrupts. But if something needs to happen RIGHT NOW as a result of the interrupt, it needs to happen inside the ISR. Just pointing out a possible point of confusion.

    • The Xmegas have 3 interrupt priority levels. I don’t know about the 32 bit AVRs.

    • All MCUs have a priority mapping in memory related to interrupts, a timer interrupt will have a higher priority compared to a flag interrupt, but a lower priority than an IRQ.

      Consult user guides to determine the interrupt priority mapping.

      Lastly, it perfectly acceptable to have an interrupt do calculations. I have done a motor speed control design where I did the conversions in the interrupt function. Granted this was the only interrupt in the design, but it was effective.

      Another design was measuring gravity based upon dropping a steel ball down a tube and it breaks 3 IR sensors points, each of these sensors went to an OR gate that went to an IRQ pin. In this case, I had to only send data to global variable for reading.

      If I remember correctly, for Freescale MCUs, there is an interrupt stack which will run. So if 3 interrupts occur at the same time, it executes them in order of priority. I could be wrong, so someone correct me.

      • This is true for interrupts happening simultaneously on AVR, but once inside an interrupt, by default Tiny and Mega cannot service a new interrupt until the current ISR returns (they are blocking). The user can manually re-enable interrupts inside the ISR to allow for nested interrupts, but that’s generally bad practice, nested interrupts can be VERY dangerous.

        • Agreed on embedded interrupt issues. If I remember from my work on Freescale MCUs, you have to disable interrupts in the function. This is because of the explicit stack it uses.

          However, to be the Devil’s Advocate, embedded interrupts can be quite powerful. I always tell people who deal with interrupts to dynamically allocate/deallocate memory when they can. This way read/write issues don’t happen.

          Also, I think you meant to reply to Dovahkiin, and not me.

          • No, I was replying to you :)

            The biggest problem with nested, non-priority interrupts is the possibility of ‘torn writes’, be that to IO or to sections of memory. I’ll use a toy example:

            Say you have an interrupt which, when a pin changes states, reads in a value from the ADC and sets 4 output pins to some value. Say it is triggered, and writes to the first two pins. Before it writes to the last two pins, it is triggered again. The value on the ADC has changed enough to cause it to write a different set of values to the pin. It finishes setting the outputs, then pops back to finish the first ISR. Except now once it writes to the last two pins, you have a set of outputs that doesn’t correspond to either the correct output for the first interrupt or for the second interrupt.

            Nested interrupts are useful with priorities (because, presumably, well written higher priority interrupts wouldn’t have interaction with lower ones), but are generally just bad news without them.

    • Another thing to note is that there aren’t interrupt priority levels on AVRs (and thus arduino).

      Actually, this isn’t true. There aren’t interrupt priority levels on the Tiny or the Mega, but there are on the Xmega (high, medium, and low priority).

      This essentially allows interrupts of a higher priority to take control from interrupts of a lower priority. The awesome part? The Xmega implements round-robin interrupt scheduling in hardware.

      So… let’s say we have four interrupts: A (low-level), B (medium-level), C (medium-level), and D (high-level).

      Interrupt A is triggered, but while it’s running, Interrupt B is triggered as well.

      Now, Interrupt B is running. Since it’s medium-level, it took priority over Interrupt A, which took priority over the standard application code.

      What if Interrupt D is triggered while Interrupt B is running? The AVR will immediately switch to Interrupt D because it’s high-priority and Interrupt B is medium-priority.

      Now, let’s say Interrupt C is triggered while Interrupt D is running. Interrupt C is a lower priority than Interrupt D, and Interrupt B is already running. Here’s what will happen.

      The AVR will wait for Interrupt D to finish, then will also wait for Interrupt B to finish. Once both are done, it will call Interrupt C.

      What about Interrupt A? Well… in this scenario, it will finish when Interrupt C is done.

      I believe (to my best knowledge) this is how interrupt priority works on the Xmega. If anyone has any corrections, you’re welcome to make them.

      Here’s what the call stack looks like (I think…) Application Code Interrupt A (low-priority) Interrupt B (medium-priority) Interrupt D (high-priority) Interrupt C (medium-priority)

  • I am using the Arduino Duemilanove board. I am using both INT0 and INT1 pins on the board to run two encoders. However, while interrupt comes on INT1, the ISR of both INT1 as well as INT0 are executed. The same problem does not occur when interrupt comes on INT0. The same problem occurs with other boards that i tested too. I am using the attachinterrupt() function.

  • Not to be the C code police, but one problem with your tutorial is that you are using global variables when they are not necessary. It would be far better to declare your last_button_time as static inside the ISR. This practice is standard in most cases as it reduces name collisions and bugs in the final program. Your code would look something like this:

    void increment {
        static unsigned long last_button_time;
        unsigned long button_time = millis();
    
        ...
    }
    

    The point is, for the example given, global variables are a poor choice, if you ask me. …and I’m also not expanding on the lack of the use of the volatile keyword if you insist on using globals…

    It’s a good tutorial otherwise. I would just like to show others better coding practices wherever possible.

    • Last_Button_Time needs to be global because it has to have its state saved between ISR calls, and ISRs cannot take or return parameters so there’s no other way to get it the information. But I agree that there’s no reason that Button_Time needs to be global, since it’s basically just a temp variable used to check against/replace the value in Last_Button_Time.

      In this situation Volatile isn’t needed because the values in the variables will never be red/written outside the scope of the ISR. Volatile simply forces the compiler to read the value from its memory location every time, rather than performing the optimization of loading its value once into a register and reading from that instead. But volatile would absolutely be needed if the ISR was going to change status variables that the main program needed to read. Otherwise the ISR would change the value in memory, but the main program might never see it because it’s just reading from the copy inside a register. Good catch.

      • I agree with Signal7. The last_button_time variable is only touched when initialized, and when it is checked or set within the ISR function. A static variable with function scope is ideal. See https://en.wikipedia.org/wiki/Static_variable for a good description.

        The button_time variable could be a stack variable or static with function scope as well, just to minimize stack use during the ISR. Not a big deal with non-reentrant ISRs; either way is fine.

        Also, where are the pinMode() calls in setup()? Am I missing a reference somewhere that explains the default state of each pin and why it’s not necessary to set pin2 to INPUT or 13 to OUTPUT?

  • The fact that there are only 2 interrupts available “easily” in the Arduino code means you run out of “interrupt” pins really quickly. Fortunately, It is actually really easy in the Arduino language to access the rest of the Pin Change Interrupts with only a couple of lines of code. It only takes two lines of code to activate them. It does require opening the datasheet for the micro-processor which might be a little bit scary for first timers, but once you’ve done it once it is easy after that. If you look at the pinout of the chip, and pin marked “PCINT#” where “#” is a number can be used as an external interrupt. The Arduino language has all of the registers mapped to constants which makes them really easy to control.

    I recently finished a scoreboard for Ultimate Frisbee games that is driven by an Arduino Pro Mini 328. I’m using pin change interrupts on almost every pin to read the state of 9 buttons and one rotary encoder. The main loop basically just sits there and updates the game clock until it sees a flag being set by the interrupt routines telling it that a button has been pressed.

    Here’s a tour of the scoreboard.

    http://youtu.be/Q-YWABsCgqs

    Here’s an excerpt from my code. This should be all you need to access the pin change interrupts (just make sure you have set the relevant pins as inputs).

    void setup() {
    PCMSK1 = _BV(PCINT14) | _BV(PCINT8) | _BV(PCINT9) | _BV    (PCINT10); //Enable Pin Change interrups on PCINT 14, 8, 9, and 10
    PCICR |= _BV(PCIE1);// Enable interrupt control register
    ...
    }
    
    
    
    
    ISR(PCINT1_vect) {
      //whenever one of the pins set to 1 in PCMSK1 changes     state, this routine runs.
      // Put some code here to figure out what which button was     pressed. 
      //set a flag  that is read by the main loop to do something     based on the 
      //pressed button. 
    
    }
    
  • I just recently got in trouble using interrupts with I2C. I am using a port expander (MCP23008) which will generate an interrupt on any of the bit changes. Great function, easy to use, except - that during the ISR (Interrupt Service Routine) I was trying to read the device to determine which bit changed. Bad idea, this chip uses the I2C communications which require interrupts, but interrupts are turned off during an ISR and everything goes kinda south.

    Bottom line - don’t do things that will require an interrupt during an ISR; rather, set a flag that will be “POLLED” to let some non-interrupt routine read the device.

  • Shouldn’t the x variable be declared volatile since this is a tutorial on interrupts? I haven’t used arduino platform before, so maybe it isn’t necessary?

    • You’re correct.

      From http://arduino.cc/en/Reference/Volatile:

      A variable should be declared volatile whenever its value can be changed by something beyond the control of the code section in which it appears, such as a concurrently executing thread. In the Arduino, the only place that this is likely to occur is in sections of code associated with interrupts, called an interrupt service routine.

  • Minor nit, your button_time/last_button_time code has integer wrapping issues. The interrupt will no longer do its work when button_time < last_button_time.

    I know that’s not the point of the tutorial, but people tend to copy code snippets like that, and that would be a real beast to debug. (button suddenly stops working after two months of operation)

    • The “standard” way of handling this is to make the counter variables unsigned, but make the comparison signed using the same size variable. If the counters wrap 0xffffffff to 0x00000000 then all is OK.

      The code as written works correctly on gcc 4.2.1 (i686-apple-darwin10; haven’t checked arduino) because the subtraction in the if statement is being done into long. One could be more explicit about it by casting the subtraction as a long before making the comparison: if ((long)(button_time - last_button_time) > 250)

      If the subtraction gets up-cast into a bigger variable, then there will be a wrap problem. For example if the counters were unsigned char, and the subtraction was not cast as char, then there will be a wrapping problem: unsigned char t1=0xff; unsigned char t2=0x01; if (t2 - t1 > 0) printf (“greater”); // Does not work! if ((char)(t2 - t1) > 0) printf (“greater”); // Works!

    • Good point

    • Funny thing about that is that there was a MAJOR air traffic control disaster caused by exactly that same problem. You know what the recommended fix was? Restart the system every three weeks.

  • The Arduino framework appears to only support the AVR’s external interrupts. The ATmega48/8/88/168/328 used in most Arduinos only has two external interrupts. The 40 pin xmegas have three, and the larger ones even more. However most recent mega AVR’s also have a pin change interrupt. This is activated when ANY pin in a port (that isn’t masked out) changes state. For the 28 pin megas that would give us three more interrupt vectors (one for each port). However, I don’t think the Arduino IDE supports port change interrupts directly (though you could still program for this by writing your own library functions).

    The port change interrupts could be used to support a rotary encoder. Simply mask off all but two pins on a given port and attach the two signals from the encoder to those pins. The function called by the interrupt resulting from the pin state change will read the new state of the two pins, and compare them in a state machine to process the new position of the encoder.

    • While this is technically true, AVR’s don’t work very well as encoder counters unless that’s all they’re dedicated to doing. The frequency of interrupt calls for even a single quadrature encoder of reasonable resolution on a motor spinning at even a few hundred RPM will eat up the vast majority of your CPU time. You’re much better off using a dedicated encoder IC such as the HCTL-20XX series (http://search.digikey.com/us/en/cat/integrated-circuits-ics/interface-specialized/2556698?k=Quadrature%20Encoder) and talking to it over SPI.

      • I think Scharkalvin was speaking of this kind of rotary encoder: https://www.sparkfun.com/products/9117

        It would take a lot of finger-gespinning to take up all the CPU with that…

      • Speaking of which… I feel like that would be a really useful product for you guys to carry

  • I don’t know what to do with a Schottky diode because I’m a mechanical engineer, LOL.
    But for debouncing, I’ve had success by adding a Schmidt trigger to an RC circuit.

  • A very helpful tutorial! :) I’ve never really understood timer interrupts though. Could someone shed some light on that?

    • It’s like having a little gnome inside the MCU holding a stopwatch and pressing an internal button right on time to fire a special interrupt.

      Many MCUs provide internal timers which are just counters. And they can be configured to fire an internal interrupt that can be handled by your code.

      There’s more features that let you pick when to fire the interrupt; perhaps when the counter wraps, or when the counter is greater than some value you set.

      That’s a gross oversimplification but… hope it is helpful.

      • Thanks for the answer, clarified the concept a bit. I’ll go and search for some tutorials again. It’s a bit confusing, because the timer tutorials I’ve read use AVR code instead of Arduino code :)

        • The reason for this is because Arduino really only allows you to access one timer for everything, most MCUs have at least two timers.

          The reason why Arduino only allows access to one is because you have to set registers to setup timers and features on the MCU, and you can really customize it. And by allowing the user to have access to more than one can break the underlying Arduino code. (This is why I hate using Arduino code for anything useful).

          I strongly recommend learning the C code on how things work, and you will realize the power you have at your control. Granted there is a learning curve, but you will see why using pure C is better than relying on ArduinoC.

          • The reason they only give you the one timer is because the other timers are used by the PWM library and a few other background tasks. It’s not that you couldn’t very easily write high level functions to set up the timers without having to deal with registers directly (it’s basically the first thing I do when working on bare AVR).

            I really wish that Arduino would publish a list of what interrupts are used by what library functions and how, so that you could overwrite them with your own if you weren’t using that particular library. It would open up the possibilities for the platform quite a bit.

            • Why would they do that? People want easy! People want things to be decided for them! I’m just happy they’re open enough to let people do whatever.

        • This is probably because there is no way to access the timers directly in Arduino, as they are used by many library functions which would break if you were to overwrite something (such as PWM). It is a sacrifice you make for the sake of simplicity, although it’s a pretty large sacrifice.

  • In the case with the button. I think you’re trying to refer to “debouncing.” Basically, buttons and switches have an issues where when you push them, they don’t do true press, but rather bounce before calming down. For an interrupt to not be hit multiple times from this, it is a good idea to either put in a software delay in the interrupt, or put additional hardware, as an example, a Schottky Diode.

    Now if you have a series of multiple interrupts that all use the IRQ line, you need to do some fancy things. Easiest way is to put all your IRQ interrupt lines through OR gates and then branch those signals off to another pin and do a switch/case for pins going high/low. It takes up more pins, but it works.