BEGINNERS: Interrupts - Why, When and How

There's been a lot of chatter about (mis)using interrupts recently... often not for the right reasons.

LET'S DISCUSS SOME ARDUINO PROGAMMING FUNDAMENTALS

setup() is executed ONCE after the bootloader, then all program flow is permanently passed to LOOP()
setup() is a convenience of the Arduino environment and doesn't exist in pure C/C++

setup() is intended to 'initialise' the operating parameters for the purposes of the program.
Usually this is all the initialising needed, but there are cases where initialisation can be performed 'locally' to conserve memory etc.
(Read about variable 'scope' and object 'instances')

loop() in Arduino-land is as close to main() is in C/C++. Normally a C/C++ developer would look after the initialisation code, and main-loop() themselves within the main() function.

loop() repeats continuously at the full clock speed of the processor (minus cycles-per-instruction and any barriers (delay etc) imposed by your code). Theoretically, this means it's possible to get close to 4-million* things per pass of loop(). In real terms - you may burn 200 instructions just doing the housekeeping - so 4-million / 200 = 20,000 loop passes per second more-or-less in a real situation.

EXAMPLE: Do you really need to check your pushbutton 20,000 times a second? Probably not.
The solution for this example is much simpler - POLLING
This underscores the reason we all denounce delay() in favour of millis() timing... why waste processor time doing nothing while delay() twiddles its thumbs... ! You'd be surprised how much work an 8-bit 16MHz processor can do!

[[Recall that an old Z80 CP/M computer of the 80's was working with less memory, a quarter of the speed - yet could handle multiple simultaneous database users on serial terminals and printers.]]

POLLING - is the action of 'testing' the input pin(s) each time we go around loop() or any other looping construct - frequently enough to detect the desired events. For a button - that would typically be something less than 10 times a second,

Remember buttons are not only pressed, but they are also released - read about detecting state-change.
This applies equally to all binary (two) state inputs.

NOW - WHAT ABOUT INTERRUPTS
Interrupts are very useful - especially for short-high-frequency events (not buttons#)
An interrupt uses some external (or programmed) event to literally interrupt the current program flow - to capture, mark or perform some other very simple activity.
Program execution returns and continues back where it was interrupted - as soon as the ISR (Interrupt Service Routine) has completed.

HANDLING INTERRUPTS
Short & Sweet. Literally!
The ISR should not spend any more time than absolutely necessary needed to identify what caused the interrupt event, increment or flag 'it' then return. This will keep your program snappy and responsive@, and will not cause any ripple consequence to the main program execution. It MUST be complete before any other interrupt occurs, or you'll start an unenviable journey down the stack & crash road.

Your main() code can check those 'ISR' flags or values to see if changes need to be processed - and perform that at a leisurely pace - as part of the normal code/scheduling within your program.

TAKE-AWAY
Interrupts are useful, but can easily be overused. Keep it simple!
The ISR function should be as short and to-the-point as possible, to reduce latency and other unwanted consequences.
Process your fast events at a leisurely pace, Rarely do you need to act on every fast input event soon as it comes in... you can accumulate or post-process when you have time in the main code.
(If you do, you're probably using the wrong processor, or need to really rethink your code architecture.)

Footnotes
* on a 16MHz 8-bit Atmel AVR
# interrupt handling of low-speed events is an option, but for specific reasons it is unlikely to be found in 90% of Arduino applications due to the side-effects / overhead of managing the interrupts.
@ consequences of overflowing interrupts and performance impacts are particularly relevant on low-speed processors with limited stack space - as each interrupt event uses the stack, and if they overlap - you will eventually 'run out' of stack space!

If you conclude that you really do need to use interrupts then Nick Gammon's interrupt tutorial is very useful.

...R

lastchancename:
NOW - WHAT ABOUT INTERRUPTS
Interrupts are very useful - especially for short-high-frequency events (not buttons#)
An interrupt uses some external (or programmed) event to literally interrupt the current program flow - to capture, mark or perform some other very simple activity.
Program execution returns and continues back where it was interrupted - as soon as the ISR (Interrupt Service Routine) has completed.

Interesting philosophy. The rules that have always been imposed on my programs in industry have been somewhat opposite. Why waste time polling for something that rarely happens (buttons)? If it occurs frequently then poll, because you are polling 20,000 time per second. Use interrupts for the buttons that occur less frequently and thus carry less overhead. Or...An absolute reason to use interrupts is to detect the change of state of anything, instantly, critically. If you are looking to measure the amount of time between occurrences (such as button presses, parts passing in front of a sensor, etc.) then one should be looking to use an interrupt. The interrupt will all the program to snapshot the timestamp closer to the event than polling will provide.

Thanks for raising that.

Oddly enough, I /partially/ agree, but why spend cycles and invest delelopment time on something occasional. and largely unnecessary with modern fast micros?
Which raises an important point about polling vs interrupts - interrupts are now, while polling is soon, so the ‘main() loop period’ vs ‘interrupt latency’ is a very convincng argument that needs to be considered.

I also did what you suggest n the past, but on hardware with more interrupt handling resources, and probably still would if using those platforms.

There is a sweet spot between hardware optimisation, and code performance, but that’s somewhere beyond this discussion and most small-system hobby coders.
Good point though.

To my mind the argument against using interrupts is the complexity they present to a newbie.

Most newbies have not grasped the concept of how a simple program should be structured and we often see cases where someone proposes to use an interrupt as a solution for poor program design.

And if the newbie has not grasped the rudiments of program structure the unpredictable way in which interrupts interfere with a program can lead to a complete mess.

...R

The very name ISR (Interrupt Service Routine) says that there is something to serve. Initiation of smoke/fire in an industry is an infrequent case; but, once it is initiated, the service has to be rendered on urgent basis, and the actions must be taken in the ISR. Therefore, returning to the MLP just by raising a flag in the ISR undermines the very good purpose of the interrupt idea.

It MUST be complete before any other interrupt occurs, or you'll start an unenviable journey down the stack & crash road.

The ISR will take as much time as it requires to finish the assigned tasks. No other interrupt will be taken care of by the processor until the present one is finished. In the Arduino Platform, the interrupt structure is disabled when the processor arrives at the ISR. The interrupt structure is automatically enabled when the control returns to MLP (Main Line Program).

@GolamMostafa, you are missing the important point that the Arduino system uses other interrupts for other purposes and the reason for a user keeping his ISR very short is to allow those other interrupts to continue their normal activities.

...R

Thanks @Robin2; I have noted down.

Probably a good time to insider that the Arduino runtime libraries
ARE NOT A REAL-TIME OPERATING SYSTEM.
If you want to use Arduino in a critical real-time control application, you may need to consider the conversation above, and some more complex libraries.
It can do a lot fast, but can’t do everything now.

TAKE-AWAY (thanks Robin2)
As-is, the Arduino run-time libraries can only handle a single interrupt at a time.
If your ISR is still busy when the next interrupt occurs - from anywhere - it will not be seen.

Keep the ISR code as short as possible... no delay(), no print(), just straight to the point. Do the thinking when you have spare time.

lastchancename:
If your ISR is still busy when the next interrupt occurs - from anywhere - it will not be seen.

Depending on the interrupt and its handling there is a possibility that it might not be recognized,
if its a different interrupt, or the same but already acknowledged at the time of occurence,
the new interrupt will be serviced after the end of the current interrupt,
or whenever interrupts are enabled inside the current ISR.