Intuition The One Core Idea
Embedded code has two halves glued together: pure thinking (math, decisions, parsing) and touching the world (reading sensors, moving motors). If you keep those two halves in separate boxes, you can test the thinking-half fast on your laptop and only visit real hardware when you truly need the world. This whole topic is the craft of drawing that dividing line and testing on the correct side of it.
Before you can read the parent note, you must own every word and squiggle it uses. We build them one at a time — each one earns its place before the next arrives.
The parent note keeps saying "host" and "target". These are just two different computers .
Definition Host and Target
The host is the computer you develop on — your laptop or desktop. Big screen, big memory, fast.
The target is the computer your program will finally run on — a tiny chip glued to a circuit board.
Look at the two boxes below. Same program, two very different homes.
Why the topic needs this: every sentence in the parent is about which of these two machines runs a given test. "Test on host" means the left box; "HIL" means the right box wired to real sensors.
Definition Microcontroller (MCU)
A microcontroller is a whole tiny computer — processor, memory, and input/output pins — on one chip . It is the "brain" sitting on the target board.
The parent writes "32KB RAM ". Let's earn every piece:
Definition RAM and the KB / KB=1024 idea
RAM (Random-Access Memory) is the scratch paper the MCU writes on while it works. Turn the power off and it is wiped.
KB means kilobyte ≈ 1000 bytes (exactly 1024 ). A byte is 8 on/off switches, enough to store one letter.
32 KB = 32 × 1024 = 32768 bytes — about 32 thousand letters of scratch space. Your laptop has millions of times more.
Intuition Why "you can't just printf debug"
printf prints text to a screen. A bare MCU has no screen and barely any scratch paper . Printing a message can cost more memory than the whole program has. That scarcity is exactly why we prefer to test the thinking-half on the roomy host.
The parent throws around GPIO, ADC, PWM, UART, CAN, timer, interrupt, ISR. These are all peripherals — little helper circuits inside or beside the MCU that connect it to the physical world. Take them one at a time.
Definition The peripheral zoo (plain words)
GPIO (General-Purpose Input/Output): a single wire the MCU can either read (is this pin high or low?) or drive (make this pin high or low). A button or an LED lives here.
ADC (Analog-to-Digital Converter): turns a smooth voltage (like 1.65 volts from a temperature sensor) into a whole number the MCU can compute with.
DAC (Digital-to-Analog Converter): the reverse — turns a number back into a real voltage on a wire.
PWM (Pulse-Width Modulation): a way to fake an "in-between" power level by switching a pin on and off very fast. On 50% of the time ≈ half power. This drives motors and dims LEDs.
Timer : a counter that ticks at a fixed rate, used to measure time or generate PWM.
UART / CAN : two ways to send bytes over wires between chips. UART = simple two-wire chat; CAN = tough multi-device bus used in cars.
Definition Interrupt and ISR
An interrupt is a hardware "tap on the shoulder": when an event happens (a byte arrives, a timer ticks), the MCU pauses whatever it was doing and jumps to handle it.
The ISR (Interrupt Service Routine) is the little function that runs when that tap happens.
Why the topic needs all this: these peripherals are exactly the parts that do not exist on your laptop . That is the whole problem the topic solves — and the reason for the next idea.
The parent's "BAD" code says ADC1->DR and TIM2->CCR1. These are registers .
A register is a special memory slot wired directly to a peripheral . Writing a number into it physically changes hardware ; reading it physically reads hardware .
ADC1->DR = read the ADC's D ata R egister → get the latest converted number.
TIM2->CCR1 = write timer 2's C ompare register → set a PWM level.
Intuition Why registers make host testing impossible
On your laptop there is no ADC1. That address points to nothing. So any function that touches a register cannot even be compiled or run on the host — unless we hide it behind a layer we can swap. That layer is next.
These four words are the beating heart of "unit tests on host".
Abstraction means putting a simple named door in front of complicated machinery, so callers use the door and never see the machinery. hal_adc_read(channel) is a door; the register poking hides behind it.
Definition HAL — Hardware Abstraction Layer
The HAL is the collection of all those doors — one tidy set of hal_... functions that every other part of the code uses instead of touching registers. See Hardware Abstraction Layer (HAL) Design .
Now the trick: because everyone knocks on the door and not the machinery, we can put different machinery behind the same door depending on where we run.
A stub is a fake function that just returns a fixed, hardcoded answer so the code compiles and runs (e.g. hal_adc_read always returns 0).
A mock is a smarter fake you can program : you tell it "when asked for channel 1, return 4095", then later check how it was called . See Mocking and Stubbing in C .
Why the topic needs this: the whole "test on host" tier only works because behind each HAL door we can swap the real register-poker for a mock we control from our test.
The parent's worked examples use two real control algorithms. You don't need to master them, but you must recognise their symbols.
A PID controller steers a measured value toward a target. Its update line uses these:
Definition PID symbols in plain words
setpoint — the value you want (target temperature 25 ).
measured — the value you have right now.
error = setpoint − measured — how far off you are.
K p , K i , K d — three tuning knobs (gains ) deciding how hard to react.
integral — a running sum of past error (memory of "we've been low for a while").
derivative — how fast the error is changing right now.
dt — the tiny time gap between two updates.
Why the topic needs this: the parent's PID test checks fabs(pid.integral) < 0.01. fabs is just absolute value (distance from zero, sign removed). The test says: after perfect tracking, the memory-of-past-error should not drift away — a pure-math property provable on the host.
The HIL example maps a throttle voltage to a PWM percentage. Two conversions appear:
Why the topic needs this: the parent asserts pwm_duty should be within 2.0 of 50.0 , and that ADC = 4095 should cap PWM at 1000 . These are the exact numbers we verify below.
Definition Real-time and deadlines
Real-time does not mean "fast" — it means "on time, every time". A response is correct only if it happens before its deadline .
T deadline — the promise: "react within this long".
T measured — the reality you actually observed on hardware.
The rule the parent gives: T measured < T deadline for every test case.
Definition The time units
1 ms (millisecond) = 1000 1 second.
1 μ s (microsecond) = 1 , 000 , 000 1 second = 1000 1 ms .
So "respond within 100 μ s " is a tenth of a millisecond — far too short to eyeball; you need an oscilloscope to see it.
Why the topic needs this: host tests mock time — they pretend time passes — so they physically cannot measure real microseconds. That gap is the entire reason HIL exists.
Definition The HIL cast of characters
DUT (Device Under Test): the real board running your firmware — the thing being tested.
Test controller : a PC running the test script that pokes the DUT and reads its answers.
Stimulus : fake sensor signals fed in (via a DAC / signal generator).
Response capture : reading the DUT's outputs back (via ADC / logic analyzer ).
The whole arrangement forms a loop : controller → stimulus → DUT → response → controller. Hence hardware-in-the-loop .
Related tools you'll meet later: Real-Time Operating Systems (RTOS) (schedules the tasks whose timing HIL checks), Fault Injection Testing (deliberately breaking inputs), and Continuous Integration for Embedded (running all this automatically).
Peripherals GPIO ADC PWM UART
Mocks and Stubs swap machinery
Interrupts and ISR timing
Real-time deadlines in us and ms
HIL testing on real board
Two-tier testing strategy
Answer each aloud before reading the parent note.
What is the difference between the host and the target? Host = the roomy computer you develop on; target = the tiny MCU your firmware finally runs on.
How many bytes is 32KB? 32 × 1024 = 32768 bytes.
What does an ADC do? Turns a real smooth voltage into a whole number the MCU can compute with.
What does a DAC do? The reverse — turns a number into a real voltage on a wire.
What is PWM in one line? Switching a pin on/off fast so the average acts like an in-between power level.
Why can't a function that reads ADC1->DR run on your laptop? There is no such register on the host; that address points to nothing.
What is a HAL? A tidy set of hal_... "doors" so code never touches registers directly, letting us swap implementations.
Mock vs stub? Stub returns a fixed hardcoded value; a mock is programmable and records how it was called.
What is fabs? Absolute value — the distance from zero, sign removed.
Does real-time mean fast? No — it means on time, before the deadline, every time.
How many microseconds are in one millisecond? 1000 .
What voltage is 50% of a 3.3V supply? 1.65 V .
What number does a 12-bit ADC give at full scale? 4095 (that is 2 12 − 1 ).
What is the DUT in a HIL rig? The Device Under Test — the real board running your firmware.