LECTURE 04

Input and Output Ports

Duration: 121 min of teaching Level: undergraduate, year 2 - recommended after Lecture 03 Discipline: Microcontrollers and Microprocessors Associated laboratory: Laboratory 02 PDF: download the notes RO versiunea română

A microcontroller that cannot touch the world around it is good for nothing. The practical value of the system is measured by what it manages to read from sensors and by what it manages to command toward actuators - the link is made through the input and output ports. A digital pin looks, at first glance, like a simple wire on which the program sets 0 or 1. Behind it, however, sits a circuit with flip-flops, power transistors, protection diodes, a switchable resistor and a synchronization chain - and not knowing this produces exactly the faults that seem inexplicable: a button that changes state on its own, a pulse that never shows up in the program, an LED that burns out the port.

1Purpose and structure of the course6 min

The chapter follows, in turn, the hardware structure of a pin, the software registers that control it, the path of the signal from outside toward the core, the problems that arise along this path and the external circuits commonly connected to ports. The reference microcontroller remains the ATmega328P, with the advantage of a particularly clear organization: three registers per port and a direct correspondence between bit and pin.

Recap from previous lectures
  • An SFR is an interface between the program and a concrete peripheral circuit (Lecture 02)
  • We always modify a single bit of a register (|=, &=~, ^=), never the whole register, except during initialization (Lectures 02-03)
Today we apply exactly these rules to port pins, the simplest and most used form of communication with the outside.
  • Distinguish DDRx, PORTx and PINx by role, and explain why three registers are needed
  • Explain the four electrical regimes of a pin (DDRx/PORTx) and the consequences of each
  • Explain metastability and the role of the two-stage synchronizer
  • Size a pull-up or limiting resistor for an LED
  • Recognize a multi-function conflict on a pin, and filter the bouncing of a button
  • Respect the per-pin, per-group and total current limits of the ATmega328P

2Input-output ports and the digital port10 min

Input and output (I/O) ports are the hardware interfaces through which the microcontroller communicates with external peripherals. A port contains no actual signal processing - it is where a logic value in memory becomes a voltage on a wire, or the other way around.

A pin is considered an I/O pin if the direction of transfer can be changed by the program, with no intervention on the physical circuit - the trait that distinguishes it from a fixed-function pin (power, reset). Pins are not treated individually, but grouped, usually in eights, forming a port: eight bits is exactly the width of a register on an 8-bit machine, so the entire port is read or written through a single instruction. The ATmega328P has three ports - B, C, D - but the pin count in the data sheet and the number actually usable on a real board do not coincide: port B has two pins taken up by the 16 MHz crystal, port C has its last pin taken up by the reset input.

Logic thresholds

The digital port classifies any applied voltage into one of two states. For an AVR powered at 5 V: any voltage below 0.3·VCC (~1.5 V) is safely read as logic 0; any voltage above 0.6·VCC (~3 V) is safely read as logic 1. Between the two thresholds there remains a forbidden band, for which nothing is guaranteed - the result can differ from one chip to another or from one reading to the next.

Logic levels do not transfer between systems powered differently

A microcontroller at 3.3 V that outputs logic 1 (3.3 V) is, for an AVR at 5 V, above the 3 V threshold - reading works. Conversely, an AVR that outputs 5 V into a 3.3 V circuit's input applies an overvoltage that can destroy it. Interfacing between different voltage domains requires resistive dividers or dedicated level translators.

Positive logic and negative logic The correspondence between voltage level and logical meaning is a design choice, not a law of nature. Negative logic (active = low voltage) shows up often precisely because a pull-up setup naturally produces logic 1 at rest and logic 0 when the button is pressed. Confusing the two conventions is one of the most frequent causes of inverted behavior in beginner programs.

3The architecture of a port: pin structure and the three registers10 min

