views:

362

answers:

8

I dont understand when and why you would use bitshift operators for microchip programming: for example...

SWITCH_DDR &= ~SWITCH_BIT;   
SWITCH_PORT |= SWITCH_BIT;

Why use these operators?

Or...

void SerialInit(void)
{
   UBRRH = ((XTAL / (8 * 250000)) - 1)>>8;   // 250kbps at 16Mhz
   UBRRL = (XTAL / (8 * 250000)) - 1;
   UCSRA = (1<<U2X);
   UCSRB = (1<<TXEN);
   UCSRC = (1<<URSEL) + (1<<UCSZ1) + (1<<UCSZ0);
}

Whats going on here??? someone please explain graphically with 0's and 1's please. Or here's another example:

ulong MesureLC(void)
{
 int i;

 TCCR1B = 0;
 CountHigh = 0;
 TCNT1 = 0;

 for (i=0;i<25000;i++)
 {
  TCCR1B = (1<<CS12) + (1<<CS11) + (1<<CS10);   // WTF ???
  UDR = 0x55;
  while(!(UCSRA & (1<<UDRE)));
 }
 while(!(UCSRA & (1<<TXC)));
 TCCR1B = 0;

 CountLow = TCNT1;
 Count = CountLow + (CountHigh << 16);

 return Count;
}

I need to understand this stuff. Any help is appreciated.

+13  A: 

A number of reasons spring to mind as to why you would use them.

The first is that sometimes memory is tight on micro-controller type devices and you may want to store as much as possible in as little space as possible.

This requirement may also be driven by the fact that other devices or software you interface with have that same minimal-space desire. They may use sub-byte data types for doing work, one example being TCP/IP headers where fields range from a single bit (such as the DF field) up to much more (such as the IP addresses).

In fact, that second bit of code you gave, SerialInit, is setting up properties for a serial comms chip (UART = Universal Asynchronous Receiver and Transmitter). UCSRx usually stands for UART Control/Status Register #x so you're writing information to a fairly low-level device to control its behaviour.

Another reason is that you may have memory mapped I/O. That means that, though you think you're writing to memory, you may well be sending those bits directly to an I/O device of some sort.

A classic example may be the use of a memory mapped byte where a seven-segment LEDs is controlled.

The LED may be structured as:

+---+---+---+---+---+---+---+---+
| d | e | g | f | a | b | c | p |
+---+---+---+---+---+---+---+---+
  #   #   #   #   #   #   #   #
  #   #   #   #   #   #   #   #===========#
  #   #   #   #   #   #   #               #
  #   #   #   #   #   #   #===========#   #
  #   #   #   #   #   #               #   #
  #   #   #   #   #   #===========#   #   #
  #   #   #   #   #               #   #   #
  #   #   #   #   #=====#         #   #   #
  #   #   #   #         #         #   #   #
  #   #   #   #     +-------+     #   #   #
  #   #   #   #     |aaaaaaa|     #   #   #
  #   #   #   #   +-+-------+-+   #   #   #
  #   #   #   #   |f|       |b|   #   #   #
  #   #   #   #===|f|       |b|===#   #   #
  #   #   #       |f|       |b|       #   #
  #   #   #       +-+-------+-+       #   #
  #   #   #=========|ggggggg|         #   #
  #   #           +-+-------+-+       #   #
  #   #           |e|       |c|       #   #
  #   #===========|e|       |c|=======#   #
  #               |e|       |c|           #
  #               +-+-------+-+ +---+     #
  #=================|ddddddd|   |ppp|=====#
                    +-------+   +---+

where each of the seven segments and the dot are controlled by a different bit. If you wanted to turn on a single segment while leaving the others as they are, you would use bit operations like your question contains.

By way of example, turning on the g segment involves an operation like:

mmap_byte |= 0x20; // binary 00100000

Turning it off involves:

mmap_byte &= 0xdf; // binary 11011111

You may also find yourself in a situation where single bits or groups of bit in a byte control totally distinct devices and you don't want the operation of one to affect the other.


As to what bitwise operators do, they are operators that work on a multi-bit value but conceptually one bit at a time:

  • AND is 1 only if both of its inputs are 1.
  • OR is 1 if one or more of its inputs are 1.
  • XOR is 1 only if exactly one of its inputs are 1.
  • NOT is 1 only if its input are 0.
  • left shift shifts bits left a certain amount.
  • right shift shifts bits right a certain amount.

Discounting the shifts for now, these can be best described as truth tables. Inputs possibilities are on the top and left, the resultant bit is one of the four (two in the case of NOT since it only has one input) values shown at the intersection of the two inputs.

