Threading¶
What you can safely call from more than one FreeRTOS task. The HAL creates no tasks of its own: every call runs in the task that makes it, and periodic work (such as LED animations) is driven by your code.
Locks¶
Each manager has one RtosMutex (from hf-utils-rtos-wrap). It is a recursive mutex, so a manager method
that holds the lock may call another method of the same manager.
| Manager | EnsureInitialized() |
Operations |
|---|---|---|
CommChannelsManager |
Locked (double-checked) | AcquireUart(), GetSystemDiagnostics() and Deinitialize() lock; GetSpiDevice(), GetCanBus(), GetUart() and the USB serial accessors return pointers without locking |
NvsManager |
Not locked | The BaseNvs objects do their own work; no manager-level lock |
GpioManager |
Locked (double-checked) | Not locked: Set(), Read(), Toggle() and the other per-pin calls go straight to the pin |
ValveManager |
Locked (double-checked) | Locked: every channel, configuration and fault call |
AdcManager |
Locked (double-checked) | Locked: each read |
TemperatureManager |
Locked (double-checked) | Locked: each read |
LedManager |
Locked (double-checked) | Locked: colour, brightness and animation calls |
Flux facade |
Not locked | Diagnostics read atomics |
Initialisation flags are std::atomic<bool>, read with acquire and written with release ordering.
What that means for your code¶
- Initialise from one task.
Flux::EnsureInitialized()andNvsManager::EnsureInitialized()have no lock; callFlux::EnsureInitialized()once fromapp_main()before you start other tasks. - Valves, ADC, temperature and LED calls may be made from several tasks; the manager serialises them. A call can block while another task holds the manager's lock (for example during an SPI transfer).
- GPIO: give each pin one owner task, or add your own lock. Two tasks driving the same pin through
GpioManagerare not serialised, and the manager's operation counters are plain integers. - Borrowed bus pointers (
GetSpiDevice(),GetCanBus(),AcquireUart()) are shared objects. The valve drivers already use the valve SPI bus; if you talk to a device yourself, keep to one task per device. - LED animations advance only when you call
flux.leds.UpdateAnimation(); call it from a periodic task (the animation step is 50 ms).
CPU affinity¶
FluxCpuAffinity.h names the logical cores your tasks should use on the
dual-core ESP32-S3: flux_hal::kFluxCpuHostIo (0) for host, system and communication work and
flux_hal::kFluxCpuRealtime (1) for high-rate loops such as pressure control. kFluxCpuNoAffinity (-1) lets the
scheduler choose. Pass them to BaseThread or os_thread_create_pinned.