Skip to content

Utility

Small helpers: wait for a condition with a timeout, read time and sleep, start a group of threads together, and turn FreeRTOS codes into text.

TestLogicWithTimeout

TestLogicWithTimeout(func, expected, timeoutMsec, checkIntervalMs) calls func() until it returns expected or timeoutMsec passes, sleeping checkIntervalMs between calls. It returns true when the value arrived. BaseThread uses it to verify starts and stops.

#include <atomic>
#include "Utility.h"

static std::atomic<bool> link_up{false};

bool WaitForLink() {
    return TestLogicWithTimeout([] { return link_up.load(); }, true,
                                /*timeoutMsec=*/2000, /*checkIntervalMs=*/10);
}

The check interval is passed to os_delay_msec() as a uint16_t. On the no-RTOS backend without an external clock, time does not move, so the loop never times out: define HF_RTOS_NONE_EXTERNAL_CLOCK in host builds that call it (see Backends).

Time

Call Header Returns
RtosTime::GetCurrentTimeUs() RtosMutex.h Microseconds since start, from the kernel tick (tick resolution) or the external clock
RtosTime::MsToTicks(ms) RtosMutex.h Ticks, at least 1 for a non-zero ms
os_get_elapsed_time_msec() OsUtility.h Milliseconds since start, as uint32_t (wraps after 49.7 days)
os_delay_msec(ms) OsUtility.h Sleeps ms (a uint16_t, at most 65 535), converted with os_convert_msec_to_delay_ticks(): at least one tick for a non-zero ms, so 1 ms on a 100 Hz tick sleeps 10 ms, never 0
os_convert_msec_to_delay_ticks(ms) OsUtility.h Ticks, rounded down, at least 1 for a non-zero ms; 0 for 0
os_convert_hz_to_delay_ticks(hz) OsUtility.h Period in ticks, rounded down, at least 1 for a non-zero hz; 0 for 0
os_delay_time(t, unit) OsUtility.h Sleeps t in TIME_UNIT_US, TIME_UNIT_MS or TIME_UNIT_S
#include <cstdint>
#include "OsUtility.h"
#include "RtosMutex.h"

uint64_t MeasureUs() {
    const uint64_t t0 = RtosTime::GetCurrentTimeUs();
    os_delay_msec(5);
    return RtosTime::GetCurrentTimeUs() - t0;
}

BaseThreadsManager

Starts and stops a set of BaseThread objects together, named by an enum, with or without waiting for each to confirm. The enum must be unscoped (enum, not enum class) because its count also sizes a std::bitset.

#include <map>
#include "BaseThread.h"
#include "BaseThreadsManager.h"

enum AppThread : int { kControl, kTelemetry, kAppThreadCount };  // unscoped: also the bitset size

static const char* AppThreadName(AppThread t) {
    return t == kControl ? "control" : "telemetry";
}

class AppThreads : public BaseThreadsManager<AppThread, kAppThreadCount> {
public:
    using BaseThreadsManager::BaseThreadsManager;
};

bool StartApp(BaseThread& control, BaseThread& telemetry) {
    static AppThreads threads({{kControl, &control}, {kTelemetry, &telemetry}}, AppThreadName);
    return threads.EnsureInitialized() && threads.StartAllAndWaitToVerify(/*waitToVerifyTimeoutMsec=*/1000);
}
Group Calls
Start StartAll(), StartSelected(list), StartAllExceptSelected(list), and each ...AndWaitToVerify(..., ms)
Stop StopAll(), StopSelected(list), StopAllExceptSelected(list), and each ...AndWaitToVerify(..., ms)
Resume ResumeAll(), ResumeSelected(list)
Hooks PreThreadInitializationActions(), PostThreadInitializationActions() (virtual, override in a subclass)

The registry is a std::map copied from the constructor argument, so it allocates on the heap once; an internal Mutex guards it.

FreeRTOSUtils

freertos_ret_to_string(BaseType_t) and freertos_task_state_to_string(eTaskState) return a name for a FreeRTOS return code or task state, for logs. Without FreeRTOS they return "N/A (no RTOS)". Compile src/FreeRTOSUtils.c with the FreeRTOS backend.

Next: API reference.