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 trun 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 "Downlaod 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 righ-shifting is performed by the LSRS
instruciton.
Please note that the LSRS 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
instruciton.
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 sigificantly different, however. Let's pefrom 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
correspons 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 couting 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 bitwise 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 bitwise AND operator with
the complement of the LED_RED bit.
This works, because the bitwise 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 bitwise 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 cleraly understood your intent to clear the bit, and generated an even better
code for it.
I'd like you to rememer 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 bitwise 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.