Welcome to the Embedded Systems Programming course. My name is Miro
Samek and in this lesson I'll show you how to use the bit-wise operators
in C to blink all the colors of the composite LED on the Launchpad board.
As usual, let's start with making a copy of the previous "lesson5"
project and renaming it to "lesson6". If you are just joining the
course, you can download the previous projects from
state-machine.com/quickstart.
Get inside the new "lesson6" directory and double-click on the workspace
file to open the IAR toolset. If you don't have the IAR toolset, go back
to "lesson0".
So, this is the program you created in "lesson5". The program starts
with configuring the GPIO pins connected to the three-color LED, then it
enters an endless loop in which it turns the red LED on, waits a bit in
a delay loop, and turns the red LED off, waits a bit again, and loops
back. The result is blinking of the red LED.
In this lesson, your objective is to use the other colors of this
composite LED, such as the blue and green colors.
Suppose that you want to turn the blue LED on and keep it on all the
time while you blink the red component on and off. How would you do that?
The first step is simple. You need to turn the GPIOF data bit 2
corresponding to the blue LED before the endless loop.
But then, inside the loop, when you turn the red LED on, you got a
problem. When you set the bit-1 for the red LED, you also clear all
other bits, including the bit-2 for the blue LED, because all the LED
bits live inside a single register
So, what you really need is a method of setting and clearing the
individual bits, without inadvertently disturbing the other bits.
And this is where the bit-wise C operators come in.
So, let's learn about the bit-wise operators in C by experimenting with
them in the code.
Define a couple of unsigned integer variables with some initial values,
and the variable c for holding the results of various bit-wise operations.
Now, let's first write the code for testing all the six bit-wise
operators available in C, and let me hold on with explanations until you
actually see the code in action inside the debugger:
this is bit-wise OR
this is bit-wise AND
this is bit-wise exclusive OR
this is bit-wise bit inversion, also known as one's complement
this is the right bit-shift
and finally, this is the left bit-shift operator
Before compiling and running the code, please change the optimization
level to None, and the debugger to Simulator, since you don't really
need the Launchpad board.
Now, compile the code by pressing F7
Let's run this code in the debugger by pressing the "Download and Debug"
button.
Single step over the initialization of a, b, and c and go to the Locals
window to adjust the view to the binary format for.
Step over the bit-wise OR expression and examine the result in the c
variable.
As you can see, the bit-wise OR operator performs the logical OR between
each bit of a and each bit of b. If you remember the truth-tables from
your elementary school, you can verify easily verify that: false,
corresponding in binary to 0, OR true, corresponding to 1 is true-that
is one. 1 OR 0 is 1, 1 OR 1 is 1, and 0 OR 0 is 0.
In the disassembly window you can see, that the all these OR operations
on all 32-bits of the two operands are performed in just one machine
instruction ORRS, which is very fast and efficient.
The bit-wise AND operator performs the logical AND between each bit of a
and each bit of b. If you remember the truth-table for logical AND, you
can verify easily verify that: 0 AND 1 is 0. 1 AND 0 is 0, 1 AND 1 is 1,
and 0 AND 0 is 0.
In the disassembly window you can see, that the all these AND operations
on all 32-bits of the two operands are performed in just one machine
instruction ANDS
The bit-wise Exclusive-OR operator performs logical Exclusive-OR between
each bit of a and each bit of b. You can verify that: 0 XOR 1 is 1, 1
XOR 0 is 1, 1 XOR 1 is 0, and 0 XOR 0 is 0.
In the disassembly window you can see, that the all these Exclusive OR
operations on all 32-bits of the two operands are performed in just one
machine instruction EORS.
The bit-wise NOT operator is unary, meaning that it acts on just one
operand, in which it turns every 1 bit to 0 and 0 to 1.
In the disassembly window you can see that bit-wise NOT is performed by
the MVNS instruction, which stands for move-negative.
The right bit-shift operation shifts all the bits in the first operand
by the number of places specified in the second operand. For the shift
by 1, this corresponds to integer division by 2, as you can verify with
a calculator.
In the disassembly window you can see that the right-shifting is
performed by the RSRS instruction.
Please note that the RSRS instruction shifts zeros into the most
significant bit positions.
The left bit-shift operation shifts all the bits in the first operand to
the left by the number of places specified in the second operand. For
the shift by 3, this corresponds to integer multiplication by 2 to the
power 3, which is 8. But you need to be careful, because for a large
first operand, like in this case, some of the most significant bits
might "fall off the left edge", which simply means that the result after
the shift no longer fits in the 32-bits.
In the disassembly window you can see that the left-shifting is
performed by the LSLS instruction.
Please note that the LSLS instruction shifts zeros into the least
significant bit position.
So, now you know how the bit-wise operators work on unsigned numbers.
For signed numbers, the right-shift operator, works significantly
different, however. Let's perform an additional experiment.
Define a signed integer x and initialize it with a positive value.
Define another signed integer y and initialize it with a negative value.
Next, perform right-shift of x by a couple of places.
And finally perform right-shift of y by the same number of places.
Let's compile and test it.
As you can see, the positive value is shifted right exactly as before,
that is, zeros are shifted into the most significant bit position.
When you compare the decimal values of z and x, you can see that
right-shift by 3 places corresponds to division by 8, which is 2 to the
power of 3, as expected.
However, the right-shift of the negative value y behaves completely
differently than before, because now 1s are shifted to the
most-significant bit.
So, you've just found out that right-shifting of a signed integer shifts
zeros into the most-significant bit, when the bit is zero before the
shift, and ones when the bit is one before the shift. This is called
sign-extending of a negative value in the 2's complement representation,
which you learned in Lesson-1. This sign-extending is necessary to
preserve the correspondence between right-shifting and division by a
power of 2.
Indeed, when you convert the values to decimal, you can see that both z
and y are negative and that z is still equal to y divided by 8, which is
2 to the power 3.
This difference between right-shifting of signed integers versus
unsigned integers becomes very obvious when you look in disassembly. As
you can see, the compiler generated the instructions ASRS for
right-shifting of signed numbers, which means Arithmetic right shift,
whereas the compiler generated the instruction LSRS,
logical-right-shift, for right-shifting of unsigned numbers.
As an embedded systems programmer, you need to know the bit-wise C
operators inside and out, and in fact questions about various nuances,
such as logical shift versus arithmetic shift come quite often during
the embedded programming job interviews. More importantly, though, the
bit-wise operators are very useful, and you will take advantage of them
right away in your "blinky" program.
For starters, you can now define the GPIO bits that control various LED
colors by means of the bit-shift operators. For example, the red LED
corresponds to bit 1, blue LED to bit 2, and green LED to bit 3.
Please note that these bit-shift expressions are compile-time constants,
so there is absolutely no overhead compared to defining LED_GREEN as
0x08, say. But the advantage here is that you immediately see the bit
number as the shift displacement. For low-order bits this advantage is
perhaps not that impressive, but for high-order bits, like, say, bit-18,
it is not that easy at all to see that this is equivalent to 0x4000,
while it is obvious in the expression 1 right-shifted by 18. This way of
defining bit constants has saved me a lot of time counting bits and
prevented a lot of stupid bugs in my programs, so I highly recommend it.
After defining the macros for the LED colors, you can replace the
cryptic hex numbers, which greatly improves the readability of the code.
In fact, your code becomes self-explanatory, and the comment becomes
redundant.
Now, let's tackle the really interesting case of setting the red-color
bit in the GPIOF without extinguishing the blue color. For this you can
use the bit-wise OR operator between the current value of the GPIOF data
register and the red-color bit.
This works, because the bit-wise OR between any bit in GPIOF and LED_RED
preserves the original GPIOF bit, for all bits where LED_RED is zero,
and forces bit number 1 to 1.
Please note that this trick works only if you actually can read from and
write to the GPIOF register, so you need to check in the Data Sheet that
this register has Read/Write permissions.
The C language provides a special abbreviation notation for assignments
in which the left hand side also appears as the first argument on the
right hand side. You can use the operator-equals notation, which means
exactly the same as the line above.
So, finally, here is the most succinct code for setting the LED_RED bit
in the GPIOF register. Please remember this as a coding idiom for
setting a bit in C.
To clear the LED_RED bit in the GPIOF register, you need to use the
bit-wise AND operator with the complement of the LED_RED bit.
This works, because the bit-wise AND between any bit in GPIOF and
~LED_RED preserves the original GPIOF bit, for all bits where ~LED_RED
is 1, and forces bit number 1 to 0.
Again, you can use the operator-equals notation to represent this
operation more succinctly.
Please remember this as a coding idiom for clearing a bit in C.
Now that you know the coding idioms you can step back and look at your
code a bit more critically. For example, the turning the blue-LED on is
also setting a bit in the GPIOF register, so it should be coded with the
bit-set idiom.
Actually, all of the lines above also perform setting of bits in various
registers, so they too should be coded with the bit-set idiom. Please
remember, however, that you need to check in the Data Sheet that the
registers have read/write permissions.
And finally, you can use your LED macros to further improve the
readability of your code.
Before you re-compile, go to the project options and set the
optimization level back to high and the debugger to TI Stellaris.
Let's load the program into the Launchpad board and run it. As you can
see the blue-LED is on all the time, and the fainter red-LED keeps blinking.
Break into the code and set breakpoints at setting and clearing the
red-LED bit.
As you can see, setting of the red LED bit happens as a sequence of
load-modify-store operations, whereas the bit-wise ORR machine
instruction is used to modify the data.
The clearing of the red-LED bit happens as another load-modify-store
sequence, whereas interestingly, the compiler generated the beautiful
code of just one BIC (bit-clear) instruction for clearing the bit. This
is quite remarkable, because the compiler didn't literally follow your
code to perform a bit-wise AND with the complemented bitmask. Instead,
the compiler clearly understood your intent to clear the bit, and
generated an even better code for it.
I'd like you to remember this example, because it shows you that by
following established coding idioms, like the idiom for clearing a bit,
allows the compiler to understand your code at a higher level of your
intent, not just the low-level operations.
This concludes this lesson about the bit-wise operators in C. Now you
know how to set, clear, toggle, and shift bits in all sorts of
registers. So congratulations!
In the next lesson, I'd like to answer several questions about the GPIO
DATA register that were asked in the comments on YouTube. This subject
actually complements quite well the discussion of the bit-wise
operators, because the Stellaris GPIO DATA register demonstrates a very
different, hardware-assisted approach to manipulating whole groups of bits..
If you like this channel, please subscribe to stay tuned. You can also
visit state-machine.com/quickstart for the class notes and project file
downloads.
---
Course web-page:
http://www.state-machine.com/quickstart
YouTube playlist of the course:
http://www.youtube.com/playlist?list=PLPW8O6W-1chwyTzI3BHwBLbGQoPFxPAPM