embassy-rs/embassy · error

PLL is unavailable in voltage range 2

Error message

PLL is unavailable in voltage range 2

What it means

While configuring the PLL, init_pll detects that the core voltage range is set to range 2; per the reference manual (RM0515 §12.4.3-12.4.5 limits checked around this code) the PLL may only run in voltage range 1, so its configuration is rejected. The faulting input is the combination of a PLL-based clock config with a VOS/range-2 low-power setting.

Solutions

  1. Configure the regulator to VoltageScale::Range1 when using the PLL.
  2. Remove the PLL from the clock config and derive sysclk directly from HSI/HSE if Range2 must be kept.
  3. Check that sysclk <= 100 MHz (typical Range1 constraint) so Range1 is valid for your application.

Example fix

// before
Config { voltage_scale: VoltageScale::Range2, clocks: Clocks { pll: Some(..), .. } }
// after
Config { voltage_scale: VoltageScale::Range1, clocks: Clocks { pll: Some(..), .. } }
Defensive patterns

Strategy: validation

Validate before calling

if let Some(_pll) = &config.clocks.pll { assert!(config.voltage_scale == VoltageScale::Range1, "PLL requires voltage Range1"); }

Type guard

fn pll_allowed(vs: VoltageScale) -> bool { matches!(vs, VoltageScale::Range1) }

Prevention

When it happens

Trigger: Running rcc init with voltage_scale = VoltageScale::Range2 while also requesting a PLL config (Some(pll)).

Common situations: Low-power configurations that drop to Range2 for lower voltage while still needing a PLL-derived clock; copying a PLL setup from another board that runs in Range1.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at embassy-stm32/src/rcc/wba.rs:518

    // Only divide by the HSE prescaler when the PLL source is HSE
    let src_freq = match pll.source {
        PllSource::Hse => {
            // read the prescaler bits and divide
            let hsepre = RCC.cr().read().hsepre();
            pre_src_freq / hsepre
        }
        _ => pre_src_freq,
    };

    // Calculate the reference clock, which is the source divided by m
    let ref_freq = src_freq / pll.prediv;
    // Check limits per RM0515 § 12.4.3
    assert!(Hertz::mhz(4) <= ref_freq && ref_freq <= Hertz::mhz(16));

    // Check PLL clocks per RM0515 § 12.4.5
    let (vco_min, vco_max, out_max) = match voltage_range {
        VoltageScale::Range1 => (Hertz::mhz(128), Hertz::mhz(544), Hertz::mhz(100)),
        VoltageScale::Range2 => panic!("PLL is unavailable in voltage range 2"),
    };

    // Calculate the PLL VCO clock
    // let vco_freq = ref_freq * pll.mul;
    // Calculate VCO frequency including fractional part: FVCO = Fref_ck × (N + FRAC/2^13)
    let numerator = (ref_freq.0 as u64) * (((pll.mul as u64) + 1 << 13) + pll.frac.unwrap_or(0) as u64);
    let vco_hz = (numerator >> 13) as u32;
    let vco_freq = Hertz(vco_hz);
    assert!(vco_freq >= vco_min && vco_freq <= vco_max);

    // Calculate output clocks.
    let p = pll.divp.map(|div| vco_freq / div);
    let q = pll.divq.map(|div| vco_freq / div);
    let r = pll.divr.map(|div| vco_freq / div);
    for freq in [p, q, r] {
        if let Some(freq) = freq {
            assert!(freq <= out_max);
        }

View on GitHub (pinned to 463a07b963)