AND | 0 1     OR | 0 1     XOR | 0 1    NOT | 0 1
----+-----    ---+----     ----+----    ----+----
 0  | 0 0      0 | 0 1       0 | 0 1        | 1 0
 1  | 0 1      1 | 1 1       1 | 1 0

One example is if you only want the lower 4 bits of an integer, you AND it with 15 (binary 1111) so:

    201: 1100 1001
AND  15: 0000 1111
------------------
 IS   9  0000 1001

Another example is if you have two 4-bit values that you want to pack into an 8-bit one, you can use all three of your operators (left-shift, and and or):

packed_val = ((val1 & 15) << 4) | (val2 & 15)
  • The & 15 operation will make sure that both values only have the lower 4 bits.
  • The << 4 is a 4-bit shift left to move val1 into the top 4 bits of an 8-bit value.
  • The | simply combines these two together.

If val1 is 7 and val2 is 4:

                val1            val2
                ====            ====
 & 15 (and)   xxxx-0111       xxxx-0100  & 15
 << 4 (left)  0111-0000           |
                  |               |
                  +-------+-------+
                          |
| (or)                0111-0100
paxdiablo
+5  A: 
  1. If you have feature which can be enabled or disabled, you can use single bit to set state: 1 or 0. This way in 1 byte you are able to set 8 features.

  2. If you need to store value (lets say somewhere from 0 to 7) you only need 3 bits for that (000 in binary = 0 in dec, 001 = 1, 010 = 2, ..., 111 = 7). And you still have 5 out of 8 bits free.

Whats going on here??? someone please explain graphically with 0's and 1's

1 << 2 = 00000001 << 2 = 00000100 = 4

4 >> 2 = 00000100 >> 2 = 00000001 = 1

Im0rtality
+1  A: 