Communication is bidirectional, and bidirectionality must be built in hardware. Each pin contains an output buffer (drives the voltage), an input buffer (reads the voltage without changing it) and an enable line (decides which buffer is actually connected) - controlled by a bit in a configuration register. When the output buffer is disabled, the pin goes into high impedance (HiZ), almost like a broken wire.

The five blocks of a pin

inside the microcontrollerDDRx bitPORTx bitPINx bitoutput stagepush-pullSchmitt trigger+ synchronizerpull-upPINVCCGNDoutside
The internal structure of a port pin. The three flip-flops on the left are the bits in DDRx, PORTx and PINx; in the middle, the push-pull output stage and the input chain; on the right, the switchable pull-up resistor and the protection diodes toward the supply and ground.

The configuration flip-flops (direction + output value, static storage). The push-pull output stage: two complementary transistors, never conducting at the same time - the top one supplies current (source), the bottom one absorbs current (sink). The switchable pull-up resistor (20-50 kΩ on AVR). The protection diodes (toward VCC and toward ground, reverse-biased during normal operation). The input chain (Schmitt trigger + synchronizer, discussed separately).

Why the two transistors never conduct at the same time

That would mean a direct short circuit between the supply and ground inside the chip. The control logic of the output stage guarantees the exclusivity of the two states - a fault in this logic is one of the few ways an AVR chip can electrically destroy itself.

The three registers of a port

For port B: DDRB, PORTB, PINB (similarly for C, D).

DDRx, PORTx, PINx
DDRx (Data Direction Register) - direction: 0=input, 1=output. After reset, all bits are 0 (all pins start as inputs - the safe choice, an input cannot damage anything).
PORTx - dual role: for an output pin, the value driven on the pin; for an input pin, enables (1) or disables (0) the internal pull-up resistor.
PINx - the only one that tells the truth about the physical state of the pin, regardless of what the program wrote into PORTx. It is not ordinary storage, but a window onto the flip-flop at the end of the synchronization chain - writing a 1 to a bit in PINx toggles the corresponding bit in PORTx.
The classic beginner mistake: reading PORTx instead of PINx

An input pin with the pull-up enabled has the value 1 in PORTx, but may have the value 0 in PINx, if the button is pressed. Reading PORTx to find out a button's state makes the program report 1 unwaveringly, because that is what it wrote there - the real state of the pin never reaches the test. The practical rule, in three words: DDRx for direction, PORTx for what we want, PINx for what actually is.

4Configuring ports: through registers and through Arduino12 min

Configuring the direction is the first thing done in a microcontroller program, before the main loop - everything else depends on it, including the electrical integrity of the setup (a wrong direction can put two voltage sources face to face).

DDRxnPORTxnRegimeElectrical behavior
00Input, high impedancethe pin left floating, undefined level
01Input with pull-upthe resistor pulls toward VCC; the outside can pull it down to ground
10Output at logic 0pin tied to ground, can sink current
11Output at logic 1pin tied to VCC, can source current
The global PUD bit in MCUCR

When set, it simultaneously disables all pull-up resistors on all ports, regardless of the contents of the PORTx registers - useful for minimal power draw, but a serious source of confusion if left set by mistake: button circuits suddenly appear to stop working, even though the initialization code looks correct.

Configuration and reading through registers
#include <avr/io.h>

int main(void)
{
    DDRB |= (1 << PB5);             /* PB5: LED, output */
    DDRD &= ~(1 << PD2);            /* PD2: button, input */
    PORTD |= (1 << PD2);            /* internal pull-up enabled */

    while (1) {
        if ((PIND & (1 << PD2)) == 0) {   /* pressed = tied to ground */
            PORTB |= (1 << PB5);
        } else {
            PORTB &= ~(1 << PB5);
        }
    }
}
Why the test is done on PIND, not PORTD, and not with ==

PIND & (1<<PD2) isolates the desired bit and gives either zero or a non-zero value. Writing PIND == 0 would require all eight pins of the port to be simultaneously zero - a frequent mistake that makes the condition almost always false.

