So you have embarked on the journey of programming, and feel the urge to learn the magic of bits and Bytes. Well look no further as I hope to enlighten you to the workings of AND, OR, NOT and SHIFT.
If you have been working with Arduino you might know a little bit about this but I guess some of the magic have been hidden from you, so read on and be amazed.
First of I fell that I should tell you the difference between the bit, and a Byte. A Byte contains 8 bits and are usually numbered from 0 to 7, the reason for that I believe is that the value of each bit is 2 to the power of its number, so for the first bit would be 2 to the power of 0, which is 1, and for the second one would be 2 to the power of 1, which is 2, and they follow like 4,8,16,32,64,127. so if we look at a byte, the bit values are:
128 - 64 - 32 - 16 - 8 - 4 -2 - 1
Now, lets put the boring math aside and jump into what this magic is and what we can do with it.
Step 1: Bitwise NOT - '~'
The NOT operator for bits is the 'tilde' ~ character and what is does is that it flips all the '0' in a byte to '1' and all the '1' to '0'. Lets look at an example:
1010 1010 ~ = 0101 0101
easy enough right ? and maybe you see what we can use this for as well, but as it is the simplest of the bitwise operators I'll leave the imagination up to you with this.
Sidenote: the ~ operator is not the same as the Bytewise NOT operator ! (exclamation mark) that inverts the true or false value of a whole Byte.
Step 2: Bitwise AND - '&'
Different from the NOT operator the AND operator '&' needs 2 bytes to work with, as the NOT operator just flips all the bits in one byte, the AND operator takes one byte and AND's it with another byte.
We will first take a look at a truth table for AND:
1 & 1 = 1 0 & 1 = 0 1 & 0 = 0 0 & 0 = 0
So, what this tells us is that for us to get a '1' out both of the bits in the same place in the Bytes need to be '1', let's try it on a byte:
0101 0101 & 0000 1111 = 0000 0101
Ok, that was easy enough, but what can we use this for?
Well if you have ever painted before you probably used masking tape to protect the places you don't want paint, the AND function is great to mask away bits that we are not interested in. like if we have one button connected on a port of our Arduino when we read the port, we get the whole port Byte, but as we are only interested in the one bit we can mask it out by AND'ing it with a mask. Here is an example.
Our port byte = 01101011
and we only want to know if the 4'th bit (bit 3) is '1' or '0' so we do like this:
Button = Port_byte & 00001000
Now Button will be true if the 4'th bit is high, and false if it is low.
Another use is to set a bit to '0' by masking with a '0' like this:
Port = Port & 1111 0111
Now if you look at the truth table you will see that both bytes need to have a '1' to get a '1' out so when we get to our masked '0' that bit will be set to '0'.
Step 3: Bitwise OR - '|'
As with the AND operator the OR operator '|' (pipe) requires 2 bytes to work with, where it OR's the bits with another, let's look at the truth table for OR:
1 | 1 = 1 1 | 0 = 1 0 | 1 = 1 0 | 0 = 0
Now this is quite different from the AND operator, this only gives out '0' if both bits are '0', what use is that ?
Let's say we have a Led on the 6'th pin (bit 5) of our port, and we want to turn it on but there is other leds connected on that port and we don't want to mess with them while turning only this one on, well we could test and keep the others on or off but that's a lot of work, so lets rather use some bitwise magic:
Our Port looks like this : 0101 1010
Now we only want to set the 6'th bit to '1' we put that into a mask: 0010 0000
Now we OR those together and Voila, the 6'th bit is '1'
0101 1010 |
0010 0000 =
We could of course use this on multiple bits if necessary by adding '1's to our mask.
Step 4: Bitwise XOR - '^'
XOR is a OR with a twist, or as the name says Exclusive OR, it gives a '1' exclusively to a single OR, so lets look at a truth table:
1 ^ 1 = 0
0 ^ 1 = 1
1 ^ 0 = 1
0 ^ 0 = 0
Now what can this be used for ?
Lets do a experiment and see:
0101 0101 ^
0000 1111 =
Well well, what do we have here, as you can see, if we XOR with '0's there's no change.
But if we XOR with '1' we invert the bits.
We have already looked at setting a bit and clearing a bit, and now this 'Flipping' a bit, as we don't need to know whether it's high or low as we just want the opposite,
Step 5: Bitwise SHIFT - '<<' - '>>'
Now we are going to shift gear and look at SHIFTing right and left , what this does is simply move the bits in the byte right or left a number of places. The operator for this operation is '<<' for left and '>>' for right.
Like the NOT operator this only works on 1 byte at a time. Lets have a go with the SHIFT LEFT operator:
0000 0001 << 1 = 0000 0010
Here we moved our bits 1 place to the left. In math terms this is the same as multiplying with 2, 1 x 2 = 2 right.
This shifting is often used to ease the comprehension of registers, as we already know how to set a bit with the OR operator we can use SHIFT to say: I want to set bit 4 in a register. And we can do that like this:
Register = Register | (1 << 4)
which translates to:
Register = Register | 0001 0000
Remember we begin at bit 0, right!
Often when coding Arduino many of the Registers have predefined names, so when we use those names we can get a line that looks like this:
PORTB = PORTB | (1 << PORTB5)
Where we use the defined names for both PORTB which is an address in our microcontroller, and PORTB5 which is the number 5.
Now what can we do with the SHIFT RIGHT operator '>>' ? Well i must admit i have not often had use for that but it's nice to know its there and how to use it.
Step 6: Bitwise Practical Uses
As I have used a few examples like this :
PORT = PORT & 0001 000
I want to show you that there are some things that can be done otherwise, the example above can also be written like this:
PORT &= 0001 0000
This makes things easier when you get to know more of what's going on, but for now I will just show you the shorthands:
AND - "PORT &= 0101 0101" = "PORT = PORT & 0101 0101" OR - "PORT |= 0101 0101" = "PORT = PORT | 0101 0101" Xor - "PORT ^= 0101 0101" = "PORT = PORT ^ 0101 0101"
A few repetitions:
Set a bit:
PORT = PORT | 0001 0000 or PORT = PORT | (1 << 4) or PORT |= (1 << 4)
Clear a bit:
PORT = PORT & 1110 1111 or PORT = PORT & ~(0001 0000) //"remember the NOT operator ?" or PORT = PORT & ~(1 << 4) or PORT &= ~(1<<4)
Flip a bit:
PORT = PORT ^ 0001 0000 or PORT = PORT ^ (1 << 4) or PORT ^= (1 << 4)
So lets now go and make this more useful, sometimes we need to set more than one bit in a port or register, why not make it easily readable like this:
PORTB |= (1 << PORTB5) | (1 << PORTB3)
Here we set pin 5 and 3 at the same time, nice huh ? almost the same thing with clearing them like this:
PORTB &= ~((1 << PORTB5) | (1 << PORTB3) )
Step 7: The End
Now i hope you have a small understanding of how to play around with the bits inside a byte.
I have only talked about byte size in this instructable but if you understand how this works there are not much difference in dealing with larger variables or registers.
If things doesn't do as you suspect, write it down and check with the truth tables and you'll probably find the fault in no time.
As an exam. tell me the outcome of PORT:
PORT = ~( 0b1111 1111 << (1 | 2))