Input/Output and DMA
Introduction
Input/Output (I/O) connects the CPU to the outside world: GPIO, ADC, UART, SPI, I2C, Ethernet MAC, and more. On embedded systems, I/O is usually memory-mapped — reading/writing specific addresses controls hardware registers. DMA (Direct Memory Access) moves data between memory and peripherals without the CPU copying every byte — essential for audio, SD cards, and high-speed sensors.
Programmed I/O
The CPU explicitly reads/writes each data item:
1 2 3 | |
| Mode | Description |
|---|---|
| Polling | CPU loops checking status flags — simple, wastes cycles |
| Interrupt-driven | Device signals ready; CPU handles batches |
| DMA | Hardware transfers blocks; CPU notified on complete |
Use polling only for bring-up or very slow I/O; prefer interrupts or DMA for throughput.
I/O configuration
Peripherals expose control/status registers:
| Register type | Examples |
|---|---|
| Data | TX/RX buffer, ADC result |
| Status | Busy, error, buffer full |
| Control | Enable, mode, baud rate, interrupt enable |
| Configuration | Pin mux, clock source |
HAL (Hardware Abstraction Layer) wraps registers; bare-metal accesses volatile uint32_t * pointers — both map to the same silicon.
Pin multiplexing
One physical pin serves multiple functions (GPIO vs SPI vs UART). Pin mux must match PCB wiring — a common source of "it compiles but does nothing."
Synchronous vs asynchronous buses
| Synchronous | Asynchronous | |
|---|---|---|
| Clock | Shared clock line | Handshake signals (ready/valid) |
| Examples | SPI, I2C | UART, some parallel buses |
| Timing | Edge-aligned to clock | Receiver samples with agreed baud or strobe |
| Distance/speed | Short, fast on PCB | UART tolerant of cable length at lower speed |
SPI: master clocks MOSI/MISO; chip select per slave.
I2C: open-drain, pull-ups, addresses on 2 wires.
UART: start bit + data + optional parity + stop bits — see Serial communication protocols.
DMA
DMA controller performs memory ↔ peripheral transfers while CPU runs other code (or sleeps).
Benefits
- Lower CPU load for large transfers
- Steadier sampling (ADC + DMA ring buffer)
- Required for SDMMC, Ethernet, display controllers at speed
Caveats
- Buffer alignment and cache coherency (Cortex-A)
- Circular (ring) mode for continuous ADC streams
- Race conditions — do not read buffer while DMA writes same region
I/O addressing schemes
| Scheme | Used on |
|---|---|
| Memory-mapped | ARM, RISC-V, most MCUs — *(uint32_t*)0x40021018 |
| Port-mapped (isolated) | x86 IN/OUT instructions — rare in embedded |
| Message-signalled | PCIe on high-end SoCs |
Embedded: assume memory-mapped unless datasheet says otherwise.
Storage attachment (embedded Linux)
| Interface | Typical device |
|---|---|
| SD/MMC | Removable SD card |
| eMMC | Soldered flash on module |
| NOR/NAND | Raw flash with MTD driver |
| USB mass storage | Thumb drive |
SAN/NAS appear when the device is a network client to remote storage — see Memory systems.
Relevant topics
- Computer architecture
- Memory systems
- Serial communication protocols
- Hardware architecture
- DMA (Wikipedia)
Starting points
- Trace one UART TX byte from
write()to register level in debugger. - Implement interrupt-driven RX ring buffer — compare CPU load to polling.
- Configure ADC + DMA circular mode — plot samples in serial plotter.
- Document pin mux table for your board (pin, function, pull-up/down).
Focus points
- Volatile keyword for hardware registers — compiler must not optimize away reads.
- Clear flags in ISR per reference manual (some need write-1-to-clear).
- DMA half/full complete interrupts for double buffering.
- Do not start DMA before peripheral and clocks are enabled.
Key points
- Programmed I/O uses CPU loads/stores; DMA offloads bulk transfers.
- Memory-mapped registers configure and use peripherals on typical MCUs.
- Sync buses (SPI/I2C) use a clock; async (UART) rely on timing agreement.
- Pin mux, alignment, and cache matter for correct and efficient I/O.