AVR Bitwise Operations in C

C defines a special operation for dealing with bits, and it is represented by two less than symbols in a row << or two greater than symbols in a row >>. This is called a bit shift. The value on the left side of the << is shifted a number times equal to the value on the right hand side of the <<. If you use << then the bits are shifted to the left, and if you use >> then the bits are shifted to the right. Here is a table of examples.

 

Bit Shift Statement In Hex Resulting Value In Hex
0x01 << 1 0x02
0x02 << 1 0x04
0x04 << 1 0x08
0x01 << 2 0x04
0x01 << 3 0x08
0x0F << 1 0x10
Bit Shift Statement in Binary Resulting Value in Binary
00000001 << 1 00000010
00000001 << 2 00000100
00001111 << 1 00011110
00001111 << 2 00111100

 

Lets look at some examples. If you wanted to turn on the lowest bit on PORTA, then you can simply write a 1 to PORTA because a 1, in 8 bit binary, is really 00000001.

PORTA = 1;

However, if you wanted to turn on the second lowest bit on PORTA, then an easy way to do it is with a bit shift:

PORTA = (0x01 << 1);

In the above line, we used 0x01 for the value to be shifted simply to make the code more self documenting. In this example, the value 0x01 is shifted to the left 1 time, and becomes 0x02.

Likewise, if you wanted to turn on the highest bit on PORTA, then an easy way to do it is with this bit shift:

PORTA = (0x01 << 7);

Or Multiple Values Together

Sometimes it is handy to turn on 2 bits at the same time, and not have to calculate the resulting value by hand. For instance, if you want to turn on the lowest bit and the highest bit on PORTA, you can simply or the two values together. In C, the or command is a vertical pipe like this |.

PORTA = (1<<0) | (1<<7)

Precompiler

Keep in mind that these bit shifts are computed by the precompiler before you code gets to the compiler, so they do not take up any extra code space or execution time in your AVR.

Macro _BV

The guys who wrote WinAVR decided that bit value manipulations are so common that a macro should be make to take care of them. Here is what they came up with.

#DEFINE _BV(bit) (1 << bit)

This macro is predefined for you, so you do not have to define it for yourself. You can use it any time you like. Using this defined macro, you can rewrite your bitwise operation to turn on the lowest and highest bits of PORTA like this.

PORTA = _BV(0) | _BV(7);

These macro’s do not exist in other versions of C. If you get addicted to them and want to use them in other versions of C, simple copy and paste the #DEFINE statement above into your project and you are good to go.

Updating the PORT Value One Bit at a Time

If you want to modify just one port bit, then you can easily do this in just one line. To turn bit 3 on, leaving the rest of the bits as they were, you can use this command:

PORTA = PORTA | _BV(3)

This line tells the compiler to read the status of the PORTA register, and OR it with _BV(3), which is the same as ORing it with (1<<3). The end effect is that no matter what value PORTA had in it, the 3rd bit is now set.

C provides a shortcut for reading a value and updating it in one line. The following line is an equivalent way to accomplish this.

PORTA |= _BV(3)

The |= command is an OR and an assignment in one step. value |= 1; is the same thing as saying value = value | 1;

Turning Bits Off

Turning bits off is very similar to turning bits on, you just have to think in logical negatives. To turn a bit off, you invert the new value (determine its compliment) then AND it to the value you want to modify. To invert a value in C, precede it with a tilde ~.

For instance, if you want to clear (set to 0) the first bit of PORTA, this is how you do it:

PORTA &= ~_BV(0)

Lets take a look at how that works. Assume that PORTA currently has the value 0x55 in it (01010101). If you want to clear the least significant bit, you need to AND the port with111111110, which is the inverse of 00000001.

 

PORTA 01010101
Mask 11111110
Logical AND 01010100

See how the AND preserved that status of all of the bits, except for the one that you want to clear? To do this operation in C, you could any of the following lines:

PORTA &= ~_BV(0x01);
PORTA &= ~(1<<0);
PORTA = PORTA & ~_BV(0x01);
PORTA = PORTA & ~(1<<0);

The compiler and precompiler working together will generate the exact same source code for all 4 of the above statements.

Let Macros Do The Work

If you really want to make your code self documenting, then you can define a couple of macros to do your bit setting and your bit clearing. WinAVR used to have a couple of macros like this defined, but they have sense removed them. WinAVR called them setb and clrb, so to keep from confusing the names with the old WinAVR version, we’ll name ours setbitand clearbit. They look like this:

#define setbit(port, bit) (port) |= (1 << (bit))
#define clearbit(port, bit) (port) &= ~(1 << (bit))

As you can see, these macros do exactly what we have talked about above. If you include them in your source code, then you can turn bits on and off with these simple lines:

setbit(PORTA, PA0)
clearbit(PORTA, PA7);

This makes your code much easier to understand. The precompiler will substitute the macros out at compile time and you will be left with the exact same code in your AVR.

Port Input

Continue on to our next guide to see how to read the input pins on an AVR using C.

Or head back to our index of AVR Guides here.

2 comments

Leave a Reply

Your email address will not be published. Required fields are marked *