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; use crate::pac::pwr::vals;
crate::pac::PWR.svmcr().modify(|w| { 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)] #[cfg(stm32u5)]

View File

@ -1,8 +1,9 @@
pub use crate::pac::pwr::vals::Vos as VoltageScale; pub use crate::pac::pwr::vals::Vos as VoltageScale;
use crate::pac::rcc::regs::Cfgr1; use crate::pac::rcc::regs::Cfgr1;
use core::ops::Div;
pub use crate::pac::rcc::vals::{ pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Hsepre as HsePrescaler, Ppre as APBPrescaler, Sw as Sysclk, Pllsrc as PllSource, 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::rcc::vals::Pllrge;
use crate::pac::{FLASH, RCC}; use crate::pac::{FLASH, RCC};
@ -20,6 +21,23 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000);
// HSE speed // HSE speed
pub const HSE_FREQ: Hertz = Hertz(32_000_000); 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)] #[derive(Clone, Copy, Eq, PartialEq)]
pub struct Hse { pub struct Hse {
pub prescaler: HsePrescaler, pub prescaler: HsePrescaler,
@ -71,6 +89,7 @@ pub struct Config {
// sysclk, buses. // sysclk, buses.
pub sys: Sysclk, pub sys: Sysclk,
pub ahb_pre: AHBPrescaler, pub ahb_pre: AHBPrescaler,
pub ahb5_pre: AHB5Prescaler,
pub apb1_pre: APBPrescaler, pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler, pub apb2_pre: APBPrescaler,
pub apb7_pre: APBPrescaler, pub apb7_pre: APBPrescaler,
@ -93,6 +112,7 @@ impl Config {
pll1: None, pll1: None,
sys: Sysclk::HSI, sys: Sysclk::HSI,
ahb_pre: AHBPrescaler::DIV1, ahb_pre: AHBPrescaler::DIV1,
ahb5_pre: AHB5Prescaler::DIV1,
apb1_pre: APBPrescaler::DIV1, apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1, apb2_pre: APBPrescaler::DIV1,
apb7_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 hclk1 = sys_clk / config.ahb_pre;
let hclk2 = hclk1; let hclk2 = hclk1;
let hclk4 = hclk1; let hclk4 = hclk1;
// TODO: hclk5
let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre); 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 (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre);
let (pclk7, _) = super::util::calc_pclk(hclk1, config.apb7_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); 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))] #[cfg(all(stm32wba, peri_usb_otg_hs))]
let usb_refck = match config.mux.otghssel { let usb_refck = match config.mux.otghssel {
Otghssel::HSE => hse, Otghssel::HSE => hse,
@ -245,6 +285,7 @@ pub(crate) unsafe fn init(config: Config) {
hclk1: Some(hclk1), hclk1: Some(hclk1),
hclk2: Some(hclk2), hclk2: Some(hclk2),
hclk4: Some(hclk4), hclk4: Some(hclk4),
hclk5: Some(hclk5),
pclk1: Some(pclk1), pclk1: Some(pclk1),
pclk2: Some(pclk2), pclk2: Some(pclk2),
pclk7: Some(pclk7), 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"), PllSource::_RESERVED_1 => panic!("must not select RESERVED_1 source as DISABLE"),
}; };
let hse_div = RCC.cr().read().hsepre(); // Only divide by the HSE prescaler when the PLL source is HSE
let src_freq = pre_src_freq / hse_div; 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 // Calculate the reference clock, which is the source divided by m
let ref_freq = src_freq / pll.prediv; 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 // 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); assert!(vco_freq >= vco_min && vco_freq <= vco_max);
// Calculate output clocks. // 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_pllq(pll.divq.unwrap_or(PllDiv::DIV1));
w.set_pllr(pll.divr.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 { let input_range = match ref_freq.0 {
..=8_000_000 => Pllrge::FREQ_4TO8MHZ, ..=8_000_000 => Pllrge::FREQ_4TO8MHZ,

View File

@ -108,7 +108,12 @@ fn common_init<T: Instance>() {
critical_section::with(|_| { critical_section::with(|_| {
crate::pac::PWR.svmcr().modify(|w| { crate::pac::PWR.svmcr().modify(|w| {
w.set_usv(crate::pac::pwr::vals::Usv::B_0X1); 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)] #[cfg(peri_usb_otg_hs)]
{ {
crate::pac::PWR.vosr().modify(|w| { crate::pac::PWR.vosr().modify(|w| {
w.set_usbpwren(true);
w.set_usbboosten(true); w.set_usbboosten(true);
}); });
while !crate::pac::PWR.vosr().read().usbboostrdy() {} 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))] #[cfg(all(stm32wba, peri_usb_otg_hs))]
{ {
critical_section::with(|_| { critical_section::with(|_| {
crate::pac::RCC.apb7enr().modify(|w| { // crate::pac::RCC.apb7enr().modify(|w| {
w.set_syscfgen(true); // w.set_syscfgen(true);
}); // });
crate::pac::RCC.ahb2enr().modify(|w| { crate::pac::RCC.ahb2enr().modify(|w| {
w.set_usb_otg_hsen(true); w.set_usb_otg_hsen(true);
w.set_usb_otg_hs_phyen(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 // Configuring Vbus sense and SOF output
match core_id { match core_id {
0x0000_1200 | 0x0000_1100 | 0x0000_1000 => self.inner.config_v1(), 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_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => self.inner.config_v2v3(),
0x0000_5000 => self.inner.config_v5(), 0x0000_5000 | 0x0000_6100 => self.inner.config_v5(),
_ => unimplemented!("Unknown USB core id {:X}", core_id), _ => 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_executor::Spawner;
use embassy_futures::join::join; use embassy_futures::join::join;
use embassy_stm32::rcc::{PllSource, PllPreDiv, PllMul, PllDiv}; 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::usb::{Driver, Instance};
use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; use embassy_stm32::{bind_interrupts, peripherals, usb, Config};
use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
@ -25,26 +25,28 @@ async fn main(_spawner: Spawner) {
let mut config = Config::default(); let mut config = Config::default();
// External HSE (32 MHz) setup // External HSE (32 MHz) setup
config.rcc.hse = Some(Hse { // config.rcc.hse = Some(Hse {
prescaler: HsePrescaler::DIV2, // prescaler: HsePrescaler::DIV2,
}); // });
// Fine-tune PLL1 dividers/multipliers // Fine-tune PLL1 dividers/multipliers
config.rcc.pll1 = Some(embassy_stm32::rcc::Pll { config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {
source: PllSource::HSE, source: PllSource::HSI,
prediv: PllPreDiv::DIV2, // PLLM = 2 → HSE / 2 = 8 MHz prediv: PllPreDiv::DIV1, // PLLM = 1 → HSI / 1 = 16 MHz
mul: PllMul::MUL60, // PLLN = 60 → 8 MHz * 60 = 480 MHz VCO mul: PllMul::MUL30, // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO
divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk) divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)
divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (USB) // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED)
divp: Some(PllDiv::DIV15), // PLLP = 15 → 32 MHz (USBOTG) divq: None,
frac: Some(4096), // Fractional part (enabled) divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG)
frac: Some(0), // Fractional part (enabled)
}); });
config.rcc.ahb_pre = AHBPrescaler::DIV1; config.rcc.ahb_pre = AHBPrescaler::DIV1;
config.rcc.apb1_pre = APBPrescaler::DIV1; config.rcc.apb1_pre = APBPrescaler::DIV1;
config.rcc.apb2_pre = APBPrescaler::DIV1; config.rcc.apb2_pre = APBPrescaler::DIV1;
config.rcc.apb7_pre = APBPrescaler::DIV1; config.rcc.apb7_pre = APBPrescaler::DIV1;
config.rcc.ahb5_pre = AHB5Prescaler::DIV4;
// voltage scale for max performance // voltage scale for max performance
config.rcc.voltage_scale = VoltageScale::RANGE1; config.rcc.voltage_scale = VoltageScale::RANGE1;