In the last module, we went from using massive chunks of code to flip individual bits, to a more elegant solution that replicated the ability of the built-in shiftOut() function. Now we’ll use the tools from the previous module to expand our abilities to modify the individual values contained in a byte.

Objectives

  1. Understand what the term bit masking means.
  2. Continued practice using bitwise operators.
  3. Be able to set, clear or retrieve individual bits from an 8-bit variable.

Background
Bit Shifting the 74HC595
Boolean and Bitwise Operators


Schematic
Education Shield – Shift Register Subsystem


Setup
For this module, you’ll need the following equipment:

1. Place jumpers on the 74HC595 Enable block along the edge of the Education Shield. This will enable microcontroller interaction with the shift register.

2. Mate the Education Shield with your Arduino UNO R3.

3. Finally, connect your Arduino to your USB cable, and upload this little sketch to drive the OE pin low.

Alternatively, you can use the Manual Serial Communications Trainer instead. Connect 5V and CLR to 5V, GND and OE to GND, DATA to D8, LATCH to D7, and CLOCK to D6. The code above is not necessary in this configuration.


Bit Masking

Every chip you will use with serial communications will need configuration, and in some cases, the datasheet will show that various portions of the data stream, either sent or received, need to be ignored. The method used to process this is called bit masking. Bit masking is a way to modify or isolate the contents of a chunk of data in a way that ensures the rest of the data remains unaltered. It’s called a mask because it functions in the same way you use masking tape when you paint a wall — the tape isolates the paint to a specific area of operation. That is a key point: your work within a configuration register, or manipulation of a data stream needs to be surgical in nature — go in, change a bit, get out.

To implement this, we’ll make repeated use of the boolean and bitwise operators discussed previously. I’ll also present the information in both binary, which is much easier to understand intuitively but only available in the Arduino IDE, and hex which is used commonly and available universally. Also note, that the bits are counted from the 0th bit to the 7th, right to left, not from the 1st to the 8th.


Problem: The data sheet says that in order to turn the chip functionality on, we have to enable the sixth bit. The other seven bits are factory configured, not described and should not be changed. This is a common configuration method for a chip that has a power save feature.

So, we have something that looks like Bx0xxxxxx. We need to change the 0 to a 1, and we need to make sure that none of the other bits are changed. If you look at the surgical tools of bit operation, you’ll see the and, or, xor and not. Rather than detail the various truth tables again, let’s think of how each of those works in plain English.

If you compare a value with and, it will set everything to zero, unless you have a one in both places. That sounds like it would be good for blanking things out since it makes it really easy to create zeros.

If you compare a value with or, it will set everything to one, unless you have a zero in both places. That sounds like it would be good for enabling things since it makes it really easy to create ones.

If you compare a value with xor, it will flip anything that doesn’t match from the original to its opposite, but leave anything that does match the same.

not isn’t a comparison, it’s just a hammer for very specific nails, taking the entire set of data you give it and flipping each bit to its opposite.

It sounds like using the or bitwise operator will be of best use here, since we can use it to leave the original values the same except for the single 1 we want to ensure is a 1. We do this by passing in a bunch of zeros. If there is a zero in the original byte, it will still be a zero after applying our test, because 0|0 = 0. If there is a one in the original byte, it will still be a one after applying our test, because 1|0 = 1. On the sixth bit though, we will put a one in our test. That will force a one into our result because 0|1 = 1.

Remember, the datasheet doesn’t say what the original values of those bits are. We could sample them with an oscilloscope or a logic analyzer if we cared to, but they will only tell us what they are, not what they’re used for… that’s a secret known only to the manufacturer. But in this case, we will just treat it like a black box and enable our bit. Here’s how we would do it in code.

Again, the binary values only apply to writing code in the Arduino IDE, so to adhere more closely to how other code is written, let’s use hex instead: B01000000 = 0x40

This code can be optimized further…

Here is a sketch you can use to visualize the bit mask by sending the values to the shift register so they can be displayed on the LEDs of the Education Shield…

The code above uses a function called refreshShiftRegister. All it does is manage the process of sending a byte of data to shiftOut and cycles the latchPin.

Problem: We have a chip that does on-board processing of data. It lets us know that the processing is finished and the data is available by enabling a data ready bit, which, for reasons known only to the manufacturer, is the 3rd bit inside 8-bits of unrelated data that we need to ignore.

What we need to do is isolate the data ready bit, determine if it’s a one or a zero and then take action based on the result. If we tried using the method from the first problem, we would create a bug because using the or operator would result in all the other bits retaining their value, and a one always being placed in the position we’re checking. We need to blank out the rest of the byte so that the state of this individual bit stands out. That means we need to create a bunch of zeros, and to do that, the and operator is our friend. We are going to and the incoming data with the value B00001000. If a bit in the original value is a zero, then our mask will return a zero, because 0&0 = 0. If a bit in the original value is a one, then our mask will return a zero as well, because 1&0=0. On the third bit though, we will retrieve the bit in the original value: if it’s a one, then 1&1=1, and if it’s a zero, then 0&1=0.

Again, we’re treating the unrelated bits as black boxes, we don’t know what they are, and we don’t care at this point, we just need to eliminate them so we can test to see if the data is ready for processing. Here is how it would look in code…

… and here it is with hex: B00001000 = 0x08

This code can also be optimized further…

