Utilities¶
Helpers in inc/utils/ for code that uses the interfaces: what each one is for, how
to use it and what it costs.
| Header | What it is |
|---|---|
DigitalOutputGuard.h |
RAII: a BaseGpio is active while the guard lives |
SoftChipSelectGuard.h |
RAII: a software chip select asserted for one SPI frame |
HfSpscRing.h |
Lock-free single-producer, single-consumer ring for ISR-to-task hand-off |
PlatformMutex.h |
Mutex, shared mutex and lock guards with a compile-time backend |
HfTime.h |
Monotonic microsecond clock, selected per MCU |
McuSelect.h |
Target MCU, family and capability macros |
memory_utils.h |
make_unique_nothrow<T>(): allocation that returns nullptr instead of throwing |
AsciiArtGenerator.h |
Text banners for test output |
DigitalOutputGuard¶
Sets a pin to output and active on construction, inactive on destruction, including every early return. Use it for an enable line that must drop when a function exits.
hf_gpio_err_t test_with_driver_enabled(BaseGpio& driver_enable_pin) {
DigitalOutputGuard enable(driver_enable_pin); // output + active
if (!enable.IsValid()) {
return enable.GetLastError();
}
run_motor_test();
return hf_gpio_err_t::GPIO_SUCCESS;
} // inactive here, on every return path
Cost: a SetDirection() (when ensure_output_mode is true), a SetActive() and a SetInactive(); no
allocation.
SoftChipSelectGuard¶
Runs an assert callable on construction and a deassert callable on destruction; Release() deasserts early.
It only owns the chip-select edges: hold the bus lock for the whole frame yourself.
void one_frame(BaseGpio& cs_pin) {
SoftChipSelectGuard cs{[&] { (void)cs_pin.SetActive(); }, [&] { (void)cs_pin.SetInactive(); }};
// the SPI frame goes here; the caller already holds the bus lock
} // chip select released here
Cost: the two callables, inlined; no allocation.
HfSpscRing¶
One producer (usually an interrupt) calls TryPush(), one consumer calls TryPop(); neither blocks, locks or
allocates. Init(n) allocates the storage once, rounded up to a power of two (at most kMaxCapacity, 65536). A
push into a full ring drops the new element and counts it in Overruns(). EspCan uses it for received frames.
Cost: n elements of storage; each push or pop is two atomic loads and one atomic store.
PlatformMutex¶
PlatformMutex, PlatformSharedMutex, PlatformUniqueLock and PlatformSharedLock mirror the standard
library types. With no backend they compile to nothing; with a PlatformMutexBackend.h on the include path they
use the RTOS (see Architecture).
HfTime¶
hf_time::MonotonicUs() reads the platform's microsecond counter (esp_timer on ESP32), inlined, without a
virtual call. MonotonicMs() and ElapsedUs(t0) are built on it. Under HF_TARGET_MCU_NONE it returns 0;
tests that need time inject their own clock.
Next: Examples.