variable is used, but it may not have a rule to look at the
interrupt service routines to see if the variable is used there.
If it doesn't look there, then it may reasonably decide that
you made a mistake and aren't really using the variable, and
then optimize it away in the code generated from the C
source code. This can cause another tricky bug since that
variable is in your source code and you may not realize that
the optimizer got rid of it. To prevent this error, you should
declare all variables that will be changed by an interrupt as
'volatile' which tells the compiler that — with all evidence to
the contrary — you actually know what you are doing so
don't get rid of this volatile variable.
C Knows Nothing About Interrupts
■ FIGURE 3. Joystick mechanical drawing.
some particularly pernicious bugs. For example, your code is
reading an integer from memory; an integer is made of two
bytes. The code gets the first byte, then is stopped by an
interrupt that changes the value of that very same integer
before returning control to the part of the code that was
reading it, which then gets the second byte of the integer.
The integer will be wrong because it is made from half of the
pre-interrupt value and half from the post-interrupt value. The
crazy-making debugging problem is that the interrupt can
happen at any time; maybe only rarely during the integer
read. Your system can run like a champ and then lock up for
no apparent reason. You don't want this kind of bug in your
pacemaker. So you prevent it by disabling interrupts before
reading variables that can be changed by interrupts, and
then re-enabling them after you've got the correct number.
It is important to realize that the C programming
language knows nothing about interrupts. C is machine
independent — interrupts are machine dependent. Even
different versions of the same machine — such as the AVR —
may handle interrupts differently. Also, different compilers for
the same microcontroller will often have different ways to
use interrupts. We are fortunate to have the free WinAVR
tool set with the GCC avrlibc to handle some of the more
gory details, though it will get pretty gory anyway.
A Quick Look At Registers
Potential Interrupt Bug 2
Another problem with interrupts changing variables can
occur when you have the variable in a section of your code
that looks to the compiler optimizer like it is not being used.
The optimizer has various rules to search around to see if a
■ FIGURE 4. Butterfly
■ FIGURE 5. Associating pin
change interrupts with port B pins.
The AVR is a very flexible device and many of its
features have several different ways they can be used. For
instance, the ATmega169 I/O pin that we will be using for
our UP selection in the joystick can be set up to be used in
one of various ways: as either input or output; with or
without an internal pull-up; it can be turned off (tri-stated) to
remove it from the circuit or work for PWM output compare;
or it can be used as an input interrupt. Other pins may be
even more complex in their possible uses, but since each
can only be used in one of its many possible configurations,
the AVR must keep track of how it is supposed to be used.
Setting bits in a special set of I/O registers to configure and
keep track of various possible states does this.
AVR registers are eight bit memory locations that can be
quickly accessed by the CPU. The AVR sets aside 64 I/O
and 160 Extended I/O Registers in memory locations that
can be used for peripheral configuration and state flags. The
ATmega169 uses over 100 of these registers. Each of these
registers is named and may have two to
eight named bits for each. As usual for
microcontrollers, the names may be
cryptic and confusing. This provides us
with a bewildering excess of riches. You
can see the list on page 341 of the
ATmega169 data book — pretty scary,
isn't it? But, relax because we won't have
to memorize any of the details. Over
time, we will carefully set up each
register only when it is needed for a
particular application and once we get