Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C Programming for microcontrollers (Joe Pardue, 2005).pdf
Скачиваний:
272
Добавлен:
12.08.2013
Размер:
4.55 Mб
Скачать

Chapter 4: C Types, Operators, and Expressions

Bitwise Operators

Table 5: Bitwise Operators

Operator

Name

Example

Defined

~

Bitwise complement

~x

Changes 1 bits to 0 and 0 bits to 1

 

NOT

 

 

&

Bitwise AND

x&y

Bitwise AND of x and y

|

Bitwise OR

x|y

Bitwise OR of x and y

^

Bitwise exclusive OR

x^y

Bitwise XOR of x and y

<<

Left shift

x<<2

Bits in x shifted left 2 bit

 

 

 

positions

>>

Right shift

x>>3

Bits in x shifted right 3 bit

 

 

 

positions

Bitwise operators are critically important in microcontroller software. They allow us to do many things in C that can be directly and efficiently translated into microcontroller machine operations. Keep in mind that these operators work on bits but are similar enough to the logical operators that you will get confused. Let’s look at the truth tables for &, |, and ^:

AND

OR

XOR

0 & 0 = 0

0 | 0 = 0

0 ^ 0 = 0

0 & 1 = 0

0 | 1 = 1

0 ^ 1 = 1

1 & 0 = 0

1 | 0 = 1

1 ^ 0 = 1

1 & 1 = 1

1 | 1 = 1

1 ^ 1 = 0

Let’s create a variable, myByte and do some bitwise operations on it:

unsigned char myByte = 0;

We can set bit 3 (numbering from the right starting with 0):

myByte = myByte | 0x08;

To see what’s happening Let’s look at these in binary:

myByte = 00000000 = 0x00

53

Chapter 4: C Types, Operators, and Expressions

0x08 = 00001000 = 0x08

------------------------

OR = 00001000 = 0x08

Suppose myByte = 0xFF:

myByte = 11111111 = 0xFF 0x08 = 00001000 = 0x00

------------------------

OR = 11111111 = 0xFF

Or maybe myByte = 0x55:

myByte = 01010101 = 0x55 0x08 = 00001000 = 0x08

-----------------

OR = 01011101 = 0x5D

This all shows that only the 3rd bit of myByte is affected by the OR operation, since it is the only bit equal to 1 in 0x08.

Now let’s do the same thing with the & operator:

unsigned char myByte = 0;

We can set bit 3 with:

myByte = myByte & 0x08;

To see what’s happening Let’s look at these in binary:

myByte = 00000000 = 0x00 0x08 = 00001000 = 0x08

------------------------

AND = 00000000

Suppose myByte = 0xFF:

myByte = 11111111 = 0xFF 0x08 = 00001000 = 0x08

------------------------

54

Chapter 4: C Types, Operators, and Expressions

AND = 00001000

Or maybe myByte = 0x55:

myByte = 01010101 = 0x55 0x08 = 00001000 = 0x08

------------------------

AND = 00000000 = 0x00

And maybe myByte = 0xAA:

myByte = 10101011 = 0xAA 0x08 = 00001000 = 0x08

------------------------

AND = 00001000 = 0x08

In each of the above cases we are only dealing with a single bit, but we might be interested in any or all of the bits. One of the most important features of using masks with bitwise operators is that it allows us to set or clear a specific bit or set of bits in a byte without knowing or affecting the bits we aren’t interested in. For example, suppose we are only interested in bits 0, 2, and 6. Let’s set bit 6, regardless of its present value, then clear bits 0 and 2, also regardless of their present value and, here’s the trick, leave bits 1, 2, 4, 5, and 7 as they were when we began. Let’s have myByte starting equal to the secret to life the universe and everything, which according to Douglas Adams is 42, but remember that the start value doesn’t matter to us since we are going to force 3 bits to values regardless of the start value.

NOTE:

myByte = myByte | 0x08; is the same as

myByte |= 0x08; which we will use from no on.

At the beginning myByte is equal to 42 = 0x2B = 00101011. We set bit 6 with:

myByte |= 0x40;

which does the following:

55

Chapter 4: C Types, Operators, and Expressions

myByte = 00101011 = 0x2B 0x40 = 01000000 = 0x40

------------------------

AND = 01101011 = 0x6B

Next we want to clear bits 0 and 2:

myByte &= 0xFA;

which does the following:

myByte = 01101011 = 0x6B 0x40 = 11111010 = 0xFA

------------------------

AND = 01101010 = 0x6A

So in summary we set bits with ‘|’ and clear bits with ‘&’.

The Butterfly Software has a clever snippet in LCD_driver.c where the ‘&’ and ‘~’ operators are used to convert a lowercase letter to a capital:

// c is a letter

// Convert to upper case

if (c >= 'a')

c &= ~0x20;

// if necessary