A bit-shift operator comes in two main varieties (and I'm not talking about the direction): shift, and rotate.

Additionally, both come in two directions, so typically you have four:

  • shift-left
  • shift-right
  • rotate left
  • rotate right

The first two shift the bits a number of bits to one direction. Any bits that "fall off" the end disappear. Any bits that "appear" on the other end, are zero.

Typically you also specify how many bits to shift the value.

So:

1000 shl 1 = 0000 (the 1 fell off the end, and a 0 appeared on the other end)
1000 shr 1 = 0100 (a zero fell off the right end)

Rotation doesn't lose bits that fall off, instead they are rotated back in on the other side.

1000 rol 1 = 0001 (the 1 was rotated back in on the other side)

You can think of the two operations as:

  • For shifting, the number contains an infinite number of zeroes on both ends, that follows the value as you shift it
  • For rotation, the number is repeated an infinite number in both directions, that follows the value as you shift it

There's also a variant of the rotation one, rotate through carry, that uses the carry flag in the process as an extra bit.

If the carry flag starts as 0, here's what happens:

1000 rcl 1 = 0000 (rcl = rotate through carry to left)
0000 rcl 1 = 0001 (now the 1 came back, it was temporarily stored in carry flag)

The last one can, in machine code, be used to move single bits from one register to another:

rcl ax, 1    ; rotate AX-register, 16-bit, left 1 bit, through carry
rcr bx, 1    ; rotate BX-register, 16-bit, right 1 bit, through carry

Here we take the leftmost bit from AX, temporarily rotate it out into the carry flag, and then rotate it back into the leftmost bit of BX.

Now, you can usually combine shifting with the other bitwise operators. For instance, to set bit N of value (where N is 0-based, and bit 0 is the rightmost one), you can do this:

value = value OR (1 shl N)

Here we first shift the value 1 N times to the left. If N is 0, this does not shift the bit at all.

Then we OR the result of that shifting with the existing value, and store it. OR has the effect of combining the 1's so that if either value has a 1-bit in a specific position, the result is also a 1-bit in that position.

So for the shifting:

1 shl 0 = 00000001 shl 0 = 00000001
1 shl 1 = 00000001 shl 0 = 00000010
1 shl 2 = 00000001 shl 0 = 00000100
1 shl 3 = 00000001 shl 0 = 00001000
1 shl 4 = 00000001 shl 0 = 00010000
1 shl 5 = 00000001 shl 0 = 00100000
1 shl 6 = 00000001 shl 0 = 01000000
1 shl 7 = 00000001 shl 0 = 10000000

Then the OR:

???????? OR 00100000 = ??1?????, where ? means whatever it was before

Let me take a couple of the lines of code you've posted:

UBRRH = ((XTAL / (8 * 250000)) - 1)>>8;   // 250kbps at 16Mhz
UBRRL = (XTAL / (8 * 250000)) - 1;

The first does a calculation, (XTAL / (8 * 250000)) - 1, which I don't know the purpose behind. This is normal math, however, so it calculates something. Let's call it a frequency (judging by the comment.)

This value is calculated twice, so let's rewrite the above statements:

UBRRH = value >>8;   // 250kbps at 16Mhz
UBRRL = value;

Here I have to guess, but I'm guessing that UBRRH and UBRRL are both declared to be of type "BYTE", which means they can store at most 8 bits of value each. This means that the code actually reads like this:

  1. UBRRH takes the upper 8 bits of "value", shifts them into the lower 8 bits, and stores those. Since it only stores a byte, it chops off the rest, which means it grabs bits 8-15
  2. UBRRL takes the lower 8 bits, and chops off the rest, which means it grabs bits 0-7

Since the names of the two end in L and H, they fit with the assumption.

Lasse V. Karlsen
XTAL is most likely a #define, so the duplicate calculations would be evaluated at compile time. You've got your 250kHz number in there, and a factor of 8... which probably simply means the UART needs to be run at a frequency of 8 times the bit rate. That's pretty standard stuff for a UART.
darron
+1  A: 

The bitwise operators operate (as their name suggests on bits). In the first chunk of code you have given these operators are used for setting and reseting a specific bit. For simplicity assume that SWITCH_DDR is a 8bit integer and that SWITCH_BIT is also a 8bit integer that has a constant value of 2:

SWITCH_DDR = 00000000;  // initial value of SWITCH_DDR is 0
SWITCH_BIT = 00000010;

Then you can use the bitwise OR to set the specific bit of SWITCH_DDR to 1:

SWITCH_DDR |= SWITCH_BIT; // SWITCH_DDR is 00000010 now

To verify if the SWITCH_BIT bit is set you use the AND operator:

TEMP = 10101010 & SWITCH_BIT; // TEMP is 00000010 now (1 in TEMP is set only if there's 1 in both operands)
if (TEMP == SWITCH_BIT) // The condition is true
{ /* Do something */ }
TEMP = SWITCH_DDR & SWITCH_BIT;  // TEMP is again 00000010 because we set it to 00000010 before and the AND operator doesn't therefore change anything
if (TEMP == SWITCH_BIT)  // The condition is also true
{ /* Do something */ }

To unset a specific bit you can use this:

TEMP = ~SWITCH_BIT;  // TEMP is now 11111101
SWITCH_DDR &= TEMP;  // This preserves everything (because 1 & 1 = 1 and 1 & 0 = 0) but the SWITCH_BIT bit which will be always set to 0 (anything & 0 = 0)

The shift operators just shift the bits left or right:

RESULT = 10010010 << 1;  // RESULT is 00100100
RESULT <<= 1;  // RESULT is 01001000
RESULT <<= 2;  // RESULT is 00100000
RESULT >>= 1;  // RESULT is 00010000

There is one special thing about the shift operators - you can use them for a fast division/multiplication with powers of 2:

RESULT = 3 << 1;  // Result is 6 (3 * 2)
RESULT = 5 << 2;  // Result is 20 (5 * 4)
RESULT = 1 << 7;  // Result is 128 (1 * 128)
RESULT = 36 >> 1; // Result is 18 (36 / 2)
RESULT = 35 >> 1; // Result is 17 (35 / 2)
dark_charlie
+3  A: 

In the first example you gave:

SWITCH_DDR &= ~SWITCH_BIT;   
SWITCH_PORT |= SWITCH_BIT;

That's not a bitshift, instead it's a bitmask.

Specifically the first statement turns off a given bit in a value, and the second turns the same bit on, leaving all other bits untouched.

Let's say SWITCH_DDR and SWITCH_PORT are special memory values that control the behaviour of some device. Each of its bits turns on/off a feature. If you want to control a given feature separately, you have to be able to change a bit without interfering with the others. Let's also say the feature controlled by SWITCH_BIT is the leftmost bit in a byte. So SWITCH_BIT would have the value 0x80 (10000000 in binary). When you do the first statement you are inverting SWITCH_BIT with the ~ operator (obtaining 01111111 in binary) and applying a binary AND to SWITCH_DDR. That efectively clears the leftmost bit and leaves the others unchanged. The second statement does a binary OR, therefore the opposite result.

Now, about the shift operations, there are many applications to them, (much of was already mentioned in another answers), I will simply explain the specific use in the code you posted. So you have:

void SerialInit(void)
{
   UBRRH = ((XTAL / (8 * 250000)) - 1)>>8;   // 250kbps at 16Mhz
   UBRRL = (XTAL / (8 * 250000)) - 1;
   UCSRA = (1<<U2X);
   UCSRB = (1<<TXEN);
   UCSRC = (1<<URSEL) + (1<<UCSZ1) + (1<<UCSZ0);
}

The thing here is similar to the previous example (setting specific bits in a value) but there are a few differences due to the situation:

a) While in the first example you had the bitmask already made (SWITCH_BIT), here the constants are simply the POSITION of the bit. For instance, U2X contains the position of the bit that needs to be turned on/off (from right to left), not its bitmask. Doing (1<<U2X) effectively produces the corresponding bitmask. If the bit that needs to be turned on is the leftmost (as I made in SWITCH_BIT example), U2X would be 7 and the result of the shift would be the same 10000000 in binary.