To pull the curtain back a little further on this example, realize that if the data isn’t ready, the masked isDataReady value will equal zero. However, after the processing has occurred and the data ready bit has been flipped, the masked isDataReady value will equal 0x08. You can treat that as either the actual value or simply as a non-zero value to test. Typically, you would grab the masked isDataReady value from a function that does the actual serial communication and masking, and then use it in a polling algorithm, either testing with an if statement, or more likely, putting yourself in a holding pattern with a do…while loop waiting for the processing to complete. Here’s some psuedocode to illustrate the do…while loop example.

And here is some visualization code…

In the example code above, the LATCH button on the Education Shield is used to simulate a chip setting the DATAREADY bit. When the LATCH button is pressed, the value of the latchPin is shifted into the dataready bit position to be tested by the mask. When the change is detected in the DO…WHILE loop, the original value of the byte is flashed with the mask to show you which bit is being tested, and then shows the end result after masking, before looping to the start.

In the DO…WHILE loop, you will see the pinMode of latchPin flipped to an input state, and then after these two lines, the latchPin is immediately changed back to an OUTPUT. One of the limitations of the Education Shield is that the buttons are directly connected to pins which are also connected via jumpers to the 74HC595, so while you’re using the shift register, the buttons could potentially interfere with the functioning of the code. I use the LATCH button because it has no affect on the shift register unless data is in the process of being transferred. Since my control code in the DO…WHILE loop isn’t clocking in data, I can quickly utilize the debounced LATCH button as an input, as long as I turn it back to an output before data is clocked. This can be used as a handy work around for any code you may want to use in the future.

There is a large section of serial debug output that can help you to see the values as they are changed.

In the example code where the masked bits are flashed three times, you see the following:

Why does the mask have the not bitwise operator applied to it and what is the affect?

Hover to read answer

Problem: Imagine we have a chip that can control whether the hot water tap is turned on or off, and we want to control the tap based on how hot the water is. The datasheet tells us that when we retrieve a byte of temperature data from our chip, the least significant five bits of that byte contain the temperature, and the most significant three bits configure how open we allow the tap to be. The datasheet tells us that the various byte values map to this…

Bxxx00001 = COLD
Bxxx00011 = WARM
Bxxx00111 = HOT
Bxxx01111 = VERY HOT
Bxxx11111 = HOLY HELL
B001xxxxx = COMPLETELY CLOSED
Bx11xxxxx = HALF OPEN
B111xxxxx = COMPLETELY OPEN

Let’s call the tap control bits “C” and the temperature value bits “T”. The byte we get back would look like this: BCCCTTTTT. When we retrieve the data, our temperature value needs to be isolated from the control value or we’ll get crazy readings, and since it makes sense that the configuration of the tap would be based on the temperature, we need to make sure that we don’t mess with the temperature bits when we set the tap bits.

Let’s rewrite the byte map in hex to show concrete values for the various levels. It will be the goal of our code to replace all the x’s with zeros.

0x01 = COLD
0x03 = WARM
0x07 = HOT
0x0F = VERY HOT
0x1F = HOLY HELL
0x20 = COMPLETELY CLOSED
0x60 = HALF OPEN
0xE0 = COMPLETELY OPEN

So let’s assume we have a two variables, one called currentTemp that will hold the water temperature and tapConfig that will hold the tap configuration we want to send to the chip, and we have two functions, getData pulls the byte from the chip and setTap(tapConfig), sends the byte back to the chip.

What code would you use to retrieve and isolate the current temperature?


What code could you use to set the tap to completely closed?


Problem: You have a 24 bit ADC, but your serial communications method is only able to transfer 8 bits at a time. The datasheet goes on to say that the values are sent least significant byte first. How do you assemble a complete and accurate value for each reading when your data is only sent in chunks.

The first thing to note is the data width of our ADC value: 24 bits. Immediately, we know that we can’t store a 24 bit value in a byte (1 byte = 8 bits), and an integer variable is 16 bits, so still too small. You have to go to a long, capable of holding a 32 bit value, in order to accept the full value from the ADC. We’ll create a long called fullValue to hold our ADC reading.

So now we have a place to put the data, but we need to load it into the variable in the correct sequence. Let’s assume that there’s a function, getADCdata() that performs the serial interchange and returns our data to us in three bytes (3 x 1 byte = 24 bits = ADC reading), but in the sequence described in the datasheet. Those three bytes are called adcLeastSigByte , adcMidSigByte and adcMostSigByte .

By using a combination of bit shifting and oring, we can assemble our long from the three component parts.

I’m using separators to make it easy to see what’s happening, but this wouldn’t work in code, primarily because you can’t specify a binary value beyond eight bits in the Arduino IDE, B11111111 is the largest number you can use. Hex and decimals are only limited by the size of the containing variable. Remember, the Arduino UNO is built around the ATmega328P, which is only an 8 bit processor. The Arduino designers create the binary notation out of whole cloth to make it easy for developers to understand what was going on in a byte of data. See the file in your Arduino directory .\Arduino\hardware\arduino\cores\arduino\binary.h

We’ll use the following example values…

What you do is take the Most Significant Byte and shift it to the left 16 bits, then take the Middle Significant Byte and shift it over 8 and finally tack the Least Significant Byte onto the end. Each chunk shifted to the left is ored with the next incoming value…

fullValue = ((adcMostSigByte << 16) | (adcMidSigByte << 8) | (adcLeastSigByte) );

That code essentially performs the following…

If the most significant bit of the 24 bits returned was a validation bit indicating that the ADC was ready to perform another sample, how would you isolate that bit to test?

Hover mouse to read answer:

Previous Post
Bit Shifting the 74HC595
Next Post
Timing Diagram Basics
You might also like
Menu