Skip to content

Platform overview

The layers of the HardFOC platform, the repository that holds each one, and the path one call takes from a manager to the chip.

Layer diagram of the HardFOC platform. At the bottom, the MCU SDK and RTOS and the device driver repositories. Above them hf-core-drivers (drivers, interface wrap, pin config) and hf-core-utils (RTOS wrapper, general and CANopen utilities). On those, the hf-core handlers. Above the handlers, the board HALs, and at the top the products.Layer diagram of the HardFOC platform. At the bottom, the MCU SDK and RTOS and the device driver repositories. Above them hf-core-drivers (drivers, interface wrap, pin config) and hf-core-utils (RTOS wrapper, general and CANopen utilities). On those, the hf-core handlers. Above the handlers, the board HALs, and at the top the products.
Where hf-core sits
Figure description and sources

From the bottom up. Each device driver is its own repository and talks to its chip through a small CRTP interface; hf-core-drivers collects them as submodules, together with hf-internal-interface-wrap (the Base* interfaces and their ESP32 and STM32 implementations) and hf-pincfg. hf-core-utils collects the RTOS wrapper, the general utilities and the CANopen helpers. hf-core's handlers sit on top of those three: each one wraps one driver behind a thread-safe API that takes Base* interfaces. A board HAL creates the MCU peripherals, constructs the handlers it needs and exposes managers; a product is firmware on one board HAL. The driver list is read from cmake/hf_core_build_settings.cmake and the handler list from handlers/.

Drawn from: cmake/hf_core_build_settings.cmake driver and utility paths; handlers/ handler folders; figure module docs/diagrams/figures/platform-layers.py.

Layers

Layer Repository What it holds Knows the MCU?
Product product firmware repositories Tasks, control loops, protocols No
Board HAL hf-hal-vortex-v1, hf-hal-flux-v1 Creates the buses and pins, constructs the handlers, exposes managers Yes, in its platform files only
Handlers hf-core handlers/ One thread-safe class per device family over Base* interfaces No
Device drivers hf-core-drivers external/ One repository per chip, CRTP interface, no heap No
Interfaces hf-internal-interface-wrap (in hf-core-drivers internal/) BaseSpi, BaseI2c, BaseUart, BaseCan, BaseGpio, BaseAdc, ... and their Esp* / Stm* implementations Only the implementations
Pin maps hf-pincfg (in hf-core-drivers internal/) Functional pin tables per board Board-specific
Utilities hf-core-utils: hf-utils-rtos-wrap, hf-utils-general, hf-utils-canopen RTOS wrapper, containers and filters, CANopen frames Only the RTOS backends

hf-core pins one commit of hf-core-drivers and one of hf-core-utils as submodules, so a HAL that pins hf-core gets a set of drivers, interfaces and utilities that were built and tested together.

One call, through the layers

A HAL manager reads the rotor angle from the AS5047U. Only the last step knows it runs on an ESP32.

sequenceDiagram
  participant M as HAL manager
  participant H as As5047uHandler
  participant D as AS5047U driver
  participant A as As5047uSpiAdapter
  participant S as BaseSpi (EspSpi on ESP32)
  M->>H: visitDriver([](auto& d) { return d.GetAngle(); })
  H->>H: lock the handler mutex, EnsureInitialized()
  H->>D: GetAngle()
  D->>A: transfer(tx, rx, len), static dispatch
  A->>S: Transfer(tx, rx, len)
  S-->>D: frame with CRC (retried on a CRC error)
  D-->>M: 14-bit angle, mutex released

Design choices

Choice Why
Drivers are CRTP templates, handlers are plain classes The driver dispatch costs nothing; the handler gives HAL code one non-template type per chip
Handlers take Base* references and never own them The HAL decides bus sharing and lifetime; a test passes a mock
Lazy initialisation Constructing a handler never touches the bus, so construction order does not matter at boot
One recursive mutex per handler, optional shared bus mutex Methods may call each other; devices on one bus can share a lock
Raw function pointers with void* user_data for low-level callbacks No std::function and no heap on interrupt paths
The host build is part of CI It is the proof that everything above the interfaces is free of vendor headers

Source map

Path Contents
handlers/<chip>/ One handler: its CRTP adapter(s), the handler class, wrappers that expose it as BaseGpio, BaseAdc, BaseTemperature
handlers/common/ HandlerCommon.h (delays, log routing), PlatformMutexBackend.h, the CANopen bridge
handlers/logger/ The Logger singleton and the per-MCU backend factories
cmake/hf_core_build_settings.cmake The build entry point for every consumer
CMakeLists.txt, CMakePresets.json The host build
tests/host/ Host unit tests, mocks (mocks/) and the test harness (support/)
tests/docs/ The code on these pages, compiled and run by the host build
examples/esp32/ ESP32 test applications, one per handler or utility
hf-core-drivers/, hf-core-utils/ Submodules

Next: Handler model.