CONFIDENTLY USING INTERRUPTS IN YOUR MICROCONTROLLER PROJECT
What Is an Interrupt
If you don’t use interrupts, I hope that I’ve motivated you
to learn more about them. Let’s start by defining an interrupt. Although there are many different types of interrupts,
in this article I will only discuss periodic timer-generated
interrupts because I’ve found them to be the most useful;
they are also available on almost any microcontroller. Once
you’ve learned about timer interrupts, though, it will be easy
to extend the basic concepts to other types of interrupts.
In the case of a timer-generated interrupt, the interrupt
is an event in the microcontroller that lets you run a short
section of your code at a regular interval. For instance, you
might set up an interrupt to run at 1 kHz — or 1,000 times
per second. In this case, every one thousandth of a second,
you will get an interrupt. Like the ticking of a sped-up
clock, the interrupts occur on a set schedule and they
occur no matter what else is going on in the main body of
your microcontroller code.
Each time the interrupt occurs, the microcontroller will
run a certain section of the code you’ve loaded into its memory.
The code it runs is often referred to as the interrupt service
routine (ISR). This is where you do the work of your interrupt.
For instance, imagine you are designing a digital clock with
a microcontroller and had set it up to have a 100 Hz interrupt.
In the ISR code, which would run 100 times each second, you
would have a counter that counted up to 100. During each
interrupt, you would increment a counter variable and then
check to see if that counter had reached 100. If it was 100,
you would set it back to 0 and increment the seconds counter
on your clock. So, the seconds digit on your clock would
increment once every 100 interrupts — exactly once a second.
This is a very basic use for an interrupt.
To explore a more complex example, suppose you
want to control a motor with a signal generated on one of
your microcontroller’s pins. To control the motor’s speed,
you could generate a pulse width modulation (PWM)
signal using an interrupt. In this case, you would set the
interrupt frequency to be equal to the PWM signal frequency,
multiplied by the PWM resolution. Then, in each interrupt,
your code would decide whether to set the pin high or low
to generate the correct PWM duty cycle. I’ll go into this
example in more detail below and in the sidebar.
In most applications, you will probably do more than
one thing in each interrupt. If your application is a mobile
robot, you might generate a PWM signal for each wheel
motor, read a few sensor signals, and also keep a timer to
control behaviors — all in the same interrupt service routine.
How an Interrupt Works
Microcontrollers have special hardware built into them
to generate and handle interrupts. In the case of a timer-generated interrupt, the microcontroller generates an
interrupt whenever a specific timer rolls over. Almost all
microcontrollers have timers; timers are built-in register
FIGURE 1. An oscilloscope trace of a typical interrupt.
variables that are incremented every instruction cycle.
Counting the instruction cycles is the same thing as counting
the time. Certain instructions take more than one instruction cycle to run, but the instruction cycle time is constant.
When a timer reaches its maximum value (255 for an
eight-bit timer), it rolls over to a value of 0. If your timer
interrupt is enabled, this rollover will generate an interrupt.
For eight-bit microcontrollers, timers that can generate
interrupts are usually eight bits or 16 bits, sometimes with
prescalers to extend their ranges. A prescaler allows you to
get slower interrupt rates with an eight-bit timer, though
with less precision. For instance, a prescaler value of 4
tells the microcontroller to increment the timer every
fourth instruction cycle instead of every cycle, so that your
interrupt frequency is a fourth as fast.
What exactly does it mean to generate an interrupt? To
understand this, you need to know what is normally
happening when a microcontroller is running. When you
program a microcontroller, you place the assembly
instructions of your code into sequential program memory
addresses. In the simplest case, a microcontroller
performs the instructions in its program memory in order.
If it just performed the instruction at address location 123,
then it will run the instruction at location 124 next.
However, when an interrupt is generated, the micro-controller jumps to a fixed interrupt address instead of
going to the next instruction. This special address is sometimes called the interrupt vector and is often toward the
beginning of the program memory. This location is where
you put the start of your interrupt code. So, generating an
interrupt really means making your microcontroller jump
to a known address — the start of your interrupt routine.
When the interrupt has done its work, you want the
microprocessor to go back to whatever it was doing just
before the interrupt happened. There is a special return-from-interrupt instruction that does just this. It signals that
the interrupt processing is over and that the microcontroller should go back to where it was prior to the interrupt.
One thing to keep in mind when using an interrupt is
that you don’t want to overwrite variables you were using
in your main code. This is especially true of the accumulator
or working register and any status flags. Some microcontrollers will store some of these important variables for you
automatically and restore them at the end of the interrupt.
If you are using a compiler, it will usually do this for you if