NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » confusing bitwise arithmetic example

December 01, 2012
by keith712
keith712's Avatar

i picked up a copy of Joe Pardue. C programming for microcontrollers. to learn more about AVR-C. in chapter four Joe gives the following example:

register TCCR0A has some value xxxxxxxx.

TCCR0A's bits are: 0 cs00, 1 cs01, 2 cs02, 3 wgm01, 4 com0a0, 5 com0a1, 6 wgm00, 7 foc0a

the problem is how to put the value x1xx1100 into register TCCR0A?

Joe says to do the following:

TCCR0A |= (1<<wgm00) | (1<<wgm01) | (4<<cs00)

which Joe says sets TCCR0A to x1xx1100.

but i think the above would give TCCR0A = x1xx11xx because

xxxxxxxx (= some intermediate value of TCCR0A) | 00000100 (= 4<<cs00) = xxxxx1xx

does the left shift function 4<<cs00 somehow set the values of cs02=1, cs01=0 and cs00=0?

December 01, 2012
by Noter
Noter's Avatar

The best thing to do is code it up and run a test to see who is correct, you or Joe. For what it's worth, my money is not on Joe this time.

December 02, 2012
by Rick_S
Rick_S's Avatar

For what it's worth, most likely Joe is talking about initializing that register. By default at boot, all values in that register are zero. Thus Or'ing the way Joe did would give the desired result. However, Keith, you are correct that Joe's way does not guarantee the value of the remaining register values if they had been changed prior.

A simple way to guarantee Joe's method would work would be to simply place the statement:

TCCR0A = 0;

Before the OR statement. This would set all the bits zero first.

Rick

December 02, 2012
by keith712
keith712's Avatar

thanks for the input Noter and Rick. i'm new to all this and don't have any self-confidence yet. i checked to see if Joe initialized TCCR0A = 0 but he didn't.

to guarantee that TCCR0A is changed from xxxxxxxx to x1xx1100 without disturbing the foc0a com0a1 and com0a0 bits and maintaining the self-documentation of the code would look something like:

TCCR0A |= (1<<wgm01) | (1<<wgm00); // set waveform generation bits to 11 = fast PWM mode

TCCR0A &= ~(7<<cs00); // clear clock select bits cs02 cs01 cs00 to 000 = no clock source

TCCR0A |= (4<<cs00); // set clock select bits to 100 = clkio/256 mode

what do you all think?

December 02, 2012
by Noter
Noter's Avatar

Yep, I think you've got it figured out.

December 02, 2012
by Noter
Noter's Avatar

I like to use the _BV() macro because I think it makes the code a little easier to read. Instead of coding (1<<WGM01), use _BV(WGM01). Here's a sample -

// set pin OC2A for output
DDRB |= _BV(PB3);

// start the timer
TCCR2A = _BV(COM2A1) | _BV(WGM21) | _BV(WGM20);
TCCR2B = _BV(CS20);

// randomly change the pulse width
while(1){
    OCR2A = (rand()&127)+128;
    _delay_ms(100);
}
December 02, 2012
by keith712
keith712's Avatar

Noter, where can i find documentation on macros like _BV()? are macros in the standard C library or do you write your own and #include them in your programs? am i asking the right questions?

December 02, 2012
by Noter
Noter's Avatar

_BV() is part of the standard AVR lib-c. Don't remember where I ran across the _BV() macro initially but it is in this document on lib-c - http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_use_bv

December 02, 2012
by sask55
sask55's Avatar

If you prefer to always think of the location of a bit as a shift left from bit 0 then, Then in order to deal with the bit value in position x we should shift 1 (00000001) some amount x to the left.

In this method I would always use (1 << x) to point to a specific bit location. For example if wgm00 is defined as 6 therefore (1<<wgm00) is (1<<6). Shifting 00000001 to the left 6 time will give 01000000. By always shifting the value 1 and using the predefined names cs00,cs01 etc. you can refer to a desired bit location without even knowing what that location is. When always shifting the value of 1 you are dealing with one bit at a time. You will be required to use a separate (1<<x) for each bit you want to clear or set. Using |= will set the bit value in the location using &= ~ will clear the bit value in that location.

