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
- Call `timer.stop()` (or the driver's stop method) before `set_counter`, then restart it afterwards
- Route time updates through a helper that stops, sets, and restarts the timer atomically
- 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
- Always stop the aon timer before set_counter/set_datetime
- Wrap stop/set/start in one helper so ordering cannot drift
- Call is_running() defensively before time updates
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
- Clocks have not been initialized
- Clocks initialized, but clk_16k_vsys not active
- Failed to configure PLL_SYS
- Failed to configure PLL_USB
- DMA: error on DMA_0 channel
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)