In this section, you'll be learning about control individual bits within bytes using bit-wise operators and logic.

Transcript

And now we arrive at bitmath. Many of the variables that we've used so far have been simple one or zero variables, boolean logic, used with an integer variable. This is fine, but we're only using one bit's worth of data in a 16-bit number, which is wasting a lot of space. This is but one example out of many where it is incredibly valuable to be able to manipulate specific bits within data and control data at a lower level. Doing this is called bitmath, or bit manipulation, and it involves a set of operators called bitwise operators. We'll be looking at different logical operators and processes, such as AND, OR, XOR, NOT, and bit-shifting operators, which describe how different binary numbers can be manipulated. To understand this a little better, we'll look at an example of its own, and then combine them to perform powerful data functions. If this sounds a bit confusing, don't worry. Once you understand the concept of dealing with bits and bytes, it'll start to make a lot more sense. So let's take a look at some examples of these bitwise operators.

So here we have our different bitwise operators: AND, OR, XOR, NOT, and SHIFT, or bit-shift. Each of these performs a function to give an output based on binary numbers. You can also use hex numbers or decimal numbers, but it's a lot easier when it's viewed from the perspective of binary because it performs specific bit operations. So let's take a 4-bit number as an example. We'll use 0011 as our example. Now, AND is designed to take two values and output a third value, and it will copy a bit to the output only if that bit exists in both operators. To understand that, we'll take this number here, so we'll be performing it with 0011, and we'll use another 4-bit number, perhaps 1001. Now, the AND function will copy a bit to the result only if it exists in this one and that one. So it would be a 0, because there is not a bit in both sections there. Another 0, both places have a 0. Another 0, and this would be a 1, because a bit exists in both operands there, which is cool. So that is AND.

Which brings us to OR. OR says that there will be a bit in the result if it exists in either one operand or the other, or both as well, but it cannot be blank. So in this first bit here, there is a 0 here and a 1 there, which means there is a 1 in one of them, which is all we need. So that would be a 1. Now, there is neither in either of these, so that would be a 0. A bit exists in one of those, so that would be a 1. And finally, we have two 1s which still holds true, so we would have a 1. And that is the OR operator. So XOR, what's that about? Well, it is called exclusive OR, shortened to XOR. And what that does is it works in the exact same way as OR, but only if it exists in one of them, but not both, which pretty much you can equate to if the values are different. So let's take a look. Well, we have a 1 in this operand and a 0 in that part of the operand, so we would get a 1. There are 0s in both of these, so we get a 0. There is a 1 and a 0 in this place here. Now, these are both 1s, and with OR, that would result in a bit in our result. But with XOR, it's exclusive, so they have to be different. It can only have a bit in one of the operands, so that would be a 0 there, which brings us to NOT.

Now, NOT and SHIFT, which we'll get to now, are a little bit different in the sense that they don't perform a result based on two different operands. Instead, they modify a single operand. So let's take our base number, 0011 in binary, which, if you know your binary, would be... it's 3. So let's take a look. Well, NOT simply inverts whatever number you're using, and it goes each bit, and it inverts it, saying if it is a 0, then it is not a 0, which is a 1, and if it's a 1, then it becomes not a 1, which is a 0. So our output for NOT, if we performed it on this number, would equal 1100. It inverts the bits and flips them. Pretty self-explanatory. It's really cool. Now, SHIFT involves shifting all of the bits a certain amount of places. So with SHIFT, we have RIGHT SHIFT and LEFT SHIFT, and these use the standard arrow or greater than or less than symbols, but two of them. So that is SHIFT LEFT, and that one there is SHIFT RIGHT. They go in the direction that the arrows are pointing. So let's take our number here. We'll take 0011, and what this says is that if we shift it left two, each bit moves along two places, which means our new number would look like, if we shift it at one place instead, it would be, and so on and so forth.

And the SHIFT RIGHT operator works in exactly the same way. So if this is our least significant bit, then a SHIFT RIGHT operator would simply leave the result as, so write this here, what have we got? 0011 shifted right two will give our result as 0000, or 0. If it was shifted one, then we would get 0001, or 1, which is really cool. And that is a basic example, some truth table examples of how you can use the different bitwise operators. Now that you've got an understanding of how the different bitwise functions work, let's put that knowledge to use with an example using shift registers. Shift registers are a nice bit of hardware which allows you to extend the number of output or input pins on your microcontroller, using only a couple of pins to control it. You expand those pins. Think of a shift register perhaps as a power board for your I/O pins. A shift register requires three pins for control, the clock pin, the data pin, and an update pin. The clock is generated from your Arduino and allows data to be sent out one bit at a time along the data line, in series.

