embassy-rs/embassy · error

Invalid prescaler: for timer frequency: Hz

Error message

Invalid prescaler: {} for timer frequency: {}Hz

What it means

The LPTIM-based time driver computes the prescaler needed so timer ticks match TICK_HZ; it only supports division factors 1,2,4,8,16 (mapped to LPTIM Presc values). If the required division for the given timer frequency isn't one of these, init_timer panics with the prescaler and timer frequency.

Solutions

  1. Choose a TICK_HZ that divides timer_freq by exactly 1/2/4/8/16 (e.g. with 32 kHz LSE use TICK_HZ 1024 or 2048).
  2. Change the LPTIM clock source/frequency so the ratio becomes a supported power of two.
  3. Adjust the TICK_HZ feature/configuration at build time to match your timer clock.

Example fix

// before (LSE 32768 Hz)
tick_hz = 1000
// after
tick_hz = 1024 // 32768 / 32 is not supported; 32768/32 no — use power-of-two ratio ≤16:
// e.g. tick_hz = 2048 (32768/16) or raise LPTIM clock to 16 MHz and keep 1000 only if ratio fits
Defensive patterns

Strategy: validation

Validate before calling

const TICK_HZ: u32 = 1024; // ensure timer_freq / TICK_HZ is exactly 1, 2, 4, 8, or 16
fn prescaler_valid(timer_freq: u32, tick_hz: u32) -> bool {
    matches!(timer_freq / tick_hz, 1 | 2 | 4 | 8 | 16)
}

Prevention

When it happens

Trigger: Setting TICK_HZ such that timer_freq / TICK_HZ is not exactly 1, 2, 4, 8, or 16 (e.g. timer clock 32768 Hz with TICK_HZ=1000 → ratio not a supported power of two).

Common situations: Custom TICK_HZ in a build.rs/feature config that doesn't divide the LPTIM clock; boards using an unusual LPTIM clock source (e.g. LSE 32768 Hz) with a millisecond tick.

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/d861779b231482cf. Report an issue: GitHub.

Appendix: source

Thrown at embassy-stm32/src/time_driver/lptim.rs:147

        rcc::enable_and_reset_with_cs::<T>(cs);

        let timer_freq = T::frequency();

        r.cnt().write(|w| w.set_cnt(0));

        // let psc = timer_freq.0 / TICK_HZ as u32 - 1;
        let psc = timer_freq.0 / TICK_HZ as u32;
        let psc = match psc {
            128 => vals::Presc::Div128,
            64 => vals::Presc::Div64,
            32 => vals::Presc::Div32,
            16 => vals::Presc::Div16,
            8 => vals::Presc::Div8,
            4 => vals::Presc::Div4,
            2 => vals::Presc::Div2,
            1 => vals::Presc::Div1,
            // TODO: we could compute the valid TICK_HZ for the valid prescalers to include in the panic message
            _ => panic!("Invalid prescaler: {} for timer frequency: {}Hz", psc, timer_freq.0),
        };

        trace!(
            "init: setting presc: {} timer_freq: {}Hz TICK_HZ: {}",
            psc, timer_freq, TICK_HZ
        );
        r.cfgr().modify(|w| w.set_presc(psc));

        // RM says timer must be enabled before setting arr or cmp
        r.cr().modify(|w| w.set_enable(true));
        trace!("init: arr: {:?}", r.arr().read());
        // TRM says this is updated immediately if the timer is not started so no need to check for arrok! (stm32wl5 & stm32wle)
        r.arr().write(|w| w.set_arr(u16::MAX));

        // Enable overflow interrupts
        ier_set_ueie(T::regs(), true);

        <T as crate::lptim::SealedBasicInstance>::GlobalInterrupt::unpend();

View on GitHub (pinned to 463a07b963)