Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why we use PIN & (1<<PBx) to check if PIN is high?

I'm a beginner working on AVR programming on the ATmega328P. I am trying to get input from a button and then control an LED.

void setup() {
    DDRB |= (1<<PB5);
    DDRB &= ~(1<<PB4);

    PORTB |= (1<<PB4);
}

void loop() {
    // put your main code here, to run repeatedly:
    if((PINB & (1 << PB4)) == 0) {
        PORTB |= (1 << PB5);
    } else {
        PORTB &= ~(1 << PB5);
    }
}

This code is working perfectly; however, there are a few things I don't quite understand, and I would be happy if you could help.

I clearly understand why we do the first few lines, and kind of understand why we do PORTB |= (1<<PB4), but in the if statement, why do we do (PINB & (1<<PB4)) == 0? I mean, we have some value in PINB and also in PB4, but we don't compare them. We create a mask of PB4 with a 1 in the 4th bit. How does it work? If we don't use PB4, why did we pull it up before? I believe I'm missing something on the hardware level and don't understand how PINB and PORTB are related, and when they are low or high.

I checked the datasheet, and it shows them as separate registers, I mean PORTB, PINB, and DDRB. Furthermore, I tried to ask the AI, but it doesn't explain lower-level programming very well. I know this might be very basic, but it is a little confusing, and I would be happy if someone could explain it in detail.

like image 461
Toghrul Guluzade Avatar asked Dec 21 '25 13:12

Toghrul Guluzade


2 Answers

"I don't understand how PINB and PORTB are related"

DDRB, PORTB and PINB are different "views" of the same "B" port (which has 8 bits, but see note 2). They may look like "normal" variables in your code, but they're used to directly act on the bits/pins of the port. (They're actually C/C++ macros, probably associated with instrinsic functions, but you don't need to worry about that.)

Each bit in DDRB establishes the "direction" of the associated bit/pin in port "B". Setting a bit to 0 means "this pin will be used for INPUT". Setting a bit to 1 means "this pin will be used for OUTPUT". As you can't set a single bit using the C++ language, you usually do something like DDRB = DDRB | 0b00100000 (setting pin 5 for OUTPUT and leaving the other bits as they were before the assignment).

Setting a bit in PORTB to 0 (or 1) outputs a "logical 0" (or "logical 1") to the associated pin in port "B". As you can't set a single bit using the C++ language, you usually do something like PORTB = PORTB | 0b00100000 (outputting a "logical 1" to pin 5 in port "B" and leaving the other bits as they were before the assignment).

Reading a bit from PINB inputs a "logical 0" (or a "logical 1") from the associated pin in port "B". As you can't actually read a single bit using the C++ language, you read all pins/bits of the port and then hide the ones you're not interested in, doing something like if (PINB & 0b00010000 != 0) (for testing if pin 4 has a "logical 1").

The names PB0 to PB7, in the context of your code, are simple macros which equate to the literals 0 to 7. You need to use the form (1<<PBn) to get the appropriate mask. (Warning: the same names are used in other places, specially in the datasheet, for referencing the individual pins of port "B", which may be confusing.)


Note 1. A "logical 1" has a higher voltage than a "logical 0" in the ATmega328P.

Note 2. Not all bits/pins of all ports (B, C, D) can be used on an Arduino, and the actual mapping may vary, depending on the specific board and chip. Also, directly manipulating the registers and bits of the chip is usually not recommended (mainly for compatibility reasons). Please check the documentation.

Note 3. Setting a bit in PORTB only works as described above when the pin was previously established for OUTPUT. When the pin was established for INPUT, setting the bit in PORTB activates/deactivates the internal pull-up.


This is your code, with additional comments. For a better explanation of masks and bitwise operators, beyond the specific use, please check Remy's answer.

void setup() {
  DDRB |= (1<<PB5);    // DDRB = DDRB | 0b00100000 (bit 5 will be used for OUTPUT; don't change the others)
  DDRB &= ~(1<<PB4);   // DDRB = DDRB & 0b11101111 (bit 4 will be used for INPUT; don't change the others)

  PORTB |= (1<<PB4);   // PORTB = PORTB | 0b00010000 (activate the internal pull-up for bit 4)
}

void loop() {
  // put your main code here, to run repeatedly:

  if ((PINB & (1 << PB4)) == 0) {   // if (PINB & 0b00010000 == 0) (if pin 4 has a "logical 0"...
    PORTB |= (1 << PB5);            // PORTB = PORTB | 0b00100000 (...then output a "logical 1" to pin 5)
  } else {
    PORTB &= ~(1 << PB5);           // PORTB = PORTB & 0b11011111 (...else output a "logical 0" to pin 5)
  }
}

A final note: as we're activating the internal pull-up for bit 4, the logic is inverted: we read a "logical 1" (high voltage) when the circuit is open, and a "logical 0" (low voltage) when it's closed (hence we output a "logical 1" to pin 5 when we read a "logical 0" at pin 4).

like image 97
Diego Ferruchelli Avatar answered Dec 24 '25 02:12

Diego Ferruchelli


1 << PB4 creates a bitmask with 1 in the bit position denoted by PB4, and 0 in the remaining bits.

PINB & ... masks the bits of PINB with the bits of the bitmask. For each bit corresponding pair, if both bits are 1 then the bit in the result will be 1, otherwise 0.

... == 0 compares the resulting masked value to see if all of its bits are 0s or not.

So, if((PINB & (1 << PB4)) == 0) is testing whether the single bit at position PB4 in the value of PINB is 0 or not.

For example, let's say PINB has a value of 22 (b00010110) and PB4 has a value of 4, then you get:

  00010110 // PINB = 22
  00010000 // (1 << 4) = 16
& ---------
  00010000 // 16

Whereas, let's say PINB has a value of 6 (b00000110) instead, then you get:

  00000110 // PINB = 6
  00010000 // (1 << 4) = 16
& ---------
  00000000 // 0

So if((PINB & (1 << PB4)) == 0) will evaluate as true if bit 4 of PINB is 0 (result of masking is 0), otherwise it will evaluate as false (result of masking is not 0).

like image 43
Remy Lebeau Avatar answered Dec 24 '25 01:12

Remy Lebeau



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!