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
- 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)
- Print/inspect rcc.usb().frequency() (or the kernel clock mux settings) before initializing USB to confirm the value
- Use embassy-stm32's clock configuration examples for your exact chip as the starting point
- 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
- Validate rcc.usb().frequency() immediately after setting the clock tree, before USB init
- Derive the USB clock from a dedicated PLL with known output, not shared generic prescalers
- Start from the embassy-stm32 example clock config for your exact chip family
- Re-check whenever sysclk/PLL dividers change
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
- USB clock should be 48Mhz but is
- LSE frequency more than 5% off from 32.768 kHz, cannot use…
- MSIx auto-calibration is enabled for a source that has not…
- LSE frequency more than 5% off from 32.768 kHz, cannot use…
- cannot select OTG_HS reference clock with source frequency…
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)