Buses and peripherals: AHB, APB and memory-mapped I/O
The way a CPU moves a GPIO is the same as writing a value to memory. The bus structure that connects peripherals, and why clocks have to be enabled.
Memory-mapped I/O
ARM has no I/O-specific instructions. Peripheral registers are placed at memory addresses, so an ordinary memory write (STR) changes a GPIO pin and a read (LDR) fetches received UART data.
Bus hierarchy
| Bus | Speed | Connected to |
|---|---|---|
| AHB | Fast | Flash, SRAM, DMA, GPIO (some chips) |
| APB1 | Slow | UART2, I2C, general-purpose timers |
| APB2 | Medium | UART1, SPI1, ADC, advanced timers |
The APB buses branch off AHB through a bridge, and each uses a different clock division. So even the same kind of timer has a different input clock depending on whether it sits on APB1 or APB2.
Clock enable
To save power, most peripherals have their clock off after reset. Before writing a register you have to turn on that peripheral's clock in the RCC.
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; // without this line the GPIOA settings do not take
A good share of "the code is right but the pin does not move" problems are this one missing line.