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
- Disable unused peripheral clocks — each adds 10-100 μA.
- Set unused GPIOs to analog mode (lowest leakage).
- Disable debug module in Stop/Standby (
DBGMCU->CR = 0).
- Use LSE (32.768 kHz crystal) for RTC — more accurate and lower power than LSI.
- Reduce system clock before entering Stop for flash power savings.
- 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
- 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.
- Wrong AF mapping. A pin can have up to 16 alternate functions. Verify from the datasheet AF table, do not guess.
- 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.
- Timer off-by-one. Timer counts 0 to ARR inclusive, period = ARR+1. For 1000 Hz from 72 MHz: PSC=71, ARR=999.
- I2C without timeout. A stuck slave holds SDA low forever. Add a timeout and bus-reset sequence (9 SCK pulses).
- DMA stream conflict. Two peripherals cannot share the same stream. Check the DMA mapping table before assigning.
- Uncleared interrupt flags. Many peripherals require explicit flag clearing. Missing it = infinite ISR loop = hung CPU.
- 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