STM32 Peripheral Design Guide: GPIO, Timers, ADC, DMA & Serial Communication

STM32 Peripheral Design Guide

GPIO, Timers, ADC, DMA & Serial Communication

Quick Answer: What Are STM32 Peripherals?

Peripherals are the hardware modules baked into every STM32 microcontroller that handle I/O, timing, analog conversion, data transfer, and communication. They sit on the AHB/APB buses and are controlled through memory-mapped registers. The STM32 HAL and LL libraries wrap these registers into C functions, but understanding what is happening at the hardware level is the difference between a design that works by accident and one that works by design. This guide covers the eight most-used peripheral families.

1. GPIO Configuration That Does Not Bite Back

GPIO looks simple — set a pin high or low, read a pin state. But GPIO configuration is where most bring-up bugs live. An STM32 pin can be an input, output, alternate function (AF), or analog mode. Each has sub-options: pull-up, pull-down, open-drain, push-pull, and speed grade.

Mode Register Config Typical Use
Input (floating) MODER=00, PUPDR=00 Reading a sensor that drives the line actively
Input with pull-up MODER=00, PUPDR=01 Reading a button tied to GND
Input with pull-down MODER=00, PUPDR=10 Reading a button tied to VDD
Output push-pull MODER=01, OTYPER=0 Driving an LED, controlling a MOSFET gate
Output open-drain MODER=01, OTYPER=1 I2C SDA/SCL, wired-AND bus, level shifting
Alternate function push-pull MODER=10, OTYPER=0 SPI SCK/MOSI, UART TX, timer PWM
Alternate function open-drain MODER=10, OTYPER=1 I2C via AF, MCO clock output
Analog MODER=11 ADC input, DAC output, comparator

Output Speed — Not Just Faster Is Better

STM32 GPIO pins have configurable slew rates: Low, Medium, High, and Very High. Faster edges improve signal integrity for SPI above 20 MHz but increase EMI and power consumption. For a 9600-baud UART TX line, Low is more than enough. For SPI at 36 MHz, use High. Match speed to the fastest signal on that pin, not the chip’s maximum. HAL initialization:

GPIO_InitTypeDef gpio = {0};
gpio.Pin = GPIO_PIN_13;
gpio.Mode = GPIO_MODE_OUTPUT_PP;
gpio.Pull = GPIO_PULLUP;
gpio.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &gpio);

Common pitfalls: forgetting __HAL_RCC_GPIOB_CLK_ENABLE(), configuring I2C pins in push-pull instead of open-drain, and leaving unused pins floating (increases leakage — set them to analog mode).

2. Timer Peripherals — The Swiss Army Knife

STM32 timers come in three tiers: basic (TIM6/TIM7), general-purpose (TIM2-TIM5, TIM9-TIM14), and advanced-control (TIM1/TIM8/TIM20). Each tier adds real hardware capabilities.

Timer Type Key Features Typical Use Cases
Basic (TIM6, TIM7) 16-bit up-counter, prescaler, auto-reload, interrupt/DMA. No output channels. Simple timebase, scheduler tick
General-Purpose 16/32-bit counters, 2-4 PWM channels, input capture, one-pulse, encoder PWM generation, pulse measurement, quadrature encoder
Advanced (TIM1, TIM8) All GP features + complementary outputs with dead-time, break input BLDC/PMSM motor control, half-bridge converters

PWM Frequency Formula

PWM frequency: PWM_Freq = TIM_CLK / ((PSC + 1) * (ARR + 1)). For 50 Hz servo PWM on a 72 MHz APB1 timer: PSC=1439 (tick at 50 kHz), ARR=999 (period at 50 Hz, 1000-step resolution). In HAL:

htim2.Init.Prescaler = 1439;
htim2.Init.Period = 999;
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);

Input Capture

Input capture latches the counter on a rising/falling edge. Capture both edges to compute pulse width and period. Handle timer overflow in the update interrupt and add ARR+1 to the calculation when the counter wraps.

3. ADC and DAC — Bridging Analog and Digital

ADC: Getting Real-World Data In

Most STM32s have 12-bit successive-approximation ADCs (0-4095 counts). The critical parameter: sampling time. The sample-and-hold capacitor must charge through the external source impedance. Too short a sampling time with a high-impedance source gives wrong readings. For sources under 1k, 3 cycles is fine; for 10k-50k, use 28-84 cycles; above 50k, buffer with an op-amp.

Scan Mode + DMA

For multiple channels, pair scan mode with DMA in circular mode. The ADC sequences through channels and DMA fills a buffer continuously — no CPU intervention:

uint16_t adc_buffer[4];
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adc_buffer, 4);
// adc_buffer updated continuously, CPU reads at leisure

DAC: Generating Analog Outputs

The 12-bit DAC outputs 0 to VREF+ (typically 3.3V). Trigger it with a timer for waveform generation — a 100 kHz update rate with DMA-fed samples gives a basic function generator. Add an external op-amp buffer because the DAC output impedance is high.

4. DMA — Stop Wasting CPU Cycles

