Please consider a donation to the Higher Intellect project. See or the Donate to Higher Intellect page for more info.

Interrupts might seem basic, but many programmers still avoid them

From Higher Intellect Vintage Wiki
Jump to navigation Jump to search
Interrupts might seem basic, but many programmers still avoid them

Scott B Rosenthal

When I first started working with microcontrollers, interrupts seemed like
a magical mechanism that only a rare few really understood. Intimidated, I
religiously avoided them and stuck with polled I/O. After a while, though,
I decided to become one of the few, the proud, the programmers that not
only used interrupts but exploited all their capabilities. Just like taking
apart a toaster oven to see how it works, I disassembled all the
interrupt-driven code I could find. Now, interrupts are easy, fun and above
everything else, extremely necessary for today's embedded systems.

In case you're wondering why I'm taking a column to talk about something so
basic, the answer is twofold: First, in the past few months, I've examined
(and chased bugs in) several examples of code by people who were supposed
to know what they were doing with interrupts, yet they consistently got
part of the implementation wrong. Second, in my company the one part of an
instrument's operating system that I always receive questions on is how it
manages interrupts. Therefore, it's time for a review.

The basics

Obviously, each microcontroller and microprocessor handles interrupts
somewhat differently, yet they all share some common functionality.
Basically, an interrupt causes a program to suspend its current operation
and branch to a location elsewhere in memory. Then, after the program
handles the event that caused the interrupt, the interrupt service routine
(ISR) must restart the program from where it had previously been suspended.

Electrically, an interrupt is a digital signal into a CPU that indicates
some event has happened. For example, receiving serial information,
pressing a key or a timer expiring can all generate an interrupt. Expanding
on the keypress example, a common way of generating an interrupt is with
the venerable 74C923 20-position keypad encoder. When you press a key, this
chip's debounced Data Available (DA) signal goes High and stays there until
you release the key. By tying this signal to one of the CPU's interrupt
inputs, the processor can sense a keypress as soon as it happens.

Interrupts come in three flavors: edge-triggered, level-triggered and a
combination of both. As the name indicates, edge-triggered interrupts
happen on the transition of a signal from one state to another, primarily
from a Zero to a One. This type of interrupt is useful for a fleeting
signal that doesn't last long enough for the processor to recognize it
using polled I/O or for when the signal can last a long time, but the
significant event is when that signal first goes active. Again, the
keypress is an excellent example of an application that calls for an
edge-triggered interrupt. From an interrupt's viewpoint, the amount of time
you hold the key down doesn't matter. All that's important is detecting
when the event first happens.

By contrast, level-triggered interrupts are in some ways like polling
except that the CPU manages the polling without program intervention.
Typically, the processor samples the interrupt input at predefined times
during each bus cycle such as state T2 for the Z80 microprocessor. If the
interrupt isn't active when the processor samples it, the CPU doesn't see
it. One possible use for this type of interrupt is to minimize spurious
signals from a noisy interrupt line.

The last interrupt type is a hybrid, where the hardware not only looks for
a transition, but it also verifies that the interrupt signal stays active
for a certain period of time. A common hybrid interrupt is the NMI
(non-maskable interrupt) input. Because NMIs generally signal major-or even
catastrophic-system events, a good implementation of this signal tries to
ensure that the interrupt is valid by verifying that it remains active for
a period of time. This 2-step approach helps to eliminate false interrupts
from affecting the system.

Processing an interrupt

Okay, now you have an interrupt signal at the CPU, the next step is to see
how it affects the processor's operation. No single technique describes all
possible forms of interrupt processing, so I'll start with a simple
description and work up to more complicated ones.

Interrupt processing has some basic requirements from the CPU. Before it
can respond to an interrupt, the processor must wait for an "interruptable"
state in its processing. For example, if the processor's writing to memory,
it must wait until the write is done before processing the interrupt. Once
the CPU detects the interrupt, its first action is to save all the
information it will need to resume normal processing once the interrupt is
over. At a minimum, the chip saves the Program Counter (PC). This process
is analogous to you placing a finger in a book when someone interrupts you
while you're reading. After the interruption goes away, you know exactly
where to continue.

After saving this "bookmark" information, the CPU changes the PC value to a
fixed location in the processor's memory that contains a pointer to the
instructions-called an interrupt service routine (ISR)-that tells the
processor how to deal with the interrupt. When the ISR finishes, the
processor restores the original PC and merrily continues on as if nothing
had happened.

An NMI is an excellent example of this most-basic form of interrupt. Many
processors have a special interrupt pin reserved for flagging such
catastrophic events as a power failure. Using this interrupt might allow
you a couple of milliseconds in which to save crucial operating information
or shut down a system before power dies completely.

Beyond these basics, interrupts often sport some refinements that make them
even more useful. For example, one common feature is to multiplex one
interrupt input by allowing the interrupt source to send an identifying
code along with the signal that allows the processor to tell which
interrupt occurred. This code might take the form of an address where the
processor can find the ISR for that particular interrupt, or an index into
a table where the processor can look up the ISR's address. The code
typically goes into the processor on the data lines.

When the interrupt occurs, the processor also sends out an acknowledge
signal analogous to the CPU memory read signal that other parts of the
system use to place information onto the processor's data bus. I've seen
processors issue anywhere from one to four of these acknowledge signals for
each interrupt. Such multiple signals allow the processor to get additional
information about the interrupt from the hardware.

Depending on the microprocessor, this information can take the form of
either a vector address or an executable opcode. The latter can lend itself
to some rather peculiar implementations. I once worked on a very early
floppy-disk system-big 8" disks that held all of 90k bytes-that required
considerable horsepower from the processor. As each new byte came off the
disk every 35 µsec, the processor had to store the data away as it came in.
At the time, this speed was much too fast for an ISR, so instead we
implemented a halt-interrupt technique that involved executing a halt
instruction and then waiting for an interrupt-on this processor the only
way to exit the halt. When the next data byte became available, the
hardware issued an interrupt. The interrupt acknowledge read a NOP (no
operation) opcode that the processor executed, and then the processor
started executing the next instruction after the halt. This technique
allowed us to quickly read all the data while staying synchronized-but just
try to document the technique in the code comments!

ISR basics

Finally, no interrupt primer would be complete without a discussion of what
goes on inside an ISR. Basically there are two fundamental rules. First,
the ISR must save then restore all CPU, memory and I/O resources that it
uses. For example, this data might include CPU registers or memory data
such as the scratch area for a floating-point package (many of which aren't
reentrant). Second, it must get back out of the ISR as quickly as possible.
The reason for this rule is that ISRs should generally block new interrupts
until after the ISR has completed running. Therefore, an ISR should do as
little as possible so that interrupts are off for as little time as
possible. For the processors I typically use, I like to see ISRs run in
less than 40 µsec. Obviously, a 100-MHz Pentium and a 4-MHz 8051 have very
different time scales, but the concept's still the same-get out quickly!

Adapted from an article that appeared in Personal Engineering &
Instrumentation News, a monthly trade publication sent free of charge to
qualified engineers and scientists. For subscription information, call the
Circulation Hotline at 603-427-1427 or send e-mail to [email protected] .

Call MicroSol at (410) 381-8910 or email [email protected] to discuss how
your next product design can benefit from MicroSol's expertise at
developing software and electronics into successful products.

Return to the article index

MicroSol Corporation, Columbia, Maryland, USA [email protected]

(410) 381-8910 Fax (410) 381-8934 Last Modified: January 16, 1996