embassy-rs/embassy · error

psc division overflow

Error message

psc division overflow: {}

What it means

The TIM-based time driver computes psc = timer_freq/TICK_HZ - 1 and stores it in the 16-bit PSC register. If the computed value doesn't fit in u16 (timer clock too fast relative to TICK_HZ, or TICK_HZ = 0 causing underflow/overflow), init_timer panics.

Solutions

  1. Increase TICK_HZ so timer_freq/TICK_HZ - 1 fits in a u16 (≤ 65536 division).
  2. Lower the timer input clock via RCC prescalers if you need a very coarse tick.
  3. Fix TICK_HZ so it is nonzero and sensible (typically 1000).

Example fix

// before (timer_freq = 500 MHz, TICK_HZ = 1000 -> psc = 499999, > u16::MAX)
tick_hz = 1000
// after (enable APB prescaler so timer_freq = 64 MHz, psc = 63999 fits u16)
// or choose tick_hz such that timer_freq/tick_hz <= 65536
Defensive patterns

Strategy: validation

Validate before calling

fn psc_fits(timer_freq: u32, tick_hz: u32) -> bool {
    tick_hz > 0 && (timer_freq / tick_hz).saturating_sub(1) <= u16::MAX as u32
}

Prevention

When it happens

Trigger: timer_freq / TICK_HZ - 1 > 65535 (e.g. very high timer clock with very low TICK_HZ), or TICK_HZ of 0 making the subtraction underflow on u32.

Common situations: Setting an extremely low TICK_HZ with a high-speed timer clock; typo making TICK_HZ 0; running a 480 MHz timer clock with 1 Hz ticks.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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

Appendix: source

Thrown at embassy-stm32/src/time_driver/tim.rs:159

    queue: Mutex::new(RefCell::new(Queue::new()))
});

impl RtcDriver {
    /// initialize the timer, but don't start it.  Used for chips like stm32wle5
    /// for low power where the timer config is lost in STOP2.
    pub(crate) fn init_timer(&'static self, cs: critical_section::CriticalSection) {
        let r = regs_gp16();

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

        let timer_freq = T::frequency();

        r.cr1().modify(|w| w.set_cen(false));
        write_cnt(0);

        let psc = timer_freq.0 / TICK_HZ as u32 - 1;
        let psc: u16 = match psc.try_into() {
            Err(_) => panic!("psc division overflow: {}", psc),
            Ok(n) => n,
        };

        r.psc().write_value(psc);
        write_arr(Counter::MAX);

        // Set URS, generate update and clear URS
        r.cr1().modify(|w| w.set_urs(vals::Urs::CounterOnly));
        r.egr().write(|w| w.set_ug(true));
        r.cr1().modify(|w| w.set_urs(vals::Urs::AnyEvent));

        // Mid-way point
        write_ccr(0, HALF_COUNTER);

        // Enable overflow and half-overflow interrupts
        r.dier().write(|w| {
            w.set_uie(true);
            w.set_ccie(0, true);

View on GitHub (pinned to 463a07b963)