This statement first checks to see if the character c is greater than or equal to ‘a’ and uses the convenient fact that in ASCII the letters are sequential with the capitals beginning at 0x41 and the lowercase beginning at 0x61. So if the character is >= 0x61 then it is lowercase and we can derive the uppercase version by subtracting 0x20. So why do we use ‘c &= ~0x20’ instead of subtracting as in ‘c -= 0x20’? Well, it is more efficient for the machine to take the inverse of the minuend and then AND it with the subtrahend (this by the way, is the first time since grammar school that I’ve actually used minuend and subtrahend, I’m amazed that these terms actually stuck. Maybe it was the teachers steely glare and the dangerous looking pointer she held.) Let’s look at it shall we?

0x20 = 00100000 ~0x20 = 11011111

56

Chapter 4: C Types, Operators, and Expressions

‘a’ = 0x61 = 01100001 ~0x20 = 11011111

--------------------

AND = 01000001 = 0x41 = ‘A’

This is a lot harder for us than ordinary subtraction, but much easier for the machine.

While using &= and/or |= is acceptable, the Butterfly code generally does this a little differently, not to make your life harder, but to make the code a little clearer, though it won’t seem that way at first. When we create masks to set or clear bits, we will name the bits so for instance the first bit in port D is named PD0 and we can guess that the eighth bit is named PD7. That’s simple, but it gets hairy when we give complicated names to all the bits in the dozens of registers. For instance in the Timer0 register: TCCR0A, Timer/Counter Control Register A we have the following bits named (page 90 ATMEGA169 databook):

Bit 7 = FOC0A – Force Output Compare A

Bit 6 = WGM00 – Waveform Generation Mode 0

Bit 5 = COM0A1 – Compare Match Output Mode 1

Bit 4 = COM0A0 – Compare Match Output Mode 0

Bit 3 = WGM01 – Waveform Generation Mode 1

Bit 2 = CS02 – Clock Select Bit 2

Bit 1 = CS01 – Clock Select Bit 1

Bit 0 = CS00 – Clock Select Bit 0

Bits 0, 1, and 2 the Clock Select Bits are defined as:

57

Chapter 4: C Types, Operators, and Expressions

Figure 12: from page 92 of the ATMega169 data book

Let’s initialize the timer with:

// Set Fast PWM mode and CLK/256 prescaler TCCR0A |= (1<<WGM01)|(1<<WGM00)|(4<<CS00);

We use the left shift operator ’<<’ to shift the number before the operand to the numeric position in the byte specified by the number following the operand. In the case of (1<<WGM01) we shift a 1 to the left by WGM01 bit positions, and we see from iom169.h:

/* TCCR0A */

7

#define FOC0A

#define WGM00

6

#define COM0A1

5

#define COM0A0

4

#define WGM01

3

#define CS02

2

#define CS01

1

#define CS00

0

WGM01 = 3, so (1<<WGM01) is the same as (1<<3) and means to shift 0000001 three places left to 00001000. Now look at The TCCR0A register and notice where the WGM01 bit is located. Ah ha! Like I said, we have a way of dealing with a bit by a name.

58

Chapter 4: C Types, Operators, and Expressions

But wait, wouldn’t that mean that (4<<CS00) means we are setting the CS00 bit to 4? But a bit can only be 0 or 1 so how the heck do we set a bit to 4?. Well, or course we don’t. The CS00 = 0, so we are left shifting the number 4 by 0 meaning we aren’t doing any shifting of the 4, we are just ORing it with the other two values:

TCCR0A |= (1<<WGM01)|(1<<WGM00)|(4<<CS00);

Since 4 = 00000100, we will be setting the CS02 bit, not the CS00 bit. So why didn’t we say (1<<CS02) instead of (4<<CS00)? And the answer is ‘because’. Actually the answer is that the lower three bytes of the TCC0RA register can be considered a three bit field for a number used to select the clock. The number 4 selects the clock/256 prescaler (see the Clock Select Bit table in Figure 12 above). Now we can see that (5<<CS00) would mean set the clock to clk/1024 and so forth. We will often think in terms of multi-bit fields.

Our goal was to ‘Set Fast PWM mode, CLK/256 prescaler’ (this will be explained later in the timer section) so we want to set bits 6, 3, and 2 (01001100 = 0x4C) without affecting the other bits. If we OR it like before we would:

TCC0RA |= 0x4C;

Which is:

TCC0RA = xxxxxxxx = we don’t know, or need too. 0x4C = 01001100

------------------------

OR = x1xx11xx = our bits are set the rest are not

changed.

The only problem is what does it mean to setup the timer with 0x4C? When you see TCC0RA |= 0x4C; you don’t know what it is doing and you have to derive the binary and look in the data book to figure it out. But using:

TCCR0A |= (1<<WGM01)|(1<<WGM00)|(4<<CS00);

The (1<<WGM01)|(1<<WGM00)|(4<<CS00) is the same as 0x4C except that we can read that we are setting both the Waveform Generation bits and we are setting

59