embassy-rs/embassy · error

USB clock should be one of 16, 19.2, 20, 24, 26, 32Mhz but…

Error message

USB clock should be one of 16, 19.2, 20, 24, 26, 32Mhz but is {} Hz. Please double-check your RCC settings.

What it means

On H7RS, U5 (OTG_HS), and WBA (OTG_HS) parts the USBPHYC embeds a PLL that must be fed one of a fixed set of reference frequencies (16, 19.2, 20, 24, 26, or 32 MHz) to internally generate 48/60 MHz for OTG_FS/HS. embassy-stm32 checks the configured peripheral clock at common_init and panics if it is outside that set, because the PHY PLL cannot lock to any other input.

Solutions

  1. Adjust the RCC configuration so the USB peripheral clock is exactly one of 16, 19.2, 20, 24, 26, or 32 MHz (typically via a dedicated PLL or the CRS/PLL3 output)
  2. Print/inspect rcc.usb().frequency() (or the kernel clock mux settings) before initializing USB to confirm the value
  3. Use embassy-stm32's clock configuration examples for your exact chip as the starting point
  4. On U5/WBA, if you only need FS, use the OTG_FS instance instead, which does not go through this HS-PHY PLL check

Example fix

// before: PLL3 configured so usb clock = 48_000_000 (valid on F4, invalid on H7RS/U5/WBA HS)
config.pll3 = Some(Pll { divq: ..., /* -> 48 MHz */ .. });
// after
config.pll3 = Some(Pll { divq: ..., /* -> 32_000_000 */ .. });
// then verify: assert!([16,19.2,20,24,26,32].contains(&(freq/1_000_000)))
Defensive patterns

Strategy: validation

Validate before calling

let freq = <periph>::frequency();
assert!([16_000_000u32, 19_200_000, 20_000_000, 24_000_000, 26_000_000, 32_000_000].contains(&freq.0), "USB clk {} Hz not PHY-PLL legal", freq.0);

Type guard

fn usb_clk_phy_legal(hz: u32) -> bool { [16_000_000, 19_200_000, 20_000_000, 24_000_000, 26_000_000, 32_000_000].contains(&hz) }

Prevention

When it happens

Trigger: Initializing any embassy USB driver on stm32h7rs, stm32u5 with OTG_HS, or stm32wba with OTG_HS while RCC::frequency() for the USB peripheral returns a value not in {16, 19.2, 20, 24, 26, 32} MHz.

Common situations: Default or copied RCC clock trees that yield e.g. 48 MHz or 40 MHz to the USB peripheral; changing sysclk/PLL settings for performance and silently changing the USB kernel clock; porting code from F4/H7 (which need 48 MHz) to U5/H7RS/WBA (which need a PHY-PLL-legal frequency).

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

Appendix: source

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

#[cfg_attr(usb, path = "usb.rs")]
#[cfg_attr(otg, path = "otg.rs")]
mod _version;
pub use _version::*;

use crate::interrupt::typelevel::Interrupt;
use crate::rcc;

/// clock, power initialization stuff that's common for USB and OTG.
fn common_init<T: Instance>() {
    // Check the USB clock is enabled and running at exactly 48 MHz.
    // frequency() will panic if not enabled
    let freq = T::frequency();

    // On the H7RS, the USBPHYC embeds a PLL accepting one of the input frequencies listed below and providing 48MHz to OTG_FS and 60MHz to OTG_HS internally
    #[cfg(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs), all(stm32wba, peri_usb_otg_hs)))]
    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.",

View on GitHub (pinned to 463a07b963)