Arduino PinMode: How can it be possible for an Arduino pin to be either an input or an output? Most electronics is either input or output not both. So how is this magic performed? Is INPUT_PULLUP just for button push detection? And did you forget Mr. Schmitt?

All about Arduino pinMode:

  • What does pinMode do?

  • Where can you use pinMode?

  • What is the real purpose of input pull-ups: Hint it's not button press detection!

  • How does pinMode relate to the internal hardware of the ATmega328p?

  • Why do you have to write a '1' to the port output bit to enable the pull-up?

  • What does the Schmitt trigger block do?

It's easy to look at the Arduino pinMode function with a software hat on. It's trivial and allows you to set three states of a microcontroller pin:


You just set it and forget it depending on what you need to do:

  • pinMode( <pin number>, INPUT);
  • pinMode( <pin number>, OUTPUT);
  • pinMode( <pin number>, INPUT_PULLUP);

Arduino pinMode Explained

The function of Arduino pinMode is primarily to set the pin direction to either input or output where you use digitalRead to get the state of the pin for input, or digitalWrite to set the pin to 0V or 5V for output.

Originally these were the main options. INPUT_PULLUP was added later and you had to set the pin as INPUT and the write the output value as HIGH to get the pullup action (before IDE ver 1.0.1 - You can see the reason here)- which is really non obvious so the extra operation: INPUT_PULLUP was added to pinMode. You still use digitalRead when using INPUT_PULLUP mode.

However, when you really think about it (with a hardware hat on), why is it that a single bit of electronics can be either an input or an output.

The answer is that there is there's a lot more going on under the hood to achieve this multi-operation. Here we're going to look in a little more detail at the actual pin hardware.

Where you can use Arduino pinMode

You can use the Arduino pinMode function at anywhere in the code but you usually use it in setup() to initialise the pin interface. However, you can change the pin direction in any part of the code at any time.

Note: You can use the pinMode anywhere in your code.

This is especially useful, for instance, if you try and implement a software I2C implementation where the data pin is sometimes set as an output to send data, and at other times in the serial sequence it is set as an input to read data. You can also use it through level translator circuits and a capacitive touch sensor circuit etc.

Arduino pinMode Ideal operation

In the ideal case:

For 0V input, you get logical '0'.
For 5V input, you get logical '1'.
OUTPUT Output logical '0', outputs 0V.
Output logical '1', outputs 5V.
INPUT_PULLUP   No input = logical '1' as input value,
5V input = logical '1' as input value,
0V = logical '0' as input value.

Note: for different systems that don't use 5V the max. volts will be the system max. voltage e.g.3V3.

In the ideal case input voltages would have to be exactly 0V or 5V to register logical '0' or logical '1'. In the real world you don't get exact voltages so the high output may bit a bit lower due to loading or tolerance, and the low output may be higher due to ground bounce noise etc.

To make the circuits work easily there must be an allowance for these errors and these tolerances are built into all digital circuits including the ATmega328p. You can find these tolerances labelled VIL, VIH, VOL, VIH where I is Input, O is Output, L is Low, and H is High.

Arduino pinMode hardware

This is the block diagram of a generic pin for the Arduino Uno microcontroller ATmega328p:

aruino pin main blocks

    [Schematic source : ATmega328p datasheet]

 The block functions are:

Coloured Box
Functional block description
Yellow Pullup resistor control.
Red Data direction register bit DDR (with stored value in a d-type) - controls the output tristate. '1' is output, '0' is input.
Port output bit value (write) register (with stored value in a d-type) - generates and stores the data output bit.
Input tristate and sleep control for low power.
Port input bit (read) with meta-stability synchroniser d-types.

Each of the coloured square blocks contains a major function, with input and output blocks separated from each other by tristate elements (circled in red).

The circled functions are:

    Functional block description
Red Circle
    Tristate element
Blue Circle
    Schmitt trigger element.
Tristate elements are really just a single MOSFET that is either low resistance to allow current to flow or high resistance to stop current flow. The exception is in the 'sleep' tristate that has 4 triangles - this is a transmission gate - a bi-directional switch which is also better at isolating a circuit i.e. for ultra low current saving needed in sleep mode.

In red, green and blue blocks, each have a tristate that connects a data element to the processor data bus.

  • For the red box you can read the value of the data direction bit.
  • For the green box you can read the value of the port output bit.
  • For the blue box you can read value at the port pin.

These tristates do not apply to the pin, rather they allow the processor access to read the stored port register values. They work in the same way as the other tiristates but they isolate the pin circuitry from the processor data bus.

The other two tristate elements (red circled) are switched to isolate input and output circuitry. The top one enables the output circuit (green block) to connect to the pin while the bottom left one enables input from the pin (into the blue block).

There is one other element between the violet and blue boxes - circled in blue - that is the Schmitt trigger input conditioner.

The Reason for INPUT_PULLUP

The Arduino pinMode: INPUT_PULLUP mode is not just for detecting button presses!

The basic building block used inside microcontrollers and other chip requires two MOSFETS - one is an n-channel MOSFET (lower side) and one is a p-channel MOSFET (high side). Drain pins are connected together to drive the output. Each Source pin connects to respective 0V and 5V.

