In this lesson:
- the three things inside every MCU: a CPU core, memory, and peripherals
- how they're wired together by internal buses (AHB/APB on ARM)
- why peripherals running in parallel with the CPU is the key idea
- what's in a Cortex-M core specifically
- the gotcha that bites everyone: peripherals need their clock enabled first
Three kinds of blocks
Open any MCU datasheet to the block diagram and you'll see dozens of boxes. They all fall into three categories:
- The CPU core, fetches, decodes, and executes instructions. On the chips in this course, an ARM Cortex-M core.
- Memory, flash (non-volatile, holds your program) and SRAM (volatile, holds your data/stack/heap).
- Peripherals, dedicated hardware blocks that do specific jobs: GPIO, UART, SPI, I²C, timers, ADC, DAC, DMA, and more.
They're connected by internal buses that carry addresses and data between them.
flowchart TB
CPU["Cortex-M core<br/>(ALU, registers, NVIC)"]
subgraph BUS["bus matrix"]
direction LR
AHB["AHB (fast)"] --- APB["APB (slower peripherals)"]
end
CPU --> BUS
BUS --> FLASH["Flash<br/>(program)"]
BUS --> SRAM["SRAM<br/>(data/stack)"]
BUS --> PERIPH["Peripherals<br/>GPIO · UART · SPI · I2C<br/>Timers · ADC · DAC · DMA"]The CPU core
The core is where instructions run. A Cortex-M core contains the arithmetic/logic unit, a small set of fast registers, an instruction pipeline, and, integrated right into the core, the NVIC (Nested Vectored Interrupt Controller) that manages interrupts, plus the SysTick timer. Higher-end cores add an FPU (floating-point unit) and an MPU (memory protection unit).
It executes your program by fetching instructions from flash, decoding them, and operating on data in registers and SRAM. Everything else on the chip exists to feed it or to act on its behalf.
Memory: flash vs SRAM
| Flash | SRAM | |
|---|---|---|
| Volatile? | No, keeps contents with power off | Yes, lost on power-down |
| Holds | program code, const data |
variables, stack, heap |
| Speed | slower (may need wait-states at high clock) | fast (single-cycle) |
| Writes | slow, erase-before-write, limited cycles | fast, unlimited |
| Size | larger (tens of KB-MB) | smaller (KB-hundreds of KB) |
Your code runs from flash; your data lives in SRAM. We map this out in detail in the next lesson.
Peripherals: hardware that offloads the CPU
A peripheral is a dedicated hardware block that performs a specific function without the CPU doing the work bit-by-bit. This is the single most important idea in embedded:
- A UART peripheral shifts out a byte one bit at a time at a precise baud rate while the CPU goes off and does something else.
- A timer counts clock ticks and fires an interrupt every millisecond, with zero CPU involvement between ticks.
- An ADC samples an analog voltage and converts it to a number on its own.
- A DMA controller copies data between memory and a peripheral without the CPU touching each byte.
Without peripherals: CPU bit-bangs everything → no cycles left for real work
With peripherals: CPU configures the block, then does other work
while the peripheral runs in PARALLELYou interact with a peripheral by reading and writing its registers, special memory addresses that control it and report its status (next lesson). The CPU sets up the peripheral, then the peripheral runs autonomously and signals back via flags or interrupts.
Buses: AHB and APB
The core and peripherals don't all sit on one wire. ARM MCUs use a hierarchy:
- AHB (Advanced High-performance Bus), the fast backbone connecting the core, memory, DMA, and high-speed peripherals.
- APB (Advanced Peripheral Bus), slower branches hanging off the AHB for lower-speed peripherals (often split into APB1/APB2).
This is why peripherals are grouped by bus in the datasheet and why each bus has its own clock prescaler (clock-tree lesson). Bus bandwidth is finite, the CPU and DMA can contend for the same bus, which matters for performance tuning.
Everything is memory-mapped
The crucial unifying concept: memory and peripheral registers all live in one flat address space. Reading address 0x2000_0000 might hit SRAM; reading 0x4002_0014 might read a GPIO input register. To the CPU it's all just loads and stores, which is exactly why C pointers can reach hardware (the registers lesson builds on this).
Gotchas
- Peripherals are off until you enable their clock. This is the #1 beginner bug: you configure a GPIO/UART/timer and nothing happens, because its clock gate in the RCC (Reset and Clock Control) is still disabled. Enable the peripheral clock first, then configure.
- Peripheral registers are
volatile. They change outside your code's control (hardware updates status bits), so accesses must not be optimized away, recall thevolatilerule. Vendor headers handle this. - Bus contention is real. The CPU and DMA sharing a bus can stall each other; heavy DMA traffic can slow CPU memory access. Matters when tuning throughput.
- Know which bus a peripheral is on. It determines the peripheral's clock frequency (via the bus prescaler) and the register base address. Configuring a timer with the wrong assumed clock gives wrong timing.
TL;DR
- An MCU contains three kinds of blocks: a CPU core, memory (flash for code, SRAM for data), and peripherals (GPIO, UART, timers, ADC, DMA, …), wired by internal buses.
- The Cortex-M core integrates the ALU, registers, pipeline, the NVIC interrupt controller, and SysTick; higher-end cores add an FPU and MPU.
- Peripherals do work in parallel with the CPU, the core configures a block, then the block runs autonomously and signals back, which is what makes MCUs efficient.
- ARM MCUs use an AHB (fast) / APB (slower peripherals) bus hierarchy; everything, memory and registers alike, lives in one memory-mapped address space.
- Always enable a peripheral's clock (RCC) before configuring it, the most common "why isn't this working" bug.