UNIT 02 · FOUNDATIONS
Programming Close to Hardware
Where the C compiler meets the silicon.
A microcontroller does not know what a variable is. It knows addresses, bytes and a handful of instructions that move and combine them. C is the thin layer that lets you write timer->CTRL |= EN and get exactly the store instruction you meant, and the same thinness is why an off-by-one shift or a forgotten volatile produces code that is silently wrong rather than an error. This unit builds the mental model of memory and integers that the rest of the curriculum assumes.
The unit in six ideas
- 1A byte is eight bits with weights to ; hexadecimal writes every four bits as one digit, so register values can be read field by field.
- 2Fixed-width integers form a ring of values; unsigned arithmetic wraps modulo by definition.
- 3Memory is byte cells with addresses; a variable is a named run of cells;
&yields the address and*goes through one. - 4Struct members keep declaration order; each starts at an offset that is a multiple of its alignment; the total is rounded up to the largest alignment.
- 5A peripheral is a block of registers at fixed addresses; C reaches them through a cast
volatilepointer or avolatile-qualified struct overlay whose layout matches the register map. - 6Implementation-defined behaviour is documented and usable per target; undefined behaviour removes the program’s meaning, and optimisers assume it never occurs.
Lessons
Binary, Hexadecimal, and Bit Operations
Why does hardware documentation prefer the strange middle spelling, and how does one line of C turn on bit 3 of a register without disturbing the seven bits that some other part of the program relies on?
Integer Types, Signedness, and Overflow
What is the contract, and where is it written down?
Pointers, Arrays, and Addresses
What is actually stored in p, and why does adding 1 to it sometimes move it 4 bytes?
Structures, Alignment, and Data Layout
Where did five bytes come from, and why did the timestamp arrive backwards?
Memory-Mapped Registers and Volatile
What does the keyword tell it, and why is that not the same as making the code correct?
Undefined Behavior and Safe Register Access
What is the compiler allowed to assume, where does firmware most often trip over it, and what does that have to do with the fact that a one-line REG |= BIT can lose an interrupt?