Project 10: Cardputer Firmware Architecture Blueprint (HAL -> Drivers -> Services -> UI -> App)
Build a production-style firmware architecture with strict boundaries, deterministic task communication, and maintainable feature scaling.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Expert |
| Time Estimate | 2-3 weeks |
| Main Programming Language | C |
| Alternative Programming Languages | C++, Rust |
| Coolness Level | Level 4 |
| Business Potential | Level 4 |
| Prerequisites | FreeRTOS basics, modular C design |
| Key Topics | Layered architecture, state machines, memory policy, watchdog strategy |
1. Learning Objectives
- Enforce dependency direction from app down to HAL.
- Choose RTOS primitives intentionally by data-flow shape.
- Prevent priority inversion and cross-layer coupling.
- Model UI with hierarchical state transitions.
2. All Theory Needed (Per-Concept Breakdown)
2.1 Layered Embedded Architecture
Fundamentals Layering protects maintainability. Hardware specifics must remain behind drivers and HAL contracts, while app logic consumes stable service interfaces.
Deep Dive into the concept Embedded architecture fails when every feature touches every layer. A disciplined design limits each module to one responsibility and one direction of dependency. This reduces regressions, enables targeted tests, and makes parallel development possible. For Cardputer, the architecture must also account for constrained memory and real-time responsiveness. Interface design should include ownership, lifetime, and failure behavior.
2.2 FreeRTOS Communication and State Modeling
- Queues: structured messages and ownership transfer.
- Stream/ring buffers: high-rate byte flows.
- Event groups: coarse synchronization, not payload transport.
- Hierarchical state machines: deterministic UI navigation.
3. Project Specification
3.1 What You Will Build
A reusable firmware scaffold with:
- HAL, drivers, services, UI, app module boundaries.
- Module contracts and lifecycle hooks.
- Task watchdog registration and fault reporting.
- State machine-driven app navigation.
3.2 Functional Requirements
- Every module compiles behind its interface boundary.
- Task communication paths are documented and bounded.
- UI routing is state-machine based.
- Watchdog faults map to actionable logs.
3.3 Example Output
I arch: HAL init OK
I arch: service bus ready
I arch: ui state Home -> Tools -> Analyzer
I arch: watchdog healthy tasks=7
I arch: heap fragmentation index=0.06
4. Solution Architecture
[App Layer]
|
[UI Layer]
|
[Services]
|
[Drivers]
|
[HAL]
5. Implementation Guide
5.1 Core Question
“How do I scale firmware features without turning the codebase into a tightly-coupled maze?”
5.2 Design Questions
- Which layer owns hardware error translation?
- Where should retries and backoff logic live?
- Which allocations must be static?
5.3 Minimal Concrete Example
Pseudo-contract:
display_service.draw_frame(frame_id, region_list)
input_service.read_event(timeout_ms)
storage_service.atomic_commit(record)
6. Testing Strategy
- Unit: interface contract tests.
- Integration: cross-layer scenarios.
- Soak: mode switching and memory stability.
7. Common Pitfalls
- Layer bypass for “quick fixes”.
- Mutex misuse causing priority inversion.
- Hidden dynamic allocation in hot paths.
8. Extensions
- Plug-in app registry loader from SD manifest.
- Dynamic feature flags with safe fallbacks.
9. Self-Assessment
- I can explain each layer’s responsibilities.
- I can justify every RTOS primitive used.
- I can add a new feature without touching unrelated layers.