2

In a nutshell i want to read a PORTB 5th pin without using digitalRead API ( i am trying to learn baremetal using arduino-uno-r3)

Just to Learn How internal register works i did not used digitalRead API, instead i have used DDRB register to set the 5 bit of DDRB as Low indicating PORTB 5th pin is in input mode then i am using an if statment to check if 5th bit of PINB is high or low my expection was that when i connect arduino pin 13 to 5v if statment will become TRUE.

void setup() {
  // put your setup code here, to run once:
  DDRB = DDRB & ~(1<<5);
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  if(PINB & (1<<5)){
     Serial.println("Got data"); 
    }
}

When i executed above code i am constantly getting if as TRUE

11
  • 1
    Do you use an external pullup or pulldown resistor?
    – chrisl
    Commented May 27, 2020 at 14:31
  • your code is operating on the 6th bit ... bits are numbered 0 to 7, so 5th bit is bit 4
    – jsotola
    Commented May 27, 2020 at 15:08
  • I have not used any pull up resistor and i am sure PINB 5th bit is 13 pin of aurdino
    – user143252
    Commented May 27, 2020 at 15:23
  • 1
    No, digital 13 is PB5, i.e. the 6th bit. The 1st bit is PB0, the 2nd bit is PB1, etc. Commented May 27, 2020 at 15:42
  • 1
    If nothing is connected to the pin then it can read either high or low. Google “floating pin”. If you’re expecting it to read low when not connected to anything then you’re thinking wrong.
    – Delta_G
    Commented May 27, 2020 at 16:43

1 Answer 1

2

There seem to be several, separate issues in your question.

One is a simple matter of language. In English, “1st” is an abbreviation for “first”, which is an ordinal adjective applied to an item in a list meaning that there is no other item before it in that list. In the same vein, “5th” means “fifth”, which applies to the item that has exactly four items before it. For instance, the list of pins of port B is {PB0, PB1, PB2, PB3, PB4, PB5}. PB0 is the first in the list, and PB4 is the fifth. The fact the first item bears the number 0 makes this confusing. Thus, I recommend avoiding ordinal adjectives altogether and instead calling the pins by their names, like “digital 13” (Arduino naming) or “PB5” (AVR naming).

The second issue, and probably your main concern, is about the floating input pin. You cannot expect a reliable reading from a floating input. I will not try to explain why, as the issue has been discussed ad nauseam here and elsewhere. Just do a Web search. The simplest solution is to use the built-in pull-up resistor and externally connect a push button between the pin and ground. You will read LOW whenever the button is pressed:

void setup() {
    pinMode(13, INPUT_PULLUP);
    Serial.begin(9600);
}

void loop() {
    if (digitalRead(13) == HIGH)
        Serial.println("HIGH");
    else
        Serial.println("LOW");
}

The last point is about going bare metal and learning how to use the I/O registers directly. A small warning first: you have to keep in mind that this is board-specific. You tagged your question with both “arduino-uno” and “arduino-mega”, but these boards have different pin mappings (digital 13 is PB7 on the Mega). The general way of using the DDRx, PORTx and PINx registers is the same across all AVR chips, but is not portable to Arduinos based on a different architecture.

So let's assume this is intended to work only on an Uno, Nano, or other board based on an ATmega328P. Judging from the example code you show, you seem to already understand pretty well how these registers work. There is no problem in your code, beside the fact that it requires an external pull-up. If you want to enable the internal pull-up, you have to set the appropriate bit in PORTB. This can seem confusing, as the same register is used for pull-up control (when the pin is in input mode) and for output control (when the pin is an output). Essentially, each GPIO pin has four distinct electrical states which are controlled by to I/O bits:

│ DDRxn │ PORTxn │ state        │
├───────┼────────┼──────────────┤
│   0   │    0   │ INPUT        │
│   0   │    1   │ INPUT_PULLUP │
│   1   │    0   │ OUTPUT LOW   │
│   1   │    1   │ OUTPUT HIGH  │
└───────┴────────┴──────────────┘

The example program above can be translated to low-level AVR style as follows:

void setup() {
    DDRB &= ~_BV(PB5);  // input mode
    PORTB |= _BV(PB5);  // enable pullup
    Serial.begin(9600);
}

void loop() {
    if (PINB & _BV(PB5))
        Serial.println("HIGH");
    else
        Serial.println("LOW");
}
2
  • Nicely put @edgar. I might add a comment (as the OP did say he/she wanted to do bare metal programming) that different Arduinos board can contain different processors. And what works here in this answer is for a specific processor. And that other processors can and most often work very differently.
    – st2000
    Commented May 28, 2020 at 1:57
  • @st2000: Good point. Edited the answer. Commented May 28, 2020 at 9:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.