This is first part of an intended series about interrupts.
If you are just starting out with microcontrollers or microprocessors, you have probably written a simple program that turns an LED on and off, reads a button, or sends some text over a serial port. These programs usually follow a straight line: do this, then do that, then loop back and do it all again. That works fine for very simple tasks. But real-world devices rarely have just one thing to do, and the world does not wait politely while your microcontroller finishes what it is busy with. That is exactly the problem that interrupts solve.
The Problem: Doing Two Things at Once
Imagine you are cooking dinner. You have a pot of water on the stove, and you need to know the moment it starts boiling. You have two choices:
- Stand at the stove and stare at the pot constantly until it boils. You cannot do anything else during this time.
- Go about your business — chop vegetables, set the table, wash dishes — and let a kitchen timer or a whistle on the kettle interrupt you the moment something needs your attention.
Microcontrollers face the exact same dilemma. The first approach — constantly checking whether something has happened — is called polling. The second approach — letting an event notify the processor when attention is needed — is called using interrupts.
Polling: Simple but Wasteful
Here is what polling looks like in practice. Suppose you want to detect when a user presses a button connected to a pin on an Arduino:
void loop()
{
if (digitalRead(buttonPin) == LOW)
{
// Button was pressed, do something
doSomething();
}
// Everything else the program needs to do
doOtherStuff();
}
This works, but it has a serious weakness. The processor has to keep coming back to check that if statement over and over again, thousands or millions of times per second, even when nothing is happening. Worse, if doOtherStuff() takes a long time to complete, you might actually miss a button press entirely, because the processor was busy elsewhere when it happened.
On a small microcontroller running a simple loop, polling might be acceptable. But as your program grows more complex — reading sensors, driving displays, communicating over wireless — polling becomes unreliable and wastes precious processor time on busy-waiting.
Interrupts: Let the Hardware Tap You on the Shoulder
An interrupt is a signal sent to the processor by hardware — a pin, a timer, a communication peripheral, or even an internal condition — that says: “Stop what you are doing. Something important just happened. Deal with it now.”
When an interrupt fires, the processor does the following automatically:
- Finishes the current instruction it is executing.
- Saves its current state (where it was in the program, what values were in its registers) so it can come back to exactly where it left off.
- Jumps to a special function called an Interrupt Service Routine (ISR) — also called an interrupt handler.
- Executes the ISR.
- Restores the saved state and resumes the main program as if nothing happened.
From the perspective of your main program, the interrupt is nearly invisible. It is handled in the background, quickly and automatically.
A Simple Real-World Analogy
Think of the processor as a factory worker on an assembly line. Polling is like that worker stopping every few seconds to walk to the front door and check if a delivery has arrived — even when no delivery is expected. Interrupts are like installing a doorbell. The worker keeps doing their job uninterrupted, and only stops when the bell rings. The bell is the interrupt. Answering the door is the ISR.
Types of Interrupts
Not all interrupts come from the same source. They fall into several broad categories:
1. External (Hardware) Interrupts
These are triggered by a change on a physical pin of the microcontroller. A button press, a sensor signal crossing a threshold, or a signal from another chip can all cause an external interrupt. You can usually configure whether the interrupt fires on a rising edge (signal goes from low to high), a falling edge (high to low), or both edges.
2. Timer Interrupts
Microcontrollers have built-in hardware timers that count clock cycles independently of the main program. When a timer reaches a certain value, it can trigger an interrupt. This is how you create precise timing — playing audio, generating PWM signals, sampling sensors at exact intervals — without freezing your main loop to count time manually.
3. Peripheral Interrupts
Built-in peripherals such as UART (serial communication), SPI, I2C, ADC (analog-to-digital converters), and USB can all generate interrupts. For example, a UART can interrupt the processor the moment a new byte arrives over a serial connection, so the processor does not have to sit waiting for data.
4. Software Interrupts
Some processors allow the program itself to trigger an interrupt intentionally. This is used in operating systems and advanced embedded systems to switch between tasks or call system services.
5. Non-Maskable Interrupts (NMI)
Most interrupts can be temporarily disabled (masked) by the programmer when doing something critical. Non-maskable interrupts cannot be disabled. They are reserved for the most urgent events — hardware faults, power failures, memory errors — things the system must always respond to.
The Interrupt Vector Table
Every microcontroller has a structure called an Interrupt Vector Table (IVT). This is a fixed area of memory — usually at the very beginning or very end of program flash — that contains addresses pointing to each ISR. When an interrupt fires, the hardware automatically looks up the correct address in this table and jumps to it. As a programmer, you do not usually manage this table directly; the toolchain and your ISR declarations handle it for you. But it is important to know it exists.
A First Look Across Platforms
Different microcontroller families handle interrupts in slightly different ways. Here is a quick preview — we will go much deeper in later parts.
Arduino (AVR / ARM based): Arduino provides a beginner-friendly function called attachInterrupt() that lets you connect an external pin interrupt to a function with just one line. Under the hood, it is configuring the AVR or ARM hardware interrupt system for you.
// Arduino example: trigger ISR when pin 2 goes LOW to HIGH
attachInterrupt(digitalPinToInterrupt(2), myISR, RISING);
void myISR()
{
// This runs automatically when pin 2 rises
}
STM32: STM32 microcontrollers from STMicroelectronics have a dedicated peripheral called the EXTI (External Interrupt/Event Controller) and a NVIC (Nested Vectored Interrupt Controller) that gives you fine-grained control over interrupt priorities. STM32 interrupts are more powerful but also more configurable than Arduino’s simplified API.
ESP32 / ESP8266: Espressif’s ESP chips support interrupts on almost any GPIO pin and add the complication of having two processor cores (on ESP32), which means interrupts and concurrency need extra care. They also support interrupts from their Wi-Fi and Bluetooth hardware stacks.
MSP430: Texas Instruments’ MSP430 is famous for ultra-low power consumption. Its interrupt system is central to its power strategy — the processor sleeps almost all the time and wakes only when an interrupt demands attention. This makes the MSP430 ideal for battery-powered applications.
Microchip PIC / AVR: Microchip’s PIC family (and the AVR family it now owns) have straightforward interrupt systems with a global interrupt enable bit and peripheral-specific enable bits. PIC architectures vary quite a bit between the 8-bit PIC16/PIC18 and the 32-bit PIC32, which uses a MIPS core with a more sophisticated interrupt controller.
Why Interrupts Matter: A Summary
To bring it all together, here is why interrupts are one of the most important concepts in embedded systems:
- Responsiveness: Your system reacts to events the instant they happen, not whenever the main loop gets around to checking.
- Efficiency: The processor is not wasting cycles polling for events that may never come.
- Timing accuracy: Timer interrupts allow precise, hardware-driven timing that is independent of software execution time.
- Power saving: Processors can sleep deeply and wake only on interrupt, dramatically extending battery life.
- Scalability: As your firmware grows in complexity, interrupts let you handle many concurrent events cleanly.
What Is Coming in the Next Parts
Now that you understand what an interrupt is and why it exists, the following parts of this series will take you deeper:
- Part 2 — Writing your first ISR: rules, pitfalls, and the volatile keyword. Practical examples on Arduino, STM32, ESP32, MSP430, and PIC.
- Part 3 — Timer interrupts in depth: generating precise timing, PWM, and periodic tasks.
- Part 4 — Interrupt priorities, nesting, and the NVIC (STM32 focus, with comparisons to other platforms).
- Part 5 — Peripheral interrupts: UART, SPI, I2C, ADC, and how communication is driven by interrupts in real firmware.
- Part 6 — Power management and sleep modes: how interrupts wake a sleeping processor, with MSP430 and ESP32 examples.
Interrupts are not a complicated concept once you see them in action. In Part 2, we will write real interrupt handlers and learn the golden rules every embedded developer must follow to keep ISRs safe and reliable.