I'm using an MPC73831 - a LiPo charging IC - in my Arduino project. It has a status pin to allow interface to a microcontroller, which uses a tri-state operation to convey three possible states of charging. I don't have the IC and battery yet, but I wanted to write a quick program to test a generic tri-state pin for HI, LO, and HiZ while I'm waiting for it to arrive.
The algorithm I wrote is very simple:
Set pinTest to input, no pullup.
Is pinTest high?
Yes - pinTriState is HIGH, done.
Set pinTest to input with pullup.
Is pinTest high?
Yes - pinTriState is HiZ, internal pullup causes high reading.
No - pinTriState is LOW, 'cancelling out' internal pullup.
However, I'm not getting the expected results when using the internal pullup resistor - my board is showing correct analysis for HI and LO, but HiZ returns a mixture of HiZ and HIGH. I have tried this on an Uno, a Pro-Micro (32u4) and Uno->Micro and Micro->Uno. All give the same spurious results. I am baffled, therefore some advice would be appreciated. The full code is as below. I have left in code which randomises the time pinTriState stays at in a given state to keep pinTest guessing, but it's ultimately only an elaborate way of cycling through the three states.
#define pinTest 2
#define pinTriState 3
int state = 0;
char stateCh = '?';
int sect1;
int sect2;
int sectMax;
void setup()
{
Serial.begin(9600);
randomSeed(analogRead(A0));
sect1 = random(1, 20);
sect2 = random(sect1 + 1, random(sect1 + 2, sect1 + 20));
sectMax = random(sect2 + 1, random(sect2 + 2, sect2 + 20));
}
void loop()
{
// PART (1) Code to toggle three states of a tri-state pin
if (state == 0)
{
pinMode(pinTriState, OUTPUT);
digitalWrite(pinTriState, HIGH);
stateCh = 'H';
}
if (state == sect1)
{
pinMode(pinTriState, OUTPUT);
digitalWrite(pinTriState, LOW);
stateCh = 'L'
}
if (state == sect2)
{
pinMode(pinTriState, INPUT);
stateCh = 'Z';
}
// PART (2) Code to determine state of tri-state pin
Serial.print(stateCh); // Shows what it *should* be
pinMode(pinTest, INPUT);
if (digitalRead(pinTest))
Serial.println(" - HIGH");
else
{
pinMode(pinTest, INPUT_PULLUP);
if (digitalRead(pinTest))
Serial.println(" - HiZ");
else
Serial.println(" - LOW");
}
(state == sectMax ? state = 0 : state++);
}
The necessary pins are connected with a single wire. If I upload the code to two boards I remove PART (2) on the 'triState' board and PART (1) on the 'testing' board. In this case, stateCh has no function.
For a single-board setup, I get the following output:
H - HIGH
H - HIGH
...
L - LOW
L - LOW
...
Z - HiZ <-- the first Z is correct 95% of the time
Z - HIGH
Z - HIGH <-- the remaining Zs are a mixture, biased heavily towards HIGH.
Z - HiZ
...
H - HIGH <-- H always gives HIGH
...
It makes no difference if I change the operating order of pinState. I've thrown in delay(100); between almost every other line, again to no avail.
I tried with a 22k, a 33k, and a 47k external pullup resistor. The results are perfect. However, this requires an extra Arduino pin to make sure that the pullup is only active when necessary. Part (2) becomes:
// PART (2) Code to determine state of tri-state pin
Serial.print(stateCh); // Shows what it *should* be
pinMode(pinTest, INPUT);
pinMode(pinPullup, INPUT); // connected to pinTest via resistor
if (digitalRead(pinTest))
Serial.println(" - HIGH");
else
{
//pinMode(pinTest, INPUT_PULLUP); <-- not anymore
pinMode(pinPullup, OUTPUT);
digitalWrite(pinPullup, HIGH); // pulls pinTest to Vdd
if (digitalRead(pinTest))
Serial.println(" - HiZ");
else
Serial.println(" - LOW");
}
Using the extra pin to provide the pullup is a real pain in the ar...duino. I'm cripplingly short of spare pins and I don't see why this is not resolvable with internal pullups. Help?!

pinMode(pinTest, INPUT_PULLUP)is the first line in theelseblock. And that doesn't answer why it works with an external pull-up resistor that's turned on at the right time in leiu of the internal pull-up. – CharlieHanson Jun 28 '15 at 01:27pinPulluphappens to be biased towards Vcc, keeping the resistor slightly pulled up at all times? Why never pulled down by a leakage? – CharlieHanson Jun 28 '15 at 08:21pinPullup(connected topinTestthrough the external resistor) is set to an input (HiZ, so undefined, n'est pas?) except for the seconddigitalRead(pinTest), at which point it's output HIGH. In my view that copies the electrical characteristics of the internal pullup. I've edited the question to add the code for PART (2) when using an external pullup, in case it's still not clear. Ultimately it's a moot point; your reasoning for the error with the internal pullup is enough to assuage my hair-pulling confusion, and your solution to the problem is a suitable answer. – CharlieHanson Jun 28 '15 at 22:29