In this chapter we’ll make a cylon-eye-type LED back-and-forth scanner. Everyone needs one. Scanning lights were the only thing, besides maybe menace, that gave the cylons their personality. Would KITT have been half as useful to David Hasselhoff without his scrolling red lights? I think not. And now you can build your very own.
But I won’t lie to you. We’re building cylon eyes in order to learn a fundamental concept in microcontroller-specific programming: how to flip individual bits on and off inside a hardware register. And while that may not sound sexy, you’ll be using the ideas from this chapter in every AVR or microcontroller project you ever make from here on out. Flipping bits is just that important in embedded devices.
If you don’t believe me yet, think back on how we turned on LED3. We wrote something like:
PORTB = 0b00001000;
And that worked, as long as we knew for sure that we wanted all of the other LEDs turned off. But what if you need to turn on or off a particular LED without disturbing any of the others? Bitwise logical operations—“bit twiddling”—lets us modify any one bit out of the register byte without having to think explicitly about the others. Because we’ll be setting bits in registers to configure nearly every aspect of the AVR hardware, we’ll get a lot of mileage out of this investment.
Because this chapter is mostly theory, let’s get the project up and running so you
have something pretty to look at. Assuming that you still have all the LEDs
hooked up as in Chapter 3, all you have to do is enter the
cylonEyes directory, open up cylonEyes.c in your editor, and run make
to
compile the firmware and upload it to the chip.
If you have trouble uploading the program to the chip, please double-check the ISP connectors according to Figure 3-4 and make sure you’ve got the MOSFET set up right. If in doubt about the MOSFET part of the circuit, go back to the wire between the top ground rail to the bottom ground rail to double-check that’s the problem.
You got it running? Great! Now let’s look at the code in Example 4-1 and see what
makes it tick. Essentially, it’s the same as with the one-LED blinker
code, only instead of switching on and off only one of the bits in the
PORTB
register, we can toggle eight of them.
/* Cylon Eyes */
// ------- Preamble -------- //
#include <avr/io.h>
/* Defines pins, ports, etc */
#include <util/delay.h>
/* Functions to waste time */
#define DELAYTIME 85
/* milliseconds */
#define LED_PORT PORTB
#define LED_PIN PINB
#define LED_DDR DDRB
int
main
(
void
)
{
// -------- Inits --------- //
uint8_t
i
;
LED_DDR
=
0xff
;
/* Data Direction Register B:
all set up for output */
// ------ Event loop ------ //
while
(
1
)
{
while
(
i
<
7
)
{
LED_PORT
=
(
1
<<
i
);
/* illuminate only i'th pin */
_delay_ms
(
DELAYTIME
);
/* wait */
i
=
i
+
1
;
/* move to the next LED */
}
while
(
i
>
0
)
{
LED_PORT
=
(
1
<<
i
);
/* illuminate only i'th pin */
_delay_ms
(
DELAYTIME
);
/* wait */
i
=
i
-
1
;
/* move to the previous LED */
}
}
/* End event loop */
return
(
0
);
}
You should recognize the general form of the code from blinkLED.c. In
fact, the only big differences are that we have loops inside our event
loop, and we have the cryptic PORTB = (1 << i);
statement—our first
introduction to bit twiddling! Before we get on to the main course, though,
I’ll introduce our first #define
statement (C Asides: #defines). Feel free
to skim through if you’re already comfortable with C.
Looking back briefly at how we implemented blinkLED.c and even povToy.c, you’ll see
that the code turns on and off the bit that controls our LED quite
directly: PORTB = 0b0000001;
. We could make a cylon eyes animation in the same
way. To make the traditional back-and-forth scanner, we hook
up eight LEDs to the same port, and light them up one at a time,
moving from right to left and left to right. Our naive code
might look something like this:
DDRB
=
0
b11111111
;
// enable all pins for output
while
(
1
){
PORTB
=
0
b00000001
;
// set the 0th pin in PORT B high
_delay_ms
(
100
);
PORTB
=
0
b00000010
;
// set the 1st pin in PORT B high
_delay_ms
(
100
);
PORTB
=
0
b00000100
;
// set the 2nd pin in PORT B high
(
etc
.)
...
}
As you can see, that’s going to involve a lot of typing, and in the end the code is going to be difficult to maintain, tweak, and extend. So we’re going to need a better way. Enter bit twiddling! The C programming language allows the user to access bits individually, and the AVR libc (the microcontroller-specific library routines) helps even more, but it’s going to take a little getting used to.
Wouldn’t it be nice, instead of having to write out 0b00001000
, if we could
just say “give me a number with a 1 in bit number three”? (If you thought that
one was in bit number four, see Bits, Numbering, and Significance.) That way we could
code our cylon eyes by incrementing or decrementing a variable and then putting
a 1 in the corresponding bit’s place.
It turns out that the technique known as bit shifting will do exactly that for us. You’re going to be surprised at how often we use bit shifting in microprocessor-style C code.
Bit shifts have the effect of rolling all the bits’ n positions to
the left or right, depending on your command. Bits that fall off either end just
disappear, and any new bits added are all zeros. The C language
instruction for shifting all the bits in a number to the left or right
is <<
or >>
, respectively. Now let’s see how they’re used.
Bit shift examples:
0b00001110 >> 3 = 0b00000001 (Three new zeros were added to the left, and the bits on the right just rolled off the edge.) 0b00001110 << 2 = 0b00111000 (Two new bits, both zeros, are rolled in from the right.)
What this means to you as a programmer is that if you want a single 1 bit in position number three, say, you can start off with the number that has a 1 bit in position zero, and roll it to the left three spaces.
The bit-shift roll:
1 = 0b00000001 (1 << 0) = 0b00000001 (1 << 1) = 0b00000010 ... (1 << 3) = 0b00001000 ... (1 << 7) = 0b10000000
Take a minute to make sure you’ve got the logic of the bit-shift roll
down. It’s a standard idiom in microcontroller coding, and you’ll be
using it in nearly every program you write. Any time you want to set the
n
th bit to a 1, you’ll shift the value 1 over to the left n
times.
Now you’re starting to see how the cylon eyes work: start off with
the number 1, roll the bit one position, and then write that
to an output hardware register, rinse, and repeat. Shift bits left
until you end up at LED7
, then switch to shifting bits right until you hit LED0
.
So before we leave this section, let’s recap the meat of the cylon eyes code. Make sure that it makes sense to you, because we’ll be using the bit-shift roll extensively from here on out:
while
(
i
<
7
){
LED_PORT
=
(
1
<<
i
);
/* illuminate only i'th pin */
_delay_ms
(
DELAYTIME
);
/* wait */
i
=
i
+
1
;
/* increase shift amount */
}
while
(
i
>
0
){
LED_PORT
=
(
1
<<
i
);
/* illuminate only i'th pin */
_delay_ms
(
DELAYTIME
);
/* wait */
i
=
i
-
1
;
/* decrease shift amount */
}
There are two while
loops, driving the variable i
up to seven and
back down to zero. Inside each loop there is a delay, which is fairly
self-explanatory. All the action that matters is in the LED_PORT =
(1 << i);
command. We want a binary number with a 1 in the
i
th place, which will turn on only the i
th LED in our series. We
implement it by starting with a 1 in the zeroth position and
rolling it over i
times to the left.
So that’s all there is to making cylon eyes: just shifting a bit to the left or right as appropriate, and waiting. But what if we wanted to make an even more interesting pattern? Multiple lights on at once? Or random toggling? Or maybe we just need to control one bit out of a byte, without changing any of the others.
The serious limitation in the preceding code is that each time through the
while()
loop, we’re writing a whole byte to the LED port. This
works in cylon eyes because we know that we’d only like one LED on at
a time. But what about the case where we’ve already got a few LEDs
lit up, and we’d like to add another? Say just LED1? If we wrote
LED_PORT = (1<<1);
, we’d turn on LED1
all right, but in the process,
we’ll have turned off all the rest.
In this section, we’re going to learn some essential bit-twiddling techniques that will allow us to manipulate single bits easily without clobbering the rest of the contents of the register. Think of each byte in the AVR’s hardware registers as being a row of eight little switches; in this section, we’re going to use bitwise logic functions to set or toggle each switch individually without modifying the rest.
Even if you’ve programmed C since you were seven years old, it’s quite possible that you haven’t spent much time on bit-level manipulations. If you’ve ever learned a little bit about logic, digital or otherwise, the bitwise operators will seem familiar to you, but the context may be brand new. For some of you, this is all going to be brand new. Hold on to your hats.
Bitwise logical operators take full bytes as their input. They do logical operations on those bytes one bit at a time, hence the name. This makes the bitwise operators an absolutely perfect match for manipulating the individual bits inside a register byte.
To give you a bit of the flavor, the bitwise operator NOT
takes each
bit in the byte and toggles, or flips, it. A 1 becomes a 0, and a 0
becomes a 1: 10001110
turns into 01110001
. The operation happens
all at once inside the AVR chip, but you can think of it as reading
bit 0 and then writing the logical opposite to bit 0 of the result
byte. This same operation repeats seven more times, bitwise, for
each bit in the byte.
There are four bitwise logical operators in total, so let’s work through each with an example. I find it helpful to think of the two input bytes stacked on top of each other so that the bits being compared are aligned vertically. Work through the following sidebar and make sure that you see how the logical operation is being applied, bitwise down the columns, to get the final result.
Bitwise logic definitions are all well and good, and you may have seen all this already. It’s how bitwise logic is used in microcontroller coding that’s interesting, especially when combined with the side effects of setting, clearing, and testing bits in hardware registers. In short, we care about twiddling bits inside bytes a lot more in AVR programming than in most other computer programming, because bitwise logic allows us to configure the AVR’s internal hardware and to read and write from and to individual pins.
As a quick demo of the utility of bitwise logical operators, you can
convert the Cylon Eyes demo into Inverse-Video Cylon Eyes with the NOT
operator. Just replace each:
LED_PORT = (1 << i);
with:
LED_PORT = ~(1 << i);
Every LED that was previously on will be off, and vice versa.
For a different display that uses tools you’ll learn in the remainder
of this section, change the first LED_PORT
line into
LED_PORT |= (1 << i);
and the second into LED_PORT &= ~(1 << i);
See what you get!
So let’s get down to the business at hand: manipulating individual
bits within a byte. For concreteness, take the eight LEDs and suppose
that we already have a few LEDs lit: LEDs zero, one, six, and seven.
If we looked at the value currently stored in PORTB
, it would read
0b11000011
. Now say we want to turn on and off LED2
without
changing the states of any of the other LEDs. Or maybe we want to
turn on or off LEDs two and four at the same time. How can we do
this?
For our first trick, let’s learn how to set an individual bit in a
register, leaving all the other bits as they were. Thinking back on
the bitwise logical operators, let’s revisit the way the OR
operator
works. In particular, let’s think about OR
ing some bit with either a
fixed 0 or a fixed 1.
Consider OR
ing a bit with zero. If you OR
a 1 with a 0, the
result is 1. If you OR
a 0 with a 0, the result is 0.
That is, OR
ing a bit with 0 doesn’t change the initial logical bit
at all. But OR
ing with a 1 always yields a 1. This behavior
lays the groundwork for using OR
to turn bits on using bitmasks:
OR with 0: 0 | 0 -> 0 1 | 0 -> 1 OR with 1: 0 | 1 -> 1 1 | 1 -> 1
A bitmask is just a normal old byte, but we’re thinking of it as being made up of ones and zeros in particular places that we specify rather than representing a numerical value. We use a bitmask, along with a bitwise logical operator, to change some bits in a target byte.
I like to think of bitmasks almost like stencils used for spray painting. You cut away parts of the stencil where you want to change (paint) the underlying surface, and you leave the stencil intact where you don’t want paint to go.
In particular, if we want to turn on some bits in PORTB
while
leaving the others untouched, we’ll create a bitmask with ones in the
bit locations we’d like turned on. This works because a one OR
ed with anything
will return a one. So we read in PORTB
and OR
it
with the bitmask. The result should be the unaltered contents of
PORTB
everywhere that we had a zero, and ones everywhere our bitmask
had a one. If we write this back out to PORTB
, we’re set—PORTB
has all its old bits intact, except those where there was a
1 in the bitmask have been turned on. I’ve worked this all through in detail
in Example 4-2.
If LED2
is initially off:
PORTB : 0b11000011 // the current LED state (1 << 2) = 0b00000100 // the LED we want to turn on | = 0b11000111 // hooray!
LED2
is turned on, and none of the others are changed.
If LED2
is initially on:
PORTB : 0b11000111 // the current LED state (1 << 2) = 0b00000100 // the LED we want to turn on | = 0b11000111 // hooray!
LED2
is still on, and none of the others are changed.
We can also set multiple bits at once. All we have to do is create a
bitmask with the two bits (say LED2
and LED4
) that we’d like to
turn on. Since:
0b00010100 // the desired bitmask = (0b00000100 | 0b00010000) = ( (1 << 2) | (1 << 4) )
then:
PORTB = 0b11000011 // the current LED state ((1 << 2) | (1 << 4)) = 0b00010100 // bits two and four | = 0b11010111 // turned both on!
We’ve just seen how to get a copy of the byte currently in PORTB
, and how to
turn on LED2
and LED4
using OR
and a bitmask. That gives us a new byte,
which we just write out to PORTB
, and
we’re done:
PORTB = PORTB | (1 << 2);
This type of operation is so common that there’s a shorthand for it in C:
PORTB |= (1 << 2);
Either way you write it, the code ends up the same after compilation,
so pick a style that makes you happy. Both of them have the effect of
turning on LED2
and leaving the other bits as they were.
Now imagine that you want to flip a bit or two. You don’t really care if it’s
on or off right now, you just want it in the other state, whatever that is.
Imagine that you want to blink LED2
while leaving the rest of the LEDs
unchanged. To do this, you’d toggle the bit that corresponds to LED2
, delay a
while, and then toggle it again, etc. To toggle a bit, you’ll use a bitmask and
the XOR
operator.
Let’s look at XOR
again. If you XOR
any bit with a fixed 0, you
get a 1 if that bit is a 1 and a 0 if that bit is 0.
(Remember, this is the “exclusive” or and is only true if one or the
other is true, but not both.) So XOR
ing with a zero gives you the
input back again.
If you XOR
with a 1, what happens? Starting with a 0 and XORing 1 returns a 1,
and starting with a 1 and XOR
ing 1 yields 0. XOR
ing with a 1 seems a good way
to toggle bits!
XOR with 0: 0 ^ 0 -> 0 1 ^ 0 -> 1 XOR with 1: 0 ^ 1 -> 1 1 ^ 1 -> 0
As we did with OR
for setting bits, we’ll make a bitmask with a 1
where we want to toggle a bit and zeros everywhere else. To toggle a
bit in a register, we XOR
the current register value with the bitmask,
and write it back into the register. Boom. For more detail, see Example 4-3.
If LED2
is initially off:
PORTB : 0b11000011 // the current LED state (1 << 2) = 0b00000100 // bitmask LED2 | = 0b11000111 // LED2 bit flipped on
After the XOR
, LED2
is turned on, and none of the others are
changed.
And if LED2
is initially on:
PORTB : 0b11000111 // the current LED state (1 << 2) = 0b00000100 // bitmask LED2 | = 0b11000011 // LED2 bit flipped off
After the XOR
, LED2
is turned off, and none of the others are changed.
So to toggle a bit, we create a bitmask for the bit we’d like to
toggle, XOR
it with the contents of our register, and write the result
back out to the register. In one line:
PORTB = PORTB ^ (1 << 2);
or:
PORTB ^= (1 << 2);
for short. You can, of course, toggle more than one bit at once with something like:
PORTB ^= ((1 << 2) | (1 << 4));
Clearing a bit (setting it to zero) is just inconvenient, but you’ll
have to do it so often that you’ll eventually find it second nature.
So far, we’ve used OR
to set bits and XOR
to toggle them. You may not
be entirely surprised that we’re going to use AND
to clear bits.
Let’s run through the usual analysis.
If we AND
any bit with 0, the result is guaranteed to be 0.
There’s no way they can both be 1 if one of them was a 0 to start
with. This is how we’ll turn bits off:
AND with 0: 0 & 0 -> 0 1 & 0 -> 0 AND with 1: 0 & 1 -> 0 1 & 1 -> 1
This suggests using a bitmask with AND
to turn bits off. The bitmask
we’ll have to use should have a 1 where we want to keep the old data
and a 0 where we want to clear a bit. That is, to turn off LED2
,
we’ll use an AND
bitmask that looks like 0b11111011
.
But wait a minute! That bitmask is the exact opposite of the bitmasks we’ve used before—it has ones where the others had zeros and vice versa. There’s a million stupid ways to create such a mask, but the easy way is to first create the mask with a 1 where we want it, and then use NOT to flip all the bits.
So to make a bitmask to turn off LED2
, we’ll first shift a 1 over
into the right spot and then NOT
the whole mask:
(1 << 2) -> 0b00000100 ~(1 << 2) -> 0b11111011
Now we AND
that with the original value and we’re home free. I work through all
of the steps for you in Example 4-4.
If LED2
is initially off:
PORTB = 0b11000011 ~(1 << 2) = 0b11111011 & = 0b11000011
LED2
stays off, and none of the others are changed.
And if LED2
is initially on:
PORTB = 0b11000111 ~(1 << 2) = 0b11111011 & = 0b11000011
LED2
is turned off, and none of the others are changed.
Reassigning to PORTB
and writing these as
one-liners, we get:
PORTB = PORTB & ~(1 << 2);
or:
PORTB &= ~(1 <<2);
And as with the other examples, you can of course turn off multiple bits in one statement:
PORTB &= ~((1 << 2) | (1 << 4));
being careful with the NOT
outside the parentheses, because you want
to have two zeros in your bitmask.
Now that we’ve got all this bit-level manipulation under our belts, let’s make some demo code to show off a little bit. Being able to set, clear, and toggle individual bits allows a little more flexibility than cylon eyes code had, and it should give you enough basis to start experimenting. Let’s work through Example 4-5.
/* Showing off some patterns to practice our bit-twiddling */
// ------- Preamble -------- //
#include <avr/io.h>
#include <util/delay.h>
#include <avr/power.h>
#define DELAYTIME 85
/* milliseconds */
#define LED_PORT PORTB
#define LED_DDR DDRB
int
main
(
void
)
{
clock_prescale_set
(
clock_div_8
);
uint8_t
i
;
uint8_t
repetitions
;
uint8_t
whichLED
;
uint16_t
randomNumber
=
0x1234
;
// -------- Inits --------- //
LED_DDR
=
0xff
;
/* all LEDs configured for output */
// ------ Event loop ------ //
while
(
1
)
{
/* Go Left */
for
(
i
=
0
;
i
<
8
;
i
++
)
{
LED_PORT
|=
(
1
<<
i
);
/* turn on the i'th pin */
_delay_ms
(
DELAYTIME
);
/* wait */
}
for
(
i
=
0
;
i
<
8
;
i
++
)
{
LED_PORT
&=
~
(
1
<<
i
);
/* turn off the i'th pin */
_delay_ms
(
DELAYTIME
);
/* wait */
}
_delay_ms
(
5
*
DELAYTIME
);
/* pause */
/* Go Right */
for
(
i
=
7
;
i
<
255
;
i
--
)
{
LED_PORT
|=
(
1
<<
i
);
/* turn on the i'th pin */
_delay_ms
(
DELAYTIME
);
/* wait */
}
for
(
i
=
7
;
i
<
255
;
i
--
)
{
LED_PORT
&=
~
(
1
<<
i
);
/* turn off the i'th pin */
_delay_ms
(
DELAYTIME
);
/* wait */
}
_delay_ms
(
5
*
DELAYTIME
);
/* pause */
/* Toggle "random" bits for a while */
for
(
repetitions
=
0
;
repetitions
<
75
;
repetitions
++
)
{
/* "random" number generator */
randomNumber
=
2053
*
randomNumber
+
13849
;
/* low three bits from high byte */
whichLED
=
(
randomNumber
>>
8
)
&
0
b00000111
;
LED_PORT
^=
(
1
<<
whichLED
);
/* toggle our LED */
_delay_ms
(
DELAYTIME
);
}
LED_PORT
=
0
;
/* all LEDs off */
_delay_ms
(
5
*
DELAYTIME
);
/* pause */
}
/* End event loop */
return
(
0
);
/* This line is never reached */
}
Reading down from the top, I include the standard avr/io.h file, which includes
all the DDR
and PORT
macros and AVR part definitions, and then the delay
utilities. Next, a delay time and the pinouts are defined in case you want to
play around with them later on.
Down in the main()
routine, there’s not much to do for initialization. A few
variables that we’ll be needing are defined, and then the DDRB
is configured
for output on all of the pins. (0xFF
is 255 in hexadecimal, and is equivalent
to 0b11111111
.)
The event loop (while(1){...
) is divided into
three different “animations”: one that turns on all the LEDs starting
from LED0
, and then turns them all off starting from LED0
; one
that does the same thing in reverse; and one that “randomly” toggles
individual LEDs on and off.
The “go left” routine is a lot like cylon eyes, except that it doesn’t
turn off any LEDs until they’re all on, and it uses a for()
loop. Turning on each LED one at a time, without turning them back
off, ends up with all eight LEDs on. The next for()
loop in the “go
left” section turns off the LEDs one at a time, from right to left. This makes
it look like a block of LEDs, eight wide, passes through our viewing range.
You’ll be seeing a lot more for()
loops in this book, but this
one’s the first one. So make sure you know what’s going on
inside the loop’s parentheses.
First, a loop variable is initialized (i=0
). Then a test is done on that
variable (i<8
). If the test is true, the rest of the code in curly brackets
is run. If not, the loop is over and the code moves on. Finally, each time
it’s done with a loop, the third argument in the for()
parentheses is run. In
our case, we’re adding one (i++
) to the variable.
The “go right” code is a little bit more interesting, and here’s a
potential trap when coding for microcontrollers. Conceptually, it’s easiest to
think of starting with i=7
and subtracting one until it’s reached zero. The
problem with this is how we test for the end condition. We want to run the for
loop when i=0
and turn off LED0
.
If you set up the loop like this:
for (i=7; i>0; i--){}
it will stop as soon as i
is zero, so it will never set or unset LED0
. You
might try to fix this by comparing with a greater-than-or-equal-to:
for (i=7; i>=0; i--){}
then you end up with a surprising infinite loop! The reason for this gotcha is
that i
is defined as an unsigned integer, which counts from 0 to 255—it’s
only defined for positive numbers. When you subtract one from zero, it rolls
back around to the maximum value, which is 255 in the 8-bit case. This means the
condition i>=0;
is always true.
Because we want the for
loop to run when i
is 0, and then stop afterwards
when i
equals 255, we can test for i < 255
, which is exactly what the code,
as written, does.
Finally, have a brief look at the “random toggling” section of the code. The “random number” isn’t really random at all, but it looks pretty close, so it is good enough. You can see how I used bit-masking to take a 16-bit random number and turn it into a number in the range zero to seven, to match up with our LED numbers. You’ll end up seeing these tricks again, so I’ll at least introduce them here.
First, we convert the 16-bit randomNumber
variable into an 8-bit
number by bit shifting the randomNumber
over eight times:
whichLED = (randomNumber >> 8);
. This keeps the most significant
eight bits, which are the most “random” using this quick-and-dirty
algorithm.
Next, we need to limit the random number to the range zero through
seven. And what’s the largest number you can count to with three
bits? Seven. So the trick is to keep only the lowest three bits,
zeroing out the upper five. And the quickest and easiest way to do
this is using AND
and a bitmask: whichLED = whichLED & 0b00000111;
.
Now our variable whichLED
will be in the range of the number of LEDs
we actually have.
Finally, if whichLED
contains a number like zero or three or seven,
all that’s left for us to do is use XOR
and a bit-shift roll to toggle
the randomly selected bit. Voila! A random blinker, powered by bit twiddling.
You learned a lot about bit twiddling in this chapter, so we should probably recap. Last chapter you saw how to set up the data-direction registers to enable any given pin for output, and then how writing a logical one or zero to the same bit number in another register set the output voltage on that pin to 5 V or 0 V, respectively.
And because digital output is all about controlling individual bits, you
dove head-first into more advanced bit twiddling. First, using the bit-shift
roll, you saw how to get a one bit in any given position. Then you saw how
to use the various bitwise logical operations (OR
, XOR
, AND
, and NOT
)
to set, toggle, or clear bits in a byte singly or in groups.
Here, I also introduced the idea of a bitmask that’s used
with the logical operations to tweak specific bits singly or together:
BYTE |= (1 << i);
BYTE &= ~(1 << i);
BYTE ^= (1 << i);
And that’s a lot of bit twiddling for one chapter, but the groundwork we’ve laid here will serve you throughout the rest of the book and for the rest of your life with microcontrollers.
13.58.137.218