embassy-rs/embassy · error

USB clock should be 48Mhz but is

Error message

USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.

What it means

On all other STM32 USB parts (not H7RS/N6/U5-HS/WBA-HS), the USB clock must be 48 MHz with at most 0.25% deviation (±120 kHz), because the OTG core derives its timing directly from that clock. embassy-stm32 validates this in common_init and panics when abs_diff(freq, 48_000_000) > 120_000, since an off-spec clock breaks USB enumeration/timing.

Solutions

  1. Reconfigure RCC so the USB kernel clock is exactly 48 MHz (PLL Q divider, USBPRE/OTGFSPRE prescaler)
  2. On parts with HSI48, enable HSI48 with CRS sync to produce a USB-legal 48 MHz without a crystal
  3. Verify the actual frequency with rcc.usb().frequency() (or T::frequency()) before constructing the USB driver
  4. Start from the official embassy-stm32 USB example for your board, which has a known-good clock tree

Example fix

// before
config.pll.q = 5; // -> usb 47.5 MHz, out of 0.25% tolerance
// after
config.pll.q = 4; // -> usb 48 MHz
// sanity check before init:
assert!(rcc.usb().frequency().0.abs_diff(48_000_000) <= 120_000);
Defensive patterns

Strategy: validation

Validate before calling

let freq = <periph>::frequency();
assert!(freq.0.abs_diff(48_000_000) <= 120_000, "USB clk {} Hz outside 48MHz ±0.25%", freq.0);

Type guard

fn usb_clk_48mhz_ok(hz: u32) -> bool { hz.abs_diff(48_000_000) <= 120_000 }

Prevention

When it happens

Trigger: Initializing a USB driver on any non-exception part (e.g. F1/F4/L4/G4/H7-FS) while the USB peripheral's kernel clock is not within 47.88–48.12 MHz.

Common situations: RCC PLL tuned for 72/168 MHz sysclk where the USB prescaler yields 47 or 48.5 MHz; CRS/HSI48 not enabled on parts lacking a crystal; audio-oriented clock trees that intentionally detune USB; running on a board without an external 48 MHz-capable PLL path.

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

Appendix: source

Thrown at embassy-stm32/src/usb/mod.rs:37

    if ![16_000_000, 19_200_000, 20_000_000, 24_000_000, 26_000_000, 32_000_000].contains(&freq.0) {
        panic!(
            "USB clock should be one of 16, 19.2, 20, 24, 26, 32Mhz but is {} Hz. Please double-check your RCC settings.",
            freq.0
        )
    }

    // On the N6 the OTG core always runs off its integrated High-Speed PHY, whose reference
    // clock must be 19.2, 20 or 24 MHz (RM0486 Rev 4, USBPHYC_CR.FSEL, p. 3929). Panics
    // naming the offending frequency; the same mapping programs FSEL in `T::phy_init()`.
    #[cfg(stm32n6)]
    let _ = fsel_from_freq(freq);

    // Check frequency is within the 0.25% tolerance allowed by the spec.
    // Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user
    // has tight clock restrictions due to something else (like audio).
    #[cfg(not(any(stm32h7rs, stm32n6, all(stm32u5, peri_usb_otg_hs), all(stm32wba, peri_usb_otg_hs))))]
    if freq.0.abs_diff(48_000_000) > 120_000 {
        panic!(
            "USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.",
            freq.0
        )
    }

    #[cfg(any(stm32l4, stm32l5, stm32wb, stm32u0))]
    critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true)));

    #[cfg(pwr_h5)]
    critical_section::with(|_| crate::pac::PWR.usbscr().modify(|w| w.set_usb33sv(true)));

    #[cfg(stm32h7)]
    {
        // If true, VDD33USB is generated by internal regulator from VDD50USB
        // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo)
        // TODO: unhardcode
        let internal_regulator = false;

View on GitHub (pinned to 463a07b963)