Moved from HSE to HSI to generate USB_OTG_HS_CLK

This commit is contained in:
Gerzain Mata 2025-07-25 14:26:06 -07:00
parent ba5156b6cb
commit 75c1039aa1
5 changed files with 83 additions and 25 deletions

View File

@ -546,7 +546,7 @@ fn init_hw(config: Config) -> Peripherals {
{
use crate::pac::pwr::vals;
crate::pac::PWR.svmcr().modify(|w| {
w.set_io2sv(vals::Io2sv::B_0X1);
w.set_io2sv(if config.enable_independent_io_supply {vals::Io2sv::B_0X1} else {vals::Io2sv::B_0X0});
});
}
#[cfg(stm32u5)]

View File

@ -1,8 +1,9 @@
pub use crate::pac::pwr::vals::Vos as VoltageScale;
use crate::pac::rcc::regs::Cfgr1;
use core::ops::Div;
pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Hsepre as HsePrescaler, Ppre as APBPrescaler, Sw as Sysclk, Pllsrc as PllSource,
Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul,
Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, Hpre5 as AHB5Prescaler, Hdiv5,
};
use crate::pac::rcc::vals::Pllrge;
use crate::pac::{FLASH, RCC};
@ -20,6 +21,23 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000);
// HSE speed
pub const HSE_FREQ: Hertz = Hertz(32_000_000);
// Allow dividing a Hertz value by an AHB5 prescaler directly
impl Div<AHB5Prescaler> for Hertz {
type Output = Hertz;
fn div(self, rhs: AHB5Prescaler) -> Hertz {
// Map the prescaler enum to its integer divisor
let divisor = match rhs {
AHB5Prescaler::DIV1 => 1,
AHB5Prescaler::DIV2 => 2,
AHB5Prescaler::DIV3 => 3,
AHB5Prescaler::DIV4 => 4,
AHB5Prescaler::DIV6 => 6,
_ => unreachable!("Invalid AHB5 prescaler: {:?}", rhs),
};
Hertz(self.0 / divisor)
}
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Hse {
pub prescaler: HsePrescaler,
@ -71,6 +89,7 @@ pub struct Config {
// sysclk, buses.
pub sys: Sysclk,
pub ahb_pre: AHBPrescaler,
pub ahb5_pre: AHB5Prescaler,
pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler,
pub apb7_pre: APBPrescaler,
@ -93,6 +112,7 @@ impl Config {
pll1: None,
sys: Sysclk::HSI,
ahb_pre: AHBPrescaler::DIV1,
ahb5_pre: AHB5Prescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
apb7_pre: APBPrescaler::DIV1,
@ -165,7 +185,6 @@ pub(crate) unsafe fn init(config: Config) {
let hclk1 = sys_clk / config.ahb_pre;
let hclk2 = hclk1;
let hclk4 = hclk1;
// TODO: hclk5
let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre);
let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre);
let (pclk7, _) = super::util::calc_pclk(hclk1, config.apb7_pre);
@ -211,6 +230,27 @@ pub(crate) unsafe fn init(config: Config) {
w.set_ppre2(config.apb2_pre);
});
// Set AHB5 prescaler depending on sysclk source
RCC.cfgr4().modify(|w| match config.sys {
// When using HSI or HSE, use HDIV5 bit (0 = div1, 1 = div2)
Sysclk::HSI | Sysclk::HSE => {
// Only Div1 and Div2 are valid for HDIV5, enforce this
match config.ahb5_pre {
AHB5Prescaler::DIV1 => w.set_hdiv5(Hdiv5::DIV1),
AHB5Prescaler::DIV2 => w.set_hdiv5(Hdiv5::DIV2),
_ => panic!("Invalid ahb5_pre for HSI/HSE sysclk: only DIV1 and DIV2 are allowed"),
};
}
// When using PLL1, use HPRE5 bits [2:0]
Sysclk::PLL1_R => {
w.set_hpre5(config.ahb5_pre);
}
_ => {}
});
let hclk5 = sys_clk / config.ahb5_pre;
#[cfg(all(stm32wba, peri_usb_otg_hs))]
let usb_refck = match config.mux.otghssel {
Otghssel::HSE => hse,
@ -245,6 +285,7 @@ pub(crate) unsafe fn init(config: Config) {
hclk1: Some(hclk1),
hclk2: Some(hclk2),
hclk4: Some(hclk4),
hclk5: Some(hclk5),
pclk1: Some(pclk1),
pclk2: Some(pclk2),
pclk7: Some(pclk7),
@ -294,8 +335,15 @@ fn init_pll(config: Option<Pll>, input: &PllInput, voltage_range: VoltageScale)
PllSource::_RESERVED_1 => panic!("must not select RESERVED_1 source as DISABLE"),
};
let hse_div = RCC.cr().read().hsepre();
let src_freq = pre_src_freq / hse_div;
// Only divide by the HSE prescaler when the PLL source is HSE
let src_freq = match pll.source {
PllSource::HSE => {
// read the prescaler bits and divide
let hsepre = RCC.cr().read().hsepre();
pre_src_freq / hsepre
}
_ => pre_src_freq,
};
// Calculate the reference clock, which is the source divided by m
let ref_freq = src_freq / pll.prediv;
@ -309,7 +357,11 @@ fn init_pll(config: Option<Pll>, input: &PllInput, voltage_range: VoltageScale)
};
// Calculate the PLL VCO clock
let vco_freq = ref_freq * pll.mul;
// let vco_freq = ref_freq * pll.mul;
// Calculate VCO frequency including fractional part: FVCO = Fref_ck × (N + FRAC/2^13)
let numerator = (ref_freq.0 as u64) * (((pll.mul as u64) + 1 << 13) + pll.frac.unwrap_or(0) as u64);
let vco_hz = (numerator >> 13) as u32;
let vco_freq = Hertz(vco_hz);
assert!(vco_freq >= vco_min && vco_freq <= vco_max);
// Calculate output clocks.
@ -329,7 +381,7 @@ fn init_pll(config: Option<Pll>, input: &PllInput, voltage_range: VoltageScale)
w.set_pllq(pll.divq.unwrap_or(PllDiv::DIV1));
w.set_pllr(pll.divr.unwrap_or(PllDiv::DIV1));
});
RCC.pll1fracr().write(|w| {w.set_pllfracn(pll.frac.unwrap_or(1));});
RCC.pll1fracr().write(|w| {w.set_pllfracn(pll.frac.unwrap_or(0));});
let input_range = match ref_freq.0 {
..=8_000_000 => Pllrge::FREQ_4TO8MHZ,

View File

@ -108,7 +108,12 @@ fn common_init<T: Instance>() {
critical_section::with(|_| {
crate::pac::PWR.svmcr().modify(|w| {
w.set_usv(crate::pac::pwr::vals::Usv::B_0X1);
// w.set_uvmen(true);
});
crate::pac::PWR.vosr().modify(|w| {
w.set_vdd11usbdis(true);
});
crate::pac::PWR.vosr().modify(|w| {
w.set_usbpwren(true);
})
});
@ -119,7 +124,6 @@ fn common_init<T: Instance>() {
#[cfg(peri_usb_otg_hs)]
{
crate::pac::PWR.vosr().modify(|w| {
w.set_usbpwren(true);
w.set_usbboosten(true);
});
while !crate::pac::PWR.vosr().read().usbboostrdy() {}

View File

@ -330,9 +330,9 @@ impl<'d, T: Instance> Bus<'d, T> {
#[cfg(all(stm32wba, peri_usb_otg_hs))]
{
critical_section::with(|_| {
crate::pac::RCC.apb7enr().modify(|w| {
w.set_syscfgen(true);
});
// crate::pac::RCC.apb7enr().modify(|w| {
// w.set_syscfgen(true);
// });
crate::pac::RCC.ahb2enr().modify(|w| {
w.set_usb_otg_hsen(true);
w.set_usb_otg_hs_phyen(true);
@ -366,8 +366,8 @@ impl<'d, T: Instance> Bus<'d, T> {
// Configuring Vbus sense and SOF output
match core_id {
0x0000_1200 | 0x0000_1100 | 0x0000_1000 => self.inner.config_v1(),
0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 | 0x0000_6100 => self.inner.config_v2v3(),
0x0000_5000 => self.inner.config_v5(),
0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => self.inner.config_v2v3(),
0x0000_5000 | 0x0000_6100 => self.inner.config_v5(),
_ => unimplemented!("Unknown USB core id {:X}", core_id),
}
}

View File

@ -6,7 +6,7 @@ use defmt_rtt as _; // global logger
use embassy_executor::Spawner;
use embassy_futures::join::join;
use embassy_stm32::rcc::{PllSource, PllPreDiv, PllMul, PllDiv};
use embassy_stm32::rcc::{mux, AHBPrescaler, APBPrescaler, Hse, HsePrescaler, Sysclk, VoltageScale};
use embassy_stm32::rcc::{mux, AHBPrescaler, AHB5Prescaler, APBPrescaler, Hse, HsePrescaler, Sysclk, VoltageScale};
use embassy_stm32::usb::{Driver, Instance};
use embassy_stm32::{bind_interrupts, peripherals, usb, Config};
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
@ -25,26 +25,28 @@ async fn main(_spawner: Spawner) {
let mut config = Config::default();
// External HSE (32 MHz) setup
config.rcc.hse = Some(Hse {
prescaler: HsePrescaler::DIV2,
});
// config.rcc.hse = Some(Hse {
// prescaler: HsePrescaler::DIV2,
// });
// Fine-tune PLL1 dividers/multipliers
config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {
source: PllSource::HSE,
prediv: PllPreDiv::DIV2, // PLLM = 2 → HSE / 2 = 8 MHz
mul: PllMul::MUL60, // PLLN = 60 → 8 MHz * 60 = 480 MHz VCO
divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)
divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (USB)
divp: Some(PllDiv::DIV15), // PLLP = 15 → 32 MHz (USBOTG)
frac: Some(4096), // Fractional part (enabled)
source: PllSource::HSI,
prediv: PllPreDiv::DIV1, // PLLM = 1 → HSI / 1 = 16 MHz
mul: PllMul::MUL30, // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO
divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)
// divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED)
divq: None,
divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG)
frac: Some(0), // Fractional part (enabled)
});
config.rcc.ahb_pre = AHBPrescaler::DIV1;
config.rcc.apb1_pre = APBPrescaler::DIV1;
config.rcc.apb2_pre = APBPrescaler::DIV1;
config.rcc.apb7_pre = APBPrescaler::DIV1;
config.rcc.ahb5_pre = AHB5Prescaler::DIV4;
// voltage scale for max performance
config.rcc.voltage_scale = VoltageScale::RANGE1;