Interrupts are like the express lane at the supermarket. Be considerate and keep it to 10 items or less and everything will run smoothly.
If everything is high prority, then nothing is high priority.
Interrupts handlers should be used for processing high-priority, time-sensitive events only. Remember that interrupts are disabled while you are in the interrupt handler. If you try to do too much at the interrupt level, you will degrade response to other interrupts.
One interrupt at a time.
When in the ISR, interrupts are disabled. This has two very important implications:
- Work done in the ISR should be kept short so as not to miss any interrupts.
- Code in the ISR should not call anything that requires interrupts to be active (e.g. delay() or anything that uses the i2c bus). This will result in hanging your program.
Defer lengthy processing to the loop.
If you need to do extensive processing in response to an interrupt, use the interrupt handler to do only what is essential, then set a volatile state variable (see below) to indicate that further processing is required. When you call your update function from the loop, check the state variable to see if any follow-up processing is required.
Check before re-configuring a timer
Timers are a limited resource. There are only 3 on an UNO and they are used for many things. If you mess with a timer configuration, some other things may not work anymore. For example, on an Arduino UNO:
- Timer0 - used for millis(), micros(), delay() and PWM on pins 5 & 6
- Timer1 - used for Servos, the WaveHC library and PWM on pins 9 & 10
- Timer2 - used by Tone and PWM on pins 3 & 11
Share Data Safely
Because an interrupt will suspend whatever the processor is doing to process the interrupt, we have to be careful about sharing data between interrupt handlers and the code in our loop.
Sometimes the compiler will try to optimize your code for speed. Sometimes these optimizations will keep a copy commonly used variables in a register for fast access. The problem is, if one of those variables is shared between the interrupt handler and the loop code, one of them may end up looking at a stale copy instead of the real thing. Marking the variable as voltatile tells the compiler not to do those potentially dangerous kids of optimizations.
Protecting Larger Variables
Evan marking a variable volatile is not enough if it the variable is larger than an integer (e.g. strings, arrays, structures etc.). Larger variables require several instruction cycles to update, and if an interrupt occurs in the middle of that update, the data can be corrupted. If you have larger variables or structures that are shared with interrupt handlers, you should disable interrupts when updating them from the loop. (Interrupts are disabled in the interrupt handler already by default.)