C BIT MATH



BIT OPERANDS:

The most confusing part of microcontroller programming is the fact that we have to manipulate data at a bit level. While that may seem scary at first it becomes second nature over time. The problem itself is that there is no perfect examples of how this works .... hopefully this chapter will change that.

Most of the time we work with 8-bit data within the AVR (except the 10 bit ADC). Understanding bit math gives us a very powerful tool that reduces a lot of manual work.

So, were do shall we begin? How about the Bit shift function.


BIT SHIFT FUNCTIONS ( << and >>)

The bit shift function does just what it says it does, it shifts the data over to the left or over to the right by one bit. The Bit Shift Function is the most used bit math function when dealing with the AVR.

So lets see this in action in mathematical format. Please note that I will put a space every 4th character to make the numbers easy to read, so 00011000 will read as 0001 1000.

Bit shift left:

(0000 0001 << 2) = 0000 0100
(0000 0001 << 3) = 0000 1000
(0000 0010 << 1) = 0000 0100
(0000 1000 << 5) = 0000 0000

Ok what happened in that last example? why does the math come out to be 0000 0000 and not 1 0000 0000, well this is actually what happens in an 8 bit register if you shift bites to fare over they basically fall off the edge of the world never to bee seen again. So be careful not to shift bits too far, as this will end up loosing data.

Bit shift Right:

(0000 0010 >> 1) = 0000 0001
(0001 0000 >> 3) = 0000 0010
(1000 0000 >> 0) = 1000 0000
(0000 0001 >> 1) = 0000 0000

Did I do something strange again? in the 3rd example perhaps? nothing moved did it, because as you can see, the byte was shifted by 0 spaces. But I bet you noticed that the bit in the last example fell of the .... well beginning of the world this time.

Bit Shifts in C:

So now that we have seen a bit of bit shifting in the mathematical model lets take a look at it in code.

    i = 1;         // i = 0000 0001
    i = i << 2;    // i is now = 0000 0100
    i = i >> 2;    // i is now = 0000 0001

See it's not as hard as it may seem. However, if you don't understand the theory so far please go back and re-read this tutorial or post a question, the following theory will build upon this.

There is one final thing to note about bit shifts, they could be used to multiply or divide any number by any power of 2 (ie 2, 4, 8, 16, 32 ... etc). Lets see this in action:

    i = 10;        // i = 0000 1010
    i = i >> 1;    // i = 0000 0101 = 5 (Shift >> by 1 to divide by 2)
    i = i << 2;    // i = 0001 0100 = 20 (Shift << by 2 to multiply by 4)

Now this is useful because multiplication and division use 2 clock cycles, while bit shifting uses 1, so start replacing that / and * with a bit shift the next time you need to divide or multiply by 2s 4s and 8s and save those cycles.

So what's next?


NOT (~)

Not is fairly simple, it basically flips the bit to its opposite.

~0 = 1
~1 = 0

If used in a big register if you will change each individual bit to the opposite state.

~ 0000 1111 = 1111 0000

Not in C:

Not is very simple to use, so here it is.

    i = 0b10101010;
    i = ~i;                // i is now 01010101
    i = 0b11111111;
    i = ~i;                // i is now 0

AND ( & )

Mathematically and is fairly easy, you compare 2 numbers together, and if they are both TRUE(1) then your answer is TRUE(1). I like to remember this as: If a AND b is TRUE(1) then the answer is TRUE(1).

0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1

On a bit level it looks fairly easy, however, when we expand the length of bits is looks a bit confusing, so it is sometimes best to rewrite the byte and put them one on top of the other.

15 & 170 = 10

0000 1111 & 1010 1010 = 0000 1010
    0000 1111
& 1010 1010
    ----------------
    0000 1010
153 & 170 = 136

1001 1001 & 1010 1010 = 1000 1000
    1001 1001
& 1010 1010
    ----------------
    1000 1000
6 & 11 = 2

0000 0110 & 0000 1011 = 0000 0010
    0000 0110
& 0000 1011
    ----------------
    0000 0010

And in C:

As before this function is fairly simple when you see it in C:

    i = 6;
    i = i & 11;    // 6 & 11 therefore i = 2
                   // see last math example above for details

Now, here is where things get a bit confusing, in microcontroller programming you will often see the above code being abbreviated by using &=

    i = 153;
    i &= 170;        // same as i = i & 170 or i = 153 & 170
                     // see second last math example above for details

OR and XOR ( | and ^ )

This might be a bit confusing, there are 2 types of OR functions, the INCLUSIVE OR ( | ) and the EXCLUSIVE OR ( ^ ).

OR (INCLUSIVE):

The Inclusive OR is always referred to simply as OR.

The OR function checks to see if ether bit is a 1 so basically your asking is variable a OR b true, and include both true (get it include, inclusive? that's how I always remember it)

0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1

So lets see this in action using an 8-bit number. Once again, I will re-arrange the register one on-top of the other to make it easier to see what is happening.

15 | 170 = 175

0000 1111 | 1010 1010 = 1010 1111
    0000 1111
| 1010 1010
    ----------------
    1010 1111
153 | 170 = 187

1001 1001 | 1010 1010 = 1011 1011
    1001 1001
| 1010 1010
    ----------------
    1011 1011
6 | 11 = 15

0000 0110 | 0000 1011 = 0000 1111
    0000 0110
| 0000 1011
    ----------------
    0000 1111

OR in C:

So without delay here is what OR looks like in C. As always I use the same numbers in my C code as I do in my mathematical model above.

    i = 15 | 170;     // i is now equal to 175 (see above example for the math)
    i = 6;            // lets set i to 6 for our next example
    i = i | 11;       // i is now 15 (see above for the math)

XOR:

XOR is the same as OR, but it excludes both true values, in other words its only true if both values are different.

0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0

Without delays, lets see the mathematical model once again.

15 ^ 170 = 165

0000 1111 ^ 1010 1010 = 1010 1010
    0000 1111
^ 1010 1010
    ----------------
    1010 0101
153 ^ 170 = 51

1001 1001 ^ 1010 1010 = 0011 0011
    1001 1001
^ 1010 1010
    ----------------
    0011 0011
6 ^ 11 = 13

0000 0110 ^ 0000 1011 = 0000 1101
    0000 0110
^ 0000 1011
    ----------------
    0000 1101

XOR in C:

And for the final time here is what the XOR logic looks like in C.

    i = 15 ^ 170;      // i now equals 165

    i = 6;             //sets set i to 6 for our next example
    i = i ^ 11;        // i now equals 13 (see above)

Well, that's all, our next chapter will cover advance Bit math functions, which in lamest terms covers how to use multiple functions together in order to achieve common AVR tasks such as setting a single pit in a register.

I hope that I was able to shine some light on a difficult topic. This was the topic that gave me the most amount of problems when I started out with microcontrollers.

And now I'm off to watch cartoons!!!!


Cheers
Q