How does this work? (bitmath)

I know a bit about bitmath, but I came across this construct and I wonder how it works and why it is used:

TCCR2B = 0<<CS22 | 1<<CS21 | 0<<CS20;

I understand from the comment in the code that this sets the prescaler of Timer2 to the value of 010 (divide by 8).

Couldn't this also just be written as 'TCCR2B = 0x02'? Is this notation used for portability with other types of ATmega processors?

Couldn't this also just be written as 'TCCR2B = 0x02'?

Yes it could, but it doesn't tell the reader anything about what "0x02" means.

Imagine now I write:

TCCR2B = MyCS22<<CS22 | MyCS21<<CS21 | MyCS20<<CS20;

where "MyCSxx" is a variable, or a #define, or a constant.
Makes reading and modifying the program much easier.

I agree that using the bit names in stead of using a hex notation is more easy readable, but I'm still a bit confused about what seems like a shorthand with the | (OR) symbol.

Are there more ways to write this, possibly in more steps?

Also, suppose CS21 equals 1, won't the second bit become 0 (1<<1), thus messing up the prescaler value?

Also, suppose CS21 equals 1, won't the second bit become 0 (1<<1),

I don't understand what you're saying.
1 << 1 = 2, not zero.

You're looking at single bits; shifting them left simply mutiplies them by a power of two.

Maybe I'm making this more complicated than it is :-[

The CSxx are the clock setting bits. What's the effect of bit shifting a single bit by 1 if its value is already 1?

What's the effect of bit shifting a single bit by 1 if its value is already 1?

You multiply it by 2.
1 << 0 = 1 B00000001 (1 = 20)
1 << 1 = 2 B00000010 (2 = 21)
1 << 2 = 4 B00000100 (4 = 22)
1 << 3 = 8 B00001000 (8 = 23)

Saxdude - the "1" I think you're talking about describes the bit's position in the word, not its value.
CS20 has a value of zero, because it is the least significant bit.
CS21 has a value of one because it is more significant than CS20.
It is the name of the bit (or a description of its ordinality), if you prefer, not its value.

If you look in the processor's datasheet, you'll see somewhere a description of the register TCCR2B.
It will show an eight bit wide register, and the three least significant bits will be called
CS22, CS21 and CS20.

Is that what you were trying to understand?

Saxdude,

Please forgive me if I am being too elementary here, but I want to start at the beginning in case there are other readers who need this information.

Registers, like those controlling a timer, typically contain multiple fields. Each field is made up of one or more bits. For example, a field that can have four different values would have two bits, and the possible values would be 00, 01, 10, and 11.

In your program, it's easy to have the constants B00, B01, etc., but those have the two bits in the least significant bits of the constant. In the register, those bits might be three bits to the left, like this:0 0 0 X X 0 0 0where X X represents those two bits.

Using the shift operator repositions those bits in the number. For the above example, to put the value 10 into that field on the register, you would sayREG = B10 << 3;Of course, that would put zeroes in the other six bits of the register. If you wanted to put something besides zeroes in other fields in the register, you would use the OR operator ( | ) to set those values, as in the example in your first post.

So, the point of the shift is to move the bit or bits into the correct position to set a field in a register.

There is another way to do it, without the shifts. You could pre-define constants for each value in the field. The constants would have the bits already in the right place. You can then combine constants with OR operators to build the correct value for the register. For example, let's say that the two bits I mentioned above set one of four different modes for the register. Let's also say that the least significant bit turns a function on or off. We could define these constants and use them like this:

#define REG_MODE_A B00000000
#define REG_MODE_B B00001000
#define REG_MODE_C B00010000
#define REG_MODE_D B00011000
#define REG_OFF B00000000
#define REG_ON  B00000001

// turn function on, mode c
REG = REG_ON | REG_MODE_C;
// turn function off, mode b
REG = REG_OFF | REG_MODE_B;
// etc.

As you can imagine, with all of the fields and values, just thinking up names for all of those constants can be quite a chore! That's why it's sometimes easier to just code up a shift.

-Mike

@everybody: thanks for taking the time to try and explain this. Much appreciated!

@Groove: so if I understand, 0<<CS22 just means 'shift a 0 to the position of CS22'.

TCCR2B = 0<<CS22 | 1<<CS21 | 0<<CS20;

And the use of OR is shorthand that allows you to put three assignments into one.