Configuration through the Arduino functions

const int LED = 13, BUTTON = 2;
void setup() {
    pinMode(LED, OUTPUT);
    pinMode(BUTTON, INPUT_PULLUP);
}
void loop() {
    digitalWrite(LED, digitalRead(BUTTON) == LOW ? HIGH : LOW);
}

INPUT_PULLUP does, in a single call, exactly what the two lines with DDRx and PORTx did. PORTB |= (1<<PB5) compiles into a single instruction, one cycle. digitalWrite translates the pin number into port+bit through two tables in Flash, checks and stops any active PWM channel, disables and re-enables interrupts - dozens of cycles. For an LED controlled by a button, the difference is irrelevant; for a precise edge or an interrupt that must finish quickly, it becomes decisive.

Match the regime with the bit combination
Safely configuring an output pin that drives a relay

5Data transfer: sampling the signal9 min

The voltage applied on a pin does not reach the program instantly - the path is sliced by the system clock. A latch-type flip-flop, driven by the clock, captures the value on the pin only once per cycle, at a precise edge. Consequence: the pin's state is not read continuously, but sampled - the program sees the last snapshot taken of the pin.

Sampling delay On AVR, the delay between a pin's physical change and the new value being available in PINx is between half a clock cycle and one and a half cycles, depending on when the change happens relative to the edge. At 16 MHz (62.5 ns/cycle), the delay is on the order of tens of nanoseconds - negligible for buttons, not at all negligible for a fast synchronous protocol.
Pulses shorter than one clock cycle can be lost entirely

If the signal goes up and comes back down between two sampling instants, the flip-flop has no way to know that anything happened - at both samplings the value was the same. For this reason, very short pulses are not detected by repeatedly reading PINx in a loop, but through dedicated hardware mechanisms: edge-triggered external interrupts (Lecture 05) or timer capture.

Worked exercise - can a 200 ns pulse be read by polling?

An incremental encoder produces 200 ns pulses at maximum speed. Can it be read by polling PINx in a loop, on an ATmega328P at 16 MHz?

See the solution

One clock cycle: 1/16 MHz = 62.5 ns, so the pulse covers ~3 cycles - the sampling itself could catch it.

The problem is the program loop: an iteration that reads PINx, masks the bit, compares and takes a conditional branch consumes at least 5-6 cycles, that is over 350 ns - more than the duration of the pulse. There are therefore iterations in which the pulse is born and dies between two consecutive reads and is lost.

The correct solution: an edge-triggered external interrupt, which does not depend on the moment the program gets around to reading.

6Metastability9 min

Metastability is an unstable state in which the output of a flip-flop sits in a transition zone between the two logic levels, being neither 0 nor 1, for an unpredictable duration. It happens when the input signal changes right at the moment of sampling.

The internal mechanism
A flip-flop holds its value through a positive feedback loop - two inverters connected in a ring. The loop has two stable states, but also a third one, of unstable equilibrium, in which both inverters feed each other the same intermediate voltage - like a ball placed exactly on top of a hill: in principle it can stay there, in practice it will fall, but there is no way to know beforehand which way, or after how long.

The flip-flop ends up there if, at the clock edge, the input voltage is not far enough from the switching threshold - the condition expressed in data sheets through the setup time and the hold time around the edge.

Metastability is not eliminated, it is made improbable Exiting the metastable state is exponential: the probability of still being undecided decreases exponentially with the time elapsed since the edge. There is no time after which the output is mathematically guaranteed, but there is a time after which the probability becomes practically zero over the equipment's lifetime - an observation that underlies all the practical solutions.

The causes: the lack of correlation between the external signal and the internal clock (a button pressed by a person follows no synchronization rule with a 16 MHz clock) and slow edges (a signal that travels a long cable spends a lot of time in the intermediate zone, directly raising the probability of sampling it there).

