digital pins register confusion: INPUT_PULLUP, HIGH and LOW

I am new to microprocessors and am trying to figure out the following description on INPUT_PULLUP, HIGH and LOW.
Unfortunately it lacks a code example to demonstrates the concept.
I studied it yesterday and again today and I still don't see it.
Please provide a simple code example or point me to an example that illustrates the concept.

Thank you.

(The following was copied from http://arduino.cc/en/Tutorial/DigitalPins)

Pullup Resistors
Note also that the pullup resistors are controlled by the same registers (internal chip memory locations) that control whether a pin is HIGH or LOW. Consequently a pin that is configured to have pullup resistors turned on when the pin is an INPUT, will have the pin configured as HIGH if the pin is then switched to an OUTPUT with pinMode().

This shows the idea:

http://gammon.com.au/switches

Digital pins have two modes: INPUT or OUTPUT

Digital outputs have two states: HIGH or LOW

The "third" mode is INPUT_PULLUP which is INPUT plus an internal pullup to +5V.

Hi Nick,

The Switches tutorial was very clear, and I learned something about switch state.
But my question is about pullup resistors controlled by the same registers that control whether a pin is HIGH or LOW
PULLUP = HIGH
PULLUP disabled = LOW

Here is an example code snippet based on the description:

pinMode(8, OUTPUT);
digitalWrite(8, HIGH);
pinMode(8, INPUT);	// is pin 8 PULLUP set or disabled?

According to the Digital Pins page, pin 8 PULLUP is set:
“an output pin that is left in a HIGH state will have the pullup resistors set if switched to an input with pinMode().”

According to the pinMode() page, pin 8 PULLUP is disabled:
“INPUT mode explicitly disables the internal pullups.”
http://arduino.cc/en/Reference/PinMode

One of them is wrong or I am not understanding something.

The only reliable reference is the Arduino core source code. Ignore the documentation, it is invariable wrong or misleading.

    if (mode == INPUT) {
        uint8_t oldSREG = SREG;
                cli();
        *reg &= ~bit;
        *out &= ~bit;
        SREG = oldSREG;
    } else if (mode == INPUT_PULLUP) {
        uint8_t oldSREG = SREG;
                cli();
        *reg &= ~bit;
        *out |= bit;
        SREG = oldSREG;
    } else {
/...

There you can see that *out is either explicitly set or cleared depending on the _PULLUP.

One of the releases changed the way that those functions worked.

“an output pin that is left in a HIGH state will have the pullup resistors set if switched to an input with pinMode().”

I think that is wrong.

The documentation does not necessarily keep up with the program changes. It used to be true. :slight_smile:

These are the four possible cases:

Mode    State     Effect

OUTPUT  LOW   ->  pin sinks current
OUTPUT  HIGH  ->  pin sources current

INPUT   LOW   ->  pin is input, high impedance, no pull-up resistor
INPUT   HIGH  ->  pin is input, pull-up resistor enabled

Code for above:

pinMode (pin, OUTPUT); digitalWrite (pin, LOW);
pinMode (pin, OUTPUT); digitalWrite (pin, HIGH);
pinMode (pin, INPUT); 
pinMode (pin, INPUT_PULLUP);

Personally I don’t particularly like that setting it input changes the state, but I didn’t write this stuff. :wink:

Previously the last line could be written:

pinMode (pin, INPUT); 
digitalWrite (pin, HIGH);

Or you could swap the order of those two lines. In fact you can still write it that way because setting the pin HIGH does not cancel INPUT. However this, now, does not work:

digitalWrite (pin, HIGH);
pinMode (pin, INPUT);

But my question is about pullup resistors controlled by the same registers that control whether a pin is HIGH or LOW

You are wrong.there are two registers involved, one controls if a pin is an input or output. The other controls if the pull up is enabled or not. It is just now they can be accessed from the one pinMode function call.

Grumpy_Mike:

But my question is about pullup resistors controlled by the same registers that control whether a pin is HIGH or LOW

You are wrong.there are two registers involved, one controls if a pin is an input or output. The other controls if the pull up is enabled or not. It is just now they can be accessed from the one pinMode function call.

I think you're miss-reading there. He's not on about DDRx and PORTx, as you seem to be, but PORTx being used for both pin level setting and pull-up resistor setting.

majenko:
but PORTx being used for both pin level setting and pull-up resistor setting.

yes he might think that but there is no way that is actually happening.

majenko:
He's not on about DDRx and PORTx, as you seem to be, but PORTx being used for both pin level setting and pull-up resistor setting.

The combination of DDRx and PORTx lead to four possibilities as I described above. You can't really take either on their own.

Grumpy_Mike:

majenko:
but PORTx being used for both pin level setting and pull-up resistor setting.

yes he might think that but there is no way that is actually happening.

Why do you say that?

The PORTx register is used for both writing a 1 or 0 to a pin, and also for turning on and off the pull-up resistors. It’s all a matter of context - i.e., the setting of the DDRx register at the current point in time.

Yes you can. The problem here is that the Arduino core API doesn't take them on their own. That is what the OP is querying.

You do not set both DDR and PORT in the same operation. You do not even need to set both of them if one is already in a known state. Yes, one affects the other, but they are two separate registers and you modify them separately. Unless you're the Ardunio API when you do them both in the same function, but then don't have that correct information in the documentation, which then confuses people.

It's not so much a question of how it works (which I am intimate with, by the way) but how it is wrongly described on the website.

Personally I think that the way Atmel went about it is completely wrong and having one register that does two completely different jobs depending on the state of another register is just barmy.

Another incidentally: in the chipKIT API we enhanced it further to make use of the considerably more enhanced resistor configurations in the MX1 and MX2 devices - we have not only INPUT and INPUT_PULLUP, but INPUT_PULLDOWN and INPUT_PULLUPDOWN. The latter being particularly interesting, where it enables both the weak pull-up and the weak pull-down resistors, creating an input that is neither HIGH nor LOW (i.e., in the noise margin) and yet is tied to a stable level so it's not actually floating. Great for if you want an input that can be connected to an external device that might have pull-up, or might have pull-down, or may not even be connected.

majenko:
Personally I think that the way Atmel went about it is completely wrong and having one register that does two completely different jobs depending on the state of another register is just barmy.

Perhaps, but the entire chip is like that. For example, A4/A5 can be used for I2C or for analog input. D0/D1 can be used as digital in/out or serial comms.

Pins sharing functionality is normal and to be expected. If every peripheral had its own set of pins you'd have to have a BGA chip for just the simplest of cores. But registers changing their meaning depending on another setting? No.

Thank you for the code Nick and Majenko.
The following example code summarizes what you stated.
Is there a way to test this code?

// The following code demonstrates that pullup registers are controlled
//  by the same registers that control whether a pin is HIGH or LOW.
// INPUT and LOW disable the internal pullups.
// INPUT_PULLUP and HIGH enable the internal pullups.

// OUTPUT syntax				// resulting high/low register state
pinMode (pin, OUTPUT); digitalWrite (pin, LOW);	// LOW
pinMode (pin, OUTPUT); digitalWrite (pin, HIGH);// HIGH

// INPUT syntax					// resulting pullup register state
pinMode (pin, INPUT);				// pullup disabled
pinMode (pin, INPUT_PULLUP);			// pullup enabled

// mixing OUTPUT syntax and INPUT syntax	// resulting pullup register state
pinMode (pin, INPUT); digitalWrite (pin, HIGH);	// pullup enabled
digitalWrite (pin, HIGH); pinMode (pin, INPUT);	// pullup disabled
pinMode (pin, INPUT); digitalWrite (pin, LOW);	// pullup disabled
digitalWrite (pin, LOW); pinMode (pin, INPUT);	// pullup disabled

Do it one at a time, and test the output of the pin with a multimeter? Or, add a resistor and LED to the pin and watch it light up (output high, or input no pullup), not light up (output low or input no pullup), or glow dimly (input pullup).

Thanks Majenko. Test results were as expected:

// The following code demonstrates that pullup registers are controlled
//  by the same registers that control whether a pin is HIGH or LOW.
// INPUT and LOW disable the internal pullups.
// INPUT_PULLUP and HIGH enable the internal pullups.

void setup()
{
	Serial.begin(9600);
	delay(500);
}

void verify(char *expected_result)
{
	int pin_button = 8;				// pin with switch
	pinMode (pin_button, INPUT_PULLUP);		// pullup enabled

	Serial.println(expected_result);
	Serial.println("Press button for next test.");
	delay(500);					// debounce
	while(digitalRead(pin_button) == HIGH)		// wait for button press
	{
	}
}

void loop()
{
	int pin = 12;					// pin with LED

	// OUTPUT syntax				// resulting high/low register state
	pinMode(pin, OUTPUT); digitalWrite(pin, LOW);	// LOW
	verify("LED is off");
	pinMode(pin, OUTPUT); digitalWrite(pin, HIGH);	// HIGH
	verify("LED is bright");

	// INPUT syntax					// resulting pullup register state
	pinMode (pin, INPUT);				// pullup disabled
	verify("LED is off");
	pinMode (pin, INPUT_PULLUP);			// pullup enabled
	verify("LED is dim");

	// mixing OUTPUT syntax and INPUT syntax	// resulting pullup register state
	pinMode(pin, INPUT); digitalWrite(pin, HIGH);	// pullup enabled
	verify("LED is dim");
	digitalWrite(pin, HIGH); pinMode(pin, INPUT);	// pullup disabled
	verify("LED is off");
	pinMode(pin, INPUT); digitalWrite(pin, LOW);	// pullup disabled
	verify("LED is off");
	digitalWrite(pin, LOW); pinMode(pin, INPUT);	// pullup disabled
	verify("LED is off");

	Serial.println("End of tests.");
	while(true)
	{ 
	} 
}

majenko:
Pins sharing functionality is normal and to be expected. If every peripheral had its own set of pins you’d have to have a BGA chip for just the simplest of cores. But registers changing their meaning depending on another setting? No.

Well, you had better write to Atmel and complain, majenko.

There are a lot of registers whose meaning changes depending on other registers. Timers for example. In different modes (where the mode is in one register) other registers take on different meanings.