9. Firmware architecture and testing¶
Time: 3 to 4 weeks · Board: STM32 Nucleo (and your laptop) · Prerequisites: Module 8
Why this matters¶
Everything so far was a few hundred lines that you held in your head. Products are tens of thousands of lines written by several people over years, and the difference between a codebase that survives that and one that collapses is structure: layers with clear boundaries, state machines instead of flag soup, buffers that do not lose data, tests that run on a laptop in seconds, and a coding standard so the code looks like one person wrote it. This is the module hiring managers care about most.
You will be able to¶
- Split firmware into hardware, HAL, driver, and application layers and explain what each may and may not know about.
- Write a driver from a datasheet with a clean interface that can be tested without the hardware.
- Implement a state machine and a ring buffer correctly, and know when to use event-driven design.
- Unit test firmware on your laptop with test doubles for the hardware, and run those tests in CI.
- Follow a coding standard and explain why MISRA C exists.
- Describe how a bootloader and a firmware update work.
Learn¶
Architecture and patterns¶
- Book Paid Making Embedded Systems, 2nd Edition by Elecia White ~15 h
The book for this module and the one we would recommend if you buy only one. Architecture, drivers, state machines, debugging, testing, and shipping, from someone who has done it many times. Companion code on GitHub. Libraries often have it; the O'Reilly platform has a free trial. - Article Peripherals and Device Drivers by Michael Barr, from Programming Embedded Systems ~45 min
The classic recipe for a driver: register interface, state, and API. Free online. - Article Important Programming Concepts, Part V: State Machines by Jason Sachs ~45 min
Read this again now that you have something to apply it to. - Video State Machines by Miro Samek ~3 h
From if-else soup to proper state machines to hierarchical ones. From the author of the standard book on the topic. - Video Event-Driven Programming, Part 1 and Part 2 by Miro Samek ~1 h
Why "run to completion" event handlers beat blocking code, and how that shapes a whole system. - Article Creating a Circular Buffer in C and C++ by Phillip Johnston ~45 min
The data structure under every UART driver, with the thread-safety subtleties explained. - Video What is a BSP (Board Support Package)? ~10 min
The layer between "this chip" and "this board," and why products have one. - Article Using Asserts in Embedded Systems by Memfault Interrupt ~30 min
Cheap, and they find bugs weeks earlier.
Standards and quality¶
- Book Embedded C Coding Standard (BARR-C) by Michael Barr ~2 h, free PDF
A readable, free coding standard aimed at avoiding bugs. Adopt it for your projects until your team gives you theirs. - Video An Introduction to MISRA C ~30 min
What MISRA is, why safety-critical industries require it, and what "mandatory, required, advisory" mean. The current standard is MISRA C:2023. - Article Efficient C Code for 8-bit Microcontrollers and Optimizing Embedded Code for Size and Speed by Barr Group ~45 min
Measure first, then the handful of techniques that actually help.
Testing¶
- Article Embedded C/C++ Unit Testing Basics by Memfault Interrupt ~1 h
Building firmware modules on the host with CppUTest, and faking the hardware. Follow along. - Tool Unity and Ceedling by ThrowTheSwitch Setup ~1 h
The most common C unit test framework in firmware, with a mock generator (CMock). Alternative: CppUTest. - Video Embedded Software Testing by Phil Koopman, CMU ~1.5 h
Test plans, coverage, and why "it worked on my desk" is not a test strategy, from a safety expert. - Book Paid Test-Driven Development for Embedded C by James Grenning
The book that convinced firmware engineers TDD applies to them. Read chapters 1 to 5 if you can get it.
Shipping¶
- Article Device Firmware Update Cookbook by Memfault Interrupt ~1 h
Bootloader, application slots, image headers, and rollback. How products update themselves without bricking. - Article Building a Tiny CLI Shell for Tiny Firmware by Memfault Interrupt ~45 min
A command line over UART is the most useful debugging and factory-test tool you can add to any product. - Docs Semantic Versioning and Doxygen ~30 min
Version numbers that mean something, and API docs generated from comments.
Do¶
Build these as one growing project on the Nucleo. It becomes the base for Module 10 and your capstone.
- Checkpoint 9.1: Layer it. Restructure your Module 8 code into folders:
hw/(register access, vendor HAL),drivers/(UART, I2C, your sensor),app/(the logic), andbsp/(which pin is which on this board). The rule:app/includes nothing fromhw/. Draw the dependency diagram in your README. - Checkpoint 9.2: Ring buffer and interrupt-driven UART. Write a ring buffer module with a unit test that runs on your laptop and covers empty, full, wrap-around, and overflow. Then use two of them for interrupt-driven UART TX and RX on the Nucleo. Blast data at it faster than the app consumes and prove nothing is silently lost (or that overflow is detected).
- Checkpoint 9.3: State machine. Implement a small product behavior as an explicit state machine: for example, a device that is IDLE, then SAMPLING when a button is pressed, ALERTING when the sensor crosses a threshold, and back to IDLE after a timeout. Events come from a queue; no blocking anywhere. Draw the state diagram first. Unit test the state machine on the host with fake events.
- Checkpoint 9.4: Test the driver without the hardware. Give your sensor driver a small interface for "read register" and "write register." In the unit test, replace it with a fake that returns canned bytes, and test that the driver decodes temperature correctly, handles a NACK, and rejects a wrong chip ID. Run the tests with one command.
- Checkpoint 9.5: CI and standards. Put the host tests in GitHub Actions so every push runs them and cross-compiles the firmware. Add a formatter configuration and apply BARR-C rules you can automate. Add a CLI with
version,sensor read, andrebootcommands. Tag a release with a semantic version that the firmware reports over the CLI.
Check yourself¶
- Why should the application layer not include the vendor's HAL header? What does it cost you when it does?
- Explain how a single-producer, single-consumer ring buffer can be safe between an ISR and
main()without disabling interrupts. What assumption makes it work? - What is wrong with a
switchon a state variable where each case has nestedifs on flags? What does a table-driven or hierarchical state machine buy you? - How do you unit test a function that writes to a hardware register?
- A firmware update loses power halfway. Describe what a well-designed bootloader does on next boot.
Go deeper¶
Optional extras
- Book Practical UML Statecharts in C/C++, 2nd ed. by Miro Samek Free PDF
The full treatment of hierarchical state machines and active objects. - Book Programming Embedded Systems by Michael Barr and Anthony Massa Free online chapters
The 2006 classic, free chapter by chapter. Dated examples, timeless structure. - Video Circular Buffer Implementation in C and Producer Consumer Pattern ~30 min
Two short videos if the articles were not enough. - Article Secure Firmware Updates with Code Signing by Memfault Interrupt ~1 h
The next step after the DFU cookbook. Returns in Module 14. - Community Embedded Artistry and Beningo Embedded Group Ongoing
Two practitioners who write constantly about firmware architecture, process, and quality.