embassy-rs/embassy · error

AHB frequency is too low

Error message

AHB frequency is too low

What it means

calculate_trdt computes the USB turnaround time (TRDT) from the AHB bus clock; at high speed the OTG core requires AHB >= 30 MHz (or an H7RS part, which the driver treats as exempt) to produce a valid TRDT encoding per RM0431/RM0090/RM0390. Below that threshold no correct TRDT value exists, so the driver panics.

Solutions

  1. Raise the AHB/HCLK frequency to at least 30 MHz in the RCC configuration
  2. If low AHB is mandatory, configure the peripheral as full speed (Dspd::FULL_SPEED_EXTERNAL/INTERNAL) instead of high speed
  3. Use an H7RS part, where the driver permits high speed regardless of the 30 MHz rule, if hardware allows
  4. Compute and print ahb_freq before USB init and assert it meets the 30 MHz floor

Example fix

// before
config.ahb_pre = AHBPrescaler::DIV4; // ahb = 24 MHz, HS USB panics
// after
config.ahb_pre = AHBPrescaler::DIV2; // ahb = 48 MHz
Defensive patterns

Strategy: validation

Validate before calling

let ahb = rcc.ahb1().frequency();
assert!(ahb.0 >= 30_000_000, "AHB {} Hz too low for high-speed USB", ahb.0);

Type guard

fn ahb_ok_for_hs(hz: u32) -> bool { hz >= 30_000_000 }

Prevention

When it happens

Trigger: Initializing the OTG peripheral in Dspd::HIGH_SPEED mode while the AHB clock is below 30 MHz on a non-H7RS target (calculate_trdt called during common_init).

Common situations: Low-power clock trees (e.g. 24 MHz AHB) combined with a high-speed USB configuration; forgetting to raise AHB after lowering it for power experiments; copying an FS clock tree into an HS application.

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

Appendix: source

Thrown at embassy-stm32/src/usb/otg.rs:1044

        async fn bus_reset(&mut self) {
            self.inner.bus_reset().await
        }
    }
}

#[cfg(feature = "usb-host")]
pub use host_impl::*;

fn calculate_trdt<T: Instance>(speed: Dspd) -> u8 {
    let ahb_freq = T::bus_frequency().0;
    match speed {
        Dspd::HIGH_SPEED => {
            // From RM0431 (F72xx), RM0090 (F429), RM0390 (F446)
            if ahb_freq >= 30_000_000 || cfg!(stm32h7rs) {
                0x9
            } else {
                panic!("AHB frequency is too low")
            }
        }
        Dspd::FULL_SPEED_EXTERNAL | Dspd::FULL_SPEED_INTERNAL => {
            // From RM0431 (F72xx), RM0090 (F429)
            match ahb_freq {
                0..=14_199_999 => panic!("AHB frequency is too low"),
                14_200_000..=14_999_999 => 0xF,
                15_000_000..=15_999_999 => 0xE,
                16_000_000..=17_199_999 => 0xD,
                17_200_000..=18_499_999 => 0xC,
                18_500_000..=19_999_999 => 0xB,
                20_000_000..=21_799_999 => 0xA,
                21_800_000..=23_999_999 => 0x9,
                24_000_000..=27_499_999 => 0x8,
                27_500_000..=31_999_999 => 0x7, // 27.7..32 in code from CubeIDE
                32_000_000..=u32::MAX => 0x6,
            }
        }

View on GitHub (pinned to 463a07b963)