BITWISE OPERATORS
What is a bitwise operator? Bitwise operators are used to manipulate one or more bits of operands. Our variables and port registers are operands, and things like +, -, and = are operators. Here is an example of using an operator:
00010101 + 10110110 = 11001011
In this example, we have taken two bytes and added them together using the "plus" operator to produce a result. Let's think about our last project, where we flashed a LED on and off by manipulating a single port register bit (GP0). How can we change the state of only GP0 on the port register without accessing it directly? Using a bitwise operator would do the trick.
Figure 1. |
Figure 2. |
AND
The bitwise operator AND, represented by the ampersand symbol "&" compares the two inputs and says that if input 1 AND input 2 are true, the output is true. Let's look at an example where we have two variables, A and B.
A is equal to 10011010
B is equal to 00110111 If we perform a bitwise AND operation on these two variables:
AND result 00010010
Both bits must be true for the result to be true, otherwise the result is false. Here is how this looks in the C language:
A & B = result
OR
The bitwise operator OR, represented by the vertical line symbol "|" or sometimes called a pipe, says that if bit 1 OR bit 2 are true, the result is true. If neither bit is true, the result is false.
10011010 A
00110111 B
10111111 Result
A | B = result
EXCLUSIVE OR
The bitwise operator XOR (Exclusive OR) represented by the carrot symbol "^" says that if either A OR B but not both is true, the result is true, otherwise the result is false. This is the same as saying the output is on if the inputs are different.
10011010 A
00110111 B
10101101 Result
A ^ B = result
NOT
Let's look at the bitwise operator NOT real quick, because the last two operators that follow are combinations of NOT and other bitwise operators. NOT, represented by a tilde "~" is nothing more than an inverse and does not compare. It simply makes a true a false, and a false a true, and has only one input and one output.
10011010 A
01100101 NOT A
~A = result
NOT AND
The bitwise operator NAND is a combination of NOT and AND "&~". It says if, and only if both inputs are true, the output will be false, otherwise the result is true. It is the direct inverse of a regular AND operation. The result is thus the inverse of AND.
10011010 A
00110111 B
11101101 Result
A &~ B = result
NOT OR
Finally, the bitwise operator NOR (NOT OR) "|~" is a combination of NOT and OR, which is the direct inverse of the OR operation. It says that if any input is true, the output is false, thus producing a result which is the inverse to a regular OR.
10011010 A
00110111 B
01000000 Result
A |~ B = result
USING BITWISE OPERATORS
Using bitwise operators is the easiest way to compare every bit across two bytes. We can make a change to just one bit of a port without changing the other bits and without directly accessing the bit, such as in GP0=1. Let's look at some ways we can do this using the bitwise operators.
All of our projects to this point have involved turning on and off a LED using the output of one of our I/O pins. We know ahead of time what the state of all the GPIO pins will be when we're working with a simple project like the LED, as we have cleared all the pins at the beginning of our program (made them all 0). So GPIO will equal 00000000. When we want to turn on the LED we need to make GP0 equal to 1, and up to this point we have been accomplishing this task by writing GP0 = 1. But we know now that directly accessing the port pins is not advised, so we need to do the same thing using a bitwise operator.
A very common practice in programming is using what is called a mask, where we define the position of a bit we want to manipulate. A mask for our LED connected to port 0 would look like this: #define LEDmask 0b00000001. The # define preprocessor macro tells the compiler that every time it sees the label "LEDmask", it is replaced with the binary value 00000001 (remembering that binary values are preceded by "0b").
We now have two operands, the GPIO port register "00000000" and the LEDmask "00000001". If we want to perform a bitwise operation on these two operands to make GP0 turn on, how could we do that? Let's perform a bitwise OR operation!
GPIO 00000000
mask 00000001
result 00000001
Each bit has been compared with "if input 1 OR input 2 is true, result is true", and the result is that bit 0 is true. In order to use this result to make GPIO equal to the result, we will combine operators and tell the program, perform this OR operation and make GPIO equal to the result.
GPIO |= LEDmask;
Now the OR operation has been performed and the result has been copied to the GPIO register.
What if we don't know the state of the GPIO pins, except for the bit we want to change? Our LED on pin GP0 is off, but the other pins are performing other tasks and could be 1 or 0 at any given time, will this mask still work?
GPIO 10011010
mask 00000001
result 10011011
Yes! None of the other GPIO pins have been changed from their original state except for the bit we wanted to change. How cool!
Now we want to turn off the LED, how can we do that? We return now to knowing the state of our pins, GPIO is equal to 00000001 and our led mask is equal to 00000001. What bitwise operator will change GPIO to 00000000? We know that if we have 0 and 0, we want the result to be 0, and if we have 1 and 1, we want that result to be 0 as well. We definitely don't want any result to be 1 because we don't want to turn on any pins. What truth table matches up to what we want to do? Exclusive OR (XOR) looks good.
GPIO 00000001
mask 00000001
result 00000000
That worked out good! Now what if our pins are unknown state, will XOR still work?
GPIO 10011011
mask 00000001
result 10011010
That worked too! Because the XOR operator produces a false result when both pins are the same (0 or 1) but a true result when they're different, all pins will stay in the same state except the one our mask indicates. Pretty neat huh?
Let's look at one more way to turn off the LED using bitwise operators. What if we inverted the LED mask using the NOT operator. Then 00000001 becomes 11111110. Now if we AND together GPIO with GP0 turned on, and the inverted mask:
GPIO 00000001
mask 11111110
result 00000000
GP0 is now cleared and thus turned off. The code that would fully perform this function looks like this:
GPIO &= ~LEDmask;
This says GPIO ANDED together with the inverse of LEDmask and made equal to the result. All of these bitwise operators have a use, and the more you write programs in C and want to manipulate bits, the more useful you will find them.
So far in our examples we have still been directly manipulating the GPIO port register. Even writing GPIO &= LEDmask; is directly manipulating the bits of the GPIO register because the operation is performed one bit at a time. There is one more thing we must do to avoid doing this, and it will conclude our discussion on shadow registers. We have already seen the use of a variable named GPIOinit, which is a copy of the GPIO port register to define the initialization state. To fully avoid direct manipulation of the register, we must declare another GPIO variable that we will make all of our changes to. I like to use GPIOimg as my variable name, which indicates that the variable is an image of GPIO but you could easily name this variable something like GPIO_copy. It should be an 8-bit variable, so the unsigned char type would work great.
Now when we use the bitwise operators, we perform them on the variable instead of the actual register so that the bit-by-bit operation is not done on the actual register. Then we copy the variable to the actual register which takes place as a byte-size operation. Here is how that looks:
GPIOimg &= LEDmask;
GPIO = GPIOimg;
The bitwise operation took place using the image, then the image is copied to the register. No direct access to the GPIO bits is allowed using this setup, which completely eliminates the read/modify/write issues discussed earlier. Our code is stable, and we are happy programmers!
PUTTING THIS ALL TOGETHER
We have learned quite a lot in this lesson and your head may be reeling, so let's put this lesson to practice and rewrite our last program using the bitwise operators. Figure 3 shows the updated program, which flashes the LED for the number of times our count variable is equal to. This time however, we are using bitwise operators and shadow registers.
Figure 3. |
The large blank section in this program between lines 28 and 42 was code for the next lesson and can be ignored in this lesson, thus it was blanked out to avoid confusion. This program will operate exactly the same as in the last lesson, the only difference being that this program is considered good, stable code. You can copy this program into your MPLab software and try it for yourself.
I hope that at this point you are more familiar with the full use of shadow registers, and have a beginner's grasp on using bitwise operators. Please feel free to comment.