Maker.io main logo

The Do’s and Don'ts of Using Arduino Interrupts

2022-03-09 | By Maker.io Staff

In a previous article, I outlined what interrupts are and how to use them in your custom Arduino sketches. In that article, I didn’t discuss Interrupt Service Routines (ISRs) in too much detail. Therefore, this article introduces you to some best practices when working with interrupt handlers, and it also discusses a few things you should not do inside an ISR.

A Simple ISR Example (The Do’s)

In that previous article, I used the following short interrupt-driven sketch, which I’ll also use throughout this article:

Copy Code
volatile bool execute = false;

void setup()
{
  pinMode(2, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(2), onDetectInterrupt, FALLING);
}

void onDetectInterrupt()
{
  execute = true;
}

void loop()
{
  /* Other code */

  if(execute)
  {

	// Don't forget to reset the flag
	execute = false;
  }
}

This brief example already demonstrates two things you should remember when working with interrupt callback functions. First, keep the ISR as short as possible. Whenever the Arduino calls an ISR, it interrupts the other process it was running when it encountered the event that caused the interrupt. The microcontroller (MCU) interrupts not only previous code execution, but it will also not immediately react to other interrupts while it’s busy executing an active ISR. Therefore, long interrupt callbacks delay the execution of not only the regular program code but also of other incoming interrupts. ISRs should be as short as possible so that the MCU does not spend more time executing them than is absolutely necessary.

Next, it’s common to use global variables inside of ISRs to communicate with other parts of the program. However, you have to make sure that all global variables you use inside and outside of ISRs are marked as volatile so that the compiler does not attempt to optimize them. If you don’t label a variable as volatile, the variable might end up in a processor register. However, the CPU will always read volatile variables from memory. Therefore, do not mark variables as volatile unless needed, as accessing the main memory is slower than reading a variable from a register. Further, use only atomic operations, such as assignments, on variables in your program that are shared between the ISR and non-ISR code. Otherwise, changes to the variable may be lost.

Lastly, you can see that I only used a single assignment call in the ISR. All other tasks that might take longer to process will be performed in the loop()-method once the MCU returns from executing the ISR.

Things to Avoid in Programs with Interrupts (The Don’ts)

Do not use delay(), millis(), or micros() inside of an ISR. These functions rely on interrupts themself, so they won’t work while the processor handles your custom interrupt callback function. You can request the current time using millis(), for example, but the value returned by the function will not change inside an ISR. In general, you should never have to use any wait operation inside an ISR. As discussed above, the runtime of an interrupt handler should be as short as possible. However, delayMicroseconds() will function inside custom ISRs because it does not rely on interrupts.

You also should not use loops in an ISR. These might take too long to complete, which will delay all other interrupts and code execution. Instead, set a flag and put the loop in the Arduino sketch’s loop()-method.

Finally, do not use Serial.print() statements inside an ISR, and don’t attempt to read serial input in an ISR. The serial library uses interrupts as well, and even if they didn’t, both operations would still take way too long to execute and cause problems that are difficult to trace and debug.

Summary

Custom interrupt handlers in Arduino programs allow the MCU to react to external events. There are a few things to keep in mind when writing an ISR. Most importantly, ensure that the ISR is as short as possible and only performs atomic operations on shared variables. Furthermore, you should declare shared variables as volatile to prevent them from getting cached in a CPU register. Do not use loops, delay(), millis(), serial print and read commands, or micros() inside an ISR.

If you have to perform complex tasks whenever a specific input happens, it’s best to set a flag when the Arduino calls the ISR. Then, check the flag in the loop()-method of the Arduino sketch. If the flag is set, perform the more elaborate processing in the loop()-function. Do not put tasks inside of an ISR that take a long time to process, as long-running ISRs can cause various problems that are very difficult to debug.

制造商零件编号 ABX00028
ARDUINO NANO EVERY
Arduino
制造商零件编号 A000093
ARDUINO MICRO W/OUT HDRS ATMEGA3
Arduino
制造商零件编号 ABX00027
ARDUINO NANO 33 IOT
Arduino
制造商零件编号 A000073
ARDUINO UNO SMD R3 ATMEGA328
Arduino
制造商零件编号 A000066
ARDUINO UNO R3 ATMEGA328P BOARD
Arduino
制造商零件编号 ABX00012
ARDUINO MKR ZERO W/ HDR ATSAMD21
Arduino
制造商零件编号 K000007
STARTER KIT W/ARDUINO BOARD
Arduino
Add all DigiKey Parts to Cart
TechForum

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.

Visit TechForum