Timing & Clocks — Time

One object, four verbs. Time covers the whole timing vocabulary: a yielding sleep, two monotonic clocks, and a spin for sub-millisecond protocol timing. Every call lowers to one Zephyr kernel call — k_msleep(), k_uptime_get(), k_busy_wait().

import { Time } from '@typecad/hal';

Sleep

Blocking sleep

Time.sleep(1000);        // k_msleep — blocks the calling thread

At the top level this blocks main, the way delay() did on Arduino. Inside a Thread it blocks that thread only — the scheduler runs everything else.

Cooperative sleep

Inside an async function, await the same call and it stops blocking: the async state machine arms a deadline and yields, so every task sharing the loop runs until the deadline passes.

async function poll() {
  while (true) {
    await Time.sleep(50);      // other tasks run during the wait
    // …read a sensor, update a display…
  }
}

Busy wait

Time.busyWaitUs(3);       // k_busy_wait — spins, no schedule point

For sub-millisecond protocol timing where a yield would break the waveform. This is the only Time call that never yields — do not use it for long waits; it holds the CPU.

Clocks

Two monotonic clocks, both counting from boot. Both return doubles, so they never wrap.

const start = Time.now();       // milliseconds since boot
const us = Time.nowUs();        // microseconds since boot

Elapsed-time arithmetic is plain subtraction — there is no unsigned wraparound to guard against:

if (Time.now() - start > 5000) {   // correct at minute 1 and month 6
  // five seconds have passed
}

Time.now() is the equivalent of Arduino’s millis(), with one deliberate difference: Arduino’s version is a uint32_t that wraps every 49.7 days and needs the (long)(now - start) subtraction trick; Time.now() is a double that holds milliseconds exactly for centuries.

ArduinoCuttlefish
millis()Time.now() — ms, monotonic, no wrap
micros()Time.nowUs() — µs, monotonic, no wrap
delay(ms)Time.sleep(ms) — yielding; await-able
delayMicroseconds(us)Time.busyWaitUs(us) — spin, no yield

Time.nowUs() is uptime-derived on every board, so its resolution is the uptime tick — one millisecond. The cycle-counter alternative reads as a constant on chips without a free-running 64-bit counter, so the uniform expression wins over per-chip precision. For sub-millisecond deterministic timing use the hardware Counter — it fires from interrupt context and does not depend on any thread.

Periodic work

Time deliberately has no callback timers. Periodic work belongs to a Thread:

import { Thread } from '@typecad/hal';

const blinker = new Thread(0, { stackKb: 2 });
blinker.start(() => {
  while (true) {
    led.toggle();
    Time.sleep(500);
  }
});

Or, when the period must be exact to the microsecond, to a hardware Counter alarm.


API Reference

MemberDescription
Time.sleep(ms)Yielding sleep (k_msleep); await-ing it inside async code is cooperative.
Time.now()Milliseconds since boot, monotonic, double — no wrap.
Time.nowUs()Microseconds since boot, double; millisecond resolution on every board.
Time.busyWaitUs(us)Spin-wait (k_busy_wait) — no yield; sub-ms protocol timing only.