These bits are received by the shift register and then sent out to each output channel in parallel, so the shift register that we're going to be working with has eight channels, so we need to send it one byte of data. You can get parallel input/serial output registers, which are used for adding additional inputs, and serial input/parallel output registers, which are used for adding additional outputs. They can't work as both, and today we're going to be working with the output type. Today we'll be looking at the parallel type to control some LEDs, however the concept is the same only in reverse for using the input type. A handy feature of shift registers is that they contain a serial output pin, which allows data to be received at the input to be passed on to the next chip in the chain, allowing for multiple chips to be connected up in series. We'll be using a single chip for our example with eight outputs, and using the bitwise operators that we've just looked at to control the data being sent to some LEDs. So let's take a look. Here on the breadboard you can see that I've got eight LEDs that are all connected up, and they've got a resistor running to ground on the ground rail of my breadboard, and the wiring diagram is up on the text area of the workshop, so take a look at that.

Then I've got eight wires going to the outputs of the shift register, and I'll have a pin map diagram of this particular chip. It's a 74HC595N shift register. And then we've got those resistors running to ground, our power rails, power connected to the chip, set up in a pretty standard example, and you can easily chain multiple chips as we said before. Then I've got power running from my Arduino here, and then I've got the three pins data, clock, and the latch pin, or the update pin. So let's take a look at the code that we're going to use to run these. So we'll go right from the very top, and if it appears a bit complicated at first, bear with it, because it's only a simple number of different functions combined to give a few different results. So we've got three constants here, our data pin, our clock pin, and our latch pin. And the shift out functionality that we're going to use is embedded within the Arduino. It's not an external library that we have to add, and because of that we need to declare each of these pins as an output, because there's no library to initialize that for us. Here we've got two different global variables. One is a byte variable, which we haven't looked at yet, and byte is specific to Arduino. It's not part of the standard C makeup, but what it does is it creates a single byte variable rather than an integer, which takes up two bytes, so handy for saving space.

And this is going to be LEDMAP, and it's going to be our base example in which we perform operations on. So much like in the whiteboard example where we had a sample number that we used for the different operations, that's what LEDMAP is, and it has to be an 8-bit number, or a single byte, and you can go through and change this yourself. I've just made it to be 11110000, or that's 0xF0 if you're using a hexadecimal. So that is our binary, and 0B just denotes that it is a binary number. If it was 0x, that would be a hex number. Then delay time is we're just using for some timing control. So setting our pin modes as output, initializing the serial monitor, and I'm using a function that we've got written down below called shift right, and this is just going to control the shift register. And all I'm doing here is setting all of the LEDs as 0 for starters. So we're using some hex here, 0x00, which is simply 8 zeros in binary or 0 in decimal, so it sets all of the outputs as off. Then we're going to serial print some instructions for ourselves because we're going to be using the serial monitor to input some data to control the LEDs.

Then our loop has a few different elements here. We're saying if serial is available, which we've looked at before, which says yeah if there's some data waiting in the serial input that we want to use, then we're going to pass that as an integer to a variable, a local variable, that we've called input value for short. So then we're saying if input value is greater than 255, because we only need a single byte of data, so the instructions say to enter a number between 0 and 255, but what if your friend who's testing out your demo is a little thick and enters something greater than that? Well we've accounted for that and given some instructions saying, oh no, try again, again enter a number between 0 and 255 and return to the top of our loop. So let's assume that they've entered the correct number between 0 and 255. Very good. Now what we can do here is we're saying serial print decimal and then we're printing the input value as shown and that outputs it or prints it to the serial monitor as a simple decimal number as you would input it into the serial monitor because the serial monitor passes string values which we're simply going to use as decimal numbers.

