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:
INPUT
OUTPUT
INPUT_PULLUP
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:
Type
Action
INPUT
For 0V input, you get logical '0'.
For 5V input, you get logical '1'.
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:
[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.
Green
Port output bit value (write) register (with stored value in a d-type) - generates and stores the data output bit.
Violet
Input tristate and sleep control for low power.
Blue
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:
Coloured
Circle
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:
PUD: Pull Up Disable bit.
Data direction bit (DDR).
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.
How to get accurate DHT22 digital humidity sensor readings with an Arduino. Did you know it also measures temperature as Well? Find out why, in this page...
A PIR sensor lets your Arduino sense movement without contact. This tutorial covers PIR sensor basics, connecting one to an Arduino board and coding a motion detector.
Arduino Hall Effect Sensor: Add magnetic sensing superpowers to your Arduino projects with an easy-to-use hall effect sensor. With full code and layout...
Comments
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.