b) In the last line:

UCSRC = (1<<URSEL) + (1<<UCSZ1) + (1<<UCSZ0);

This is combining the bitmasks produced by each constant (whose values, again, are the bit positions from the right). The thing is that the programmer decided to use the + operator instead of binary OR because he/she knew that when you don't have "colliding" bits, their result is the same. Personally, I would always use the binary OR, to make clear that what I'm doing is not an arithmetic addition.

c) Finally, about the first line

UBRRH = ((XTAL / (8 * 250000)) - 1)>>8;

Seems more complicated, apparently is a clock division factor, I'd have to look at the documentation to understand better. I'd not worry about it for now, it probably will become clear in time.

Fabio Ceconello
+3  A: 

I think the real question is not "what the hell does this mean", but rather "why the hell do developers writing programs for micro-controllers seem to love this way?"

Well I don't know if they love that way. I'm not a developer at all and I'm just a little bit interested in micro-controllers, but here is what I think.

Have You ever seen a documentation for a chip? Here is an example from some Atmel pdf.

USART Control and Status Register – UCSRnB

• Bit 7 – RXCIEn: RX Complete Interrupt Enable n Writing this bit to one enables interrupt on the RXCn Flag. A USART Receive Complete interrupt will be generated only if the RXCIEn bit is written to one, the Global Interrupt Flag in SREG is written to one and the RXCn bit in UCSRnA is set.

• Bit 6 – TXCIEn: TX Complete Interrupt Enable n Writing this bit to one enables interrupt on the TXCn Flag. A USART Transmit Complete interrupt will be generated only if the TXCIEn bit is written to one, the Global Interrupt Flag in SREG is written to one and the TXCn bit in UCSRnA is set.

... • Bit 5 – UDRIEn: ... • Bit 4 – RXENn: ... • Bit 3 – TXENn: ... • Bit 2 – UCSZn2: ... • Bit 1 – RXB8n: ... • Bit 0 – TXB8n: ...

The problem is, that the chip is controlled by setting or clearing individual bits in some control registers. Those registers have ugly names in the documentation and the bits have ugly names too. The development environment comes already with all the macros with the names resembling those form the documentation defined.

Now suppose one wants to enable an interupt on the RXCn Flag and leave all other settings unchanged. This requires setting one bit in one particular register leaving other bits unchanged. The operator |= is the easiest way to do this

Assumming the address of the register is 0x3F (I made up the number, it doesn't matter) one could write this:

*((unsigned char*)0x3F) |= 0x80;// I want to set bit number 7 (RXCIEn)

Now, how readable is this?

In the development environment, there are already defined macros like UCSRnB and RXCIEn and using them is an obvious thing. It just so happens that RXCIEn is a bit number, not a value of that bit, so to code the same as above one has to write

UCSRnB |= (1 << RXCIEn);

Thanks to the documentation something like

UCSRnB = (1<<RXENn)|(1<<TXENn);

is considered more readable than

*((unsigned char*)0x3F) = 0x18; // I want to set bits number 4 and 3 (RXENn and TXENn)

The documentation itself is full of examples of code using those defined macros and I guess developers get used to it too quickly, to try to find a better way.

Maciej Hehl
+2  A: 
dwelch
A: 

It's worth noting, btw, that on many small processors there are specialized instructions for setting and clearing individual bits of an I/O port. I would suggest that you find out something about any processor you'll be using, because optimal coding styles vary from one to another. For example, consider the groups of statements:

  some_port |= 8;  /* Statement #1a -- Note value is a power of 2 */
  some_port |= 2;  /* Statement #1b -- Note value is a power of 2 */

  some_port |= 9;  /* Statement #2 -- Note value is not a power of 2 */

On some processors (e.g. ARM or Z80), if some_port is declared volatile (which it should be), each statement will be three instructions, so the first sequence will take twice the time and space of the first. On some others (e.g. PIC) the first two statements will be one instruction each, while the second will be two instructions, so both sequences will take the same time and space. Still others will offer a space advantage to one and a time advantage to the other.

Time/space for 1st and 2nd methods of setting bits
(time in cycles unless noted)
1st  2nd
2/4  2/3  8051 (standard)
4/4  3/3  8051 (accelerated clone)
2/2  2/2  PIC
10/4 8/6  6805
supercat