EmbeddedRelated.com

GPIO

Category: Peripherals | Also known as: general-purpose I/O, general-purpose input/output

GPIO (general-purpose input/output) is a digital pin on a microcontroller or SoC whose direction (input or output) and state (high or low) are controlled entirely by software, as opposed to pins dedicated to a fixed peripheral function. Most MCUs expose anywhere from a handful to over a hundred GPIO pins (package constraints often limit the accessible count), often sharing physical pins with alternate peripheral functions such as UART, SPI, or PWM.

In practice

GPIO is the fundamental building block of embedded hardware interaction. In output mode, a pin is driven high or low by writing to a port data or set/clear register, which in turn sources or sinks current to control LEDs, drive logic signals, toggle chip-select lines, or generate bit-banged waveforms. In input mode, the pin state is sampled by reading a port input register, which lets firmware detect button presses, read digital sensor outputs, or sample external logic levels. Many MCU GPIO controllers support configurable internal pull-up or pull-down resistors, which can reduce the need for discrete resistors on floating inputs; however, internal pulls are not universal and are often weaker than external resistors, so noisy or timing-sensitive designs may still require discrete parts.

Output drive configuration matters in practice. Common options include push-pull (the pin actively drives both high and low) and open-drain (the pin can only pull low; an external resistor pulls the line high). Open-drain outputs are required or preferred on shared buses such as I2C and can be useful in some level-shifting circuits, though open-drain is one of several approaches for interfacing devices at different supply voltages rather than a universal requirement. Exceeding a pin's rated source/sink current -- typically 4 mA to 25 mA per pin depending on the device -- can cause voltage levels to fall outside valid logic thresholds or, in extreme cases, damage the pin driver.

On most modern MCUs, GPIO pins that share pads with alternate functions must be switched into the correct mode via a pin multiplexer or pin function select register before use. Forgetting to configure the alternate function, or leaving a pin in its default state after reset when a peripheral is expected to drive it, is a common source of bringup bugs. Interrupt-capable GPIO inputs, where a pin state change triggers an ISR rather than requiring polled reads, add further configuration options such as edge selection (rising, falling, or both) and debounce.

When running embedded Linux on an application processor (Cortex-A, RISC-V SoC, etc.), GPIO is accessed through the kernel's GPIO subsystem rather than by direct register writes. The modern, recommended interface is the character device API exposed by libgpiod, which replaces the older sysfs-based approach. Direct register access from userspace is generally not the recommended approach on Linux targets because it bypasses the kernel's resource management and can conflict with kernel drivers, though some specialized platforms and setups do use it -- a point covered in the blog post "Libgpiod - Toggling GPIOs The Right Way In Embedded Linux".

Discussed on EmbeddedRelated

Frequently asked

What is the difference between push-pull and open-drain GPIO output modes?
In push-pull mode the output driver actively drives the pin to both VDD (high) and GND (low). In open-drain mode the driver can only pull the pin low; the high state is achieved by an external (or internal) pull-up resistor. Open-drain is required for wired-AND buses like I2C, and is useful when interfacing devices operating at different supply voltages.
Why should floating GPIO inputs be avoided?
A floating input has no defined voltage level; it picks up noise and can read as either high or low unpredictably, causing erratic firmware behavior. Always connect an input to a defined level, either through an external resistor, the MCU's internal pull-up or pull-down resistor, or a driving source. On many MCUs, unused pins should also be configured as outputs or have pull resistors enabled to prevent oscillation that can increase supply current.
How much current can a GPIO pin source or sink?
This varies by device and must be checked in the datasheet. Many general-purpose MCU pins are rated for 4 mA to 8 mA (for example, many STM32 pins at 5 mA typical), while some higher-drive pins on devices like the Raspberry Pi RP2040 can be configured for up to 12 mA. Exceeding the per-pin or total-port current limits can shift output voltage levels outside valid logic thresholds or permanently damage the silicon.
Can GPIO pins be used to implement serial protocols without a dedicated peripheral?
Yes. Manually toggling GPIO pins in software to implement a serial protocol is called bit-banging. It works for protocols like UART, SPI, and I2C when a hardware peripheral is unavailable or all peripheral instances are already in use. The trade-off is increased CPU load and less precise timing compared to a hardware peripheral -- timing jitter from interrupts or cache misses can corrupt the signal, especially at higher baud rates. The blog post 'Bit-Banged Async Serial Output And Disciplined Engineering' discusses the practical considerations in detail.
How is GPIO accessed differently on bare-metal MCUs versus embedded Linux?
On bare-metal MCUs, GPIO is typically controlled by direct memory-mapped register reads and writes to port data, direction, and configuration registers. On embedded Linux systems (Cortex-A SoCs, for example), direct register access from userspace is generally not the recommended approach because it bypasses kernel resource management. The recommended approach is to use the kernel GPIO character device API through a library such as libgpiod, which provides safe, portable access without risking conflicts with kernel drivers.

Differentiators vs similar concepts

GPIO is sometimes confused with analog I/O pins. A GPIO pin operates only in digital mode -- it reads or drives a logic high or low. Many MCU pins are physically shared between GPIO and an ADC or DAC channel, but the analog and digital functions are distinct modes selected during pin configuration. Reading an analog voltage requires switching the pin to ADC mode and using the ADC peripheral; it cannot be done through the GPIO data register.