Then it prints out the binary format so that we have an example to compare against easily as we've seen in our whiteboard demonstration. It's much easier to just use binary values for these different bit operations rather than decimal. Hex is still fairly easy but binary is by far in a way the easiest because you can modify the bytes and see what's happening directly. Now we're printing the value of our number in binary, so serial print our variable and then we put a comma and we're telling the format that we wish to print that variable in. Bin stands for binary. And then a spare line for some formatting. Now what we're doing here is we've got serial print and this follows we've got three different chunks of code for AND, OR and XOR functions. We've omitted the NOT and SHIFT functions because they're fairly self-explanatory and they perform only on a single number but these perform on two different operands. So serial print AND result giving us some formatting there and then on the same line we're performing LED map and input value as a binary number. So we're saying all right we're going to take LED map which if we scroll up is 11110000 or 0xF0 or just F0 in hexadecimal and we're performing the AND function on it with our input value.

So we input a value and it ANDs it with LED map and then prints the output result. Then it goes to the function SHIFTWRITE and passes it one piece of data which is the result of LED map and we ANDed with input value. So we'll scroll down here and in SHIFTWRITE you see it gets passed it takes one parameter it gets passed one argument here and digital write this is how we use the shift register. It's really simple we simply pull the latch pin low to say hey we're sending some data be ready to update. We use SHIFTOUT and we pass it the pin that our data pin is connected to, the pin our clock pin is connected to, whether we want the most significant bit or the least significant bit going first and you can just use that to toggle the direction that your data is going in and then we print out the value which in this case is the parameter value that we're being passed. Then we take a latch pin and pull it back up again saying the transfer is complete so we're only passing a single byte of data and then it will be all sorted and update that value when we set it high again which is really cool so that's how the SHIFTWRITE function works.

So we go and pass it the LED map being ANDed with input value and we wait three seconds wait for us to have a look at it and then we do the same thing but with OR and XOR and we use the pipe or vertical bar and the up arrow symbol there to perform those different functions and pass it on to SHIFTWRITE and at the end we rinse and repeat that loop. So let's take a look I'm going to connect my Arduino up make sure you guys can see that pretty well. All right now let's go to our serial monitor and see if that runs. First we're going to upload the sketch, wait for it to compile and upload and then we can go ahead and open up our serial monitor. All right fantastic so it's given us some instructions here. Enter a number between 0 and 255. Let's say so look here if we enter 256 it will recognize that's greater than the limit that we've set and say oh no try again so let's put in 188. So it says 188 in decimal is 10111100 in binary and then we've actually just missed it on the shift register but if you were quick and you were watching each of those output values would be mirrored on our shift register. So we'll run that again 188 and you can see the AND result is there followed by the OR result followed by the XOR result and you can see that the XOR result is one bit shorter than the other two and that's because it simply has a zero as the first bit or the number of bits before it receives a one because in binary that's the same thing.

So that would read exactly the same if that was shifted over one with a zero before it. So run that one more time and I'll point you in the direction of the LEDs here. So that's the AND result, the OR result and the XOR result in binary with our LEDs lining up to indicate a bit is set. Really really cool use of an LED. Alright so let's take a look let's try the number 10. Now 10 ANDed with our number is zero. ORed with it gives us a fairly large number and XORed gives us the exact same number as it works out. So you can go ahead and you can change LED map here to be whatever you want. So if it was if it was 255. Let's try uploading that and see how it goes. So in this case because our base number is different if we enter 10 instead of AND being zero we're going to get a different number again because binary 10 in binary is 1 0 1 0 and by comparing that against 8 1's we can see the difference between the XOR and the OR results. So that's pretty nifty it's a really cool use of shift registers and some really simple bit logic. You can see there's not a lot going on we're just performing a few different functions to get the different results. Use a shift register to visually display them and print them to the serial monitor which is really cool and whilst it might not be apparent what the power of using bitwise operators is they're incredibly valuable because most of the logic behind the built-in Arduino functions uses these bitwise operators to function.

Whereas if you were writing to bare metal if you were using an AVR microcontroller with a straightforward GCC compiler then you would need to set each output each port each input by itself and you're going to need to use a whole bunch of different bitwise operators in a single line to perform these functions. So it's really useful to know and as you start getting into some more advanced projects as we'll look in for future chapters you're going to use them heaps and heaps. So check out the next section on using the EEPROM.

Feedback

Please continue if you would like to leave feedback for any of these topics:

  • Website features/issues
  • Content errors/improvements
  • Missing products/categories
  • Product assignments to categories
  • Search results relevance

For all other inquiries (orders status, stock levels, etc), please contact our support team for quick assistance.

Note: click continue and a draft email will be opened to edit. If you don't have an email client on your device, then send a message via the chat icon on the bottom left of our website.

Makers love reviews as much as you do, please follow this link to review the products you have purchased.