embassy-rs/embassy · error

timer must be stopped before setting counter

Error message

timer must be stopped before setting counter

What it means

`AonTimer::set_counter` on RP2040/RP2350 writes the POWMAN time registers directly, which the hardware only permits while the always-on timer is halted. The driver guards this and panics if `is_running()` returns true. The doc comment explicitly requires stopping the timer first.

Solutions

  1. Call `timer.stop()` (or the driver's stop method) before `set_counter`, then restart it afterwards
  2. Route time updates through a helper that stops, sets, and restarts the timer atomically
  3. Check `is_running()` before calling `set_counter` if the timer's state is uncertain at that call site

Example fix

// before
timer.set_counter(ms_since_epoch); // panics: timer running
// after
timer.stop();
timer.set_counter(ms_since_epoch);
timer.start();
Defensive patterns

Strategy: validation

Validate before calling

fn set_counter_safe(timer: &mut AonTimer, value_ms: u64) {
    if timer.is_running() { timer.stop(); }
    timer.set_counter(value_ms);
    timer.start();
}

Prevention

When it happens

Trigger: Calling `set_counter(value_ms)` (directly or via `set_datetime`) while the always-on timer is still running, e.g. setting the RTC datetime right after boot without stopping the timer, or updating the clock during operation.

Common situations: Initializing the RTC to epoch time at startup before stopping the timer; synchronizing time from NTP while the timer ticks; calling `set_datetime` on an already-running timer in a time-update routine.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10). Data as JSON: /api/errors/fc76a90c3303f0ef. Report an issue: GitHub.

Appendix: source

Thrown at embassy-rp/src/aon_timer/mod.rs:391

            let upper2 = powman.read_time_upper().read();

            // If upper didn't change, we got a consistent read
            if upper1 == upper2 {
                return ((upper1 as u64) << 32) | (lower as u64);
            }
            // Otherwise retry (rollover occurred)
        }
    }

    /// Set the counter value in milliseconds
    ///
    /// This allows you to initialize the counter to any value (e.g., milliseconds since epoch,
    /// or 0 to start counting from boot).
    ///
    /// Note: Timer must be stopped before calling this function.
    pub fn set_counter(&mut self, value_ms: u64) {
        if self.is_running() {
            panic!("timer must be stopped before setting counter");
        }
        let powman = pac::POWMAN;
        powman.set_time_15to0().write(|w| {
            w.0 = ((value_ms & 0xFFFF) as u32) | POWMAN_PASSWORD;
        });
        powman.set_time_31to16().write(|w| {
            w.0 = (((value_ms >> 16) & 0xFFFF) as u32) | POWMAN_PASSWORD;
        });
        powman.set_time_47to32().write(|w| {
            w.0 = (((value_ms >> 32) & 0xFFFF) as u32) | POWMAN_PASSWORD;
        });
        powman.set_time_63to48().write(|w| {
            w.0 = (((value_ms >> 48) & 0xFFFF) as u32) | POWMAN_PASSWORD;
        });
    }

    /// Set an alarm for a specific counter value (in milliseconds)
    ///

View on GitHub (pinned to 463a07b963)