ADVANCE BIT MATH


QUICK REVIEW OF THE PREVIOUS LESSON:
SHIFT LEFT SHIFT RIGHT NOT AND OR XOR
<< >> ~ & | ^
1 << 2
=
0000 0100
or
4
8 >> 2
=
0000 0010
or
2
~0 = 1
~1 = 0
0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0

In the previous chapter we discussed the basics of bit math. The good news is that we don’t have to learn any more operations; We are simply going to discuss some abbreviations and common multi-operation formulas. Once again a warning, if you do not understand the information in the Bit Math Chapter please re-read it, this chapter builds heavily on the theory introduced in the previous chapter.


ABBREVIATIONS:

Most of the time, we are going to take a register, read its value, perform an operation and load the new value back into the same register. Previously, I had always used “i = i <operation> #” in order to show this. However, there is an easier way that seems to be the preferred way among microcontroller programmers.

If you use “<operand>=” (eg. “|=” for OR) the compiler will assume that your using the destination register data, performing an operation on it and then writing it back into itself.

Confused? Hopefully this code example will clarify things a bit.

    i <<= 1;    // is the same as i = i << 1
    i >>= 2;    // is the same as i = i >> 2
    i ~=;       // is the same as i = ~i
    i &= 3;     // is the same as i = i & 3
    i |= 4;     // is the same as i = i | 4
    i ^=5;      // is the same as i = i ^ 5

From now on we will see this a lot. But don’t worry, I will include the long hand version when I’m solving the equations.


MASKING:

Definitely not as fun as Halloween, but a lot more useful.

When we are working with registers we are generally interested in the status of specific bits, the best way to get the information about specific bits is to build what is called a mask. A mask is simply a number (in binary) that contains 1s in the bits that we are interested in.

Say we want to do work on the 3 bit and the 5 bit, we would create a mask that looks like this 0010 1000 (remember the first bit is a 0bit not a 1s bit therefore the highest bit is the 7th bit not the 8th bit … terminology, it will get you all the time). The 0s represent the bits that we are not interested in and the 1s represent the bits that we are interested in.

So now remember back last chapter, the AND operation caused a TRUE(1) if both numbers have the same bits TRUE(1). So if we take our mask and AND it with the original register, we will end up with a value of 0 if the 3th and/or the 5th bit isn’t true and a value greater then 1 one or both of the registers are true.

    x = 64;
    y = x & 0b00101000;    // y = 0 if 5th or 7th bits are not true, and y>0 if one or both are true

Mathematically here is what we did above:

    0100 0000         x (set to 64 on the first line)
& 0010 1000         mask (created with 0b00101000 on the second line)
    ---------------
    0000 0000         result, loaded into y

Now while the above code works, most programmers will not build a mask in such a way; 00101000 looks confusing, and it is only an 8 bit register, can you imagine how confusing a 16bit or 32 bit register will be? Instead, most will use a combination of a BIT SHIFT LEFT and an OR in order to create a mask as follows:

    x = 64;
    y = x & ( (1<<5) | (1<<3) );

Mathematically here is what we did:

Solve the brackets:

(1 << 3)
creates 0000 0001
shift it left by 3 to get 0000 1000

(1 << 5)
create 0000 0001
shift it left by 5 to get 0010 0000

Rearranged to solve the ( (1<<5) | (1<<3) ) part of the equation:

   0000 1000     (1 << 3)
| 0010 0000     (1 << 5)
   ---------------
   0010 1000     notice that we just created 0b00101000

Substitute:

y = x & 0010 1000

Now rearrange to solve:

    0100 0000     x (set to 64 on the first line)
& 0010 1000     mask (created with ( ((1<<5) | (1<<3)) )
    ---------------
    0000 0000     result, loaded into y

You might wonder, why create so much math, the answer is simple, anytime you see (1 << X) you can automatically think that you are putting a 1 into the nth spot of the register. Or make a mask (in binary) a 1 with X Zeros behind it.


PUTTING IT ALL TOGETHER:

Yes you can put both of the above lessons together so lets take y and set the 3 bit and store the value back into y.

    y = 64;
    y |= (1<<3);
Expand:

y = y | (1 << 3)

Solve the brackets:

(1 << 3)
creates 0000 0001
shift it left by 3 to get a 0000 1000

Substitute:

y = y | 0000 1000

And finally rearrange to solve:

   0100 0000     y (set to 64 on the first line)
| 0000 1000     mask (created with (1<<3) )
   ---------------
   0100 1000     result, loaded into y

COMMONLY USED EQUATIONS:

There are 4 major equations that you will see all over the place when dealing with a microcontroller, so let's examine them to debunk any myths or questions.

Set Bit, Clear bit and flip bit are most commonly used for outputs. and Get Bit bit is often used for inputs.

Set Bit:

This equation is used to set a single bit in a register, lets set the 2 bit in the PORTB register.

    PORTB = 8;
    PORTB |= (1 << 2);
Expand:

PORTB = PORTB | (1 << 2)

Solve the brackets:

(1 << 2)
creates 0000 0001
shift it left by 2 to get a 0000 0100

Substitute:

PORTB = PORTB | 0000 0100

And finally rearrange to solve:

   0000 1000     PORTB (set to 8 on the first line)
| 0000 0100     mask (created with (1<<2) )
   ---------------
   0000 1100     result, loaded into PORTB

Clear Bit:

This equation is used to clear a specific bit in a register, lets clear the 5 bit in the PORTC register.

    PORTC = 34;
    PORTC &= ~(1 << 5);
Expand:

PORTC = PORTC & ~(1 << 5)

Solve the brackets:

(1 << 5)
creates 0000 0001
shift it left by 5 to get 0010 0000

Substitute:

PORTC = PORTC & ~(0010 0000)

NOT the brackets:

~(...) to get 1101 1111

Substitute:

PORTC = PORTC & 1101 1111

Rearrange to solve:

    0010 0010     PORTC (set to 34 on the first line)
& 1101 1111     mask (created with (1<<5) )
    ---------------
    0000 0010     result, loaded into PORTC

Flip Bit:

If you want to flip the value of a bit in the register, so lets flip bit 5 in the PORTD register.

    PORTD = 8;
    PORTD ^= (1 << 5);
Expand:

PORTD = PORTD ^ 0010 0000

Solve the brackets:

(1 << 5)
creates 0000 0001
shift it left by 5 to get a 0010 0000

Substitute:

PORTD = PORTD ^ 0010 0000

And finally rearrange to solve:

    0000 1000     PORTD (set to 8 on the first line)
^ 0010 0000     mask (created with (1<<5) )
    ---------------
    0010 1000     result, loaded into PORTD

Get Bit:

If you want to find out what the state of a bit within a register is this is the formula. The formula will output 0 if the bit is not set(0) and greater than 1 if it is set(1). Let's see if 3 bit is set.

    PORTB = 8;
    if ( (PORTB & (1 << 3)) > 0)
    {
        // do this if the 3 bit is set(1)
    }
    else
    {
        // do this if the 3 bit is not set(0)
    }
Solve the brackets:

(1 << 3)
creates 0000 0001
shift it left by 3 to get a 0000 1000

Substitute:

PORTB & 0000 1000

And finally rearrange to solve:

    0000 1000     PORTB (set to 8 on the first line)
& 0000 1000     mask (created with (1<<3) )
    ---------------
    0000 1000     result > 0 therefore the 3rd bit is set(1)

Hope that helps.


Cheers
Q