The Schmitt trigger cleans up edges, but does not solve metastability

An ordinary comparator has a single threshold - if the signal crosses it slowly, carrying a bit of noise, the output oscillates several times. The Schmitt trigger has two thresholds (V_hi, V_lo); the difference between them, hysteresis, is a measure of noise immunity. The result: no matter how slowly the voltage rises, the trigger's output makes a single, clean transition. But a perfectly sharp edge that falls exactly in the setup-hold window produces metastability just as well as a slow one - for the timing of the edge, something else is needed (next section).

7The synchronizer circuit8 min

The solution for the asynchronous nature of external signals is the synchronizer: it does not eliminate metastability, but gives it time to settle before the value reaches the program. Two flip-flops are placed in series, driven by the same clock, and the value used further on is the one at the output of the second one.

Why it works The first flip-flop samples an asynchronous signal and can enter metastability. Its output does not go directly to the program, but to the second flip-flop, which samples it only on the following cycle. In this one full cycle, the first flip-flop has time to settle - the probability of it still being undecided after a full cycle is exponentially small, so the second flip-flop samples, practically always, an already clean signal.

What the synchronizer does not guarantee is which value will be propagated if the change landed exactly on the edge - old or new, which just means the transition was seen one cycle earlier or later, almost always acceptable. What it guarantees is that the program will never see a value that is neither 0 nor 1.

The total input delay: d_in = d_latch + d_sync, where d_sync is the number of propagation cycles through the synchronizer. The more stages it has, the lower the probability of error, but the higher d_sync - two stages are the usual compromise for ordinary microcontrollers; very fast or safety-critical systems may use three or more.

The synchronizer is applied to the signal, not to the bus

If the same asynchronous signal must be used in two different places in the circuit, it must be synchronized once, and the result distributed. Two parallel synchronizers on the same signal can, in the unlucky case, give different results - the circuit ends up in an inconsistent state, with the two halves "agreeing" on different moments of the same transition.

8Pull-up and pull-down resistors10 min

A digital input not connected to any defined signal source sits in high impedance (HiZ) - counterintuitive for a beginner, for whom "an unconnected wire is at zero". It is not: the CMOS input has a resistance on the order of hundreds of megaohms, and the potential of a floating pin is set by whatever else is around (capacitive coupling, the 50 Hz mains hum, the electrostatic charge of a nearby hand) - the readings look random.

A second downside: current from the intermediate zone

When the voltage on a pin sits in the intermediate zone, both transistors in the input stage conduct partially, and a current with no useful role flows through them. A few pins left floating can double the idle power draw of a battery-powered application. The general recommendation: no pin should ever be left floating.

The fix is a pull-up resistor (between the line and VCC, fixes the rest state at logic 1) or a pull-down resistor (between the line and ground, fixes the rest state at logic 0). The pull-up-with-button-to-ground setup is the most common, producing negative logic: rest = 1, pressed = 0 - preferred because AVR has an internal pull-up, but not an internal pull-down, and the ground trace is usually easier to route on a board than the supply trace.

Worked exercise - current through a pull-up

A button is wired through a 10 kΩ pull-up to 5 V. What current flows with the button pressed, and what does the setup consume if it is held pressed continuously?

See the solution

With the button pressed, the resistor is connected directly between 5 V and ground: I = 5V/10kΩ = 0.5 mA. Power dissipated: P = U·I = 5×0.5×10⁻³ = 2.5 mW - nothing for a 0.25 W resistor.

If 100 Ω were used instead of 10 kΩ, the current would become 50 mA and the power 250 mW - an unacceptable waste on battery power, with visible heating of the resistor. With the AVR's internal pull-up (20-50 kΩ), the current would be between 0.1 and 0.25 mA.

