digitalReadFast weirdness

I have run into a peculiar issue using digitalReadFast with my Arduino Mega 1280. Namely, it seems to be yielding inconsistent results upon first power-up. Once the Arduino is powered and reset, it works every time - and ordinary digitalRead works every time, even upon first powerup.

Unfortunately the Mega is integrated into a very complex hardware setup & I can’t remove it for testing. So I will try to describe the relevant hardware, and provide a minimal code fragment that displays the issue:

Hardware:
Mega 1280 powered by external 5V regulator feeding into the 5V pin.
3 different LED anodes connected to pins 3,4,5; cathodes to ground.
A SPDT Center-Off switch, with the common connected to ground and the two poles connected to pins 43 and 41.

At startup, the system enables the pullups on pins 43 and 41, and reads their state to determine the switch position. The switch position dictates which of three “modes” the system operates in, and it lights one of the LEDs to indicate the mode.

#include <digitalWriteFast.h>

const byte passiveLED = 4;  // pins for LED connections
const byte semiLED = 3;
const byte autoLED = 5;

const byte autoPort = 43;  // pins for SPDT center-off connections
const byte semiPort = 41;

byte operatingMode;

void setup(){

  Serial.begin(115200);
  
  Serial.print("Operating in ");
  operatingMode = setMode();
  switch (operatingMode) {
  case 0:
    Serial.print("passive");
    break;
  case 1:
    Serial.print("semi-automatic");
    break;
  case 2:
    Serial.print("automatic");
    break;
  }
  Serial.println (" mode");
}

void loop(){}

byte setMode()
{
  byte portBits = 0;
  pinModeFast(autoPort,INPUT);      // ports connected to poles of SPDT switch
  pinModeFast(semiPort,INPUT);
  digitalWriteFast(autoPort,HIGH);  // set pullups for mode switch
  digitalWriteFast(semiPort,HIGH);

  do {
    bitWrite(portBits,0,digitalReadFast(semiPort));  // read switch state
    bitWrite(portBits,1,digitalReadFast(autoPort));

    switch (portBits) {
    case B11:  // center position
      pinMode(passiveLED,OUTPUT);
      digitalWriteFast(passiveLED,HIGH);
      return 0;
      break;
    case B10:  // switch down
      pinMode(semiLED,OUTPUT);
      digitalWriteFast(semiLED,HIGH);
      return 1;
      break;
    case B01:  // switch up
      pinMode(autoLED,OUTPUT);
      digitalWriteFast(autoLED,HIGH);
      return 2;
      break;
    }
  } 
  while (portBits == 0);  // keep reading until we get a valid switch state
}

I know this code looks weird, but it’s part of a much larger system. This fragment displays the problem.

When the switch is in the center position (neither pin 41 nor 43 grounded), the “passive” LED should light up. This happens 100% reliably when the system is reset after power-up, OR if plain digitalRead is used instead of digitalReadFast. However, with the code as written, upon initial power-up the “semi” LED nearly always lights up instead- indicating that pin 41 is reading LOW where it should read HIGH because the pullup is enabled.

I find this completely baffling - why would a pin read correctly on power-up when digitalRead is used, but not when digitalReadFast is used??
Is it possible that reading too quickly after the pullup is enabled could give a spurious reading?

(I tried to reproduce this issue on a Duemilanove but could not, so it may have something to do with the Mega and/or the use of pins 41 and 43.)

AHA!

If I add delay(1); after enabling the pullups, it works every time!

So it seems that reading too soon after enabling the pullups is not reliable. digitalRead provides enough delay to mask the problem, but digitalReadFast exposes it.

Moral of the story: don't read a pin immediately after enabling the internal pullup.

I have run into a peculiar issue using digitalReadFast with my Arduino Mega 1280.

Once the mode is determined, are those pins re-purposed? If not, I can't see that the few microseconds gained using the Fast functions during setup() really gains you anything.

No, there's no advantage to using Fast here except for the flash memory saved by never compiling the standard functions.

Mostly I found it a little unsettling to realize that:

  • Activation of the internal pullups is different between first power-up and subsequent resets
  • It is not safe to assume that the Fast functions may always be substituted for the standard functions.

These behaviors could lead to some hard-to-track-down bugs. I was lucky to quickly identify digitalReadFast as the problem.

An addendum to this upon further reading and thought:

I suspect the problem manifested here only because the wires to the switch were quite long (perhaps six feet). Hence, large capacitance? Therefore, takes a while for the weak internal pullup to raise the voltage on the line HIGH.

So I might not have noticed any difference between digitalRead and digitalReadFast if the lines to the switch had been short.