a peripheral register
Think of a hardware device — a serial port, a timer, a set of pins — as a machine with a small control panel of switches and gauges. A peripheral register is one switch or gauge on that panel, except that you operate it by reading or writing a specific MEMORY ADDRESS. Because of memory-mapped I/O, the chip wires certain addresses not to storage but to a device's controls, so 'writing 1 to address 0x40020014' might literally raise a voltage on a pin and light an LED.
Each peripheral has several registers, each at a known offset from the peripheral's base address (all listed in the chip's reference manual). Registers come in flavors. A control register (you write to it to configure or command the device, like setting a pin to output mode). A status register (you read it to learn what the device is doing, like 'data has arrived'). A data register (you read or write the actual bytes, like the byte received on a serial port). Individual BITS within a register often mean different things — bit 5 might be 'enable', bit 0 might be 'busy' — so you manipulate them with bitwise operations: set a bit with reg |= (1u << 5), clear it with reg &= ~(1u << 5), test it with (reg & (1u << 0)). In C you model a register as a pointer to a volatile fixed-width integer at the right address: volatile uint32_t *reg = (uint32_t *)0x40020014;, then read *reg or write *reg = value. Many vendor headers instead give you a struct overlaying the whole peripheral so you write GPIOA->ODR.
It matters because peripheral registers are the ONLY way your code touches the physical world on an MCU — every LED, motor, sensor, and message goes through them. The honest, non-negotiable rules: the pointer must be volatile, because the value can change on its own (a status bit set by hardware) and a write has a side effect, so the compiler must never cache, reorder, or eliminate the access; some registers are write-only or read-only or even clear-on-read (reading them changes them), so a careless read-modify-write can corrupt state; and bit positions and the base address must come from the reference manual, never a guess, because a wrong address writes into the wrong peripheral or into nothing.
// Turn on pin 5 by setting a bit in a GPIO output register: #define GPIOA_ODR (*(volatile uint32_t *)0x40020014) GPIOA_ODR |= (1u << 5); // set pin 5 high GPIOA_ODR &= ~(1u << 5); // set pin 5 low if (GPIOA_ODR & (1u << 5)) { /* pin 5 is high */ }
A register is a volatile integer at a fixed address; individual bits are configured with set/clear/test bitwise operations.
A peripheral register is not ordinary memory: it must be volatile, some bits are clear-on-read or write-only, and a register can change with no code touching it. Treating one like a normal variable is a classic silent embedded bug.