When the internal pull-up is not enough Long or medium lines with strong electromagnetic interference (near a PWM-driven motor): tens of kilohms leave the line too soft. Buses with open-drain outputs (I2C): the pull-up must charge the line fast enough for the protocol's edge timing, the internal values being far too large. A rest level needed before the microcontroller runs its initialization, or even with the microcontroller in reset (the enable signal of a motor driver). Pull-down is always external on AVR, which has no internal resistors of this type.

9Pins with multiple functions7 min

The number of pins on a package is limited, while the number of peripherals on a chip keeps growing - the fix is function multiplexing: the same pin can be used, at different times, by different peripherals. In its ordinary mode, as a plain digital pin, it is called GPIO (general purpose input/output).

PinAlternative functionEffect on GPIO
PD0, PD1RXD, TXD (UART)taken over by the serial module; also wired to USB on the Arduino Uno
PB2, PB5SS, MOSI, MISO, SCK (SPI)enabling SPI takes over the pins; SCK is also the on-board LED pin
PC4, PC5SDA, SCL (I2C)taken over by the TWI, with an external pull-up mandatory
PD2, PD3INT0, INT1remain readable, but generate edge-triggered interrupts if enabled

Configuring these functions is not done from the port registers, but from the SFRs of the respective peripheral (e.g. UCSR0B for UART, SPCR for SPI). As long as an alternative function is active, the pin can no longer function as GPIO - writes to PORTx no longer have effect, reads from PINx may no longer reflect what is expected.

The classic conflicts on the Arduino Uno

Pins 0 and 1 (RXD/TXD): any call to Serial.begin takes over these two pins - if something else in the circuit is also wired to them, communication with the computer and the circuit interfere with each other. The tone and analogWrite functions can use the same timer; the Servo library takes over timer 1, thereby disabling PWM on pins 9 and 10.

The working rule Before assigning a function to a pin, check the microcontroller's alternative-function table for what other peripherals use it, and plan pin allocation at the start of the project, not as modules are added. The most frequent source of hard-to- diagnose bugs is exactly this: the program looks correct, and the pin does not respond.

10Protecting digital pins12 min

Pins are points of contact between a few-nanometer silicon structure and an outside world full of parasitic voltages. Manufacturers provide internal protections, but with well-defined limits - exceeding them destroys the chip.

Internal protections

ESD (electrostatic discharge): the diodes from each pin toward VCC and ground divert the pulse - protection for short, rare events, not for sustained overvoltage. Overvoltage: the same diodes, in clamping mode - work only if a small current (milliamps) flows through the diode; exceeding it destroys the diode and can raise the whole supply of the chip.

Classic AVRs have no overcurrent protection

A short circuit between an output pin at 1 and ground produces a current limited only by the internal resistance of the transistor - tens of milliamps. The typical result is not immediate destruction, but progressive degradation of the stage: the pin drives "weaker", then stays stuck at a value, and eventually the entire supply of the chip can be affected.

Maximum current - three simultaneous limits

LimitValue (ATmega328P)Note
per pin40 mA absolute, ~20 mA recommendeda destruction value, not a design value
per pin group100 mAeight LEDs at 20 mA each cannot be lit on the same group
total200 mAincludes the core, the peripherals and everything flowing through outputs
The design rule The microcontroller is not a power source - it is a control element. Anything needing more than a few milliamps (relays, motors, LED strips, solenoid valves) is driven through an intermediate element: a transistor, an integrated driver, a relay or an optocoupler.
Worked exercise - the limiting resistor for an LED

A red LED (V_F = 2 V) must be driven from a 5 V pin, at 10 mA. Calculate the limiting resistor, the power dissipated and the maximum number of such LEDs on a pin group.

See the solution

The voltage drop across the resistor: 5 - 2 = 3 V. R = 3V/10mA = 300 Ω. The next standard value up (E12 series): 330 Ω, which gives 3/330 ≈ 9 mA.

Power dissipated: P = 3×9×10⁻³ ≈ 27 mW - a 0.25 W resistor is far more than enough.

