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.
- 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)
- 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.
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.
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
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).
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).
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.
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).
| DDRxn | PORTxn | Regime | Electrical behavior |
|---|---|---|---|
| 0 | 0 | Input, high impedance | the pin left floating, undefined level |
| 0 | 1 | Input with pull-up | the resistor pulls toward VCC; the outside can pull it down to ground |
| 1 | 0 | Output at logic 0 | pin tied to ground, can sink current |
| 1 | 1 | Output at logic 1 | pin tied to VCC, can source current |
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.
#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);
}
}
}
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.
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.
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.
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 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.
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).
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.
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.
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.
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.
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.
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).
| Pin | Alternative function | Effect on GPIO |
|---|---|---|
| PD0, PD1 | RXD, TXD (UART) | taken over by the serial module; also wired to USB on the Arduino Uno |
| PB2, PB5 | SS, MOSI, MISO, SCK (SPI) | enabling SPI takes over the pins; SCK is also the on-board LED pin |
| PC4, PC5 | SDA, SCL (I2C) | taken over by the TWI, with an external pull-up mandatory |
| PD2, PD3 | INT0, INT1 | remain 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.
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.
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.
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
| Limit | Value (ATmega328P) | Note |
|---|---|---|
| per pin | 40 mA absolute, ~20 mA recommended | a destruction value, not a design value |
| per pin group | 100 mA | eight LEDs at 20 mA each cannot be lit on the same group |
| total | 200 mA | includes the core, the peripherals and everything flowing through outputs |
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.
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.
14Self-check questions7 min
- 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.
- 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?
- 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?
- What is metastability, what internal mechanism explains it, and why can it not be eliminated completely, only made improbable?
- Compare the role of the Schmitt trigger with the role of the two-stage synchronizer - which cause of metastability does each solve?
- List three situations where the AVR's internal pull-up is not enough and explain why, for each one.
- 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.