DMA transfers data between peripherals and memory without CPU involvement. STM32 has two controllers (DMA1, DMA2), each with streams and channels. A stream is a transfer engine; a channel selects which peripheral triggers it. Always check your part’s DMA request mapping table.

Mode Behavior Use Case
Normal Transfers N items, then stops One-shot block transfer
Circular Transfers N items, wraps to start Continuous ADC scan, audio DAC
Memory-to-Memory No peripheral trigger, single burst Fast memcpy
Double-Buffer Alternates two memory banks Streaming data processed in chunks

The DMA + UART Pattern

For variable-length UART reception, use UART IDLE detection with DMA on F0/F3/F4/F7/G0/G4/H7/L4:

HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buffer, RX_BUF_SIZE);
// HAL_UARTEx_RxEventCallback() fires when line goes idle

This eliminates the old “poll until timeout” hacks that waste CPU and add latency.

5. Serial Communication — SPI, I2C, and UART

SPI: Speed Over Distance

SPI is full-duplex, master-driven, with SCK, MOSI, MISO, and NSS. The critical parameters: CPOL (clock idle polarity) and CPHA (sampling edge), defining SPI modes 0-3. Most devices use Mode 0 or 3 — confirm from the slave datasheet. For high-speed SPI above 20 MHz, keep traces under 50 mm and use 22-47 ohm series resistors near the driver.

I2C: Two Wires, One Headache

I2C uses SDA and SCL, both open-drain with external pull-ups. Pull-up sizing: 4.7k for standard mode (100 kHz), 2.2k for fast mode (400 kHz), 1.0k for fast-mode plus (1 MHz). Estimate bus capacitance at 10 pF per device + 3 pF per cm of trace. If the bus hangs, clock out 9 extra SCK pulses to release a stuck slave.

UART: The Workhorse

UART is asynchronous — both sides agree on baud, data bits, stop bits, and parity. Use the STM32 Clock Configuration Calculator to verify baud accuracy. For reliable reception, use hardware flow control (RTS/CTS) or an application protocol with start/end delimiters, length byte, and checksum.

Feature SPI I2C UART
Wires (min) 3 + 1/slave 2 2
Max speed 50 Mbps 400 kbps (1 Mbps FM+) 10.5 Mbps
Duplex Full Half Full
Multi-slave Yes (NSS) Yes (address) No
Clock Master Master None
Error detect None ACK bit Parity bit

6. NVIC and Interrupt Management

The Nested Vectored Interrupt Controller handles up to 240 external interrupt lines with configurable priorities. Priority is split into preemption (can interrupt a lower-priority ISR) and sub-priority (decides order when two same-preemption interrupts are pending).

Use 4 bits of preemption, 0 bits of sub-priority (NVIC_PriorityGroup_4) — 16 levels of pure nesting, simple and predictable. Cortex-M interrupt latency is 12 cycles, but tail-chaining cuts it to 6 cycles between back-to-back interrupts. Keep ISRs short: do the minimum work, set a flag, handle the rest in the main loop.

EXTI for External Signals

For button presses and sensor alerts, configure EXTI lines with trigger edges:

HAL_NVIC_SetPriority(EXTI15_10_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);

Do not debounce inside the ISR. Capture a timestamp and check the stable state after a 20-50 ms window in the main loop.

7. Watchdog Timers — Your Silent Guardian

IWDG: Independent Protection

The Independent Watchdog runs from its own internal 32 kHz LSI oscillator. If the main oscillator fails or code hangs, the IWDG still ticks and forces a reset if not refreshed. A 1-2 second timeout works for most applications:

hiwdg.Init.Prescaler = IWDG_PRESCALER_64;  // 32kHz / 64 = 500 Hz
hiwdg.Init.Reload = 1000;                   // 2-second timeout
HAL_IWDG_Init(&hiwdg);
// In main loop: HAL_IWDG_Refresh(&hiwdg);

WWDG: Catching Timing Violations

The Window Watchdog runs from PCLK1 and must be refreshed within a time window. Refresh too late or too early and it resets. This catches code running at the wrong rate — exactly the bug IWDG would miss. Ideal for control loops with precise timing requirements.

8. Low-Power Modes — Sleep, Stop, and Standby

Battery-powered STM32 applications depend on low-power design. The Cortex-M core offers three modes:

Mode CPU Clocks Wakeup Current (F4) Wakeup
Sleep Stopped Peripheral clocks on Any interrupt ~1 mA Instant
Stop Stopped HSI/HSE/PLL off EXTI, RTC, IWDG ~100 μA ~10 μs
Standby Off All off except RTC WKUP pin, RTC, NRST ~3 μA ~400 μs

Sleep is for waiting on a peripheral interrupt — call __WFI(). Stop preserves SRAM, wakes on RTC alarm or EXTI — ideal for sensor nodes. Standby loses SRAM, wakes through full reset — for coin-cell devices waking a few times per day.

Low-Power Checklist

  1. Disable unused peripheral clocks — each adds 10-100 μA.
  2. Set unused GPIOs to analog mode (lowest leakage).
  3. Disable debug module in Stop/Standby (DBGMCU->CR = 0).
  4. Use LSE (32.768 kHz crystal) for RTC — more accurate and lower power than LSI.
  5. Reduce system clock before entering Stop for flash power savings.
  6. On L4/L5/U5 families, use the SMPS regulator for ~40% run-mode savings.
