Confusion using digitalRead and internal pull up resistors

The behavior of the pins seems different whether I write the code as:

  pinMode(freshwater_high_float_switch, INPUT);
  pinMode(freshwater_fill_float_switch, INPUT);
  digitalWrite(freshwater_high_float_switch, HIGH);
  digitalWrite(freshwater_fill_float_switch, HIGH);

or as this:

  digitalWrite(freshwater_high_float_switch, HIGH);
  digitalWrite(freshwater_fill_float_switch, HIGH);
pinMode(freshwater_high_float_switch, INPUT);
  pinMode(freshwater_fill_float_switch, INPUT);

When I print the results of digitalRead on these two pins using:

Serial.print("fill float switch is ");
  Serial.println(digitalRead(freshwater_fill_float_switch));
  Serial.print("high float switch is ");
  Serial.println(digitalRead(freshwater_high_float_switch));
  delay(500);

the first way above works correctly and the pins will read logic 1 until I ground them and then they will read logic 0. The second way above will read logic 1 for a few iterations and then one of the pins will start reading a logic 0 and then both pins will read a logic 0 a few iterations later. What is going on here?

The behavior of the pins seems different whether I write the code as:

Pins are INPUT and LOW by default.

When you set the mode, the pin is turned off, if it was on. Then, turning the pin back on activates the pullup resistor.

So, your two blocks of code do not produce the same behavior. Only the first is actually correct, if the objective is to enable the pullup resistor.

The second block of code is turning the input pins on, then setting them as input again, with the result that they are turned off. The pullup resistors end up being turned off.

Without the pullup resistors, or external resistors, the pins float. Floating pins are not a good thing.

That makes sense. Does it work the other way as well? When setting the pins as an output I would expect that it would be prudent to initialize the pins to a known state (high or low) before exposing them by setting the corresponding bit in the data direction register.

It is a little bit more complicated than that.

A few things at work:

  1. AVR port behavior. The datasheet says that when a pin is configured as input, writing a '1' to it activates the pull-up. pinMode() follows that to establish pull-up. So writing a '1' to the port after having turned it into input activates the pull-up. That's what your 1st block of code does.
  2. capacitance: In the 2nd block of code, the port is (presumed) high before it was turned into input. When the port was high, it has charged up pin capacitance or any stray capacitance on that pin. Once you have it turned into (high impedance) input, the charges stored on those capacitance start to discharge, in part through your read action. Over time, it reads logic '0'. You can test that for example by putting a larger capacitance on that (the pin capacitance is around 7pf), and you will see that it reads '1' longer.

The 2nd approach actually serves as the basis for some capacitance meter.

dhenry:
It is a little bit more complicated than that.

A few things at work:

  1. AVR port behavior. The datasheet says that when a pin is configured as input, writing a '1' to it activates the pull-up. pinMode() follows that to establish pull-up. So writing a '1' to the port after having turned it into input activates the pull-up. That's what your 1st block of code does.
  2. capacitance: In the 2nd block of code, the port is (presumed) high before it was turned into input. When the port was high, it has charged up pin capacitance or any stray capacitance on that pin. Once you have it turned into (high impedance) input, the charges stored on those capacitance start to discharge, in part through your read action. Over time, it reads logic '0'. You can test that for example by putting a larger capacitance on that (the pin capacitance is around 7pf), and you will see that it reads '1' longer.

The 2nd approach actually serves as the basis for some capacitance meter.

I understand how that would happen. I was just thinking back to when I read the data sheet years ago, I seem to recall that setting the port bits to one and then exposing them by setting the data direction register would cause the pins to either come on as a weak pull-up or a strong pull up depending on whether you wrote ones or zeros to the data direction register. I didn't realize that by setting pinMode from OUTPUT to INPUT causes the port bits to be cleared when the DDRx bits are cleared.