What Hardware to Buy
Firmware is the one branch of software engineering where you genuinely cannot do the work on the machine you write the code on. A simulator will run your main(), but it will not show you that the sensor holds the clock line low for 40 microseconds longer than the datasheet suggests, that your board browns out when the motor starts, or that the pin you thought was an output has been floating since reset. Every important lesson in this section arrives through a physical board, and the reason newcomers stall here is not the money — the whole kit costs less than a mid-range monitor — but the catalogue. There are hundreds of development boards, every tutorial assumes a different one, and nothing on the vendor's site tells you which one the thing you are reading was written against.
Reading a Datasheet
Coming from software, the instinct when you meet a new chip is to look for "the docs" — one document, searchable, that tells you everything. That document does not exist, and looking for it is the reason people bounce off hardware. Silicon vendors ship a set of documents, deliberately separated, because they answer questions that different people ask at different times: the person choosing a part, the person laying out the board, the person writing the firmware, and the person whose product works on the bench but fails one unit in fifty. Each document is written for one of those people and is close to useless for the others.
Reading a Schematic
A schematic is not a picture of a board. It is a graph: components are nodes, and the wires between them — nets — are edges. Two points drawn at opposite corners of the page with the same net label are the same electrical point, as surely as two references to the same object in memory. Once you read it as a graph rather than as a drawing, the intimidating density stops mattering, because you are never reading the whole thing. You are tracing one path.
Voltage Levels and Logic
There is no 1 on a wire. There is a voltage, and there is a receiver that has decided in advance which range of voltages it will call one and which it will call zero. Digital logic is an agreement layered on top of an analogue quantity, and the reason firmware normally gets to ignore that is that the agreement usually holds — the hardware on both ends was designed to the same convention, so the bits you read are the bits that were sent.
How a GPIO Pin Behaves
GPIOA->ODR |= (1 << 5); looks exactly like every other memory write you have ever done, and that resemblance is the problem. On the far side of that register is not a bit of storage but a pair of transistors, wired to a physical pin, with a maximum current, a maximum switching rate, and a real-world net on the other end that may already be being driven by something else. The register model hides all of it, right up until the moment it matters.
Clocks and Oscillators
On a desktop machine the clock is somebody else's problem — it was configured by firmware you never see, and by the time your program runs it is a constant. On a microcontroller you are that firmware. The chip comes out of reset running on a cheap internal RC oscillator at a fraction of its rated speed, with almost every peripheral's clock switched off, and the first job your code has is to build the clock tree the rest of the system will run on. Nothing you write behaves as intended until that is done.
Power Supplies and Regulators
Firmware is written as though the supply rail were a constant — a number in the datasheet, 3.3 V, always there. The rail is not a constant. It is the output of a control loop with finite bandwidth, fed through traces with real resistance and inductance, feeding a load whose current draw your own code is modulating thousands of times a second. Every time the CPU switches from an idle loop to a burst of floating-point work, every time a GPIO drives an LED, every time the chip wakes from Stop mode, the load steps and the rail moves.
Reset and Boot Configuration
There is a gap between the moment power reaches the chip and the moment your first instruction executes, and firmware engineers habitually treat it as empty. It is not. In that gap the supply supervisor decides whether the rail is trustworthy, a pulse generator stretches whatever event caused the reset into a signal long enough for every block on the die to see it, an option-byte loader runs, boot-mode pins are sampled and latched, an address decoder is reconfigured so that a completely different memory appears at address zero, and only then does the CPU fetch two words and start running.
Analog Basics
An analog-to-digital converter looks, from firmware, like a register you read. That framing hides the two things that actually determine whether the number is right. First, the conversion is a comparison against a reference, so the answer is a ratio, not a voltage — and a wrong or noisy reference is invisible in the result. Second, before any comparing happens the converter must charge a small capacitor through your circuit, and if you did not give it long enough, it will confidently report the voltage it managed to reach rather than the voltage that was there.
Signal Integrity and Noise
A schematic draws a wire as a line with no properties. That abstraction holds beautifully for DC and falls apart on the edges — the few nanoseconds after a driver switches, when the wire is not a connection but a component, with inductance, capacitance, a characteristic impedance and a finite speed. For most of the time your signal is idle and the abstraction is fine. For the small fraction of time when it is changing, the wire is the circuit.
Lab Equipment
The debugger on your Nucleo can single-step your code, read every register, and show you the contents of memory — and it is blind to everything that happens outside the package. It will tell you, truthfully, that you wrote 0xA5 to the SPI data register. It cannot tell you whether 0xA5 left the pin, whether the clock that carried it was clean, whether the device on the other end was even powered.