embassy-rs/embassy · error
The top value cannot be changed after the initialization.
Error message
The top value cannot be changed after the initialization.
What it means
The LPC55 PWM driver only allows setting the timer TOP (period) value during initialization; once the PWM is initialized with a TOP value, calling `configure` with a different `config.top` panics. This is because the match registers that reset the counter are latched at init and changing them at runtime is not supported by this driver implementation.
Solutions
- Keep `config.top` identical to the value used at initialization for all subsequent `configure()` calls
- Only vary `config.compare` (duty cycle) at runtime, not the period
- De-initialize and re-create the PWM instance if the period genuinely must change
- Precompute all needed frequencies at init and allocate one PWM instance per period
Example fix
// before
pwm.configure(channel, &Config { top: 10_000, compare: 5_000, .. }); // top differs from init
// after
pwm.configure(channel, &Config { top: INITIAL_TOP, compare: 5_000, .. }); // same top as at init
Defensive patterns
Strategy: validation
Validate before calling
fn assert_same_top(init_top: u32, cfg: &PwmConfig) -> Result<(), &'static str> {
if cfg.top != init_top { return Err("top must stay fixed after PWM init"); }
Ok(())
}
Prevention
- Treat config.top as immutable after Pwm::new; only change compare values at runtime
- Store the initialized top in a const and reference it in all subsequent configs
- Wrap configure() calls in a helper that enforces the invariant
When it happens
Trigger: Calling `Pwm::new(...)` first with one `config.top`, then calling `configure()` (or constructing again) with a `config.top` value different from the initial one on the same SCT instance.
Common situations: Dynamically changing PWM frequency/period at runtime for motor control or LED breathing effects; reusing a PWM instance across modules with conflicting period settings; copying example config values without realizing `top` must stay constant.
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
- baudrate is not permitted in this mode
- Requested divider is too large
- Can only take the executor once
- Invalid FilterConfig (TooManyFilters)! A FilterConfig must…
- Invalid FilterConfig (EmptyFilterConfig)! A FilterConfig…
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/309d41797110a23b.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-nxp/src/pwm/lpc55.rs:164
SCT0.config().modify(|w| {
w.set_unify(vals::Unify::UnifiedCounter);
w.set_clkmode(vals::Clkmode::SystemClockMode);
w.set_noreload_l(true);
w.set_autolimit_l(true);
});
// Before setting the match registers, we have to make sure that `compare` is lower or equal to `top`,
// otherwise the counter will not reach the match and, therefore, no events will happen.
assert!(config.compare <= config.top);
if TOP_VALUE.load(Ordering::Relaxed) == 0 {
// Match 0 will reset the timer using TOP value
SCT0.match_(0).modify(|w| {
w.set_matc_hn_l((config.top & 0xFFFF) as u16);
w.set_matc_hn_h((config.top >> 16) as u16);
});
} else {
panic!("The top value cannot be changed after the initialization.");
}
// The actual matches that are used for event logic
SCT0.match_(output_number + 1).modify(|w| {
w.set_matc_hn_l((config.compare & 0xFFFF) as u16);
w.set_matc_hn_h((config.compare >> 16) as u16);
});
SCT0.match_(15).modify(|w| {
w.set_matc_hn_l(0);
w.set_matc_hn_h(0);
});
// Event configuration
critical_section::with(|_cs| {
// If it is already set, don't change
if SCT0.ev(0).ev_ctrl().read().matchsel() != 15 {
SCT0.ev(0).ev_ctrl().modify(|w| {
w.set_matchsel(15);View on GitHub (pinned to 463a07b963)