Driver & HAL Design

Layer firmware so application code never touches registers — and so it can be unit-tested on a PC.

As firmware grows, register accesses scattered through the application become a liability: porting to another chip means rewriting everything, and nothing can be tested without hardware. The answer is layering.

Polling, interrupts or DMA

app.c: USART2->DR = c;

app.c: uart_write(&console, &c, 1);

Register access in application code ties the whole program to one chip and makes host-side testing impossible.

When would you choose DMA over interrupt-driven transfers?

For large or continuous data (audio, ADC sampling, display updates, fast UART) where an interrupt per byte would consume too much CPU time.

How does an ops table make firmware unit-testable?

Tests can pass a mock implementation of the same struct, so application logic runs on a PC without the hardware.