Interrupts are an integral parts of computing (not just in embedded systems) but often misunderstood. Modern computers cannot function without interrupts, as for embedded systems? Well, that depends on your design. Sometimes you won’t need interrupts, and sometimes they will be a critical part of your application. To understand if you need to use interrupts, you need to understand what an interrupt is.
Introduction to Interrupts
Let’s imagine that you are home. You are expecting a parcel; a new computer, a video game, tickets to a concert… Anything you want. Since the local delivery system informed you that delivery will occur somewhere between 8AM and 8PM, you do not have a precise idea when the delivery will occur.
You keep on working throughout the day, but you are eager to know when the delivery occurs. So, once every so often, you stop what you are doing and look at the letterbox. No parcel? Back to work.
Now imagine that the parcel is being delivered by another delivery service, one that knocks on the door. You are busy working, and then you hear the doorbell. You stop what you are doing, go and answer the door, and get your parcel.
This analogy is incorrect for a few reasons, but it does illustrate the idea, and introduces two notions; polling and interrupts.
Constantly looking at the letterbox is known as polling; you are actively looking at something to see if it has changed. In development, this is equal to:
if(readDigitalPin(10) == 1).
You are looking at a value at this specific moment, and looking at the value. The value might have been different before, and might be different later. If you are looking at the state of a pushbutton, it will return the state of the pushbutton to react to a user’s input, but any input done previously might not have been taken into account.
An interrupt is when a notification arrives, forcing you to stop what you are doing. When the doorbell rings, you need to get up and answer it, otherwise the deliveryman will leave a note saying you weren’t home. An interrupt arrives during program execution, and forces the program to stop, while you run an interrupt handler. In the case of an embedded system, your application will immediately be informed of the user activating the pushbutton.
What the deliver analogy fails to explain is the way in which interruptions are handled. When the doorbell rings, your brain stops writing that email, and concentrates on the doorbell. A quick calculation is done, and your brain is informed that there is something else that needs to be done. However, there is a decision that needs to be made.
Your microcontroller is designed to run code. Anything that stops it from performing that task is known as an Exception. Interrupts are exceptions, even a reset is an exception (since, by definition, a reset stops a device from performing the task at hand).
A Vector Table is a location in memory that contains memory addresses. It contains either the memory address of the code to run (known as Predefined), or contains the address of the address to run (known as Fetch). When an exception occurs, the table is read, and depending on the type of table, some actions are performed. Generally, further interrupts are disabled. Then the ISR is called.
The microcontroller is in a specific state. Since interrupts are now disabled, any further input will be ignored, until interrupts are enabled again. Therefore, the ISR has to be as quick as possible. If your pushbutton input is supposed to turn on an LED, then that is fast enough, and can be placed inside the ISR. On the other hand, if the pushbutton initiates a network transfer of a data value, then this has to be handled outside of the ISR. In this case, you would activate a flag, and handle this later.
Once the ISR is complete, interrupts are reactivated, and your microcontroller returns to the memory location where it left off, continuing the program as if nothing happened. Inside your main loop, you can periodically check to see if the flag was raised or not, and then act on that information.