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

  1. Keep `config.top` identical to the value used at initialization for all subsequent `configure()` calls
  2. Only vary `config.compare` (duty cycle) at runtime, not the period
  3. De-initialize and re-create the PWM instance if the period genuinely must change
  4. 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

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


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)