Maybe this question will be kinda stupid, but I wonder why if we want to write 0 on some specific position in register we have to write like this:
PORTB &= ~(1 << 2);
And not like this (cause it will not work):
PORTB &= (0 << 2);
Does 0 mean here like 0b00000000 (for 8-bit register)?
CodePudding user response:
Let’s break this down step by step:
// PORTB &= ~(1 << 2);
int bit_position = 2;
int bit_mask = 1; // = 0000 0001
bit_mask <<= bit_position; // = 0000 0100
int inverted_mask = ~bit_mask; // = 1111 1011
int PORTB = 0x77; // 0111 0111
PORTB &= inverted_mask; // & 1111 1011
// = 0111 0011
And, your suggested alternative:
// PORTB &= (0 << 2);
int bit_position = 2;
int bit_mask = 0; // = 0000 0000
bit_mask <<= bit_position; // = 0000 0000
int PORTB = 0x77; // 0111 0111
PORTB &= bit_mask; // & 0000 0000
// = 0000 0000
The point being that the computer simply performs the operations one by one and on whole registers. It doesn’t know that it was supposed to care about a specific zero out of all the zeroes in that number. So you are not ANDing by “zero at position 2”, but simply by the number zero. And indeed, you are not shifting just one zero or one, but the entire number.
This is why to set a specific bit to zero, we have to AND with a mask that has ones in every position other than the one we wish zero. (ANDing with 1 does not change the bit, whereas ANDing with 0 makes it zero.)
CodePudding user response:
Using a byte for simplicity, 0 << 2
is just equal to zero - 0b00000000
. Any value you AND with this will also produce zero as a result.
1 << 2
is equal to 0b00000100
~(1 << 2)
is equal to 0b11111011
Now you have a mask to AND with which will only set a specific bit to zero. The rest of the bits will be unchanged.
CodePudding user response:
You can understand why, when you disassemble the code:
PORTB &= ~(1 << 2);
is translated like that:
PORTB = PORTB & (~(0b00000001 << 2));
to
PORTB = PORTB & (~(0b00000100));
to
PORTB = PORTB & ((0b11111011));
This means it will take the old value of PORTB and just write bit #2 of it to 0 while keeping the rest of the bits to their original values (whether they are 0 or 1)
This is done because you only want to change the value of PIN2 of PORTB while not changing any other pin value.
While the other line:
PORTB &= (0 << 2);
evalutes to
PORTB = PORTB & ((0b00000000));
Which in turn will reset all the PORTB pins to 0.
So it really depends on what exactly you want to do.
Another example is if you want to set PIN2 of PORTB to 1:
PORTB = PORTB | (0b00000100);
which can be written like that:
PORTB |= (1 << 2);
This will make sure to set only PIN2 and no other pin of PORTB to 1.