TCCR0A= x1xx1100 could be done as.

Set bit 6,3 and 2 TCCR0A |= (1<<wgm00) | (1<<wgm01) | (1<<cs02);

Which isTCCR0A |= (1<<6) | (1<<3) | (1<<2);

Clear bits 1 and 0

TCCR0A &= ~(1<<cs01);

TCCR0A &= ~(1<<cs00);

Which is

TCCR0A &= ~(1<<1);

TCCR0A &= ~(1<<0);

If you prefer to think of TCCROA as one byte you don’t really need use to shift at all.

Thinking binary

TCCR0A |= 01001100 will set the bit values in locations 6,3,2

Decimal equivalent would be

64 + 8 + 4 = 76

TCCR0A |= 76;

TCCR0A &= ~(00000011) will clear the bit 0 and 1

Decimal equivalent would be

2 +1 =3

TCCR0A &= ~(3);

that is just the way I think of clearing or setting bits.

December 02, 2012
by Ralphxyz
Ralphxyz's Avatar

are macros in the standard C library or do you write your own

As Noter said "_BV() is part of the standard AVR lib-c."

But you can also write your own macros.

Ralph

December 02, 2012
by keith712
keith712's Avatar

thanks Darryl, i understand exactly what you're doing which would not have been true yesterday... there are several ways to skin this cat... i think that using separate lines of code to clear or set each bit or group of bits may be easier to read or debug then your second method of eliminating the left shift by manipulating the whole byte... using macros like _BV() looks like a good way to keep the source code readable, too...

Noter and Ralph, i just read a section of the book where Joe shows how to use the #define statement to write macros instead of making and calling functions...

i'm going to download the avr lib-c document... this hobby this could turn into a full time job!!!

December 02, 2012
by BobaMosfet
BobaMosfet's Avatar

Think of an OR as an ADD. Just no carry.

00000001<<4 = 00010000
00000000<<2 = 00000100

00010000 | 00000100 = 16 + 4 = 00010100 = 20

MSB on left, LSB on right

People get confused by what statements like 'TCCR0A |= (1<<wgm00) | (1<<wgm01) | (4<<cs00)' mean, because they think they are shifting the register. The are not. They are shifting a 1 into a new position and adding that to the register. Note that there is a '4' being shifted zero places.

In other words if:

wgm00 = 6
wgm01 = 3
cs00 = 0

then

TCCR0A |= (1<<wgm00) | (1<<wgm01) | (4<<cs00)

becomes

TCCR0A += 01000000 + 00001000 + 00000100
TCCR0A = 64 + 8 + 4
TCCR0A = 01001100
TCCR0A = 76

Enjoy.

BM

December 03, 2012
by keith712
keith712's Avatar

thanks BM... there are more ways to skin this cat then i ever would have guessed... this discussion has been very helpful... i think i've learned more in the last day and a half then i could have in a weeks worth of studying on my own... thanks to everyone... i can't wait to run into another problem... just kidding... k

December 16, 2012
by cadiz1
cadiz1's Avatar

Question on bit shifting:

Q1. If I bit shift 0000 0000(start value) (1<<3) is it 0000 1000 or 0000 0100. My confusion is, do we start with bit 0 and count 0,1,2,3 ? which makes it 0000 1000.

Q2. If the start byte is 0010 0010. (1<<3) is bit 1 and 5 shifted forward ?

December 16, 2012
by pcbolt
pcbolt's Avatar

cadiz1 -

When you write (1<<3), you are always starting with 0000 0001 and ending up with 0000 1000. The expression (1<<0) will give you 0000 0001, (1<<1) will be 0000 0010, (1<<2) will be 0000 0100 and so on. If the start byte is 0010 0010, this is the value 34 (in decimal). So you would have to write (34<<3) to get 0001 0001 0000.

Post a Reply

Please log in to post a reply.

Did you know that you can use printf and scanf functions to talk to your computer from your USB NerdKit? Learn more...