3
\$\begingroup\$

Setup (Arduino Pro Mini ATmega328p 16MHz, 5V, no pin connected):

Photo of the Arduino on a breadboard, with FTDI attached

When called from the setup(), this works:

pinMode(11, INPUT_PULLUP);
bool identifiesAsFast = digitalRead(11); // always true

This doesn't:

const uint8_t identificationPinBitNumber = PB3; // pin 11
const byte identificationPinBit = bit(identificationPinBitNumber);
DDRB &= ~identificationPinBit;
PORTB |= identificationPinBit;
bool identifiesAsFast = PINB & identificationPinBit; // occasionally false

Pin 11 is not connected, but - as you can see above - I have it pulled high. So I would expect it to always have a value of 1.

What am I missing when doing direct port manipulation?

I get the behavior with four different Arduino Pro Minis. All are original Pro Minis, designed by Sparkfun, made in the USA and bought from Digi-Key. One of them I pulled freshly out of the original bag. I may have to remove and insert the FTDI adapter around thirty times, but eventually I am able to reproduce the issue.

Adding a delay of 100 ms in between pulling pin 11 high and reading it seems to resolve the issue. However, I don't understand why that delay would be necessary.

PB3 for pin 11 is correct, as I verified by connecting an LED and making it blink. See also the pin mapping diagram which can be found elsewhere on the web:

Pin mapping

Full code:

// Felix E. Klee <[email protected]>

const uint8_t ledPinBitNumber = PB5; // pin 13
const byte ledPinBit = bit(ledPinBitNumber);

const uint8_t identificationPinBitNumber = PB3; // pin 11
const byte identificationPinBit = bit(identificationPinBitNumber);

unsigned long delayDuration; // ms

void turnLedOn() {
  PORTB |= ledPinBit;
}

void turnLedOff() {
  PORTB &= ~ledPinBit;
}

void setDelayDuration() {
#if 0
  pinMode(11, INPUT_PULLUP);

  bool identifiesAsFast = digitalRead(11); // always true
#else
  // Input pullup:
  DDRB &= ~identificationPinBit;
  PORTB |= identificationPinBit;

  bool identifiesAsFast = PINB & identificationPinBit; // occasionally false
#endif

  delayDuration = identifiesAsFast ? 300 : 3000;
}

void setup() {
  DDRB |= ledPinBit; // Set up LED pin for output
  setDelayDuration();
}

void loop() {
  turnLedOn();
  delay(delayDuration);
  turnLedOff();
  delay(delayDuration);
}
\$\endgroup\$

2 Answers 2

4
\$\begingroup\$

As weird as this is going to sound, your code is too fast.

The pin (and the part of the breadboard that it's plugged into) have some capacitance. It takes time for the resistive pullup in the AVR to pull the pin high.

The Arduino pinMode() and digitalRead() functinons are slow enough that, by the time they get around to reading the pin, the pullup has done its job. Your code is accessing registers directly, which is considerably faster -- so it's possible for the pin to still be low when you read it.

The amount of delay you need is pretty minimal, though. 50 microseconds (not milliseconds!) should be sufficient.

\$\endgroup\$
2
  • \$\begingroup\$ Speaking about the influence of the breadboard: With the pristine Arduino, it took me much longer to reproduce the issue. I.e. I had to remove and insert the FTDI close to thirty times. In comparison to the other three Arduinos, the pristine Arduino was not in a breadboard. It didn’t even have headers on it. \$\endgroup\$
    – feklee
    May 2, 2018 at 6:46
  • \$\begingroup\$ Also, in the datasheet version DS40001984A I now found on page 102: “When reading back a software-assigned pin value, a nop instruction must be inserted as indicated in the following figure.” This has to do with timing, not with capacitance. In fact, while adding a single NOP has a positive effect, there is still the occasional wrong value. \$\endgroup\$
    – feklee
    May 2, 2018 at 6:58
4
\$\begingroup\$

You are doing it the wrong way !

An Arduino port consists of 8 pins, for example PB0, PB2, PB3, ..., PB7.

If you want to manipulate a register or a bit or some bits, you may have to do like this:

PORTB |= (1 << PB5) // Set a bit

PORTB &= ~(1 << PB5) // Clear a bit

\$\endgroup\$
4
  • 2
    \$\begingroup\$ Your clear a bit line should read PORTB &= ~(1 << PB5) // Clear a bit I can't edit as it's a single char change. \$\endgroup\$
    – Colin
    May 1, 2018 at 8:32
  • \$\begingroup\$ No. Substituting bit (defined in Arduino.h) and constants, we see that PORTB |= identificationPinBit equals PORTB |= 1ul << PB3. The issue lies elsewhere. \$\endgroup\$
    – feklee
    May 1, 2018 at 13:10
  • \$\begingroup\$ You can also use the _BV() macro nongnu.org/avr-libc/user-manual/FAQ.html#faq_use_bv \$\endgroup\$
    – filo
    May 1, 2018 at 13:11
  • \$\begingroup\$ @feklee ;) Can't believe there is someone who dig that deep ! Ok, I think that define is used somewhere in Arduino internal machine, what I find about bit() function may be refer to this. \$\endgroup\$
    – Long Pham
    May 1, 2018 at 13:58

Your Answer

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

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