How Do GPIO Pins Work on a Microcontroller?
Last updated 26 June 2026 · 6 min read
Direct Answer
GPIO (General Purpose Input/Output) pins are individually configurable digital I/O lines on a microcontroller that can be set by software as either an input (reading a voltage level as logic high or low) or an output (driving a pin high or low). Each GPIO pin has configurable hardware options — push-pull or open-drain output, and internal pull-up or pull-down resistors on the input — that determine how it interacts with external circuitry.
Detailed Explanation
GPIO is the most fundamental interface between a microcontroller and the physical world. Every MCU has at least a handful of GPIO pins; larger parts can have over a hundred. They are the building blocks of almost everything else — LEDs, buttons, chip selects, motor driver signals, and the control lines for peripheral protocols.
GPIO as an input
When configured as an input, a GPIO pin samples the voltage at its pin and reports it as a digital logic level: high (typically at or near VCC) or low (at or near GND). The threshold between high and low is set by the MCU's input voltage levels, which are listed in the datasheet's electrical characteristics as VIH (input high minimum) and VIL (input low maximum).
Internal pull resistors are almost always available. Enabling the internal pull-up resistor connects a high-value resistor (typically 20–50 kΩ) between the pin and VCC internally, so an undriven or open-circuit pin reads as high. This is the standard configuration for a button input wired to pull the pin low when pressed.
// STM32 HAL example: configure PA0 as input with internal pull-up
GPIO_InitTypeDef gpio = {0};
gpio.Pin = GPIO_PIN_0;
gpio.Mode = GPIO_MODE_INPUT;
gpio.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &gpio);
GPIO as an output
When configured as an output, the firmware drives the pin to a defined logic level by writing to a register. The two common output modes are:
Push-pull: the output stage uses two transistors — one pulls the pin to VCC (high), one pulls it to GND (low). The MCU actively drives both states. This is the standard output mode for LED driving, digital signals, and SPI/UART data lines.
Open-drain: the output stage has only the pull-to-GND transistor. The pin can be driven low, or released (floating high, held only by an external pull-up). Multiple open-drain outputs can share the same wire safely — if any one device pulls low, the bus goes low — which is how I2C achieves its multi-controller bus arbitration.
GPIO alternate function modes
Most GPIO pins on modern MCUs double as pins for on-chip peripherals: a GPIO that can function as UART TX, SPI MOSI, I2C SDA, or a timer PWM output. The peripheral block connects to the pin through an alternate function (AF) multiplexer. Configuring a pin for an alternate function routes its signal through the peripheral rather than through the software-controlled output register.
Getting the alternate function number right is one of the most common STM32 bring-up pain points — the AF number must match what the MCU datasheet's pinout table specifies for that specific pin, peripheral, and function.
Output drive strength and slew rate
Many MCUs let you configure the output drive strength (high or low current) and slew rate (how fast the edge transitions). Faster edges radiate more EMI and require more careful PCB routing. Use the lowest drive strength and slew rate that meets your timing requirements — this is especially relevant for clock signals and high-frequency digital lines.
GPIO interrupts
A GPIO pin configured as an input can also be set to trigger a CPU interrupt on a rising edge, falling edge, or both edges, allowing firmware to respond to external events immediately rather than polling. This is how button presses, encoder pulses, and hardware alert signals are handled efficiently.
Practical Examples
A typical LED + button circuit demonstrates the two core modes:
- LED on GPIO output (push-pull): wire LED + resistor to the pin. Writing 1 drives the pin high (LED on); writing 0 drives the pin low (LED off). For a common-cathode LED, drive the GPIO high to turn on.
- Button on GPIO input (pull-up): one side of the button connects to the GPIO pin, the other to GND. Internal pull-up enabled. Pin reads 1 at rest (pulled to VCC through the internal resistor); reads 0 when pressed (button connects pin directly to GND). No external resistor required.
An I2C bus illustrates open-drain: both SDA and SCL are open-drain outputs on every device on the bus. External pull-up resistors hold both lines high when idle. Any device can assert a low, but no device can fight another device trying to go high — the bus is naturally wire-AND'd.
Design Considerations
-
Voltage compatibility: 3.3 V MCU GPIO cannot directly drive or be driven by 5 V signals. A 5 V input driving a 3.3 V GPIO pin can permanently damage it — always check the datasheet's absolute maximum ratings and use a level shifter when the voltage domains differ.
-
Decoupling near GPIO peripherals: GPIO switching events generate current spikes on the VCC supply. Ensure adequate decoupling capacitors near the MCU's power pins, especially when driving multiple GPIOs simultaneously.
-
Current budgeting: total GPIO current draw across all output pins on the same VCC domain is bounded by the MCU's package current rating, not just the per-pin rating. Check the datasheet's IO current section when many pins switch simultaneously.
-
Firmware-hardware handoff: pin configurations (input/output, pull-up/down, AF number) must be agreed between hardware design and firmware. Mismatches — especially wrong AF numbers or missing pull resistors — are a common source of bring-up issues. Zeus Design integrates hardware and firmware design to catch these mismatches before PCB fabrication.
-
Unused GPIO pins: floating unused GPIO inputs can draw excess current and introduce noise. Configure unused pins as outputs driving low, or as inputs with a pull resistor, per the MCU vendor's low-power guidance.
-
MCU-specific GPIO constraints matter. On the ESP32, GPIO34–GPIO39 are input-only (no internal pull resistors, no output driver), GPIO0/2/5/12/15 are strapping pins that affect boot mode, and ADC2 channels share hardware with the Wi-Fi radio. See How Do You Use GPIO, ADC, and Timers on the ESP32? for the full ESP32 GPIO and peripheral API reference.
-
Raspberry Pi GPIO differs fundamentally from MCU GPIO. The Raspberry Pi's GPIO operates at 3.3 V only, is accessed through the Linux kernel character device interface rather than direct register writes, and requires device tree overlays to enable I2C, SPI, UART, and PWM. Python libraries (lgpio, gpiozero) replace the HAL/register code used on MCUs. See How to Interface Sensors and Peripherals with Raspberry Pi GPIO for the Pi-specific interfacing guide.
Common Mistakes
- Floating input pins: an input with no pull resistor and no external drive reads random noise. Always configure a pull-up or pull-down, or add an external resistor to hold the pin at a known level.
- Driving loads directly from GPIO: a relay coil, small motor, or high-brightness LED draws far more current than a GPIO pin can supply. Use a transistor, MOSFET, or driver IC as an intermediary.
- Wrong alternate function number: on STM32, each GPIO pin has multiple AF options. Selecting AF1 instead of AF2 for a UART peripheral connects the pin to the wrong peripheral and produces no output. Always cross-check the AF table in the datasheet pinout section.
- Not accounting for GPIO state during power-up: GPIO pins may float or sit in an undefined state before the firmware initialises them. If this causes hardware issues (e.g. an output driving a reset line unexpectedly), use pull resistors on the PCB or configure the boot-time GPIO state explicitly in
SystemInit.
Frequently Asked Questions
- What is the difference between push-pull and open-drain GPIO output?
- A push-pull output actively drives the pin both high (to VCC) and low (to GND) — the MCU's internal transistors pull in both directions. An open-drain output can only pull the pin low; to drive it high, an external pull-up resistor is required. Open-drain is used on shared buses like I2C, where multiple devices drive the same line and a single device pulling low must not fight another device's push-pull high driver.
- Why do GPIO inputs need pull-up or pull-down resistors?
- A floating (unconnected) input pin picks up noise and reads unpredictably — it may oscillate between high and low without a signal connected. A pull-up resistor ties the pin to VCC through a high resistance so it reads high when nothing drives it, while a pull-down ties it to GND so it reads low. Most MCUs include optional internal pull-up and pull-down resistors (typically 20–50 kΩ) that can be enabled by software, avoiding the need for an external resistor in many cases.
- How much current can a GPIO pin source or sink?
- Most MCU GPIO pins are rated at 8–25 mA continuous output current, with an absolute maximum that must not be exceeded. Driving a standard LED (requiring 5–20 mA) is within this range, but driving a motor, relay coil, or high-power LED directly from a GPIO will damage the pin. Use a transistor or dedicated driver IC for loads above 20–25 mA, and always check the MCU datasheet's GPIO current specifications for the specific part.
References
Related Questions
What Is a Microcontroller (MCU)?
A microcontroller (MCU) combines a CPU, flash, RAM, and peripherals on one chip. Learn how MCUs work and how they differ from microprocessors and FPGAs.
What Are Interrupts in Embedded Systems and How Do They Work?
Interrupts let a microcontroller respond to hardware events instantly without polling. Learn how ISRs, NVIC priority, and interrupt latency work.
PWM Explained: Frequency, Duty Cycle, Dead-Time, and Hardware Timers
Learn how PWM works: duty cycle, frequency, resolution, dead-time for H-bridge drives, and when to use hardware timers versus software PWM on microcontrollers.
How to Interface Sensors and Peripherals with Raspberry Pi GPIO
Interface sensors and peripherals with Raspberry Pi GPIO: 3.3 V limits, level shifting, I2C/SPI/UART device tree configuration, lgpio API, and i2cdetect.
How Should You Place Decoupling Capacitors on a PCB?
Decoupling capacitors need to sit close to the power pin they protect with a short, low-inductance path to ground. Here's how placement affects performance.
What Is a Voltage Divider and How Does It Work?
A voltage divider splits a voltage using two resistors in series. Covers the Vout formula, loading effect, Thevenin equivalent, and when not to use one.
Related Forum Discussions
STM32 GPIO interrupt configured but ISR never fires — what am I missing?
Trying to use a button on PA0 to trigger an interrupt on an STM32F411 Nucleo board. Using HAL, generated the init code with CubeMX. The GPIO
Can't decide between FreeRTOS and bare-metal for a simple sensor node — what's the tipping point?
Working on a temperature and humidity monitoring node — STM32F103 target, BME280 over I2C, reports data every 60 seconds over UART to a Rasp