For the number of LEDs: the group limit is 100 mA; at 9 mA/LED this gives 100/9 ≈ 11, but this calculation uses up the group's entire margin. A cautious design stops at half - five or six LEDs at once on a group. If 100 Ω were mounted instead of 330 Ω, the current would become 30 mA (below the absolute maximum of 40 mA) - the LED would work, but the pin would be operating permanently in the not-recommended zone, and three such LEDs would already exceed the group limit.

Sizing the series resistor and checking the pin limits

12Frequent mistakes4 min

  • "I read a button's state from PORTD, it is simpler." False - PORTD holds what the program wrote (the command or the pull-up bit), not the real state on the pin. The program will report unwaveringly what it wrote there, no matter what the button does. Any reading of an input is done from PINx, never from PORTx.
  • "An unused pin, left unconnected, causes no harm at all." False - a floating pin has an undefined potential, set by the surrounding noise, with seemingly random readings and extra consumption through the partial conduction of both input transistors. Explicitly configure any unused pin as an input with pull-up, or as an output at a fixed value.
  • "A relay is driven directly from a pin, like an LED." False twice over - the coil exceeds the maximum current on a pin, and on interruption it generates a spike of reverse voltage of hundreds of volts, which can destroy the driving transistor and reach, through the protection diodes, the microcontroller's supply. Always use a switching transistor plus a flyback diode in parallel with the coil.

13Summary and glossary5 min

A port groups eight pins to allow access through a single instruction. Each pin has three registers - DDRx for direction, PORTx for command (or pull-up, on input), PINx for the real state - and they play an independent logical role: they cannot be merged into a single one without losing information. The input signal is sampled, not read continuously, which introduces a small delay and can lose pulses shorter than one clock cycle. Sampling an asynchronous signal right at the edge can produce metastability - solved not by elimination, but by a two-stage synchronizer, which makes it improbable. A floating pin is undefined, not zero; a pull-up or pull-down resistor gives it a rest state. Function multiplexing takes a pin away from the GPIO role while the associated peripheral is active. The current limits - per pin, per group, total - are not negotiable, and any load exceeding a few milliamps needs an intermediate power element.

GPIO
a general-purpose pin, as opposed to an alternative peripheral function.
HiZ
high impedance - the state of a pin whose output buffers are disabled.
Metastability
an unstable state of a flip-flop, between the two logic levels, with unpredictable duration.
Schmitt trigger
a comparator with hysteresis, which cleans up a slow or noisy edge.
Bouncing
the contact oscillation of a mechanical button on closing or opening.
Flyback diode
a recovery diode mounted in parallel with an inductive load, to limit the voltage spike on disconnection.

14Self-check questions7 min

  1. Explain why an AVR port needs three separate registers and describe a situation where the value in PORTx differs from the one read from PINx for the same pin.
  2. A pin has DDRD bit 4 at 0 and PORTD bit 4 at 1. What regime is it in, what level is read at rest, and what happens if an outside contact ties it to ground?
  3. Why is reading a pin delayed relative to the physical change of the voltage, and how long is this delay on an ATmega328P at 16 MHz?
  4. What is metastability, what internal mechanism explains it, and why can it not be eliminated completely, only made improbable?
  5. Compare the role of the Schmitt trigger with the role of the two-stage synchronizer - which cause of metastability does each solve?
  6. List three situations where the AVR's internal pull-up is not enough and explain why, for each one.
  7. A green LED (V_F=2.1 V) must be driven at 12 mA from a 5 V pin. Calculate the limiting resistor, choose the standard value and determine the power dissipated.

15Directions for further study2 min

The next lecture moves from manually polling a pin to the hardware mechanism that solves exactly the problem of missed short pulses: the interrupt system - vectors, priorities, latency and the pitfalls of writing a correct handler routine.

The registers, configuration examples and circuits from this lecture become a real setup in Laboratory 02.