Calculator Tip: Use the STM32 Clock Configuration Calculator to find PLL settings that minimize system clock while meeting your baud-rate and timer resolution requirements. Lower clock = lower power.

9. System Design Walkthrough: A Battery Data Logger

A practical design: a battery-powered data logger reading two sensors (temperature via ADC, pressure via I2C), timestamping via RTC, buffering via DMA, and sending packets over UART. 48-hour battery target.

Function Peripheral Pins
Temperature ADC1 IN0 PA0 (Analog)
Pressure I2C1 PB6, PB7 (2.2k pull-ups)
Buffer DMA1 Stream0 — (circular, half-transfer IRQ)
Output USART1 PA9 (TX, 115200 8N1)
Timestamp RTC + LSE PC14, PC15 (32.768 kHz)
Sample trigger TIM2 — (1 Hz update)
Health monitor IWDG — (2 s timeout)

Between samples, the STM32 enters Stop mode. Every second, RTC wakes the CPU. The ISR triggers an ADC read (DMA handles the result), reads I2C pressure (3-byte transaction), formats a packet, and returns to Stop. Active time per sample: under 500 μs. Average current: ~120 μA. With a 2000 mAh battery, that is over 16,000 hours — far exceeding the 48-hour requirement. Each peripheral does one job, and reliability comes from clean partitioning.

Common Mistakes

  1. Forgetting the clock enable. Every peripheral is clock-gated. Without __HAL_RCC_xxx_CLK_ENABLE(), register writes are silently ignored — the #1 bring-up bug.
  2. Wrong AF mapping. A pin can have up to 16 alternate functions. Verify from the datasheet AF table, do not guess.
  3. ADC sampling time too short. A 3-cycle sample at 30 MHz ADC clock gives only 100 ns for the S/H capacitor to charge through a 50k source.
  4. Timer off-by-one. Timer counts 0 to ARR inclusive, period = ARR+1. For 1000 Hz from 72 MHz: PSC=71, ARR=999.
  5. I2C without timeout. A stuck slave holds SDA low forever. Add a timeout and bus-reset sequence (9 SCK pulses).
  6. DMA stream conflict. Two peripherals cannot share the same stream. Check the DMA mapping table before assigning.
  7. Uncleared interrupt flags. Many peripherals require explicit flag clearing. Missing it = infinite ISR loop = hung CPU.
  8. Debug mode in low-power measurement. DBGMCU keeps clocks running. Your Stop-mode current reads 5 mA instead of 100 μA. Disconnect the debugger before measuring.

Frequently Asked Questions

What is the difference between HAL and LL drivers?

HAL provides high-level APIs with state machines — easier but heavier. LL drivers are thin register wrappers — faster, smaller. Mix both: HAL for complex init (USB, Ethernet), LL for time-critical runtime ops (GPIO toggle in ISR).

How do I choose between basic, general-purpose, and advanced timers?

Basic (TIM6/TIM7): periodic interrupts and timebases. General-purpose: PWM, input capture, encoder mode. Advanced (TIM1/TIM8): complementary outputs with dead-time for motor control. Do not waste an advanced timer on a simple LED PWM.

Why does my SPI slave miss bytes in back-to-back frames?

The slave ISR cannot finish before the next byte arrives. Solutions: slow the SPI clock, use DMA on the slave side, or insert an NSS gap between frames on the master.

What is the real-world ADC sampling rate in scan mode?

At 30 MHz ADC clock, one channel takes ~0.5 μs. A 4-channel scan is ~2 μs or 500 kHz aggregate. With realistic sampling times (15-28 cycles), expect 250-400 kHz. Use DMA circular mode for maximum throughput.

How do I recover a stuck I2C bus?

Disable the I2C peripheral, configure SCL as GPIO output, toggle SCL 9 times (clocks out stuck slave), generate a STOP condition, reconfigure pins to I2C AF, reinitialize. HAL lacks built-in recovery — implement in your error handler.

How much current in Sleep vs. Stop vs. Standby?

For STM32F405 at 3.3V, 25°C: Sleep ~1 mA, Stop ~100 μA (with RTC), Standby ~3 μA (with RTC). STM32L4/L5/U5 families achieve sub-microamp Standby and 0.5-1 μA Stop with SRAM retention. Always measure on your board.

Can I use DMA with the DAC for waveform generation?

Yes. Configure DAC trigger source as a timer, set timer to sample rate, enable DAC DMA in circular mode pointed to a waveform lookup table. The CPU is uninvolved — producing clean, zero-jitter analog output.

What happens if I forget to clear a peripheral interrupt flag?

The NVIC sees the interrupt as still pending. When the ISR returns, it immediately re-enters — creating an infinite loop that starves the main program. Check the peripheral datasheet for which flags auto-clear and which require explicit write-to-clear.

Related Calculators & Tools

STM32
Embedded Engineering
Microcontrollers
GPIO
Timers
ADC/DAC
DMA
SPI/I2C/UART
NVIC
Watchdog
Low Power

发表评论