When the input voltage is high, the low side n-channel MOSFET is active and it sinks current at the output pin (drives to 0V).

When the input voltage is low the high side p-channel MOSFET is active and it sources current at the output pin (drives to 5V).

When these 'ideal' voltages are used each MOSFET is either fully ON or fully OFF.

However, if you let the input float (no connection) then the voltage can stabilise somewhere in the middle! When this happens a lot of current is drawn as both MOSFETs are somewhat ON.

Note: This is the reason that higher clocked CMOS circuits use more current! i.e. when you switch the input from low to high (or vice versa), at some point both MOSFETS are ON - if you do this faster and faster then they are both 'ON' more of the time so more current is drawn as the clock rate increases.

This is the reason for a pullup - it stops the floating action and sets the input to a fully defined 5V. There's no shorting of the power supply through both MOSFETs, since one is ON and the other is OFF - so current can not flow.

The input pull-up problem

It's not really a big problem but an observation; If you were in a commercial firm and you wanted a reliable system all inputs would have to be pulled up.

Say you were designing a system that was housed in an enclosure (ok every system in existence!), then there will be noise, so any floating input can potentially be an unnecessary current draw!

The problem is that when designing a system you often forget the default state of the processor and just focus on the problem at hand. In fact all the digital pins of the ATmega328p default to input [Datasheet Section 14.2.1 configuring the pin and 14.4.3 DDRB] so any of the 18 digital inputs could be a problem as they default to inputs!

Usually, in a company you will have a design review document that goes through each item that has previously been figured out and ask the designer has this or that been done in a design review. In this case; have all pull-ups been enabled in the software?

One revealing case that had a huge effect on the current drawn was getting ultra low power circuits (See Section: Clock and Sleep mode Tests) to operate at low current! This is probably because the measurements of current were so small it was easy to see the effect of not using a pull-up!

Why Pull-Up and not Pull-Down

Usually a pull-up resistor is connected to 5V and the reason is historical - older TTL had an asymmetric design and when the input was low it would draw more current - so a pull-up resistor is the default choice.

Note: There's nothing stopping you adding an external pull down. For instance, if you wanted a button push to be active high, add a 47k pull-down with the button connecting to 5V! But it is easier to use active low with pull-up, and invert the variable in software if you want to detect active high - plus you don't need to add more components!

Arduino pinMode: Pull up Enable Logic

The logic gate at the top of the schematic is a three input AND gate with the upper two signals inverted. The three signals are:

  1. PUD: Pull Up Disable bit.
  2. Data direction bit (DDR).
  3. Output bit.

The PUD bit is a global control on all pull-ups in all ports. It is used to globally disable pull-ups in the ATmega328p.

The pullup resistor is only enabled if the AND gate outputs a '1'. In this case the subsequent inverter outputs a '0' and the MOSFET is active i.e. the pull-up is active.

For the AND gate to be active high:

  • PUD (Global Pull Up Disable) must be low i.e. not disabled = enabled.
  • DDR bit must be '0' i.e. must be an input.
  • Value at the output bit must be '1'.

The last condition is interesting and is the origin of the original method (IDE 1.0.0) for enabling the pullup resistor where you had to write a '1' to the output register even though not in use (the pin is an input). The Arduino software still does this but in the background.

The design makes use of the fact that the output port bit is unused because of the tristate being enabled (the pin is set as input), isolating this part of the hardware. The port output bit hardware is still active, so designers used the value from the d-type to allow individual bit control of the pull-up. It saves needing an extra set of register bits for each pin to control the pull-up state.

So the pull-up is enabled if the PUD is disabled (globally active pull-ups are enabled), the DDR register bit sets the pin as an input, and the port output bit is high.

Arduino pinMode: The Schmitt Trigger

The Schmitt trigger is a building block that adds hysteresis to an input signal (using a bit of positive feedback). Its main use is to stop noisy signals triggering a logic element when the input signal is a slowly rising signal.

If you have a slowly rising signal with noise on it, at the logic trigger point the output will be an oscillating signal as the noise causes the input to go above and below the trigger point. By using the Schmittt trigger the effect of noise is removed so you don't get oscillation.

On some microcontrollers only specific pins have a Schmitt trigger inputs but on the ATmega382p it looks like you have a Schmitt trigger on every input pin.

Arduino: pinMode Frequently asked questions

What does pinMode do in Arduino?

Arduino pinMode configures the internal hardware to isolate input and output circuits, selecting one of these for use which allows switching a pin from input to output on the fly. It also allows enabling of the internal pullup resistor.

What pinMode should be used for analog read?

It's really simple just set the Arduino pinMode to INPUT this connects the pin directly to the extra pin functionality of the ADC Multiplexer.

How to set Arduino pinMode Tristate

The condition of tristate simply means a pin allows output values of 0V and 5V but also can be turned into a high impedance input i.e. three states : 0V, 5V, tristate.

However, when you say you want to tristate a pin it is usually used to mean put it into a high impedance state. When a pin is set as input it draws low current and is therefore the tristate state.


Have your say about what you just read! Leave me a comment in the box below.

Don’t see the comments box? Log in to your Facebook account, give Facebook consent, then return to this page and refresh it.

Privacy Policy | Contact | About Me

Site Map | Terms of Use