Merge branch 'main' into issue-1974-add-sai-driver
This commit is contained in:
176
embassy-stm32/src/rcc/bd.rs
Normal file
176
embassy-stm32/src/rcc/bd.rs
Normal file
@ -0,0 +1,176 @@
|
||||
#[allow(dead_code)]
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub enum LseDrive {
|
||||
#[cfg(any(rtc_v2f7, rtc_v2l4))]
|
||||
Low = 0,
|
||||
MediumLow = 0x01,
|
||||
#[default]
|
||||
MediumHigh = 0x02,
|
||||
#[cfg(any(rtc_v2f7, rtc_v2l4))]
|
||||
High = 0x03,
|
||||
}
|
||||
|
||||
#[cfg(any(rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l4))]
|
||||
impl From<LseDrive> for crate::pac::rcc::vals::Lsedrv {
|
||||
fn from(value: LseDrive) -> Self {
|
||||
use crate::pac::rcc::vals::Lsedrv;
|
||||
|
||||
match value {
|
||||
#[cfg(any(rtc_v2f7, rtc_v2l4))]
|
||||
LseDrive::Low => Lsedrv::LOW,
|
||||
LseDrive::MediumLow => Lsedrv::MEDIUMLOW,
|
||||
LseDrive::MediumHigh => Lsedrv::MEDIUMHIGH,
|
||||
#[cfg(any(rtc_v2f7, rtc_v2l4))]
|
||||
LseDrive::High => Lsedrv::HIGH,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use crate::pac::rcc::vals::Rtcsel as RtcClockSource;
|
||||
|
||||
#[cfg(not(any(rtc_v2l0, rtc_v2l1, stm32c0)))]
|
||||
#[allow(dead_code)]
|
||||
type Bdcr = crate::pac::rcc::regs::Bdcr;
|
||||
|
||||
#[cfg(any(rtc_v2l0, rtc_v2l1))]
|
||||
#[allow(dead_code)]
|
||||
type Bdcr = crate::pac::rcc::regs::Csr;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct BackupDomain {}
|
||||
|
||||
impl BackupDomain {
|
||||
#[cfg(any(
|
||||
rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb, rtc_v3,
|
||||
rtc_v3u5
|
||||
))]
|
||||
#[allow(dead_code, unused_variables)]
|
||||
fn modify<R>(f: impl FnOnce(&mut Bdcr) -> R) -> R {
|
||||
#[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1, rtc_v2l0))]
|
||||
let cr = crate::pac::PWR.cr();
|
||||
#[cfg(any(rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
|
||||
let cr = crate::pac::PWR.cr1();
|
||||
|
||||
// TODO: Missing from PAC for l0 and f0?
|
||||
#[cfg(not(any(rtc_v2f0, rtc_v3u5)))]
|
||||
{
|
||||
cr.modify(|w| w.set_dbp(true));
|
||||
while !cr.read().dbp() {}
|
||||
}
|
||||
|
||||
#[cfg(any(rtc_v2l0, rtc_v2l1))]
|
||||
let cr = crate::pac::RCC.csr();
|
||||
|
||||
#[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
|
||||
let cr = crate::pac::RCC.bdcr();
|
||||
|
||||
cr.modify(|w| f(w))
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb, rtc_v3,
|
||||
rtc_v3u5
|
||||
))]
|
||||
#[allow(dead_code)]
|
||||
fn read() -> Bdcr {
|
||||
#[cfg(any(rtc_v2l0, rtc_v2l1))]
|
||||
let r = crate::pac::RCC.csr().read();
|
||||
|
||||
#[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
|
||||
let r = crate::pac::RCC.bdcr().read();
|
||||
|
||||
r
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb, rtc_v3,
|
||||
rtc_v3u5
|
||||
))]
|
||||
#[allow(dead_code, unused_variables)]
|
||||
pub fn configure_ls(clock_source: RtcClockSource, lsi: bool, lse: Option<LseDrive>) {
|
||||
if lsi {
|
||||
#[cfg(rtc_v3u5)]
|
||||
let csr = crate::pac::RCC.bdcr();
|
||||
|
||||
#[cfg(not(rtc_v3u5))]
|
||||
let csr = crate::pac::RCC.csr();
|
||||
|
||||
// Disable backup domain write protection
|
||||
Self::modify(|_| {});
|
||||
|
||||
#[cfg(not(any(rcc_wb, rcc_wba)))]
|
||||
csr.modify(|w| w.set_lsion(true));
|
||||
|
||||
#[cfg(any(rcc_wb, rcc_wba))]
|
||||
csr.modify(|w| w.set_lsi1on(true));
|
||||
|
||||
#[cfg(not(any(rcc_wb, rcc_wba)))]
|
||||
while !csr.read().lsirdy() {}
|
||||
|
||||
#[cfg(any(rcc_wb, rcc_wba))]
|
||||
while !csr.read().lsi1rdy() {}
|
||||
}
|
||||
|
||||
if let Some(lse_drive) = lse {
|
||||
Self::modify(|w| {
|
||||
#[cfg(any(rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l4))]
|
||||
w.set_lsedrv(lse_drive.into());
|
||||
w.set_lseon(true);
|
||||
});
|
||||
|
||||
while !Self::read().lserdy() {}
|
||||
}
|
||||
|
||||
match clock_source {
|
||||
RtcClockSource::LSI => assert!(lsi),
|
||||
RtcClockSource::LSE => assert!(&lse.is_some()),
|
||||
_ => {}
|
||||
};
|
||||
|
||||
if clock_source == RtcClockSource::NOCLOCK {
|
||||
// disable it
|
||||
Self::modify(|w| {
|
||||
#[cfg(not(rcc_wba))]
|
||||
w.set_rtcen(false);
|
||||
w.set_rtcsel(clock_source);
|
||||
});
|
||||
} else {
|
||||
// check if it's already enabled and in the source we want.
|
||||
let reg = Self::read();
|
||||
let ok = reg.rtcsel() == clock_source;
|
||||
#[cfg(not(rcc_wba))]
|
||||
let ok = ok & reg.rtcen();
|
||||
|
||||
// if not, configure it.
|
||||
if !ok {
|
||||
#[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
|
||||
assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
|
||||
|
||||
#[cfg(not(any(rcc_l0, rcc_l1)))]
|
||||
Self::modify(|w| w.set_bdrst(true));
|
||||
|
||||
Self::modify(|w| {
|
||||
// Reset
|
||||
#[cfg(not(any(rcc_l0, rcc_l1)))]
|
||||
w.set_bdrst(false);
|
||||
|
||||
#[cfg(not(rcc_wba))]
|
||||
w.set_rtcen(true);
|
||||
w.set_rtcsel(clock_source);
|
||||
|
||||
// Restore bcdr
|
||||
#[cfg(any(rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
|
||||
w.set_lscosel(reg.lscosel());
|
||||
#[cfg(any(rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
|
||||
w.set_lscoen(reg.lscoen());
|
||||
|
||||
w.set_lseon(reg.lseon());
|
||||
|
||||
#[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
|
||||
w.set_lsedrv(reg.lsedrv());
|
||||
w.set_lsebyp(reg.lsebyp());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
56
embassy-stm32/src/rcc/bus.rs
Normal file
56
embassy-stm32/src/rcc/bus.rs
Normal file
@ -0,0 +1,56 @@
|
||||
use core::ops::Div;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::pac::rcc;
|
||||
pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Ppre as APBPrescaler};
|
||||
use crate::time::Hertz;
|
||||
|
||||
impl Div<AHBPrescaler> for Hertz {
|
||||
type Output = Hertz;
|
||||
|
||||
fn div(self, rhs: AHBPrescaler) -> Self::Output {
|
||||
let divisor = match rhs {
|
||||
AHBPrescaler::DIV1 => 1,
|
||||
AHBPrescaler::DIV2 => 2,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::DIV3 => 3,
|
||||
AHBPrescaler::DIV4 => 4,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::DIV5 => 5,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::DIV6 => 6,
|
||||
AHBPrescaler::DIV8 => 8,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::DIV10 => 10,
|
||||
AHBPrescaler::DIV16 => 16,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::DIV32 => 32,
|
||||
#[cfg(not(rcc_wba))]
|
||||
AHBPrescaler::DIV64 => 64,
|
||||
#[cfg(not(rcc_wba))]
|
||||
AHBPrescaler::DIV128 => 128,
|
||||
#[cfg(not(rcc_wba))]
|
||||
AHBPrescaler::DIV256 => 256,
|
||||
#[cfg(not(rcc_wba))]
|
||||
AHBPrescaler::DIV512 => 512,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Hertz(self.0 / divisor)
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<APBPrescaler> for Hertz {
|
||||
type Output = Hertz;
|
||||
|
||||
fn div(self, rhs: APBPrescaler) -> Self::Output {
|
||||
let divisor = match rhs {
|
||||
APBPrescaler::DIV1 => 1,
|
||||
APBPrescaler::DIV2 => 2,
|
||||
APBPrescaler::DIV4 => 4,
|
||||
APBPrescaler::DIV8 => 8,
|
||||
APBPrescaler::DIV16 => 16,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Hertz(self.0 / divisor)
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::pac::flash::vals::Latency;
|
||||
use crate::pac::rcc::vals::{Hsidiv, Ppre, Sw};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
@ -58,8 +58,8 @@ impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
mux: ClockSrc::HSI(HSIPrescaler::NotDivided),
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb_pre: APBPrescaler::NotDivided,
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb_pre: APBPrescaler::DIV1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -151,20 +151,21 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
}
|
||||
|
||||
let ahb_div = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => 1,
|
||||
AHBPrescaler::Div2 => 2,
|
||||
AHBPrescaler::Div4 => 4,
|
||||
AHBPrescaler::Div8 => 8,
|
||||
AHBPrescaler::Div16 => 16,
|
||||
AHBPrescaler::Div64 => 64,
|
||||
AHBPrescaler::Div128 => 128,
|
||||
AHBPrescaler::Div256 => 256,
|
||||
AHBPrescaler::Div512 => 512,
|
||||
AHBPrescaler::DIV1 => 1,
|
||||
AHBPrescaler::DIV2 => 2,
|
||||
AHBPrescaler::DIV4 => 4,
|
||||
AHBPrescaler::DIV8 => 8,
|
||||
AHBPrescaler::DIV16 => 16,
|
||||
AHBPrescaler::DIV64 => 64,
|
||||
AHBPrescaler::DIV128 => 128,
|
||||
AHBPrescaler::DIV256 => 256,
|
||||
AHBPrescaler::DIV512 => 512,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let ahb_freq = sys_clk / ahb_div;
|
||||
|
||||
let (apb_freq, apb_tim_freq) = match config.apb_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
|
@ -1,174 +0,0 @@
|
||||
use core::ops::Div;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::pac::rcc;
|
||||
use crate::time::Hertz;
|
||||
|
||||
/// Voltage Scale
|
||||
///
|
||||
/// Represents the voltage range feeding the CPU core. The maximum core
|
||||
/// clock frequency depends on this value.
|
||||
///
|
||||
/// Scale0 represents the highest voltage range
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum VoltageScale {
|
||||
Scale0,
|
||||
Scale1,
|
||||
#[cfg(not(any(rcc_wl5, rcc_wle)))]
|
||||
Scale2,
|
||||
#[cfg(not(any(rcc_wl5, rcc_wle)))]
|
||||
Scale3,
|
||||
}
|
||||
|
||||
/// AHB prescaler
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum AHBPrescaler {
|
||||
NotDivided,
|
||||
Div2,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
Div3,
|
||||
Div4,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
Div5,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
Div6,
|
||||
Div8,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
Div10,
|
||||
Div16,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
Div32,
|
||||
Div64,
|
||||
Div128,
|
||||
Div256,
|
||||
Div512,
|
||||
}
|
||||
|
||||
impl Div<AHBPrescaler> for Hertz {
|
||||
type Output = Hertz;
|
||||
|
||||
fn div(self, rhs: AHBPrescaler) -> Self::Output {
|
||||
let divisor = match rhs {
|
||||
AHBPrescaler::NotDivided => 1,
|
||||
AHBPrescaler::Div2 => 2,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::Div3 => 3,
|
||||
AHBPrescaler::Div4 => 4,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::Div5 => 5,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::Div6 => 6,
|
||||
AHBPrescaler::Div8 => 8,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::Div10 => 10,
|
||||
AHBPrescaler::Div16 => 16,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::Div32 => 32,
|
||||
AHBPrescaler::Div64 => 64,
|
||||
AHBPrescaler::Div128 => 128,
|
||||
AHBPrescaler::Div256 => 256,
|
||||
AHBPrescaler::Div512 => 512,
|
||||
};
|
||||
Hertz(self.0 / divisor)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(rcc_g4, rcc_wb, rcc_wl5, rcc_wle)))]
|
||||
impl From<AHBPrescaler> for rcc::vals::Hpre {
|
||||
fn from(val: AHBPrescaler) -> rcc::vals::Hpre {
|
||||
use rcc::vals::Hpre;
|
||||
|
||||
match val {
|
||||
#[cfg(not(rcc_u5))]
|
||||
AHBPrescaler::NotDivided => Hpre::DIV1,
|
||||
#[cfg(rcc_u5)]
|
||||
AHBPrescaler::NotDivided => Hpre::NONE,
|
||||
AHBPrescaler::Div2 => Hpre::DIV2,
|
||||
AHBPrescaler::Div4 => Hpre::DIV4,
|
||||
AHBPrescaler::Div8 => Hpre::DIV8,
|
||||
AHBPrescaler::Div16 => Hpre::DIV16,
|
||||
AHBPrescaler::Div64 => Hpre::DIV64,
|
||||
AHBPrescaler::Div128 => Hpre::DIV128,
|
||||
AHBPrescaler::Div256 => Hpre::DIV256,
|
||||
AHBPrescaler::Div512 => Hpre::DIV512,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
impl From<AHBPrescaler> for u8 {
|
||||
fn from(val: AHBPrescaler) -> u8 {
|
||||
match val {
|
||||
AHBPrescaler::NotDivided => 0x0,
|
||||
AHBPrescaler::Div2 => 0x08,
|
||||
AHBPrescaler::Div3 => 0x01,
|
||||
AHBPrescaler::Div4 => 0x09,
|
||||
AHBPrescaler::Div5 => 0x02,
|
||||
AHBPrescaler::Div6 => 0x05,
|
||||
AHBPrescaler::Div8 => 0x0a,
|
||||
AHBPrescaler::Div10 => 0x06,
|
||||
AHBPrescaler::Div16 => 0x0b,
|
||||
AHBPrescaler::Div32 => 0x07,
|
||||
AHBPrescaler::Div64 => 0x0c,
|
||||
AHBPrescaler::Div128 => 0x0d,
|
||||
AHBPrescaler::Div256 => 0x0e,
|
||||
AHBPrescaler::Div512 => 0x0f,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// APB prescaler
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum APBPrescaler {
|
||||
NotDivided,
|
||||
Div2,
|
||||
Div4,
|
||||
Div8,
|
||||
Div16,
|
||||
}
|
||||
|
||||
impl Div<APBPrescaler> for Hertz {
|
||||
type Output = Hertz;
|
||||
|
||||
fn div(self, rhs: APBPrescaler) -> Self::Output {
|
||||
let divisor = match rhs {
|
||||
APBPrescaler::NotDivided => 1,
|
||||
APBPrescaler::Div2 => 2,
|
||||
APBPrescaler::Div4 => 4,
|
||||
APBPrescaler::Div8 => 8,
|
||||
APBPrescaler::Div16 => 16,
|
||||
};
|
||||
Hertz(self.0 / divisor)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(rcc_f1, rcc_f100, rcc_f1cl, rcc_g4, rcc_h7, rcc_h7ab, rcc_wb, rcc_wl5, rcc_wle)))]
|
||||
impl From<APBPrescaler> for rcc::vals::Ppre {
|
||||
fn from(val: APBPrescaler) -> rcc::vals::Ppre {
|
||||
use rcc::vals::Ppre;
|
||||
|
||||
match val {
|
||||
#[cfg(not(rcc_u5))]
|
||||
APBPrescaler::NotDivided => Ppre::DIV1,
|
||||
#[cfg(rcc_u5)]
|
||||
APBPrescaler::NotDivided => Ppre::NONE,
|
||||
APBPrescaler::Div2 => Ppre::DIV2,
|
||||
APBPrescaler::Div4 => Ppre::DIV4,
|
||||
APBPrescaler::Div8 => Ppre::DIV8,
|
||||
APBPrescaler::Div16 => Ppre::DIV16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
impl From<APBPrescaler> for u8 {
|
||||
fn from(val: APBPrescaler) -> u8 {
|
||||
match val {
|
||||
APBPrescaler::NotDivided => 1,
|
||||
APBPrescaler::Div2 => 0x04,
|
||||
APBPrescaler::Div4 => 0x05,
|
||||
APBPrescaler::Div8 => 0x06,
|
||||
APBPrescaler::Div16 => 0x07,
|
||||
}
|
||||
}
|
||||
}
|
@ -163,8 +163,8 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
// Only needed for stm32f103?
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_adcpre(Adcpre::from_bits(apre_bits));
|
||||
w.set_ppre2(Ppre1::from_bits(ppre2_bits));
|
||||
w.set_ppre1(Ppre1::from_bits(ppre1_bits));
|
||||
w.set_ppre2(Ppre::from_bits(ppre2_bits));
|
||||
w.set_ppre1(Ppre::from_bits(ppre1_bits));
|
||||
w.set_hpre(Hpre::from_bits(hpre_bits));
|
||||
#[cfg(not(rcc_f100))]
|
||||
w.set_usbpre(Usbpre::from_bits(usbpre as u8));
|
||||
@ -184,6 +184,6 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
apb1_tim: Hertz(pclk1 * timer_mul1),
|
||||
apb2_tim: Hertz(pclk2 * timer_mul2),
|
||||
ahb1: Hertz(hclk),
|
||||
adc: Hertz(adcclk),
|
||||
adc: Some(Hertz(adcclk)),
|
||||
});
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
use core::convert::TryFrom;
|
||||
use core::ops::{Div, Mul};
|
||||
|
||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::pac::flash::vals::Latency;
|
||||
use crate::pac::rcc::vals::{Pllp, Pllsrc, Sw};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::bd::BackupDomain;
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::rtc::RtcClockSource;
|
||||
use crate::time::Hertz;
|
||||
|
||||
/// HSI speed
|
||||
@ -201,7 +203,20 @@ pub struct PLLClocks {
|
||||
pub pll48_freq: Hertz,
|
||||
}
|
||||
|
||||
pub use super::common::VoltageScale;
|
||||
/// Voltage range of the power supply used.
|
||||
///
|
||||
/// Used to calculate flash waitstates. See
|
||||
/// RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock frequency
|
||||
pub enum VoltageScale {
|
||||
/// 2.7 to 3.6 V
|
||||
Range0,
|
||||
/// 2.4 to 2.7 V
|
||||
Range1,
|
||||
/// 2.1 to 2.4 V
|
||||
Range2,
|
||||
/// 1.8 to 2.1 V
|
||||
Range3,
|
||||
}
|
||||
|
||||
impl VoltageScale {
|
||||
const fn wait_states(&self, ahb_freq: Hertz) -> Option<Latency> {
|
||||
@ -209,7 +224,7 @@ impl VoltageScale {
|
||||
// Reference: RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock
|
||||
// frequency
|
||||
match self {
|
||||
VoltageScale::Scale3 => {
|
||||
VoltageScale::Range3 => {
|
||||
if ahb_freq <= 16_000_000 {
|
||||
Some(Latency::WS0)
|
||||
} else if ahb_freq <= 32_000_000 {
|
||||
@ -230,7 +245,7 @@ impl VoltageScale {
|
||||
None
|
||||
}
|
||||
}
|
||||
VoltageScale::Scale2 => {
|
||||
VoltageScale::Range2 => {
|
||||
if ahb_freq <= 18_000_000 {
|
||||
Some(Latency::WS0)
|
||||
} else if ahb_freq <= 36_000_000 {
|
||||
@ -249,7 +264,7 @@ impl VoltageScale {
|
||||
None
|
||||
}
|
||||
}
|
||||
VoltageScale::Scale1 => {
|
||||
VoltageScale::Range1 => {
|
||||
if ahb_freq <= 24_000_000 {
|
||||
Some(Latency::WS0)
|
||||
} else if ahb_freq <= 48_000_000 {
|
||||
@ -264,7 +279,7 @@ impl VoltageScale {
|
||||
None
|
||||
}
|
||||
}
|
||||
VoltageScale::Scale0 => {
|
||||
VoltageScale::Range0 => {
|
||||
if ahb_freq <= 30_000_000 {
|
||||
Some(Latency::WS0)
|
||||
} else if ahb_freq <= 60_000_000 {
|
||||
@ -288,6 +303,9 @@ pub struct Config {
|
||||
pub pll_mux: PLLSrc,
|
||||
pub pll: PLLConfig,
|
||||
pub mux: ClockSrc,
|
||||
pub rtc: Option<RtcClockSource>,
|
||||
pub lsi: bool,
|
||||
pub lse: Option<Hertz>,
|
||||
pub voltage: VoltageScale,
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb1_pre: APBPrescaler,
|
||||
@ -302,11 +320,14 @@ impl Default for Config {
|
||||
hsi: true,
|
||||
pll_mux: PLLSrc::HSI,
|
||||
pll: PLLConfig::default(),
|
||||
voltage: VoltageScale::Scale3,
|
||||
voltage: VoltageScale::Range3,
|
||||
mux: ClockSrc::HSI,
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb1_pre: APBPrescaler::NotDivided,
|
||||
apb2_pre: APBPrescaler::NotDivided,
|
||||
rtc: None,
|
||||
lsi: false,
|
||||
lse: None,
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -379,7 +400,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
assert!(ahb_freq <= Hertz(120_000_000));
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, Hertz(freq.0 * 2))
|
||||
@ -389,7 +410,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
assert!(apb1_freq <= Hertz(30_000_000));
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, Hertz(freq.0 * 2))
|
||||
@ -414,6 +435,12 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
RCC.cr().modify(|w| w.set_hsion(false));
|
||||
}
|
||||
|
||||
BackupDomain::configure_ls(
|
||||
config.rtc.unwrap_or(RtcClockSource::NOCLOCK),
|
||||
config.lsi,
|
||||
config.lse.map(|_| Default::default()),
|
||||
);
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: sys_clk,
|
||||
ahb1: ahb_freq,
|
||||
|
@ -1,5 +1,7 @@
|
||||
#[cfg(rcc_f3)]
|
||||
use crate::pac::adccommon::vals::Ckmode;
|
||||
use crate::pac::flash::vals::Latency;
|
||||
use crate::pac::rcc::vals::{Hpre, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre};
|
||||
use crate::pac::rcc::vals::{Adcpres, Hpre, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
@ -10,6 +12,82 @@ pub const HSI_FREQ: Hertz = Hertz(8_000_000);
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(40_000);
|
||||
|
||||
impl From<AdcClockSource> for Adcpres {
|
||||
fn from(value: AdcClockSource) -> Self {
|
||||
match value {
|
||||
AdcClockSource::PllDiv1 => Adcpres::DIV1,
|
||||
AdcClockSource::PllDiv2 => Adcpres::DIV2,
|
||||
AdcClockSource::PllDiv4 => Adcpres::DIV4,
|
||||
AdcClockSource::PllDiv6 => Adcpres::DIV6,
|
||||
AdcClockSource::PllDiv8 => Adcpres::DIV8,
|
||||
AdcClockSource::PllDiv12 => Adcpres::DIV12,
|
||||
AdcClockSource::PllDiv16 => Adcpres::DIV16,
|
||||
AdcClockSource::PllDiv32 => Adcpres::DIV32,
|
||||
AdcClockSource::PllDiv64 => Adcpres::DIV64,
|
||||
AdcClockSource::PllDiv128 => Adcpres::DIV128,
|
||||
AdcClockSource::PllDiv256 => Adcpres::DIV256,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(rcc_f3)]
|
||||
impl From<AdcClockSource> for Ckmode {
|
||||
fn from(value: AdcClockSource) -> Self {
|
||||
match value {
|
||||
AdcClockSource::BusDiv1 => Ckmode::SYNCDIV1,
|
||||
AdcClockSource::BusDiv2 => Ckmode::SYNCDIV2,
|
||||
AdcClockSource::BusDiv4 => Ckmode::SYNCDIV4,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AdcClockSource {
|
||||
PllDiv1 = 1,
|
||||
PllDiv2 = 2,
|
||||
PllDiv4 = 4,
|
||||
PllDiv6 = 6,
|
||||
PllDiv8 = 8,
|
||||
PllDiv12 = 12,
|
||||
PllDiv16 = 16,
|
||||
PllDiv32 = 32,
|
||||
PllDiv64 = 64,
|
||||
PllDiv128 = 128,
|
||||
PllDiv256 = 256,
|
||||
BusDiv1,
|
||||
BusDiv2,
|
||||
BusDiv4,
|
||||
}
|
||||
|
||||
impl AdcClockSource {
|
||||
pub fn is_bus(&self) -> bool {
|
||||
match self {
|
||||
Self::BusDiv1 => true,
|
||||
Self::BusDiv2 => true,
|
||||
Self::BusDiv4 => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bus_div(&self) -> u32 {
|
||||
match self {
|
||||
Self::BusDiv1 => 1,
|
||||
Self::BusDiv2 => 2,
|
||||
Self::BusDiv4 => 4,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub enum HrtimClockSource {
|
||||
#[default]
|
||||
BusClk,
|
||||
PllClk,
|
||||
}
|
||||
|
||||
/// Clocks configutation
|
||||
#[non_exhaustive]
|
||||
#[derive(Default)]
|
||||
@ -36,9 +114,20 @@ pub struct Config {
|
||||
/// - The System clock frequency is either 48MHz or 72MHz
|
||||
/// - APB1 clock has a minimum frequency of 10MHz
|
||||
pub pll48: bool,
|
||||
#[cfg(rcc_f3)]
|
||||
/// ADC clock setup
|
||||
/// - For AHB, a psc of 4 or less must be used
|
||||
pub adc: Option<AdcClockSource>,
|
||||
#[cfg(rcc_f3)]
|
||||
/// ADC clock setup
|
||||
/// - For AHB, a psc of 4 or less must be used
|
||||
pub adc34: Option<AdcClockSource>,
|
||||
#[cfg(stm32f334)]
|
||||
pub hrtim: HrtimClockSource,
|
||||
}
|
||||
|
||||
// Information required to setup the PLL clock
|
||||
#[derive(Clone, Copy)]
|
||||
struct PllConfig {
|
||||
pll_src: Pllsrc,
|
||||
pll_mul: Pllmul,
|
||||
@ -170,6 +259,65 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
})
|
||||
});
|
||||
|
||||
#[cfg(rcc_f3)]
|
||||
let adc = config.adc.map(|adc| {
|
||||
if !adc.is_bus() {
|
||||
RCC.cfgr2().modify(|w| {
|
||||
// Make sure that we're using the PLL
|
||||
pll_config.unwrap();
|
||||
w.set_adc12pres(adc.into());
|
||||
|
||||
Hertz(sysclk / adc as u32)
|
||||
})
|
||||
} else {
|
||||
crate::pac::ADC_COMMON.ccr().modify(|w| {
|
||||
assert!(!(adc.bus_div() == 1 && hpre_bits != Hpre::DIV1));
|
||||
|
||||
w.set_ckmode(adc.into());
|
||||
|
||||
Hertz(sysclk / adc.bus_div() as u32)
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(all(rcc_f3, adc3_common))]
|
||||
let adc34 = config.adc.map(|adc| {
|
||||
if !adc.is_bus() {
|
||||
RCC.cfgr2().modify(|w| {
|
||||
// Make sure that we're using the PLL
|
||||
pll_config.unwrap();
|
||||
w.set_adc12pres(adc.into());
|
||||
|
||||
Hertz(sysclk / adc as u32)
|
||||
})
|
||||
} else {
|
||||
crate::pac::ADC3_COMMON.ccr().modify(|w| {
|
||||
assert!(!(adc.bus_div() == 1 && hpre_bits != Hpre::DIV1));
|
||||
|
||||
w.set_ckmode(adc.into());
|
||||
|
||||
Hertz(sysclk / adc.bus_div() as u32)
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(stm32f334)]
|
||||
let hrtim = match config.hrtim {
|
||||
// Must be configured after the bus is ready, otherwise it won't work
|
||||
HrtimClockSource::BusClk => None,
|
||||
HrtimClockSource::PllClk => {
|
||||
use crate::pac::rcc::vals::Timsw;
|
||||
|
||||
// Make sure that we're using the PLL
|
||||
pll_config.unwrap();
|
||||
assert!((pclk2 == sysclk) || (pclk2 * 2 == sysclk));
|
||||
|
||||
RCC.cfgr3().modify(|w| w.set_hrtim1sw(Timsw::PLL));
|
||||
|
||||
Some(Hertz(sysclk * 2))
|
||||
}
|
||||
};
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sysclk),
|
||||
apb1: Hertz(pclk1),
|
||||
@ -177,6 +325,14 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
apb1_tim: Hertz(pclk1 * timer_mul1),
|
||||
apb2_tim: Hertz(pclk2 * timer_mul2),
|
||||
ahb1: Hertz(hclk),
|
||||
#[cfg(rcc_f3)]
|
||||
adc: adc,
|
||||
#[cfg(all(rcc_f3, adc3_common))]
|
||||
adc34: adc34,
|
||||
#[cfg(all(rcc_f3, not(adc3_common)))]
|
||||
adc34: None,
|
||||
#[cfg(stm32f334)]
|
||||
hrtim: hrtim,
|
||||
});
|
||||
}
|
||||
|
||||
@ -201,9 +357,9 @@ fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) {
|
||||
// Calculates the Multiplier and the Divisor to arrive at
|
||||
// the required System clock from PLL source frequency
|
||||
let get_mul_div = |sysclk, pllsrcclk| {
|
||||
let common_div = gcd(sysclk, pllsrcclk);
|
||||
let mut multiplier = sysclk / common_div;
|
||||
let mut divisor = pllsrcclk / common_div;
|
||||
let bus_div = gcd(sysclk, pllsrcclk);
|
||||
let mut multiplier = sysclk / bus_div;
|
||||
let mut divisor = pllsrcclk / bus_div;
|
||||
// Minimum PLL multiplier is two
|
||||
if multiplier == 1 {
|
||||
multiplier *= 2;
|
||||
|
@ -3,13 +3,12 @@ use core::marker::PhantomData;
|
||||
use embassy_hal_internal::into_ref;
|
||||
use stm32_metapac::rcc::vals::{Mco1, Mco2, Mcopre};
|
||||
|
||||
use super::sealed::RccPeripheral;
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::Speed;
|
||||
use crate::pac::rcc::vals::{Hpre, Ppre, Sw};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::rcc::bd::{BackupDomain, RtcClockSource};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::rtc::{Rtc, RtcClockSource};
|
||||
use crate::time::Hertz;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
@ -38,6 +37,8 @@ pub struct Config {
|
||||
|
||||
pub pll48: bool,
|
||||
pub rtc: Option<RtcClockSource>,
|
||||
pub lsi: bool,
|
||||
pub lse: Option<Hertz>,
|
||||
}
|
||||
|
||||
#[cfg(stm32f410)]
|
||||
@ -360,8 +361,6 @@ fn flash_setup(sysclk: u32) {
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
crate::peripherals::PWR::enable();
|
||||
|
||||
let pllsrcclk = config.hse.map(|hse| hse.0).unwrap_or(HSI_FREQ.0);
|
||||
let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk);
|
||||
let sysclk_on_pll = sysclk != pllsrcclk;
|
||||
@ -501,20 +500,15 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
})
|
||||
});
|
||||
|
||||
match config.rtc {
|
||||
Some(RtcClockSource::LSI) => {
|
||||
RCC.csr().modify(|w| w.set_lsion(true));
|
||||
while !RCC.csr().read().lsirdy() {}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
config.rtc.map(|clock_source| {
|
||||
Rtc::set_clock_source(clock_source);
|
||||
});
|
||||
BackupDomain::configure_ls(
|
||||
config.rtc.unwrap_or(RtcClockSource::NOCLOCK),
|
||||
config.lsi,
|
||||
config.lse.map(|_| Default::default()),
|
||||
);
|
||||
|
||||
let rtc = match config.rtc {
|
||||
Some(RtcClockSource::LSI) => Some(LSI_FREQ),
|
||||
Some(RtcClockSource::LSE) => Some(config.lse.unwrap()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
@ -539,6 +533,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
pllsai: plls.pllsaiclk.map(Hertz),
|
||||
|
||||
rtc: rtc,
|
||||
rtc_hse: None,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::sealed::RccPeripheral;
|
||||
use crate::pac::pwr::vals::Vos;
|
||||
use crate::pac::rcc::vals::{Hpre, Ppre, Sw};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::rcc::bd::{BackupDomain, RtcClockSource};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
|
||||
@ -23,6 +23,9 @@ pub struct Config {
|
||||
pub pclk2: Option<Hertz>,
|
||||
|
||||
pub pll48: bool,
|
||||
pub rtc: Option<RtcClockSource>,
|
||||
pub lsi: bool,
|
||||
pub lse: Option<Hertz>,
|
||||
}
|
||||
|
||||
fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bool) -> PllResults {
|
||||
@ -111,8 +114,6 @@ fn flash_setup(sysclk: u32) {
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
crate::peripherals::PWR::enable();
|
||||
|
||||
if let Some(hse) = config.hse {
|
||||
if config.bypass_hse {
|
||||
assert!((max::HSE_BYPASS_MIN..=max::HSE_BYPASS_MAX).contains(&hse.0));
|
||||
@ -212,10 +213,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
if plls.use_pll {
|
||||
RCC.cr().modify(|w| w.set_pllon(false));
|
||||
|
||||
// enable PWR and setup VOSScale
|
||||
|
||||
RCC.apb1enr().modify(|w| w.set_pwren(true));
|
||||
|
||||
// setup VOSScale
|
||||
let vos_scale = if sysclk <= 144_000_000 {
|
||||
3
|
||||
} else if sysclk <= 168_000_000 {
|
||||
@ -265,6 +263,18 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
})
|
||||
});
|
||||
|
||||
BackupDomain::configure_ls(
|
||||
config.rtc.unwrap_or(RtcClockSource::NOCLOCK),
|
||||
config.lsi,
|
||||
config.lse.map(|_| Default::default()),
|
||||
);
|
||||
|
||||
let rtc = match config.rtc {
|
||||
Some(RtcClockSource::LSI) => Some(LSI_FREQ),
|
||||
Some(RtcClockSource::LSE) => Some(config.lse.unwrap()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sysclk),
|
||||
apb1: Hertz(pclk1),
|
||||
@ -278,6 +288,8 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
ahb3: Hertz(hclk),
|
||||
|
||||
pll48: plls.pll48clk.map(Hertz),
|
||||
|
||||
rtc,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::pac::flash::vals::Latency;
|
||||
use crate::pac::rcc::vals::{self, Hsidiv, Ppre, Sw};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
@ -186,8 +186,8 @@ impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
mux: ClockSrc::HSI16(HSI16Prescaler::NotDivided),
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb_pre: APBPrescaler::NotDivided,
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb_pre: APBPrescaler::DIV1,
|
||||
low_power_run: false,
|
||||
}
|
||||
}
|
||||
@ -377,7 +377,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
let ahb_freq = Hertz(sys_clk) / config.ahb_pre;
|
||||
|
||||
let (apb_freq, apb_tim_freq) = match config.apb_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq.0, ahb_freq.0),
|
||||
APBPrescaler::DIV1 => (ahb_freq.0, ahb_freq.0),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
|
@ -1,8 +1,8 @@
|
||||
use stm32_metapac::flash::vals::Latency;
|
||||
use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw};
|
||||
use stm32_metapac::rcc::vals::{Adcsel, Pllsrc, Sw};
|
||||
use stm32_metapac::FLASH;
|
||||
|
||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::pac::{PWR, RCC};
|
||||
use crate::rcc::sealed::RccPeripheral;
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
@ -14,6 +14,29 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AdcClockSource {
|
||||
NoClk,
|
||||
SysClk,
|
||||
PllP,
|
||||
}
|
||||
|
||||
impl AdcClockSource {
|
||||
pub fn adcsel(&self) -> Adcsel {
|
||||
match self {
|
||||
AdcClockSource::NoClk => Adcsel::NOCLK,
|
||||
AdcClockSource::SysClk => Adcsel::SYSCLK,
|
||||
AdcClockSource::PllP => Adcsel::PLLP,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AdcClockSource {
|
||||
fn default() -> Self {
|
||||
Self::NoClk
|
||||
}
|
||||
}
|
||||
|
||||
/// System clock mux source
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ClockSrc {
|
||||
@ -238,59 +261,29 @@ pub struct Pll {
|
||||
pub div_r: Option<PllR>,
|
||||
}
|
||||
|
||||
impl AHBPrescaler {
|
||||
const fn div(self) -> u32 {
|
||||
match self {
|
||||
AHBPrescaler::NotDivided => 1,
|
||||
AHBPrescaler::Div2 => 2,
|
||||
AHBPrescaler::Div4 => 4,
|
||||
AHBPrescaler::Div8 => 8,
|
||||
AHBPrescaler::Div16 => 16,
|
||||
AHBPrescaler::Div64 => 64,
|
||||
AHBPrescaler::Div128 => 128,
|
||||
AHBPrescaler::Div256 => 256,
|
||||
AHBPrescaler::Div512 => 512,
|
||||
}
|
||||
fn ahb_div(ahb: AHBPrescaler) -> u32 {
|
||||
match ahb {
|
||||
AHBPrescaler::DIV1 => 1,
|
||||
AHBPrescaler::DIV2 => 2,
|
||||
AHBPrescaler::DIV4 => 4,
|
||||
AHBPrescaler::DIV8 => 8,
|
||||
AHBPrescaler::DIV16 => 16,
|
||||
AHBPrescaler::DIV64 => 64,
|
||||
AHBPrescaler::DIV128 => 128,
|
||||
AHBPrescaler::DIV256 => 256,
|
||||
AHBPrescaler::DIV512 => 512,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
impl APBPrescaler {
|
||||
const fn div(self) -> u32 {
|
||||
match self {
|
||||
APBPrescaler::NotDivided => 1,
|
||||
APBPrescaler::Div2 => 2,
|
||||
APBPrescaler::Div4 => 4,
|
||||
APBPrescaler::Div8 => 8,
|
||||
APBPrescaler::Div16 => 16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Ppre> for APBPrescaler {
|
||||
fn into(self) -> Ppre {
|
||||
match self {
|
||||
APBPrescaler::NotDivided => Ppre::DIV1,
|
||||
APBPrescaler::Div2 => Ppre::DIV2,
|
||||
APBPrescaler::Div4 => Ppre::DIV4,
|
||||
APBPrescaler::Div8 => Ppre::DIV8,
|
||||
APBPrescaler::Div16 => Ppre::DIV16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Hpre> for AHBPrescaler {
|
||||
fn into(self) -> Hpre {
|
||||
match self {
|
||||
AHBPrescaler::NotDivided => Hpre::DIV1,
|
||||
AHBPrescaler::Div2 => Hpre::DIV2,
|
||||
AHBPrescaler::Div4 => Hpre::DIV4,
|
||||
AHBPrescaler::Div8 => Hpre::DIV8,
|
||||
AHBPrescaler::Div16 => Hpre::DIV16,
|
||||
AHBPrescaler::Div64 => Hpre::DIV64,
|
||||
AHBPrescaler::Div128 => Hpre::DIV128,
|
||||
AHBPrescaler::Div256 => Hpre::DIV256,
|
||||
AHBPrescaler::Div512 => Hpre::DIV512,
|
||||
}
|
||||
fn apb_div(apb: APBPrescaler) -> u32 {
|
||||
match apb {
|
||||
APBPrescaler::DIV1 => 1,
|
||||
APBPrescaler::DIV2 => 2,
|
||||
APBPrescaler::DIV4 => 4,
|
||||
APBPrescaler::DIV8 => 8,
|
||||
APBPrescaler::DIV16 => 16,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -327,6 +320,8 @@ pub struct Config {
|
||||
pub pll: Option<Pll>,
|
||||
/// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals.
|
||||
pub clock_48mhz_src: Option<Clock48MhzSrc>,
|
||||
pub adc12_clock_source: AdcClockSource,
|
||||
pub adc345_clock_source: AdcClockSource,
|
||||
}
|
||||
|
||||
/// Configuration for the Clock Recovery System (CRS) used to trim the HSI48 oscillator.
|
||||
@ -340,12 +335,14 @@ impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
mux: ClockSrc::HSI16,
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb1_pre: APBPrescaler::NotDivided,
|
||||
apb2_pre: APBPrescaler::NotDivided,
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
low_power_run: false,
|
||||
pll: None,
|
||||
clock_48mhz_src: None,
|
||||
adc12_clock_source: Default::default(),
|
||||
adc345_clock_source: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -485,22 +482,22 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
});
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => sys_clk / pre.div(),
|
||||
AHBPrescaler::DIV1 => sys_clk,
|
||||
pre => sys_clk / ahb_div(pre),
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let freq = ahb_freq / pre.div();
|
||||
let freq = ahb_freq / apb_div(pre);
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let freq = ahb_freq / pre.div();
|
||||
let freq = ahb_freq / apb_div(pre);
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
@ -549,6 +546,29 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
RCC.ccipr().modify(|w| w.set_clk48sel(source));
|
||||
}
|
||||
|
||||
RCC.ccipr()
|
||||
.modify(|w| w.set_adc12sel(config.adc12_clock_source.adcsel()));
|
||||
RCC.ccipr()
|
||||
.modify(|w| w.set_adc345sel(config.adc345_clock_source.adcsel()));
|
||||
|
||||
let adc12_ck = match config.adc12_clock_source {
|
||||
AdcClockSource::NoClk => None,
|
||||
AdcClockSource::PllP => match &pll_freq {
|
||||
Some(pll) => pll.pll_p,
|
||||
None => None,
|
||||
},
|
||||
AdcClockSource::SysClk => Some(Hertz(sys_clk)),
|
||||
};
|
||||
|
||||
let adc345_ck = match config.adc345_clock_source {
|
||||
AdcClockSource::NoClk => None,
|
||||
AdcClockSource::PllP => match &pll_freq {
|
||||
Some(pll) => pll.pll_p,
|
||||
None => None,
|
||||
},
|
||||
AdcClockSource::SysClk => Some(Hertz(sys_clk)),
|
||||
};
|
||||
|
||||
if config.low_power_run {
|
||||
assert!(sys_clk <= 2_000_000);
|
||||
PWR.cr1().modify(|w| w.set_lpr(true));
|
||||
@ -562,5 +582,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
apb1_tim: Hertz(apb1_tim_freq),
|
||||
apb2: Hertz(apb2_freq),
|
||||
apb2_tim: Hertz(apb2_tim_freq),
|
||||
adc: adc12_ck,
|
||||
adc34: adc345_ck,
|
||||
});
|
||||
}
|
||||
|
772
embassy-stm32/src/rcc/h.rs
Normal file
772
embassy-stm32/src/rcc/h.rs
Normal file
@ -0,0 +1,772 @@
|
||||
use core::ops::RangeInclusive;
|
||||
|
||||
use crate::pac;
|
||||
use crate::pac::pwr::vals::Vos;
|
||||
#[cfg(stm32h5)]
|
||||
pub use crate::pac::rcc::vals::Adcdacsel as AdcClockSource;
|
||||
#[cfg(stm32h7)]
|
||||
pub use crate::pac::rcc::vals::Adcsel as AdcClockSource;
|
||||
pub use crate::pac::rcc::vals::Ckpersel as PerClockSource;
|
||||
use crate::pac::rcc::vals::{Ckpersel, Hsidiv, Pllrge, Pllsrc, Pllvcosel, Sw, Timpre};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(64_000_000);
|
||||
|
||||
/// CSI speed
|
||||
pub const CSI_FREQ: Hertz = Hertz(4_000_000);
|
||||
|
||||
/// HSI48 speed
|
||||
pub const HSI48_FREQ: Hertz = Hertz(48_000_000);
|
||||
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
|
||||
const VCO_RANGE: RangeInclusive<u32> = 150_000_000..=420_000_000;
|
||||
#[cfg(any(stm32h5, pwr_h7rm0455))]
|
||||
const VCO_WIDE_RANGE: RangeInclusive<u32> = 128_000_000..=560_000_000;
|
||||
#[cfg(pwr_h7rm0468)]
|
||||
const VCO_WIDE_RANGE: RangeInclusive<u32> = 192_000_000..=836_000_000;
|
||||
#[cfg(any(pwr_h7rm0399, pwr_h7rm0433))]
|
||||
const VCO_WIDE_RANGE: RangeInclusive<u32> = 192_000_000..=960_000_000;
|
||||
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum VoltageScale {
|
||||
Scale0,
|
||||
Scale1,
|
||||
Scale2,
|
||||
Scale3,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum HseMode {
|
||||
/// crystal/ceramic oscillator (HSEBYP=0)
|
||||
Oscillator,
|
||||
/// external analog clock (low swing) (HSEBYP=1, HSEEXT=0)
|
||||
Bypass,
|
||||
/// external digital clock (full swing) (HSEBYP=1, HSEEXT=1)
|
||||
#[cfg(any(rcc_h5, rcc_h50))]
|
||||
BypassDigital,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub struct Hse {
|
||||
/// HSE frequency.
|
||||
pub freq: Hertz,
|
||||
/// HSE mode.
|
||||
pub mode: HseMode,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum Hsi {
|
||||
/// 64Mhz
|
||||
Mhz64,
|
||||
/// 32Mhz (divided by 2)
|
||||
Mhz32,
|
||||
/// 16Mhz (divided by 4)
|
||||
Mhz16,
|
||||
/// 8Mhz (divided by 8)
|
||||
Mhz8,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum Sysclk {
|
||||
/// HSI selected as sysclk
|
||||
HSI,
|
||||
/// HSE selected as sysclk
|
||||
HSE,
|
||||
/// CSI selected as sysclk
|
||||
CSI,
|
||||
/// PLL1_P selected as sysclk
|
||||
Pll1P,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum PllSource {
|
||||
Hsi,
|
||||
Csi,
|
||||
Hse,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Pll {
|
||||
/// Source clock selection.
|
||||
#[cfg(stm32h5)]
|
||||
pub source: PllSource,
|
||||
|
||||
/// PLL pre-divider (DIVM). Must be between 1 and 63.
|
||||
pub prediv: u8,
|
||||
|
||||
/// PLL multiplication factor. Must be between 4 and 512.
|
||||
pub mul: u16,
|
||||
|
||||
/// PLL P division factor. If None, PLL P output is disabled. Must be between 1 and 128.
|
||||
/// On PLL1, it must be even (in particular, it cannot be 1.)
|
||||
pub divp: Option<u16>,
|
||||
/// PLL Q division factor. If None, PLL Q output is disabled. Must be between 1 and 128.
|
||||
pub divq: Option<u16>,
|
||||
/// PLL R division factor. If None, PLL R output is disabled. Must be between 1 and 128.
|
||||
pub divr: Option<u16>,
|
||||
}
|
||||
|
||||
fn apb_div_tim(apb: &APBPrescaler, clk: Hertz, tim: TimerPrescaler) -> Hertz {
|
||||
match (tim, apb) {
|
||||
(TimerPrescaler::DefaultX2, APBPrescaler::DIV1) => clk,
|
||||
(TimerPrescaler::DefaultX2, APBPrescaler::DIV2) => clk,
|
||||
(TimerPrescaler::DefaultX2, APBPrescaler::DIV4) => clk / 2u32,
|
||||
(TimerPrescaler::DefaultX2, APBPrescaler::DIV8) => clk / 4u32,
|
||||
(TimerPrescaler::DefaultX2, APBPrescaler::DIV16) => clk / 8u32,
|
||||
|
||||
(TimerPrescaler::DefaultX4, APBPrescaler::DIV1) => clk,
|
||||
(TimerPrescaler::DefaultX4, APBPrescaler::DIV2) => clk,
|
||||
(TimerPrescaler::DefaultX4, APBPrescaler::DIV4) => clk,
|
||||
(TimerPrescaler::DefaultX4, APBPrescaler::DIV8) => clk / 2u32,
|
||||
(TimerPrescaler::DefaultX4, APBPrescaler::DIV16) => clk / 4u32,
|
||||
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Timer prescaler
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum TimerPrescaler {
|
||||
/// The timers kernel clock is equal to hclk if PPREx corresponds to a
|
||||
/// division by 1 or 2, else it is equal to 2*pclk
|
||||
DefaultX2,
|
||||
|
||||
/// The timers kernel clock is equal to hclk if PPREx corresponds to a
|
||||
/// division by 1, 2 or 4, else it is equal to 4*pclk
|
||||
DefaultX4,
|
||||
}
|
||||
|
||||
impl From<TimerPrescaler> for Timpre {
|
||||
fn from(value: TimerPrescaler) -> Self {
|
||||
match value {
|
||||
TimerPrescaler::DefaultX2 => Timpre::DEFAULTX2,
|
||||
TimerPrescaler::DefaultX4 => Timpre::DEFAULTX4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration of the core clocks
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
pub hsi: Option<Hsi>,
|
||||
pub hse: Option<Hse>,
|
||||
pub csi: bool,
|
||||
pub hsi48: bool,
|
||||
pub sys: Sysclk,
|
||||
|
||||
#[cfg(stm32h7)]
|
||||
pub pll_src: PllSource,
|
||||
|
||||
pub pll1: Option<Pll>,
|
||||
pub pll2: Option<Pll>,
|
||||
#[cfg(any(rcc_h5, stm32h7))]
|
||||
pub pll3: Option<Pll>,
|
||||
|
||||
pub d1c_pre: AHBPrescaler,
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb1_pre: APBPrescaler,
|
||||
pub apb2_pre: APBPrescaler,
|
||||
pub apb3_pre: APBPrescaler,
|
||||
#[cfg(stm32h7)]
|
||||
pub apb4_pre: APBPrescaler,
|
||||
|
||||
pub per_clock_source: PerClockSource,
|
||||
pub adc_clock_source: AdcClockSource,
|
||||
pub timer_prescaler: TimerPrescaler,
|
||||
pub voltage_scale: VoltageScale,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hsi: Some(Hsi::Mhz64),
|
||||
hse: None,
|
||||
csi: false,
|
||||
hsi48: false,
|
||||
sys: Sysclk::HSI,
|
||||
#[cfg(stm32h7)]
|
||||
pll_src: PllSource::Hsi,
|
||||
pll1: None,
|
||||
pll2: None,
|
||||
#[cfg(any(rcc_h5, stm32h7))]
|
||||
pll3: None,
|
||||
|
||||
d1c_pre: AHBPrescaler::DIV1,
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
apb3_pre: APBPrescaler::DIV1,
|
||||
#[cfg(stm32h7)]
|
||||
apb4_pre: APBPrescaler::DIV1,
|
||||
|
||||
per_clock_source: PerClockSource::HSI,
|
||||
adc_clock_source: AdcClockSource::from_bits(0), // PLL2_P on H7, HCLK on H5
|
||||
timer_prescaler: TimerPrescaler::DefaultX2,
|
||||
voltage_scale: VoltageScale::Scale0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
// NB. The lower bytes of CR3 can only be written once after
|
||||
// POR, and must be written with a valid combination. Refer to
|
||||
// RM0433 Rev 7 6.8.4. This is partially enforced by dropping
|
||||
// `self` at the end of this method, but of course we cannot
|
||||
// know what happened between the previous POR and here.
|
||||
#[cfg(pwr_h7rm0433)]
|
||||
PWR.cr3().modify(|w| {
|
||||
w.set_scuen(true);
|
||||
w.set_ldoen(true);
|
||||
w.set_bypass(false);
|
||||
});
|
||||
|
||||
#[cfg(any(pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))]
|
||||
PWR.cr3().modify(|w| {
|
||||
// hardcode "Direct SPMS" for now, this is what works on nucleos with the
|
||||
// default solderbridge configuration.
|
||||
w.set_sden(true);
|
||||
w.set_ldoen(false);
|
||||
});
|
||||
|
||||
// Validate the supply configuration. If you are stuck here, it is
|
||||
// because the voltages on your board do not match those specified
|
||||
// in the D3CR.VOS and CR3.SDLEVEL fields. By default after reset
|
||||
// VOS = Scale 3, so check that the voltage on the VCAP pins =
|
||||
// 1.0V.
|
||||
#[cfg(any(pwr_h7rm0433, pwr_h7rm0399, pwr_h7rm0455, pwr_h7rm0468))]
|
||||
while !PWR.csr1().read().actvosrdy() {}
|
||||
|
||||
// Configure voltage scale.
|
||||
#[cfg(any(pwr_h5, pwr_h50))]
|
||||
{
|
||||
PWR.voscr().modify(|w| {
|
||||
w.set_vos(match config.voltage_scale {
|
||||
VoltageScale::Scale0 => Vos::SCALE0,
|
||||
VoltageScale::Scale1 => Vos::SCALE1,
|
||||
VoltageScale::Scale2 => Vos::SCALE2,
|
||||
VoltageScale::Scale3 => Vos::SCALE3,
|
||||
})
|
||||
});
|
||||
while !PWR.vossr().read().vosrdy() {}
|
||||
}
|
||||
|
||||
#[cfg(syscfg_h7)]
|
||||
{
|
||||
// in chips without the overdrive bit, we can go from any scale to any scale directly.
|
||||
PWR.d3cr().modify(|w| {
|
||||
w.set_vos(match config.voltage_scale {
|
||||
VoltageScale::Scale0 => Vos::SCALE0,
|
||||
VoltageScale::Scale1 => Vos::SCALE1,
|
||||
VoltageScale::Scale2 => Vos::SCALE2,
|
||||
VoltageScale::Scale3 => Vos::SCALE3,
|
||||
})
|
||||
});
|
||||
while !PWR.d3cr().read().vosrdy() {}
|
||||
}
|
||||
|
||||
#[cfg(syscfg_h7od)]
|
||||
{
|
||||
match config.voltage_scale {
|
||||
VoltageScale::Scale0 => {
|
||||
// to go to scale0, we must go to Scale1 first...
|
||||
PWR.d3cr().modify(|w| w.set_vos(Vos::SCALE1));
|
||||
while !PWR.d3cr().read().vosrdy() {}
|
||||
|
||||
// Then enable overdrive.
|
||||
critical_section::with(|_| pac::SYSCFG.pwrcr().modify(|w| w.set_oden(1)));
|
||||
while !PWR.d3cr().read().vosrdy() {}
|
||||
}
|
||||
_ => {
|
||||
// for all other scales, we can go directly.
|
||||
PWR.d3cr().modify(|w| {
|
||||
w.set_vos(match config.voltage_scale {
|
||||
VoltageScale::Scale0 => unreachable!(),
|
||||
VoltageScale::Scale1 => Vos::SCALE1,
|
||||
VoltageScale::Scale2 => Vos::SCALE2,
|
||||
VoltageScale::Scale3 => Vos::SCALE3,
|
||||
})
|
||||
});
|
||||
while !PWR.d3cr().read().vosrdy() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Configure HSI
|
||||
let hsi = match config.hsi {
|
||||
None => {
|
||||
RCC.cr().modify(|w| w.set_hsion(false));
|
||||
None
|
||||
}
|
||||
Some(hsi) => {
|
||||
let (freq, hsidiv) = match hsi {
|
||||
Hsi::Mhz64 => (HSI_FREQ / 1u32, Hsidiv::DIV1),
|
||||
Hsi::Mhz32 => (HSI_FREQ / 2u32, Hsidiv::DIV2),
|
||||
Hsi::Mhz16 => (HSI_FREQ / 4u32, Hsidiv::DIV4),
|
||||
Hsi::Mhz8 => (HSI_FREQ / 8u32, Hsidiv::DIV8),
|
||||
};
|
||||
RCC.cr().modify(|w| {
|
||||
w.set_hsidiv(hsidiv);
|
||||
w.set_hsion(true);
|
||||
});
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
Some(freq)
|
||||
}
|
||||
};
|
||||
|
||||
// Configure HSE
|
||||
let hse = match config.hse {
|
||||
None => {
|
||||
RCC.cr().modify(|w| w.set_hseon(false));
|
||||
None
|
||||
}
|
||||
Some(hse) => {
|
||||
RCC.cr().modify(|w| {
|
||||
w.set_hsebyp(hse.mode != HseMode::Oscillator);
|
||||
#[cfg(any(rcc_h5, rcc_h50))]
|
||||
w.set_hseext(match hse.mode {
|
||||
HseMode::Oscillator | HseMode::Bypass => pac::rcc::vals::Hseext::ANALOG,
|
||||
HseMode::BypassDigital => pac::rcc::vals::Hseext::DIGITAL,
|
||||
});
|
||||
});
|
||||
RCC.cr().modify(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
Some(hse.freq)
|
||||
}
|
||||
};
|
||||
|
||||
// Configure HSI48.
|
||||
RCC.cr().modify(|w| w.set_hsi48on(config.hsi48));
|
||||
let _hsi48 = match config.hsi48 {
|
||||
false => None,
|
||||
true => {
|
||||
while !RCC.cr().read().hsi48rdy() {}
|
||||
Some(CSI_FREQ)
|
||||
}
|
||||
};
|
||||
|
||||
// Configure CSI.
|
||||
RCC.cr().modify(|w| w.set_csion(config.csi));
|
||||
let csi = match config.csi {
|
||||
false => None,
|
||||
true => {
|
||||
while !RCC.cr().read().csirdy() {}
|
||||
Some(CSI_FREQ)
|
||||
}
|
||||
};
|
||||
|
||||
// Configure PLLs.
|
||||
let pll_input = PllInput {
|
||||
csi,
|
||||
hse,
|
||||
hsi,
|
||||
#[cfg(stm32h7)]
|
||||
source: config.pll_src,
|
||||
};
|
||||
let pll1 = init_pll(0, config.pll1, &pll_input);
|
||||
let pll2 = init_pll(1, config.pll2, &pll_input);
|
||||
#[cfg(any(rcc_h5, stm32h7))]
|
||||
let _pll3 = init_pll(2, config.pll3, &pll_input);
|
||||
|
||||
// Configure sysclk
|
||||
let (sys, sw) = match config.sys {
|
||||
Sysclk::HSI => (unwrap!(hsi), Sw::HSI),
|
||||
Sysclk::HSE => (unwrap!(hse), Sw::HSE),
|
||||
Sysclk::CSI => (unwrap!(csi), Sw::CSI),
|
||||
Sysclk::Pll1P => (unwrap!(pll1.p), Sw::PLL1),
|
||||
};
|
||||
|
||||
// Check limits.
|
||||
#[cfg(stm32h5)]
|
||||
let (hclk_max, pclk_max) = match config.voltage_scale {
|
||||
VoltageScale::Scale0 => (Hertz(250_000_000), Hertz(250_000_000)),
|
||||
VoltageScale::Scale1 => (Hertz(200_000_000), Hertz(200_000_000)),
|
||||
VoltageScale::Scale2 => (Hertz(150_000_000), Hertz(150_000_000)),
|
||||
VoltageScale::Scale3 => (Hertz(100_000_000), Hertz(100_000_000)),
|
||||
};
|
||||
#[cfg(stm32h7)]
|
||||
let (d1cpre_clk_max, hclk_max, pclk_max) = match config.voltage_scale {
|
||||
VoltageScale::Scale0 => (Hertz(480_000_000), Hertz(240_000_000), Hertz(120_000_000)),
|
||||
VoltageScale::Scale1 => (Hertz(400_000_000), Hertz(200_000_000), Hertz(100_000_000)),
|
||||
VoltageScale::Scale2 => (Hertz(300_000_000), Hertz(150_000_000), Hertz(75_000_000)),
|
||||
VoltageScale::Scale3 => (Hertz(200_000_000), Hertz(100_000_000), Hertz(50_000_000)),
|
||||
};
|
||||
|
||||
#[cfg(stm32h7)]
|
||||
let hclk = {
|
||||
let d1cpre_clk = sys / config.d1c_pre;
|
||||
assert!(d1cpre_clk <= d1cpre_clk_max);
|
||||
sys / config.ahb_pre
|
||||
};
|
||||
#[cfg(stm32h5)]
|
||||
let hclk = sys / config.ahb_pre;
|
||||
assert!(hclk <= hclk_max);
|
||||
|
||||
let apb1 = hclk / config.apb1_pre;
|
||||
let apb1_tim = apb_div_tim(&config.apb1_pre, hclk, config.timer_prescaler);
|
||||
assert!(apb1 <= pclk_max);
|
||||
let apb2 = hclk / config.apb2_pre;
|
||||
let apb2_tim = apb_div_tim(&config.apb2_pre, hclk, config.timer_prescaler);
|
||||
assert!(apb2 <= pclk_max);
|
||||
let apb3 = hclk / config.apb3_pre;
|
||||
assert!(apb3 <= pclk_max);
|
||||
#[cfg(stm32h7)]
|
||||
let apb4 = hclk / config.apb4_pre;
|
||||
#[cfg(stm32h7)]
|
||||
assert!(apb4 <= pclk_max);
|
||||
|
||||
let _per_ck = match config.per_clock_source {
|
||||
Ckpersel::HSI => hsi,
|
||||
Ckpersel::CSI => csi,
|
||||
Ckpersel::HSE => hse,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
#[cfg(stm32h7)]
|
||||
let adc = match config.adc_clock_source {
|
||||
AdcClockSource::PLL2_P => pll2.p,
|
||||
AdcClockSource::PLL3_R => _pll3.r,
|
||||
AdcClockSource::PER => _per_ck,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
#[cfg(stm32h5)]
|
||||
let adc = match config.adc_clock_source {
|
||||
AdcClockSource::HCLK => Some(hclk),
|
||||
AdcClockSource::SYSCLK => Some(sys),
|
||||
AdcClockSource::PLL2_R => pll2.r,
|
||||
AdcClockSource::HSE => hse,
|
||||
AdcClockSource::HSI_KER => hsi,
|
||||
AdcClockSource::CSI_KER => csi,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
flash_setup(hclk, config.voltage_scale);
|
||||
|
||||
#[cfg(stm32h7)]
|
||||
{
|
||||
RCC.d1cfgr().modify(|w| {
|
||||
w.set_d1cpre(config.d1c_pre);
|
||||
w.set_d1ppre(config.apb3_pre);
|
||||
w.set_hpre(config.ahb_pre);
|
||||
});
|
||||
// Ensure core prescaler value is valid before future lower core voltage
|
||||
while RCC.d1cfgr().read().d1cpre() != config.d1c_pre {}
|
||||
|
||||
RCC.d2cfgr().modify(|w| {
|
||||
w.set_d2ppre1(config.apb1_pre);
|
||||
w.set_d2ppre2(config.apb2_pre);
|
||||
});
|
||||
RCC.d3cfgr().modify(|w| {
|
||||
w.set_d3ppre(config.apb4_pre);
|
||||
});
|
||||
|
||||
RCC.d1ccipr().modify(|w| {
|
||||
w.set_ckpersel(config.per_clock_source);
|
||||
});
|
||||
RCC.d3ccipr().modify(|w| {
|
||||
w.set_adcsel(config.adc_clock_source);
|
||||
});
|
||||
}
|
||||
#[cfg(stm32h5)]
|
||||
{
|
||||
// Set hpre
|
||||
RCC.cfgr2().modify(|w| w.set_hpre(config.ahb_pre));
|
||||
while RCC.cfgr2().read().hpre() != config.ahb_pre {}
|
||||
|
||||
// set ppre
|
||||
RCC.cfgr2().modify(|w| {
|
||||
w.set_ppre1(config.apb1_pre);
|
||||
w.set_ppre2(config.apb2_pre);
|
||||
w.set_ppre3(config.apb3_pre);
|
||||
});
|
||||
|
||||
RCC.ccipr5().modify(|w| {
|
||||
w.set_ckpersel(config.per_clock_source);
|
||||
w.set_adcdacsel(config.adc_clock_source)
|
||||
});
|
||||
}
|
||||
|
||||
RCC.cfgr().modify(|w| w.set_timpre(config.timer_prescaler.into()));
|
||||
|
||||
RCC.cfgr().modify(|w| w.set_sw(sw));
|
||||
while RCC.cfgr().read().sws() != sw {}
|
||||
|
||||
// IO compensation cell - Requires CSI clock and SYSCFG
|
||||
#[cfg(stm32h7)] // TODO h5
|
||||
if csi.is_some() {
|
||||
// Enable the compensation cell, using back-bias voltage code
|
||||
// provide by the cell.
|
||||
critical_section::with(|_| {
|
||||
pac::SYSCFG.cccsr().modify(|w| {
|
||||
w.set_en(true);
|
||||
w.set_cs(false);
|
||||
w.set_hslv(false);
|
||||
})
|
||||
});
|
||||
while !pac::SYSCFG.cccsr().read().ready() {}
|
||||
}
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys,
|
||||
ahb1: hclk,
|
||||
ahb2: hclk,
|
||||
ahb3: hclk,
|
||||
ahb4: hclk,
|
||||
apb1,
|
||||
apb2,
|
||||
apb3,
|
||||
#[cfg(stm32h7)]
|
||||
apb4,
|
||||
apb1_tim,
|
||||
apb2_tim,
|
||||
adc: adc,
|
||||
});
|
||||
}
|
||||
|
||||
struct PllInput {
|
||||
hsi: Option<Hertz>,
|
||||
hse: Option<Hertz>,
|
||||
csi: Option<Hertz>,
|
||||
#[cfg(stm32h7)]
|
||||
source: PllSource,
|
||||
}
|
||||
|
||||
struct PllOutput {
|
||||
p: Option<Hertz>,
|
||||
#[allow(dead_code)]
|
||||
q: Option<Hertz>,
|
||||
#[allow(dead_code)]
|
||||
r: Option<Hertz>,
|
||||
}
|
||||
|
||||
fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
||||
let Some(config) = config else {
|
||||
// Stop PLL
|
||||
RCC.cr().modify(|w| w.set_pllon(num, false));
|
||||
while RCC.cr().read().pllrdy(num) {}
|
||||
|
||||
// "To save power when PLL1 is not used, the value of PLL1M must be set to 0.""
|
||||
#[cfg(stm32h7)]
|
||||
RCC.pllckselr().write(|w| w.set_divm(num, 0));
|
||||
#[cfg(stm32h5)]
|
||||
RCC.pllcfgr(num).write(|w| w.set_divm(0));
|
||||
|
||||
return PllOutput {
|
||||
p: None,
|
||||
q: None,
|
||||
r: None,
|
||||
};
|
||||
};
|
||||
|
||||
assert!(1 <= config.prediv && config.prediv <= 63);
|
||||
assert!(4 <= config.mul && config.mul <= 512);
|
||||
|
||||
#[cfg(stm32h5)]
|
||||
let source = config.source;
|
||||
#[cfg(stm32h7)]
|
||||
let source = input.source;
|
||||
|
||||
let (in_clk, src) = match source {
|
||||
PllSource::Hsi => (unwrap!(input.hsi), Pllsrc::HSI),
|
||||
PllSource::Hse => (unwrap!(input.hse), Pllsrc::HSE),
|
||||
PllSource::Csi => (unwrap!(input.csi), Pllsrc::CSI),
|
||||
};
|
||||
|
||||
let ref_clk = in_clk / config.prediv as u32;
|
||||
|
||||
let ref_range = match ref_clk.0 {
|
||||
..=1_999_999 => Pllrge::RANGE1,
|
||||
..=3_999_999 => Pllrge::RANGE2,
|
||||
..=7_999_999 => Pllrge::RANGE4,
|
||||
..=16_000_000 => Pllrge::RANGE8,
|
||||
x => panic!("pll ref_clk out of range: {} mhz", x),
|
||||
};
|
||||
|
||||
// The smaller range (150 to 420 MHz) must
|
||||
// be chosen when the reference clock frequency is lower than 2 MHz.
|
||||
let wide_allowed = ref_range != Pllrge::RANGE1;
|
||||
|
||||
let vco_clk = ref_clk * config.mul;
|
||||
let vco_range = if VCO_RANGE.contains(&vco_clk.0) {
|
||||
Pllvcosel::MEDIUMVCO
|
||||
} else if wide_allowed && VCO_WIDE_RANGE.contains(&vco_clk.0) {
|
||||
Pllvcosel::WIDEVCO
|
||||
} else {
|
||||
panic!("pll vco_clk out of range: {} mhz", vco_clk.0)
|
||||
};
|
||||
|
||||
let p = config.divp.map(|div| {
|
||||
assert!(1 <= div && div <= 128);
|
||||
if num == 0 {
|
||||
// on PLL1, DIVP must be even.
|
||||
assert!(div % 2 == 0);
|
||||
}
|
||||
|
||||
vco_clk / div
|
||||
});
|
||||
let q = config.divq.map(|div| {
|
||||
assert!(1 <= div && div <= 128);
|
||||
vco_clk / div
|
||||
});
|
||||
let r = config.divr.map(|div| {
|
||||
assert!(1 <= div && div <= 128);
|
||||
vco_clk / div
|
||||
});
|
||||
|
||||
#[cfg(stm32h5)]
|
||||
RCC.pllcfgr(num).write(|w| {
|
||||
w.set_pllsrc(src);
|
||||
w.set_divm(config.prediv);
|
||||
w.set_pllvcosel(vco_range);
|
||||
w.set_pllrge(ref_range);
|
||||
w.set_pllfracen(false);
|
||||
w.set_pllpen(p.is_some());
|
||||
w.set_pllqen(q.is_some());
|
||||
w.set_pllren(r.is_some());
|
||||
});
|
||||
|
||||
#[cfg(stm32h7)]
|
||||
{
|
||||
RCC.pllckselr().modify(|w| {
|
||||
w.set_divm(num, config.prediv);
|
||||
w.set_pllsrc(src);
|
||||
});
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllvcosel(num, vco_range);
|
||||
w.set_pllrge(num, ref_range);
|
||||
w.set_pllfracen(num, false);
|
||||
w.set_divpen(num, p.is_some());
|
||||
w.set_divqen(num, q.is_some());
|
||||
w.set_divren(num, r.is_some());
|
||||
});
|
||||
}
|
||||
|
||||
RCC.plldivr(num).write(|w| {
|
||||
w.set_plln(config.mul - 1);
|
||||
w.set_pllp((config.divp.unwrap_or(1) - 1) as u8);
|
||||
w.set_pllq((config.divq.unwrap_or(1) - 1) as u8);
|
||||
w.set_pllr((config.divr.unwrap_or(1) - 1) as u8);
|
||||
});
|
||||
|
||||
RCC.cr().modify(|w| w.set_pllon(num, true));
|
||||
while !RCC.cr().read().pllrdy(num) {}
|
||||
|
||||
PllOutput { p, q, r }
|
||||
}
|
||||
|
||||
fn flash_setup(clk: Hertz, vos: VoltageScale) {
|
||||
// RM0481 Rev 1, table 37
|
||||
// LATENCY WRHIGHFREQ VOS3 VOS2 VOS1 VOS0
|
||||
// 0 0 0 to 20 MHz 0 to 30 MHz 0 to 34 MHz 0 to 42 MHz
|
||||
// 1 0 20 to 40 MHz 30 to 60 MHz 34 to 68 MHz 42 to 84 MHz
|
||||
// 2 1 40 to 60 MHz 60 to 90 MHz 68 to 102 MHz 84 to 126 MHz
|
||||
// 3 1 60 to 80 MHz 90 to 120 MHz 102 to 136 MHz 126 to 168 MHz
|
||||
// 4 2 80 to 100 MHz 120 to 150 MHz 136 to 170 MHz 168 to 210 MHz
|
||||
// 5 2 170 to 200 MHz 210 to 250 MHz
|
||||
#[cfg(stm32h5)]
|
||||
let (latency, wrhighfreq) = match (vos, clk.0) {
|
||||
(VoltageScale::Scale0, ..=42_000_000) => (0, 0),
|
||||
(VoltageScale::Scale0, ..=84_000_000) => (1, 0),
|
||||
(VoltageScale::Scale0, ..=126_000_000) => (2, 1),
|
||||
(VoltageScale::Scale0, ..=168_000_000) => (3, 1),
|
||||
(VoltageScale::Scale0, ..=210_000_000) => (4, 2),
|
||||
(VoltageScale::Scale0, ..=250_000_000) => (5, 2),
|
||||
|
||||
(VoltageScale::Scale1, ..=34_000_000) => (0, 0),
|
||||
(VoltageScale::Scale1, ..=68_000_000) => (1, 0),
|
||||
(VoltageScale::Scale1, ..=102_000_000) => (2, 1),
|
||||
(VoltageScale::Scale1, ..=136_000_000) => (3, 1),
|
||||
(VoltageScale::Scale1, ..=170_000_000) => (4, 2),
|
||||
(VoltageScale::Scale1, ..=200_000_000) => (5, 2),
|
||||
|
||||
(VoltageScale::Scale2, ..=30_000_000) => (0, 0),
|
||||
(VoltageScale::Scale2, ..=60_000_000) => (1, 0),
|
||||
(VoltageScale::Scale2, ..=90_000_000) => (2, 1),
|
||||
(VoltageScale::Scale2, ..=120_000_000) => (3, 1),
|
||||
(VoltageScale::Scale2, ..=150_000_000) => (4, 2),
|
||||
|
||||
(VoltageScale::Scale3, ..=20_000_000) => (0, 0),
|
||||
(VoltageScale::Scale3, ..=40_000_000) => (1, 0),
|
||||
(VoltageScale::Scale3, ..=60_000_000) => (2, 1),
|
||||
(VoltageScale::Scale3, ..=80_000_000) => (3, 1),
|
||||
(VoltageScale::Scale3, ..=100_000_000) => (4, 2),
|
||||
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
#[cfg(flash_h7)]
|
||||
let (latency, wrhighfreq) = match (vos, clk.0) {
|
||||
// VOS 0 range VCORE 1.26V - 1.40V
|
||||
(VoltageScale::Scale0, ..=70_000_000) => (0, 0),
|
||||
(VoltageScale::Scale0, ..=140_000_000) => (1, 1),
|
||||
(VoltageScale::Scale0, ..=185_000_000) => (2, 1),
|
||||
(VoltageScale::Scale0, ..=210_000_000) => (2, 2),
|
||||
(VoltageScale::Scale0, ..=225_000_000) => (3, 2),
|
||||
(VoltageScale::Scale0, ..=240_000_000) => (4, 2),
|
||||
// VOS 1 range VCORE 1.15V - 1.26V
|
||||
(VoltageScale::Scale1, ..=70_000_000) => (0, 0),
|
||||
(VoltageScale::Scale1, ..=140_000_000) => (1, 1),
|
||||
(VoltageScale::Scale1, ..=185_000_000) => (2, 1),
|
||||
(VoltageScale::Scale1, ..=210_000_000) => (2, 2),
|
||||
(VoltageScale::Scale1, ..=225_000_000) => (3, 2),
|
||||
// VOS 2 range VCORE 1.05V - 1.15V
|
||||
(VoltageScale::Scale2, ..=55_000_000) => (0, 0),
|
||||
(VoltageScale::Scale2, ..=110_000_000) => (1, 1),
|
||||
(VoltageScale::Scale2, ..=165_000_000) => (2, 1),
|
||||
(VoltageScale::Scale2, ..=224_000_000) => (3, 2),
|
||||
// VOS 3 range VCORE 0.95V - 1.05V
|
||||
(VoltageScale::Scale3, ..=45_000_000) => (0, 0),
|
||||
(VoltageScale::Scale3, ..=90_000_000) => (1, 1),
|
||||
(VoltageScale::Scale3, ..=135_000_000) => (2, 1),
|
||||
(VoltageScale::Scale3, ..=180_000_000) => (3, 2),
|
||||
(VoltageScale::Scale3, ..=224_000_000) => (4, 2),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// See RM0455 Rev 10 Table 16. FLASH recommended number of wait
|
||||
// states and programming delay
|
||||
#[cfg(flash_h7ab)]
|
||||
let (latency, wrhighfreq) = match (vos, clk.0) {
|
||||
// VOS 0 range VCORE 1.25V - 1.35V
|
||||
(VoltageScale::Scale0, ..=42_000_000) => (0, 0),
|
||||
(VoltageScale::Scale0, ..=84_000_000) => (1, 0),
|
||||
(VoltageScale::Scale0, ..=126_000_000) => (2, 1),
|
||||
(VoltageScale::Scale0, ..=168_000_000) => (3, 1),
|
||||
(VoltageScale::Scale0, ..=210_000_000) => (4, 2),
|
||||
(VoltageScale::Scale0, ..=252_000_000) => (5, 2),
|
||||
(VoltageScale::Scale0, ..=280_000_000) => (6, 3),
|
||||
// VOS 1 range VCORE 1.15V - 1.25V
|
||||
(VoltageScale::Scale1, ..=38_000_000) => (0, 0),
|
||||
(VoltageScale::Scale1, ..=76_000_000) => (1, 0),
|
||||
(VoltageScale::Scale1, ..=114_000_000) => (2, 1),
|
||||
(VoltageScale::Scale1, ..=152_000_000) => (3, 1),
|
||||
(VoltageScale::Scale1, ..=190_000_000) => (4, 2),
|
||||
(VoltageScale::Scale1, ..=225_000_000) => (5, 2),
|
||||
// VOS 2 range VCORE 1.05V - 1.15V
|
||||
(VoltageScale::Scale2, ..=34) => (0, 0),
|
||||
(VoltageScale::Scale2, ..=68) => (1, 0),
|
||||
(VoltageScale::Scale2, ..=102) => (2, 1),
|
||||
(VoltageScale::Scale2, ..=136) => (3, 1),
|
||||
(VoltageScale::Scale2, ..=160) => (4, 2),
|
||||
// VOS 3 range VCORE 0.95V - 1.05V
|
||||
(VoltageScale::Scale3, ..=22) => (0, 0),
|
||||
(VoltageScale::Scale3, ..=44) => (1, 0),
|
||||
(VoltageScale::Scale3, ..=66) => (2, 1),
|
||||
(VoltageScale::Scale3, ..=88) => (3, 1),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
debug!("flash: latency={} wrhighfreq={}", latency, wrhighfreq);
|
||||
|
||||
FLASH.acr().write(|w| {
|
||||
w.set_wrhighfreq(wrhighfreq);
|
||||
w.set_latency(latency);
|
||||
});
|
||||
while FLASH.acr().read().latency() != latency {}
|
||||
}
|
@ -1,511 +0,0 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use stm32_metapac::rcc::vals::Timpre;
|
||||
|
||||
use crate::pac::pwr::vals::Vos;
|
||||
use crate::pac::rcc::vals::{Hseext, Hsidiv, Mco1, Mco2, Pllrge, Pllsrc, Pllvcosel, Sw};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(64_000_000);
|
||||
|
||||
/// CSI speed
|
||||
pub const CSI_FREQ: Hertz = Hertz(4_000_000);
|
||||
|
||||
/// HSI48 speed
|
||||
pub const HSI48_FREQ: Hertz = Hertz(48_000_000);
|
||||
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
|
||||
const VCO_MIN: u32 = 150_000_000;
|
||||
const VCO_MAX: u32 = 420_000_000;
|
||||
const VCO_WIDE_MIN: u32 = 128_000_000;
|
||||
const VCO_WIDE_MAX: u32 = 560_000_000;
|
||||
|
||||
pub use super::common::{AHBPrescaler, APBPrescaler, VoltageScale};
|
||||
|
||||
pub enum HseMode {
|
||||
/// crystal/ceramic oscillator (HSEBYP=0)
|
||||
Oscillator,
|
||||
/// external analog clock (low swing) (HSEBYP=1, HSEEXT=0)
|
||||
BypassAnalog,
|
||||
/// external digital clock (full swing) (HSEBYP=1, HSEEXT=1)
|
||||
BypassDigital,
|
||||
}
|
||||
|
||||
pub struct Hse {
|
||||
/// HSE frequency.
|
||||
pub freq: Hertz,
|
||||
/// HSE mode.
|
||||
pub mode: HseMode,
|
||||
}
|
||||
|
||||
pub enum Hsi {
|
||||
/// 64Mhz
|
||||
Mhz64,
|
||||
/// 32Mhz (divided by 2)
|
||||
Mhz32,
|
||||
/// 16Mhz (divided by 4)
|
||||
Mhz16,
|
||||
/// 8Mhz (divided by 8)
|
||||
Mhz8,
|
||||
}
|
||||
|
||||
pub enum Sysclk {
|
||||
/// HSI selected as sysclk
|
||||
HSI,
|
||||
/// HSE selected as sysclk
|
||||
HSE,
|
||||
/// CSI selected as sysclk
|
||||
CSI,
|
||||
/// PLL1_P selected as sysclk
|
||||
Pll1P,
|
||||
}
|
||||
|
||||
pub enum PllSource {
|
||||
Hsi,
|
||||
Csi,
|
||||
Hse,
|
||||
}
|
||||
|
||||
pub struct Pll {
|
||||
/// Source clock selection.
|
||||
pub source: PllSource,
|
||||
|
||||
/// PLL pre-divider (DIVM). Must be between 1 and 63.
|
||||
pub prediv: u8,
|
||||
|
||||
/// PLL multiplication factor. Must be between 4 and 512.
|
||||
pub mul: u16,
|
||||
|
||||
/// PLL P division factor. If None, PLL P output is disabled. Must be between 1 and 128.
|
||||
/// On PLL1, it must be even (in particular, it cannot be 1.)
|
||||
pub divp: Option<u16>,
|
||||
/// PLL Q division factor. If None, PLL Q output is disabled. Must be between 1 and 128.
|
||||
pub divq: Option<u16>,
|
||||
/// PLL R division factor. If None, PLL R output is disabled. Must be between 1 and 128.
|
||||
pub divr: Option<u16>,
|
||||
}
|
||||
|
||||
impl APBPrescaler {
|
||||
fn div_tim(&self, clk: Hertz, tim: TimerPrescaler) -> Hertz {
|
||||
match (tim, self) {
|
||||
// The timers kernel clock is equal to rcc_hclk1 if PPRE1 or PPRE2 corresponds to a
|
||||
// division by 1 or 2, else it is equal to 2 x Frcc_pclk1 or 2 x Frcc_pclk2
|
||||
(TimerPrescaler::DefaultX2, Self::NotDivided) => clk,
|
||||
(TimerPrescaler::DefaultX2, Self::Div2) => clk,
|
||||
(TimerPrescaler::DefaultX2, Self::Div4) => clk / 2u32,
|
||||
(TimerPrescaler::DefaultX2, Self::Div8) => clk / 4u32,
|
||||
(TimerPrescaler::DefaultX2, Self::Div16) => clk / 8u32,
|
||||
// The timers kernel clock is equal to 2 x Frcc_pclk1 or 2 x Frcc_pclk2 if PPRE1 or PPRE2
|
||||
// corresponds to a division by 1, 2 or 4, else it is equal to 4 x Frcc_pclk1 or 4 x Frcc_pclk2
|
||||
// this makes NO SENSE and is different than in the H7. Mistake in the RM??
|
||||
(TimerPrescaler::DefaultX4, Self::NotDivided) => clk * 2u32,
|
||||
(TimerPrescaler::DefaultX4, Self::Div2) => clk,
|
||||
(TimerPrescaler::DefaultX4, Self::Div4) => clk / 2u32,
|
||||
(TimerPrescaler::DefaultX4, Self::Div8) => clk / 2u32,
|
||||
(TimerPrescaler::DefaultX4, Self::Div16) => clk / 4u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// APB prescaler
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum TimerPrescaler {
|
||||
DefaultX2,
|
||||
DefaultX4,
|
||||
}
|
||||
|
||||
impl From<TimerPrescaler> for Timpre {
|
||||
fn from(value: TimerPrescaler) -> Self {
|
||||
match value {
|
||||
TimerPrescaler::DefaultX2 => Timpre::DEFAULTX2,
|
||||
TimerPrescaler::DefaultX4 => Timpre::DEFAULTX4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration of the core clocks
|
||||
#[non_exhaustive]
|
||||
pub struct Config {
|
||||
pub hsi: Option<Hsi>,
|
||||
pub hse: Option<Hse>,
|
||||
pub csi: bool,
|
||||
pub hsi48: bool,
|
||||
pub sys: Sysclk,
|
||||
|
||||
pub pll1: Option<Pll>,
|
||||
pub pll2: Option<Pll>,
|
||||
#[cfg(rcc_h5)]
|
||||
pub pll3: Option<Pll>,
|
||||
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb1_pre: APBPrescaler,
|
||||
pub apb2_pre: APBPrescaler,
|
||||
pub apb3_pre: APBPrescaler,
|
||||
pub timer_prescaler: TimerPrescaler,
|
||||
|
||||
pub voltage_scale: VoltageScale,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hsi: Some(Hsi::Mhz64),
|
||||
hse: None,
|
||||
csi: false,
|
||||
hsi48: false,
|
||||
sys: Sysclk::HSI,
|
||||
pll1: None,
|
||||
pll2: None,
|
||||
#[cfg(rcc_h5)]
|
||||
pll3: None,
|
||||
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb1_pre: APBPrescaler::NotDivided,
|
||||
apb2_pre: APBPrescaler::NotDivided,
|
||||
apb3_pre: APBPrescaler::NotDivided,
|
||||
timer_prescaler: TimerPrescaler::DefaultX2,
|
||||
|
||||
voltage_scale: VoltageScale::Scale3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait McoInstance {
|
||||
type Source;
|
||||
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait McoInstance: sealed::McoInstance + 'static {}
|
||||
|
||||
pin_trait!(McoPin, McoInstance);
|
||||
|
||||
macro_rules! impl_peri {
|
||||
($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => {
|
||||
impl sealed::McoInstance for peripherals::$peri {
|
||||
type Source = $source;
|
||||
|
||||
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) {
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.$set_source(source);
|
||||
w.$set_prescaler(prescaler);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl McoInstance for peripherals::$peri {}
|
||||
};
|
||||
}
|
||||
|
||||
impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre);
|
||||
impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre);
|
||||
|
||||
pub struct Mco<'d, T: McoInstance> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
|
||||
impl<'d, T: McoInstance> Mco<'d, T> {
|
||||
pub fn new(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
_pin: impl Peripheral<P = impl McoPin<T>> + 'd,
|
||||
_source: T::Source,
|
||||
) -> Self {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let (vos, max_clk) = match config.voltage_scale {
|
||||
VoltageScale::Scale0 => (Vos::SCALE0, Hertz(250_000_000)),
|
||||
VoltageScale::Scale1 => (Vos::SCALE1, Hertz(200_000_000)),
|
||||
VoltageScale::Scale2 => (Vos::SCALE2, Hertz(150_000_000)),
|
||||
VoltageScale::Scale3 => (Vos::SCALE3, Hertz(100_000_000)),
|
||||
};
|
||||
|
||||
// Configure voltage scale.
|
||||
PWR.voscr().modify(|w| w.set_vos(vos));
|
||||
while !PWR.vossr().read().vosrdy() {}
|
||||
|
||||
// Configure HSI
|
||||
let hsi = match config.hsi {
|
||||
None => {
|
||||
RCC.cr().modify(|w| w.set_hsion(false));
|
||||
None
|
||||
}
|
||||
Some(hsi) => {
|
||||
let (freq, hsidiv) = match hsi {
|
||||
Hsi::Mhz64 => (HSI_FREQ / 1u32, Hsidiv::DIV1),
|
||||
Hsi::Mhz32 => (HSI_FREQ / 2u32, Hsidiv::DIV2),
|
||||
Hsi::Mhz16 => (HSI_FREQ / 4u32, Hsidiv::DIV4),
|
||||
Hsi::Mhz8 => (HSI_FREQ / 8u32, Hsidiv::DIV8),
|
||||
};
|
||||
RCC.cr().modify(|w| {
|
||||
w.set_hsidiv(hsidiv);
|
||||
w.set_hsion(true);
|
||||
});
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
Some(freq)
|
||||
}
|
||||
};
|
||||
|
||||
// Configure HSE
|
||||
let hse = match config.hse {
|
||||
None => {
|
||||
RCC.cr().modify(|w| w.set_hseon(false));
|
||||
None
|
||||
}
|
||||
Some(hse) => {
|
||||
let (byp, ext) = match hse.mode {
|
||||
HseMode::Oscillator => (false, Hseext::ANALOG),
|
||||
HseMode::BypassAnalog => (true, Hseext::ANALOG),
|
||||
HseMode::BypassDigital => (true, Hseext::DIGITAL),
|
||||
};
|
||||
|
||||
RCC.cr().modify(|w| {
|
||||
w.set_hsebyp(byp);
|
||||
w.set_hseext(ext);
|
||||
});
|
||||
RCC.cr().modify(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
Some(hse.freq)
|
||||
}
|
||||
};
|
||||
|
||||
// Configure HSI48.
|
||||
RCC.cr().modify(|w| w.set_hsi48on(config.hsi48));
|
||||
let _hsi48 = match config.hsi48 {
|
||||
false => None,
|
||||
true => {
|
||||
while !RCC.cr().read().hsi48rdy() {}
|
||||
Some(CSI_FREQ)
|
||||
}
|
||||
};
|
||||
|
||||
// Configure CSI.
|
||||
RCC.cr().modify(|w| w.set_csion(config.csi));
|
||||
let csi = match config.csi {
|
||||
false => None,
|
||||
true => {
|
||||
while !RCC.cr().read().csirdy() {}
|
||||
Some(CSI_FREQ)
|
||||
}
|
||||
};
|
||||
|
||||
// Configure PLLs.
|
||||
let pll_input = PllInput { csi, hse, hsi };
|
||||
let pll1 = init_pll(0, config.pll1, &pll_input);
|
||||
let _pll2 = init_pll(1, config.pll2, &pll_input);
|
||||
#[cfg(rcc_h5)]
|
||||
let _pll3 = init_pll(2, config.pll3, &pll_input);
|
||||
|
||||
// Configure sysclk
|
||||
let (sys, sw) = match config.sys {
|
||||
Sysclk::HSI => (unwrap!(hsi), Sw::HSI),
|
||||
Sysclk::HSE => (unwrap!(hse), Sw::HSE),
|
||||
Sysclk::CSI => (unwrap!(csi), Sw::CSI),
|
||||
Sysclk::Pll1P => (unwrap!(pll1.p), Sw::PLL1),
|
||||
};
|
||||
assert!(sys <= max_clk);
|
||||
|
||||
let hclk = sys / config.ahb_pre;
|
||||
|
||||
let apb1 = hclk / config.apb1_pre;
|
||||
let apb1_tim = config.apb1_pre.div_tim(hclk, config.timer_prescaler);
|
||||
let apb2 = hclk / config.apb2_pre;
|
||||
let apb2_tim = config.apb2_pre.div_tim(hclk, config.timer_prescaler);
|
||||
let apb3 = hclk / config.apb3_pre;
|
||||
|
||||
flash_setup(hclk, config.voltage_scale);
|
||||
|
||||
// Set hpre
|
||||
let hpre = config.ahb_pre.into();
|
||||
RCC.cfgr2().modify(|w| w.set_hpre(hpre));
|
||||
while RCC.cfgr2().read().hpre() != hpre {}
|
||||
|
||||
// set ppre
|
||||
RCC.cfgr2().modify(|w| {
|
||||
w.set_ppre1(config.apb1_pre.into());
|
||||
w.set_ppre2(config.apb2_pre.into());
|
||||
w.set_ppre3(config.apb3_pre.into());
|
||||
});
|
||||
|
||||
RCC.cfgr().modify(|w| w.set_timpre(config.timer_prescaler.into()));
|
||||
|
||||
RCC.cfgr().modify(|w| w.set_sw(sw));
|
||||
while RCC.cfgr().read().sws() != sw {}
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys,
|
||||
ahb1: hclk,
|
||||
ahb2: hclk,
|
||||
ahb3: hclk,
|
||||
ahb4: hclk,
|
||||
apb1,
|
||||
apb2,
|
||||
apb3,
|
||||
apb1_tim,
|
||||
apb2_tim,
|
||||
adc: None,
|
||||
});
|
||||
}
|
||||
|
||||
struct PllInput {
|
||||
hsi: Option<Hertz>,
|
||||
hse: Option<Hertz>,
|
||||
csi: Option<Hertz>,
|
||||
}
|
||||
|
||||
struct PllOutput {
|
||||
p: Option<Hertz>,
|
||||
#[allow(dead_code)]
|
||||
q: Option<Hertz>,
|
||||
#[allow(dead_code)]
|
||||
r: Option<Hertz>,
|
||||
}
|
||||
|
||||
fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
||||
let Some(config) = config else {
|
||||
// Stop PLL
|
||||
RCC.cr().modify(|w| w.set_pllon(num, false));
|
||||
while RCC.cr().read().pllrdy(num) {}
|
||||
|
||||
// "To save power when PLL1 is not used, the value of PLL1M must be set to 0.""
|
||||
RCC.pllcfgr(num).write(|w| {
|
||||
w.set_divm(0);
|
||||
});
|
||||
|
||||
return PllOutput {
|
||||
p: None,
|
||||
q: None,
|
||||
r: None,
|
||||
};
|
||||
};
|
||||
|
||||
assert!(1 <= config.prediv && config.prediv <= 63);
|
||||
assert!(4 <= config.mul && config.mul <= 512);
|
||||
|
||||
let (in_clk, src) = match config.source {
|
||||
PllSource::Hsi => (unwrap!(input.hsi), Pllsrc::HSI),
|
||||
PllSource::Hse => (unwrap!(input.hse), Pllsrc::HSE),
|
||||
PllSource::Csi => (unwrap!(input.csi), Pllsrc::CSI),
|
||||
};
|
||||
|
||||
let ref_clk = in_clk / config.prediv as u32;
|
||||
|
||||
let ref_range = match ref_clk.0 {
|
||||
..=1_999_999 => Pllrge::RANGE1,
|
||||
..=3_999_999 => Pllrge::RANGE2,
|
||||
..=7_999_999 => Pllrge::RANGE4,
|
||||
..=16_000_000 => Pllrge::RANGE8,
|
||||
x => panic!("pll ref_clk out of range: {} mhz", x),
|
||||
};
|
||||
|
||||
// The smaller range (150 to 420 MHz) must
|
||||
// be chosen when the reference clock frequency is lower than 2 MHz.
|
||||
let wide_allowed = ref_range != Pllrge::RANGE1;
|
||||
|
||||
let vco_clk = ref_clk * config.mul;
|
||||
let vco_range = match vco_clk.0 {
|
||||
VCO_MIN..=VCO_MAX => Pllvcosel::MEDIUMVCO,
|
||||
VCO_WIDE_MIN..=VCO_WIDE_MAX if wide_allowed => Pllvcosel::WIDEVCO,
|
||||
x => panic!("pll vco_clk out of range: {} mhz", x),
|
||||
};
|
||||
|
||||
let p = config.divp.map(|div| {
|
||||
assert!(1 <= div && div <= 128);
|
||||
if num == 0 {
|
||||
// on PLL1, DIVP must be even.
|
||||
assert!(div % 2 == 0);
|
||||
}
|
||||
|
||||
vco_clk / div
|
||||
});
|
||||
let q = config.divq.map(|div| {
|
||||
assert!(1 <= div && div <= 128);
|
||||
vco_clk / div
|
||||
});
|
||||
let r = config.divr.map(|div| {
|
||||
assert!(1 <= div && div <= 128);
|
||||
vco_clk / div
|
||||
});
|
||||
|
||||
RCC.pllcfgr(num).write(|w| {
|
||||
w.set_pllsrc(src);
|
||||
w.set_divm(config.prediv);
|
||||
w.set_pllvcosel(vco_range);
|
||||
w.set_pllrge(ref_range);
|
||||
w.set_pllfracen(false);
|
||||
w.set_pllpen(p.is_some());
|
||||
w.set_pllqen(q.is_some());
|
||||
w.set_pllren(r.is_some());
|
||||
});
|
||||
RCC.plldivr(num).write(|w| {
|
||||
w.set_plln(config.mul - 1);
|
||||
w.set_pllp((config.divp.unwrap_or(1) - 1) as u8);
|
||||
w.set_pllq((config.divq.unwrap_or(1) - 1) as u8);
|
||||
w.set_pllr((config.divr.unwrap_or(1) - 1) as u8);
|
||||
});
|
||||
|
||||
RCC.cr().modify(|w| w.set_pllon(num, true));
|
||||
while !RCC.cr().read().pllrdy(num) {}
|
||||
|
||||
PllOutput { p, q, r }
|
||||
}
|
||||
|
||||
fn flash_setup(clk: Hertz, vos: VoltageScale) {
|
||||
// RM0481 Rev 1, table 37
|
||||
// LATENCY WRHIGHFREQ VOS3 VOS2 VOS1 VOS0
|
||||
// 0 0 0 to 20 MHz 0 to 30 MHz 0 to 34 MHz 0 to 42 MHz
|
||||
// 1 0 20 to 40 MHz 30 to 60 MHz 34 to 68 MHz 42 to 84 MHz
|
||||
// 2 1 40 to 60 MHz 60 to 90 MHz 68 to 102 MHz 84 to 126 MHz
|
||||
// 3 1 60 to 80 MHz 90 to 120 MHz 102 to 136 MHz 126 to 168 MHz
|
||||
// 4 2 80 to 100 MHz 120 to 150 MHz 136 to 170 MHz 168 to 210 MHz
|
||||
// 5 2 170 to 200 MHz 210 to 250 MHz
|
||||
|
||||
// See RM0433 Rev 7 Table 17. FLASH recommended number of wait
|
||||
// states and programming delay
|
||||
let (latency, wrhighfreq) = match (vos, clk.0) {
|
||||
(VoltageScale::Scale0, ..=42_000_000) => (0, 0),
|
||||
(VoltageScale::Scale0, ..=84_000_000) => (1, 0),
|
||||
(VoltageScale::Scale0, ..=126_000_000) => (2, 1),
|
||||
(VoltageScale::Scale0, ..=168_000_000) => (3, 1),
|
||||
(VoltageScale::Scale0, ..=210_000_000) => (4, 2),
|
||||
(VoltageScale::Scale0, ..=250_000_000) => (5, 2),
|
||||
|
||||
(VoltageScale::Scale1, ..=34_000_000) => (0, 0),
|
||||
(VoltageScale::Scale1, ..=68_000_000) => (1, 0),
|
||||
(VoltageScale::Scale1, ..=102_000_000) => (2, 1),
|
||||
(VoltageScale::Scale1, ..=136_000_000) => (3, 1),
|
||||
(VoltageScale::Scale1, ..=170_000_000) => (4, 2),
|
||||
(VoltageScale::Scale1, ..=200_000_000) => (5, 2),
|
||||
|
||||
(VoltageScale::Scale2, ..=30_000_000) => (0, 0),
|
||||
(VoltageScale::Scale2, ..=60_000_000) => (1, 0),
|
||||
(VoltageScale::Scale2, ..=90_000_000) => (2, 1),
|
||||
(VoltageScale::Scale2, ..=120_000_000) => (3, 1),
|
||||
(VoltageScale::Scale2, ..=150_000_000) => (4, 2),
|
||||
|
||||
(VoltageScale::Scale3, ..=20_000_000) => (0, 0),
|
||||
(VoltageScale::Scale3, ..=40_000_000) => (1, 0),
|
||||
(VoltageScale::Scale3, ..=60_000_000) => (2, 1),
|
||||
(VoltageScale::Scale3, ..=80_000_000) => (3, 1),
|
||||
(VoltageScale::Scale3, ..=100_000_000) => (4, 2),
|
||||
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
defmt::debug!("flash: latency={} wrhighfreq={}", latency, wrhighfreq);
|
||||
|
||||
FLASH.acr().write(|w| {
|
||||
w.set_wrhighfreq(wrhighfreq);
|
||||
w.set_latency(latency);
|
||||
});
|
||||
while FLASH.acr().read().latency() != latency {}
|
||||
}
|
@ -1,879 +0,0 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_internal::into_ref;
|
||||
pub use pll::PllConfig;
|
||||
use stm32_metapac::rcc::vals::{Mco1, Mco2};
|
||||
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::Speed;
|
||||
use crate::pac::rcc::vals::{Adcsel, Ckpersel, Dppre, Hpre, Hsidiv, Pllsrc, Sw, Timpre};
|
||||
use crate::pac::{PWR, RCC, SYSCFG};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(64_000_000);
|
||||
|
||||
/// CSI speed
|
||||
pub const CSI_FREQ: Hertz = Hertz(4_000_000);
|
||||
|
||||
/// HSI48 speed
|
||||
pub const HSI48_FREQ: Hertz = Hertz(48_000_000);
|
||||
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
|
||||
pub use super::common::VoltageScale;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AdcClockSource {
|
||||
Pll2PCk,
|
||||
Pll3RCk,
|
||||
PerCk,
|
||||
}
|
||||
|
||||
impl AdcClockSource {
|
||||
pub fn adcsel(&self) -> Adcsel {
|
||||
match self {
|
||||
AdcClockSource::Pll2PCk => Adcsel::PLL2_P,
|
||||
AdcClockSource::Pll3RCk => Adcsel::PLL3_R,
|
||||
AdcClockSource::PerCk => Adcsel::PER,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AdcClockSource {
|
||||
fn default() -> Self {
|
||||
Self::Pll2PCk
|
||||
}
|
||||
}
|
||||
|
||||
/// Core clock frequencies
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CoreClocks {
|
||||
pub hclk: Hertz,
|
||||
pub pclk1: Hertz,
|
||||
pub pclk2: Hertz,
|
||||
pub pclk3: Hertz,
|
||||
pub pclk4: Hertz,
|
||||
pub ppre1: u8,
|
||||
pub ppre2: u8,
|
||||
pub ppre3: u8,
|
||||
pub ppre4: u8,
|
||||
pub csi_ck: Option<Hertz>,
|
||||
pub hsi_ck: Option<Hertz>,
|
||||
pub hsi48_ck: Option<Hertz>,
|
||||
pub lsi_ck: Option<Hertz>,
|
||||
pub per_ck: Option<Hertz>,
|
||||
pub hse_ck: Option<Hertz>,
|
||||
pub pll1_p_ck: Option<Hertz>,
|
||||
pub pll1_q_ck: Option<Hertz>,
|
||||
pub pll1_r_ck: Option<Hertz>,
|
||||
pub pll2_p_ck: Option<Hertz>,
|
||||
pub pll2_q_ck: Option<Hertz>,
|
||||
pub pll2_r_ck: Option<Hertz>,
|
||||
pub pll3_p_ck: Option<Hertz>,
|
||||
pub pll3_q_ck: Option<Hertz>,
|
||||
pub pll3_r_ck: Option<Hertz>,
|
||||
pub timx_ker_ck: Option<Hertz>,
|
||||
pub timy_ker_ck: Option<Hertz>,
|
||||
pub adc_ker_ck: Option<Hertz>,
|
||||
pub sys_ck: Hertz,
|
||||
pub c_ck: Hertz,
|
||||
}
|
||||
|
||||
/// Configuration of the core clocks
|
||||
#[non_exhaustive]
|
||||
#[derive(Default)]
|
||||
pub struct Config {
|
||||
pub hse: Option<Hertz>,
|
||||
pub bypass_hse: bool,
|
||||
pub sys_ck: Option<Hertz>,
|
||||
pub per_ck: Option<Hertz>,
|
||||
pub hclk: Option<Hertz>,
|
||||
pub pclk1: Option<Hertz>,
|
||||
pub pclk2: Option<Hertz>,
|
||||
pub pclk3: Option<Hertz>,
|
||||
pub pclk4: Option<Hertz>,
|
||||
pub pll1: PllConfig,
|
||||
pub pll2: PllConfig,
|
||||
pub pll3: PllConfig,
|
||||
pub adc_clock_source: AdcClockSource,
|
||||
}
|
||||
|
||||
/// Setup traceclk
|
||||
/// Returns a pll1_r_ck
|
||||
fn traceclk_setup(config: &mut Config, sys_use_pll1_p: bool) {
|
||||
let pll1_r_ck = match (sys_use_pll1_p, config.pll1.r_ck) {
|
||||
// pll1_p_ck selected as system clock but pll1_r_ck not
|
||||
// set. The traceclk mux is synchronous with the system
|
||||
// clock mux, but has pll1_r_ck as an input. In order to
|
||||
// keep traceclk running, we force a pll1_r_ck.
|
||||
(true, None) => Some(Hertz(unwrap!(config.pll1.p_ck).0 / 2)),
|
||||
|
||||
// Either pll1 not selected as system clock, free choice
|
||||
// of pll1_r_ck. Or pll1 is selected, assume user has set
|
||||
// a suitable pll1_r_ck frequency.
|
||||
_ => config.pll1.r_ck,
|
||||
};
|
||||
config.pll1.r_ck = pll1_r_ck;
|
||||
}
|
||||
|
||||
/// Divider calculator for pclk 1 - 4
|
||||
///
|
||||
/// Returns real pclk, bits, ppre and the timer kernel clock
|
||||
fn ppre_calculate(
|
||||
requested_pclk: u32,
|
||||
hclk: u32,
|
||||
max_pclk: u32,
|
||||
tim_pre: Option<Timpre>,
|
||||
) -> (u32, u8, u8, Option<u32>) {
|
||||
let (bits, ppre) = match (hclk + requested_pclk - 1) / requested_pclk {
|
||||
0 => panic!(),
|
||||
1 => (0b000, 1),
|
||||
2 => (0b100, 2),
|
||||
3..=5 => (0b101, 4),
|
||||
6..=11 => (0b110, 8),
|
||||
_ => (0b111, 16),
|
||||
};
|
||||
let real_pclk = hclk / u32::from(ppre);
|
||||
assert!(real_pclk <= max_pclk);
|
||||
|
||||
let tim_ker_clk = if let Some(tim_pre) = tim_pre {
|
||||
let clk = match (bits, tim_pre) {
|
||||
(0b101, Timpre::DEFAULTX2) => hclk / 2,
|
||||
(0b110, Timpre::DEFAULTX4) => hclk / 2,
|
||||
(0b110, Timpre::DEFAULTX2) => hclk / 4,
|
||||
(0b111, Timpre::DEFAULTX4) => hclk / 4,
|
||||
(0b111, Timpre::DEFAULTX2) => hclk / 8,
|
||||
_ => hclk,
|
||||
};
|
||||
Some(clk)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
(real_pclk, bits, ppre, tim_ker_clk)
|
||||
}
|
||||
|
||||
/// Setup sys_ck
|
||||
/// Returns sys_ck frequency, and a pll1_p_ck
|
||||
fn sys_ck_setup(config: &mut Config, srcclk: Hertz) -> (Hertz, bool) {
|
||||
// Compare available with wanted clocks
|
||||
let sys_ck = config.sys_ck.unwrap_or(srcclk);
|
||||
|
||||
if sys_ck != srcclk {
|
||||
// The requested system clock is not the immediately available
|
||||
// HSE/HSI clock. Perhaps there are other ways of obtaining
|
||||
// the requested system clock (such as `HSIDIV`) but we will
|
||||
// ignore those for now.
|
||||
//
|
||||
// Therefore we must use pll1_p_ck
|
||||
let pll1_p_ck = match config.pll1.p_ck {
|
||||
Some(p_ck) => {
|
||||
assert!(
|
||||
p_ck == sys_ck,
|
||||
"Error: Cannot set pll1_p_ck independently as it must be used to generate sys_ck"
|
||||
);
|
||||
Some(p_ck)
|
||||
}
|
||||
None => Some(sys_ck),
|
||||
};
|
||||
config.pll1.p_ck = pll1_p_ck;
|
||||
|
||||
(sys_ck, true)
|
||||
} else {
|
||||
// sys_ck is derived directly from a source clock
|
||||
// (HSE/HSI). pll1_p_ck can be as requested
|
||||
(sys_ck, false)
|
||||
}
|
||||
}
|
||||
|
||||
fn flash_setup(rcc_aclk: u32, vos: VoltageScale) {
|
||||
use crate::pac::FLASH;
|
||||
|
||||
// ACLK in MHz, round down and subtract 1 from integers. eg.
|
||||
// 61_999_999 -> 61MHz
|
||||
// 62_000_000 -> 61MHz
|
||||
// 62_000_001 -> 62MHz
|
||||
let rcc_aclk_mhz = (rcc_aclk - 1) / 1_000_000;
|
||||
|
||||
// See RM0433 Rev 7 Table 17. FLASH recommended number of wait
|
||||
// states and programming delay
|
||||
#[cfg(flash_h7)]
|
||||
let (wait_states, progr_delay) = match vos {
|
||||
// VOS 0 range VCORE 1.26V - 1.40V
|
||||
VoltageScale::Scale0 => match rcc_aclk_mhz {
|
||||
0..=69 => (0, 0),
|
||||
70..=139 => (1, 1),
|
||||
140..=184 => (2, 1),
|
||||
185..=209 => (2, 2),
|
||||
210..=224 => (3, 2),
|
||||
225..=239 => (4, 2),
|
||||
_ => (7, 3),
|
||||
},
|
||||
// VOS 1 range VCORE 1.15V - 1.26V
|
||||
VoltageScale::Scale1 => match rcc_aclk_mhz {
|
||||
0..=69 => (0, 0),
|
||||
70..=139 => (1, 1),
|
||||
140..=184 => (2, 1),
|
||||
185..=209 => (2, 2),
|
||||
210..=224 => (3, 2),
|
||||
_ => (7, 3),
|
||||
},
|
||||
// VOS 2 range VCORE 1.05V - 1.15V
|
||||
VoltageScale::Scale2 => match rcc_aclk_mhz {
|
||||
0..=54 => (0, 0),
|
||||
55..=109 => (1, 1),
|
||||
110..=164 => (2, 1),
|
||||
165..=224 => (3, 2),
|
||||
_ => (7, 3),
|
||||
},
|
||||
// VOS 3 range VCORE 0.95V - 1.05V
|
||||
VoltageScale::Scale3 => match rcc_aclk_mhz {
|
||||
0..=44 => (0, 0),
|
||||
45..=89 => (1, 1),
|
||||
90..=134 => (2, 1),
|
||||
135..=179 => (3, 2),
|
||||
180..=224 => (4, 2),
|
||||
_ => (7, 3),
|
||||
},
|
||||
};
|
||||
|
||||
// See RM0455 Rev 10 Table 16. FLASH recommended number of wait
|
||||
// states and programming delay
|
||||
#[cfg(flash_h7ab)]
|
||||
let (wait_states, progr_delay) = match vos {
|
||||
// VOS 0 range VCORE 1.25V - 1.35V
|
||||
VoltageScale::Scale0 => match rcc_aclk_mhz {
|
||||
0..=42 => (0, 0),
|
||||
43..=84 => (1, 0),
|
||||
85..=126 => (2, 1),
|
||||
127..=168 => (3, 1),
|
||||
169..=210 => (4, 2),
|
||||
211..=252 => (5, 2),
|
||||
253..=280 => (6, 3),
|
||||
_ => (7, 3),
|
||||
},
|
||||
// VOS 1 range VCORE 1.15V - 1.25V
|
||||
VoltageScale::Scale1 => match rcc_aclk_mhz {
|
||||
0..=38 => (0, 0),
|
||||
39..=76 => (1, 0),
|
||||
77..=114 => (2, 1),
|
||||
115..=152 => (3, 1),
|
||||
153..=190 => (4, 2),
|
||||
191..=225 => (5, 2),
|
||||
_ => (7, 3),
|
||||
},
|
||||
// VOS 2 range VCORE 1.05V - 1.15V
|
||||
VoltageScale::Scale2 => match rcc_aclk_mhz {
|
||||
0..=34 => (0, 0),
|
||||
35..=68 => (1, 0),
|
||||
69..=102 => (2, 1),
|
||||
103..=136 => (3, 1),
|
||||
137..=160 => (4, 2),
|
||||
_ => (7, 3),
|
||||
},
|
||||
// VOS 3 range VCORE 0.95V - 1.05V
|
||||
VoltageScale::Scale3 => match rcc_aclk_mhz {
|
||||
0..=22 => (0, 0),
|
||||
23..=44 => (1, 0),
|
||||
45..=66 => (2, 1),
|
||||
67..=88 => (3, 1),
|
||||
_ => (7, 3),
|
||||
},
|
||||
};
|
||||
|
||||
FLASH.acr().write(|w| {
|
||||
w.set_wrhighfreq(progr_delay);
|
||||
w.set_latency(wait_states)
|
||||
});
|
||||
while FLASH.acr().read().latency() != wait_states {}
|
||||
}
|
||||
|
||||
pub enum McoClock {
|
||||
Disabled,
|
||||
Bypassed,
|
||||
Divided(u8),
|
||||
}
|
||||
|
||||
impl McoClock {
|
||||
fn into_raw(&self) -> u8 {
|
||||
match self {
|
||||
McoClock::Disabled => 0,
|
||||
McoClock::Bypassed => 1,
|
||||
McoClock::Divided(divisor) => {
|
||||
if *divisor > 15 {
|
||||
panic!("Mco divisor must be less than 15. Refer to the reference manual for more information.")
|
||||
}
|
||||
*divisor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Mco1Source {
|
||||
Hsi,
|
||||
Lse,
|
||||
Hse,
|
||||
Pll1Q,
|
||||
Hsi48,
|
||||
}
|
||||
|
||||
impl Default for Mco1Source {
|
||||
fn default() -> Self {
|
||||
Self::Hsi
|
||||
}
|
||||
}
|
||||
|
||||
pub trait McoSource {
|
||||
type Raw;
|
||||
|
||||
fn into_raw(&self) -> Self::Raw;
|
||||
}
|
||||
|
||||
impl McoSource for Mco1Source {
|
||||
type Raw = Mco1;
|
||||
fn into_raw(&self) -> Self::Raw {
|
||||
match self {
|
||||
Mco1Source::Hsi => Mco1::HSI,
|
||||
Mco1Source::Lse => Mco1::LSE,
|
||||
Mco1Source::Hse => Mco1::HSE,
|
||||
Mco1Source::Pll1Q => Mco1::PLL1_Q,
|
||||
Mco1Source::Hsi48 => Mco1::HSI48,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Mco2Source {
|
||||
SysClk,
|
||||
Pll2Q,
|
||||
Hse,
|
||||
Pll1Q,
|
||||
Csi,
|
||||
Lsi,
|
||||
}
|
||||
|
||||
impl Default for Mco2Source {
|
||||
fn default() -> Self {
|
||||
Self::SysClk
|
||||
}
|
||||
}
|
||||
|
||||
impl McoSource for Mco2Source {
|
||||
type Raw = Mco2;
|
||||
fn into_raw(&self) -> Self::Raw {
|
||||
match self {
|
||||
Mco2Source::SysClk => Mco2::SYSCLK,
|
||||
Mco2Source::Pll2Q => Mco2::PLL2_P,
|
||||
Mco2Source::Hse => Mco2::HSE,
|
||||
Mco2Source::Pll1Q => Mco2::PLL1_P,
|
||||
Mco2Source::Csi => Mco2::CSI,
|
||||
Mco2Source::Lsi => Mco2::LSI,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait McoInstance {
|
||||
type Source;
|
||||
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait McoInstance: sealed::McoInstance + 'static {}
|
||||
|
||||
pin_trait!(McoPin, McoInstance);
|
||||
|
||||
macro_rules! impl_peri {
|
||||
($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => {
|
||||
impl sealed::McoInstance for peripherals::$peri {
|
||||
type Source = $source;
|
||||
|
||||
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) {
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.$set_source(source);
|
||||
w.$set_prescaler(prescaler);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl McoInstance for peripherals::$peri {}
|
||||
};
|
||||
}
|
||||
|
||||
impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre);
|
||||
impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre);
|
||||
|
||||
pub struct Mco<'d, T: McoInstance> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
|
||||
impl<'d, T: McoInstance> Mco<'d, T> {
|
||||
pub fn new(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
pin: impl Peripheral<P = impl McoPin<T>> + 'd,
|
||||
source: impl McoSource<Raw = T::Source>,
|
||||
prescaler: McoClock,
|
||||
) -> Self {
|
||||
into_ref!(pin);
|
||||
|
||||
critical_section::with(|_| unsafe {
|
||||
T::apply_clock_settings(source.into_raw(), prescaler.into_raw());
|
||||
pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
|
||||
pin.set_speed(Speed::VeryHigh);
|
||||
});
|
||||
|
||||
Self { phantom: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(mut config: Config) {
|
||||
// TODO make configurable?
|
||||
let enable_overdrive = false;
|
||||
|
||||
// NB. The lower bytes of CR3 can only be written once after
|
||||
// POR, and must be written with a valid combination. Refer to
|
||||
// RM0433 Rev 7 6.8.4. This is partially enforced by dropping
|
||||
// `self` at the end of this method, but of course we cannot
|
||||
// know what happened between the previous POR and here.
|
||||
#[cfg(pwr_h7)]
|
||||
PWR.cr3().modify(|w| {
|
||||
w.set_scuen(true);
|
||||
w.set_ldoen(true);
|
||||
w.set_bypass(false);
|
||||
});
|
||||
|
||||
#[cfg(pwr_h7smps)]
|
||||
PWR.cr3().modify(|w| {
|
||||
// hardcode "Direct SPMS" for now, this is what works on nucleos with the
|
||||
// default solderbridge configuration.
|
||||
w.set_sden(true);
|
||||
w.set_ldoen(false);
|
||||
});
|
||||
|
||||
// Validate the supply configuration. If you are stuck here, it is
|
||||
// because the voltages on your board do not match those specified
|
||||
// in the D3CR.VOS and CR3.SDLEVEL fields. By default after reset
|
||||
// VOS = Scale 3, so check that the voltage on the VCAP pins =
|
||||
// 1.0V.
|
||||
while !PWR.csr1().read().actvosrdy() {}
|
||||
|
||||
// Go to Scale 1
|
||||
PWR.d3cr().modify(|w| w.set_vos(0b11));
|
||||
while !PWR.d3cr().read().vosrdy() {}
|
||||
|
||||
let pwr_vos = if !enable_overdrive {
|
||||
VoltageScale::Scale1
|
||||
} else {
|
||||
critical_section::with(|_| {
|
||||
RCC.apb4enr().modify(|w| w.set_syscfgen(true));
|
||||
|
||||
SYSCFG.pwrcr().modify(|w| w.set_oden(1));
|
||||
});
|
||||
while !PWR.d3cr().read().vosrdy() {}
|
||||
VoltageScale::Scale0
|
||||
};
|
||||
|
||||
// Freeze the core clocks, returning a Core Clocks Distribution
|
||||
// and Reset (CCDR) structure. The actual frequency of the clocks
|
||||
// configured is returned in the `clocks` member of the CCDR
|
||||
// structure.
|
||||
//
|
||||
// Note that `freeze` will never result in a clock _faster_ than
|
||||
// that specified. It may result in a clock that is a factor of [1,
|
||||
// 2) slower.
|
||||
//
|
||||
// `syscfg` is required to enable the I/O compensation cell.
|
||||
//
|
||||
// # Panics
|
||||
//
|
||||
// If a clock specification cannot be achieved within the
|
||||
// hardware specification then this function will panic. This
|
||||
// function may also panic if a clock specification can be
|
||||
// achieved, but the mechanism for doing so is not yet
|
||||
// implemented here.
|
||||
|
||||
let srcclk = config.hse.unwrap_or(HSI_FREQ); // Available clocks
|
||||
let (sys_ck, sys_use_pll1_p) = sys_ck_setup(&mut config, srcclk);
|
||||
|
||||
// Configure traceclk from PLL if needed
|
||||
traceclk_setup(&mut config, sys_use_pll1_p);
|
||||
|
||||
let (pll1_p_ck, pll1_q_ck, pll1_r_ck) = pll::pll_setup(srcclk.0, &config.pll1, 0);
|
||||
let (pll2_p_ck, pll2_q_ck, pll2_r_ck) = pll::pll_setup(srcclk.0, &config.pll2, 1);
|
||||
let (pll3_p_ck, pll3_q_ck, pll3_r_ck) = pll::pll_setup(srcclk.0, &config.pll3, 2);
|
||||
|
||||
let sys_ck = if sys_use_pll1_p {
|
||||
Hertz(unwrap!(pll1_p_ck)) // Must have been set by sys_ck_setup
|
||||
} else {
|
||||
sys_ck
|
||||
};
|
||||
|
||||
// This routine does not support HSIDIV != 1. To
|
||||
// do so it would need to ensure all PLLxON bits are clear
|
||||
// before changing the value of HSIDIV
|
||||
let cr = RCC.cr().read();
|
||||
assert!(cr.hsion());
|
||||
assert!(cr.hsidiv() == Hsidiv::DIV1);
|
||||
|
||||
RCC.csr().modify(|w| w.set_lsion(true));
|
||||
while !RCC.csr().read().lsirdy() {}
|
||||
|
||||
// per_ck from HSI by default
|
||||
let (per_ck, ckpersel) = match (config.per_ck == config.hse, config.per_ck) {
|
||||
(true, Some(hse)) => (hse, Ckpersel::HSE), // HSE
|
||||
(_, Some(CSI_FREQ)) => (CSI_FREQ, Ckpersel::CSI), // CSI
|
||||
_ => (HSI_FREQ, Ckpersel::HSI), // HSI
|
||||
};
|
||||
|
||||
// D1 Core Prescaler
|
||||
// Set to 1
|
||||
let d1cpre_bits = 0;
|
||||
let d1cpre_div = 1;
|
||||
let sys_d1cpre_ck = sys_ck.0 / d1cpre_div;
|
||||
|
||||
// Refer to part datasheet "General operating conditions"
|
||||
// table for (rev V). We do not assert checks for earlier
|
||||
// revisions which may have lower limits.
|
||||
let (sys_d1cpre_ck_max, rcc_hclk_max, pclk_max) = match pwr_vos {
|
||||
VoltageScale::Scale0 => (480_000_000, 240_000_000, 120_000_000),
|
||||
VoltageScale::Scale1 => (400_000_000, 200_000_000, 100_000_000),
|
||||
VoltageScale::Scale2 => (300_000_000, 150_000_000, 75_000_000),
|
||||
_ => (200_000_000, 100_000_000, 50_000_000),
|
||||
};
|
||||
assert!(sys_d1cpre_ck <= sys_d1cpre_ck_max);
|
||||
|
||||
let rcc_hclk = config.hclk.map(|v| v.0).unwrap_or(sys_d1cpre_ck / 2);
|
||||
assert!(rcc_hclk <= rcc_hclk_max);
|
||||
|
||||
// Estimate divisor
|
||||
let (hpre_bits, hpre_div) = match (sys_d1cpre_ck + rcc_hclk - 1) / rcc_hclk {
|
||||
0 => panic!(),
|
||||
1 => (Hpre::DIV1, 1),
|
||||
2 => (Hpre::DIV2, 2),
|
||||
3..=5 => (Hpre::DIV4, 4),
|
||||
6..=11 => (Hpre::DIV8, 8),
|
||||
12..=39 => (Hpre::DIV16, 16),
|
||||
40..=95 => (Hpre::DIV64, 64),
|
||||
96..=191 => (Hpre::DIV128, 128),
|
||||
192..=383 => (Hpre::DIV256, 256),
|
||||
_ => (Hpre::DIV512, 512),
|
||||
};
|
||||
// Calculate real AXI and AHB clock
|
||||
let rcc_hclk = sys_d1cpre_ck / hpre_div;
|
||||
assert!(rcc_hclk <= rcc_hclk_max);
|
||||
let rcc_aclk = rcc_hclk; // AXI clock is always equal to AHB clock on H7
|
||||
// Timer prescaler selection
|
||||
let timpre = Timpre::DEFAULTX2;
|
||||
|
||||
let requested_pclk1 = config.pclk1.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2));
|
||||
let (rcc_pclk1, ppre1_bits, ppre1, rcc_timerx_ker_ck) =
|
||||
ppre_calculate(requested_pclk1, rcc_hclk, pclk_max, Some(timpre));
|
||||
|
||||
let requested_pclk2 = config.pclk2.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2));
|
||||
let (rcc_pclk2, ppre2_bits, ppre2, rcc_timery_ker_ck) =
|
||||
ppre_calculate(requested_pclk2, rcc_hclk, pclk_max, Some(timpre));
|
||||
|
||||
let requested_pclk3 = config.pclk3.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2));
|
||||
let (rcc_pclk3, ppre3_bits, ppre3, _) = ppre_calculate(requested_pclk3, rcc_hclk, pclk_max, None);
|
||||
|
||||
let requested_pclk4 = config.pclk4.map(|v| v.0).unwrap_or_else(|| pclk_max.min(rcc_hclk / 2));
|
||||
let (rcc_pclk4, ppre4_bits, ppre4, _) = ppre_calculate(requested_pclk4, rcc_hclk, pclk_max, None);
|
||||
|
||||
// Start switching clocks -------------------
|
||||
|
||||
// Ensure CSI is on and stable
|
||||
RCC.cr().modify(|w| w.set_csion(true));
|
||||
while !RCC.cr().read().csirdy() {}
|
||||
|
||||
// Ensure HSI48 is on and stable
|
||||
RCC.cr().modify(|w| w.set_hsi48on(true));
|
||||
while !RCC.cr().read().hsi48on() {}
|
||||
|
||||
// XXX: support MCO ?
|
||||
|
||||
let hse_ck = match config.hse {
|
||||
Some(hse) => {
|
||||
// Ensure HSE is on and stable
|
||||
RCC.cr().modify(|w| {
|
||||
w.set_hseon(true);
|
||||
w.set_hsebyp(config.bypass_hse);
|
||||
});
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
Some(hse)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let pllsrc = if config.hse.is_some() { Pllsrc::HSE } else { Pllsrc::HSI };
|
||||
RCC.pllckselr().modify(|w| w.set_pllsrc(pllsrc));
|
||||
|
||||
let enable_pll = |pll| {
|
||||
RCC.cr().modify(|w| w.set_pllon(pll, true));
|
||||
while !RCC.cr().read().pllrdy(pll) {}
|
||||
};
|
||||
|
||||
if pll1_p_ck.is_some() {
|
||||
enable_pll(0);
|
||||
}
|
||||
|
||||
if pll2_p_ck.is_some() {
|
||||
enable_pll(1);
|
||||
}
|
||||
|
||||
if pll3_p_ck.is_some() {
|
||||
enable_pll(2);
|
||||
}
|
||||
|
||||
// Core Prescaler / AHB Prescaler / APB3 Prescaler
|
||||
RCC.d1cfgr().modify(|w| {
|
||||
w.set_d1cpre(Hpre::from_bits(d1cpre_bits));
|
||||
w.set_d1ppre(Dppre::from_bits(ppre3_bits));
|
||||
w.set_hpre(hpre_bits)
|
||||
});
|
||||
// Ensure core prescaler value is valid before future lower
|
||||
// core voltage
|
||||
while RCC.d1cfgr().read().d1cpre().to_bits() != d1cpre_bits {}
|
||||
|
||||
flash_setup(rcc_aclk, pwr_vos);
|
||||
|
||||
// APB1 / APB2 Prescaler
|
||||
RCC.d2cfgr().modify(|w| {
|
||||
w.set_d2ppre1(Dppre::from_bits(ppre1_bits));
|
||||
w.set_d2ppre2(Dppre::from_bits(ppre2_bits));
|
||||
});
|
||||
|
||||
// APB4 Prescaler
|
||||
RCC.d3cfgr().modify(|w| w.set_d3ppre(Dppre::from_bits(ppre4_bits)));
|
||||
|
||||
// Peripheral Clock (per_ck)
|
||||
RCC.d1ccipr().modify(|w| w.set_ckpersel(ckpersel));
|
||||
|
||||
// ADC clock MUX
|
||||
RCC.d3ccipr().modify(|w| w.set_adcsel(config.adc_clock_source.adcsel()));
|
||||
|
||||
let adc_ker_ck = match config.adc_clock_source {
|
||||
AdcClockSource::Pll2PCk => pll2_p_ck.map(Hertz),
|
||||
AdcClockSource::Pll3RCk => pll3_r_ck.map(Hertz),
|
||||
AdcClockSource::PerCk => Some(per_ck),
|
||||
};
|
||||
|
||||
// Set timer clocks prescaler setting
|
||||
RCC.cfgr().modify(|w| w.set_timpre(timpre));
|
||||
|
||||
// Select system clock source
|
||||
let sw = match (sys_use_pll1_p, config.hse.is_some()) {
|
||||
(true, _) => Sw::PLL1,
|
||||
(false, true) => Sw::HSE,
|
||||
_ => Sw::HSI,
|
||||
};
|
||||
RCC.cfgr().modify(|w| w.set_sw(sw));
|
||||
while RCC.cfgr().read().sws().to_bits() != sw.to_bits() {}
|
||||
|
||||
// IO compensation cell - Requires CSI clock and SYSCFG
|
||||
assert!(RCC.cr().read().csirdy());
|
||||
RCC.apb4enr().modify(|w| w.set_syscfgen(true));
|
||||
|
||||
// Enable the compensation cell, using back-bias voltage code
|
||||
// provide by the cell.
|
||||
critical_section::with(|_| {
|
||||
SYSCFG.cccsr().modify(|w| {
|
||||
w.set_en(true);
|
||||
w.set_cs(false);
|
||||
w.set_hslv(false);
|
||||
})
|
||||
});
|
||||
while !SYSCFG.cccsr().read().ready() {}
|
||||
|
||||
let core_clocks = CoreClocks {
|
||||
hclk: Hertz(rcc_hclk),
|
||||
pclk1: Hertz(rcc_pclk1),
|
||||
pclk2: Hertz(rcc_pclk2),
|
||||
pclk3: Hertz(rcc_pclk3),
|
||||
pclk4: Hertz(rcc_pclk4),
|
||||
ppre1,
|
||||
ppre2,
|
||||
ppre3,
|
||||
ppre4,
|
||||
csi_ck: Some(CSI_FREQ),
|
||||
hsi_ck: Some(HSI_FREQ),
|
||||
hsi48_ck: Some(HSI48_FREQ),
|
||||
lsi_ck: Some(LSI_FREQ),
|
||||
per_ck: Some(per_ck),
|
||||
hse_ck,
|
||||
pll1_p_ck: pll1_p_ck.map(Hertz),
|
||||
pll1_q_ck: pll1_q_ck.map(Hertz),
|
||||
pll1_r_ck: pll1_r_ck.map(Hertz),
|
||||
pll2_p_ck: pll2_p_ck.map(Hertz),
|
||||
pll2_q_ck: pll2_q_ck.map(Hertz),
|
||||
pll2_r_ck: pll2_r_ck.map(Hertz),
|
||||
pll3_p_ck: pll3_p_ck.map(Hertz),
|
||||
pll3_q_ck: pll3_q_ck.map(Hertz),
|
||||
pll3_r_ck: pll3_r_ck.map(Hertz),
|
||||
timx_ker_ck: rcc_timerx_ker_ck.map(Hertz),
|
||||
timy_ker_ck: rcc_timery_ker_ck.map(Hertz),
|
||||
adc_ker_ck,
|
||||
sys_ck,
|
||||
c_ck: Hertz(sys_d1cpre_ck),
|
||||
};
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: core_clocks.c_ck,
|
||||
ahb1: core_clocks.hclk,
|
||||
ahb2: core_clocks.hclk,
|
||||
ahb3: core_clocks.hclk,
|
||||
ahb4: core_clocks.hclk,
|
||||
apb1: core_clocks.pclk1,
|
||||
apb2: core_clocks.pclk2,
|
||||
apb4: core_clocks.pclk4,
|
||||
apb1_tim: core_clocks.timx_ker_ck.unwrap_or(core_clocks.pclk1),
|
||||
apb2_tim: core_clocks.timy_ker_ck.unwrap_or(core_clocks.pclk2),
|
||||
adc: core_clocks.adc_ker_ck,
|
||||
});
|
||||
}
|
||||
|
||||
mod pll {
|
||||
use super::{Hertz, RCC};
|
||||
|
||||
const VCO_MIN: u32 = 150_000_000;
|
||||
const VCO_MAX: u32 = 420_000_000;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PllConfig {
|
||||
pub p_ck: Option<Hertz>,
|
||||
pub q_ck: Option<Hertz>,
|
||||
pub r_ck: Option<Hertz>,
|
||||
}
|
||||
|
||||
pub(super) struct PllConfigResults {
|
||||
pub ref_x_ck: u32,
|
||||
pub pll_x_m: u32,
|
||||
pub pll_x_p: u32,
|
||||
pub vco_ck_target: u32,
|
||||
}
|
||||
|
||||
fn vco_output_divider_setup(output: u32, plln: usize) -> (u32, u32) {
|
||||
let pll_x_p = if plln == 0 {
|
||||
if output > VCO_MAX / 2 {
|
||||
1
|
||||
} else {
|
||||
((VCO_MAX / output) | 1) - 1 // Must be even or unity
|
||||
}
|
||||
} else {
|
||||
// Specific to PLL2/3, will subtract 1 later
|
||||
if output > VCO_MAX / 2 {
|
||||
1
|
||||
} else {
|
||||
VCO_MAX / output
|
||||
}
|
||||
};
|
||||
|
||||
let vco_ck = output * pll_x_p;
|
||||
|
||||
assert!(pll_x_p < 128);
|
||||
assert!(vco_ck >= VCO_MIN);
|
||||
assert!(vco_ck <= VCO_MAX);
|
||||
|
||||
(vco_ck, pll_x_p)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Must have exclusive access to the RCC register block
|
||||
fn vco_setup(pll_src: u32, requested_output: u32, plln: usize) -> PllConfigResults {
|
||||
use crate::pac::rcc::vals::{Pllrge, Pllvcosel};
|
||||
|
||||
let (vco_ck_target, pll_x_p) = vco_output_divider_setup(requested_output, plln);
|
||||
|
||||
// Input divisor, resulting in a reference clock in the range
|
||||
// 1 to 2 MHz. Choose the highest reference clock (lowest m)
|
||||
let pll_x_m = (pll_src + 1_999_999) / 2_000_000;
|
||||
assert!(pll_x_m < 64);
|
||||
|
||||
// Calculate resulting reference clock
|
||||
let ref_x_ck = pll_src / pll_x_m;
|
||||
assert!((1_000_000..=2_000_000).contains(&ref_x_ck));
|
||||
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllvcosel(plln, Pllvcosel::MEDIUMVCO);
|
||||
w.set_pllrge(plln, Pllrge::RANGE1);
|
||||
});
|
||||
PllConfigResults {
|
||||
ref_x_ck,
|
||||
pll_x_m,
|
||||
pll_x_p,
|
||||
vco_ck_target,
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Must have exclusive access to the RCC register block
|
||||
pub(super) fn pll_setup(pll_src: u32, config: &PllConfig, plln: usize) -> (Option<u32>, Option<u32>, Option<u32>) {
|
||||
use crate::pac::rcc::vals::Divp;
|
||||
|
||||
match config.p_ck {
|
||||
Some(requested_output) => {
|
||||
let config_results = vco_setup(pll_src, requested_output.0, plln);
|
||||
let PllConfigResults {
|
||||
ref_x_ck,
|
||||
pll_x_m,
|
||||
pll_x_p,
|
||||
vco_ck_target,
|
||||
} = config_results;
|
||||
|
||||
RCC.pllckselr().modify(|w| w.set_divm(plln, pll_x_m as u8));
|
||||
|
||||
// Feedback divider. Integer only
|
||||
let pll_x_n = vco_ck_target / ref_x_ck;
|
||||
assert!(pll_x_n >= 4);
|
||||
assert!(pll_x_n <= 512);
|
||||
RCC.plldivr(plln).modify(|w| w.set_divn1((pll_x_n - 1) as u16));
|
||||
|
||||
// No FRACN
|
||||
RCC.pllcfgr().modify(|w| w.set_pllfracen(plln, false));
|
||||
let vco_ck = ref_x_ck * pll_x_n;
|
||||
|
||||
RCC.plldivr(plln)
|
||||
.modify(|w| w.set_divp1(Divp::from_bits((pll_x_p - 1) as u8)));
|
||||
RCC.pllcfgr().modify(|w| w.set_divpen(plln, true));
|
||||
|
||||
// Calulate additional output dividers
|
||||
let q_ck = match config.q_ck {
|
||||
Some(Hertz(ck)) if ck > 0 => {
|
||||
let div = (vco_ck + ck - 1) / ck;
|
||||
RCC.plldivr(plln).modify(|w| w.set_divq1((div - 1) as u8));
|
||||
RCC.pllcfgr().modify(|w| w.set_divqen(plln, true));
|
||||
Some(vco_ck / div)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let r_ck = match config.r_ck {
|
||||
Some(Hertz(ck)) if ck > 0 => {
|
||||
let div = (vco_ck + ck - 1) / ck;
|
||||
RCC.plldivr(plln).modify(|w| w.set_divr1((div - 1) as u8));
|
||||
RCC.pllcfgr().modify(|w| w.set_divren(plln, true));
|
||||
Some(vco_ck / div)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
(Some(vco_ck / pll_x_p), q_ck, r_ck)
|
||||
}
|
||||
None => {
|
||||
assert!(
|
||||
config.q_ck.is_none(),
|
||||
"Must set PLL P clock for Q clock to take effect!"
|
||||
);
|
||||
assert!(
|
||||
config.r_ck.is_none(),
|
||||
"Must set PLL P clock for R clock to take effect!"
|
||||
);
|
||||
(None, None, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
||||
use super::bd::BackupDomain;
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use super::RtcClockSource;
|
||||
pub use crate::pac::pwr::vals::Vos as VoltageScale;
|
||||
use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw};
|
||||
use crate::pac::RCC;
|
||||
#[cfg(crs)]
|
||||
use crate::pac::{crs, CRS, SYSCFG};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
|
||||
@ -135,6 +138,10 @@ pub struct Config {
|
||||
pub apb2_pre: APBPrescaler,
|
||||
#[cfg(crs)]
|
||||
pub enable_hsi48: bool,
|
||||
pub rtc: Option<RtcClockSource>,
|
||||
pub lse: Option<Hertz>,
|
||||
pub lsi: bool,
|
||||
pub voltage_scale: VoltageScale,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -142,16 +149,25 @@ impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
mux: ClockSrc::MSI(MSIRange::default()),
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb1_pre: APBPrescaler::NotDivided,
|
||||
apb2_pre: APBPrescaler::NotDivided,
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
#[cfg(crs)]
|
||||
enable_hsi48: false,
|
||||
rtc: None,
|
||||
lse: None,
|
||||
lsi: false,
|
||||
voltage_scale: VoltageScale::RANGE1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
// Set voltage scale
|
||||
while PWR.csr().read().vosf() {}
|
||||
PWR.cr().write(|w| w.set_vos(config.voltage_scale));
|
||||
while PWR.csr().read().vosf() {}
|
||||
|
||||
let (sys_clk, sw) = match config.mux {
|
||||
ClockSrc::MSI(range) => {
|
||||
// Set MSI range
|
||||
@ -231,6 +247,28 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
}
|
||||
};
|
||||
|
||||
BackupDomain::configure_ls(
|
||||
config.rtc.unwrap_or(RtcClockSource::NOCLOCK),
|
||||
config.lsi,
|
||||
config.lse.map(|_| Default::default()),
|
||||
);
|
||||
|
||||
let wait_states = match config.voltage_scale {
|
||||
VoltageScale::RANGE1 => match sys_clk {
|
||||
..=16_000_000 => 0,
|
||||
_ => 1,
|
||||
},
|
||||
VoltageScale::RANGE2 => match sys_clk {
|
||||
..=8_000_000 => 0,
|
||||
_ => 1,
|
||||
},
|
||||
VoltageScale::RANGE3 => 0,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
FLASH.acr().modify(|w| {
|
||||
w.set_latency(wait_states != 0);
|
||||
});
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(sw);
|
||||
w.set_hpre(config.ahb_pre.into());
|
||||
@ -239,7 +277,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
});
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
AHBPrescaler::DIV1 => sys_clk,
|
||||
pre => {
|
||||
let pre: Hpre = pre.into();
|
||||
let pre = 1 << (pre.to_bits() as u32 - 7);
|
||||
@ -248,7 +286,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
@ -258,7 +296,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
@ -269,13 +307,6 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
|
||||
#[cfg(crs)]
|
||||
if config.enable_hsi48 {
|
||||
// Reset SYSCFG peripheral
|
||||
RCC.apb2rstr().modify(|w| w.set_syscfgrst(true));
|
||||
RCC.apb2rstr().modify(|w| w.set_syscfgrst(false));
|
||||
|
||||
// Enable SYSCFG peripheral
|
||||
RCC.apb2enr().modify(|w| w.set_syscfgen(true));
|
||||
|
||||
// Reset CRS peripheral
|
||||
RCC.apb1rstr().modify(|w| w.set_crsrst(true));
|
||||
RCC.apb1rstr().modify(|w| w.set_crsrst(false));
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
@ -138,9 +138,9 @@ impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
mux: ClockSrc::MSI(MSIRange::default()),
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb1_pre: APBPrescaler::NotDivided,
|
||||
apb2_pre: APBPrescaler::NotDivided,
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -240,7 +240,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
});
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
AHBPrescaler::DIV1 => sys_clk,
|
||||
pre => {
|
||||
let pre: Hpre = pre.into();
|
||||
let pre = 1 << (pre.to_bits() as u32 - 7);
|
||||
@ -249,7 +249,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
@ -259,7 +259,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
|
@ -2,15 +2,15 @@ use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_internal::into_ref;
|
||||
use stm32_metapac::rcc::regs::Cfgr;
|
||||
use stm32_metapac::rcc::vals::{Lsedrv, Mcopre, Mcosel};
|
||||
use stm32_metapac::rcc::vals::{Mcopre, Mcosel};
|
||||
|
||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::Speed;
|
||||
use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::bd::{BackupDomain, RtcClockSource};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::rtc::{Rtc, RtcClockSource as RCS};
|
||||
use crate::time::Hertz;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
@ -241,6 +241,8 @@ pub struct Config {
|
||||
#[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
|
||||
pub hsi48: bool,
|
||||
pub rtc_mux: RtcClockSource,
|
||||
pub lse: Option<Hertz>,
|
||||
pub lsi: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -248,22 +250,19 @@ impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
mux: ClockSrc::MSI(MSIRange::Range6),
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb1_pre: APBPrescaler::NotDivided,
|
||||
apb2_pre: APBPrescaler::NotDivided,
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
pllsai1: None,
|
||||
#[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
|
||||
hsi48: false,
|
||||
rtc_mux: RtcClockSource::LSI32,
|
||||
rtc_mux: RtcClockSource::LSI,
|
||||
lsi: true,
|
||||
lse: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum RtcClockSource {
|
||||
LSE32,
|
||||
LSI32,
|
||||
}
|
||||
|
||||
pub enum McoClock {
|
||||
DIV1,
|
||||
DIV2,
|
||||
@ -410,37 +409,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
while RCC.cfgr().read().sws() != Sw::MSI {}
|
||||
}
|
||||
|
||||
RCC.apb1enr1().modify(|w| w.set_pwren(true));
|
||||
|
||||
match config.rtc_mux {
|
||||
RtcClockSource::LSE32 => {
|
||||
// 1. Unlock the backup domain
|
||||
PWR.cr1().modify(|w| w.set_dbp(true));
|
||||
|
||||
// 2. Setup the LSE
|
||||
RCC.bdcr().modify(|w| {
|
||||
// Enable LSE
|
||||
w.set_lseon(true);
|
||||
// Max drive strength
|
||||
// TODO: should probably be settable
|
||||
w.set_lsedrv(Lsedrv::HIGH);
|
||||
});
|
||||
|
||||
// Wait until LSE is running
|
||||
while !RCC.bdcr().read().lserdy() {}
|
||||
|
||||
Rtc::set_clock_source(RCS::LSE);
|
||||
}
|
||||
RtcClockSource::LSI32 => {
|
||||
// Turn on the internal 32 kHz LSI oscillator
|
||||
RCC.csr().modify(|w| w.set_lsion(true));
|
||||
|
||||
// Wait until LSI is running
|
||||
while !RCC.csr().read().lsirdy() {}
|
||||
|
||||
Rtc::set_clock_source(RCS::LSI);
|
||||
}
|
||||
}
|
||||
BackupDomain::configure_ls(config.rtc_mux, config.lsi, config.lse.map(|_| Default::default()));
|
||||
|
||||
let (sys_clk, sw) = match config.mux {
|
||||
ClockSrc::MSI(range) => {
|
||||
@ -451,7 +420,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
w.set_msirgsel(true);
|
||||
w.set_msion(true);
|
||||
|
||||
if let RtcClockSource::LSE32 = config.rtc_mux {
|
||||
if let RtcClockSource::LSE = config.rtc_mux {
|
||||
// If LSE is enabled, enable calibration of MSI
|
||||
w.set_msipllen(true);
|
||||
} else {
|
||||
@ -609,7 +578,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
});
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
AHBPrescaler::DIV1 => sys_clk,
|
||||
pre => {
|
||||
let pre: Hpre = pre.into();
|
||||
let pre = 1 << (pre.to_bits() as u32 - 7);
|
||||
@ -618,7 +587,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
@ -628,7 +597,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
@ -637,8 +606,6 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
}
|
||||
};
|
||||
|
||||
RCC.apb1enr1().modify(|w| w.set_pwren(true));
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sys_clk),
|
||||
ahb1: Hertz(ahb_freq),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use stm32_metapac::PWR;
|
||||
|
||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
@ -238,9 +238,9 @@ impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
mux: ClockSrc::MSI(MSIRange::Range6),
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb1_pre: APBPrescaler::NotDivided,
|
||||
apb2_pre: APBPrescaler::NotDivided,
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
pllsai1: None,
|
||||
hsi48: false,
|
||||
}
|
||||
@ -317,11 +317,6 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
|
||||
let freq = (src_freq / prediv.to_div() * mul.to_mul()) / div.to_div();
|
||||
|
||||
#[cfg(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx))]
|
||||
assert!(freq <= 120_000_000);
|
||||
#[cfg(not(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx)))]
|
||||
assert!(freq <= 80_000_000);
|
||||
|
||||
RCC.pllcfgr().write(move |w| {
|
||||
w.set_plln(mul.into());
|
||||
w.set_pllm(prediv.into());
|
||||
@ -407,7 +402,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
});
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
AHBPrescaler::DIV1 => sys_clk,
|
||||
pre => {
|
||||
let pre: Hpre = pre.into();
|
||||
let pre = 1 << (pre.to_bits() as u32 - 7);
|
||||
@ -416,7 +411,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
@ -426,7 +421,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
|
71
embassy-stm32/src/rcc/mco.rs
Normal file
71
embassy-stm32/src/rcc/mco.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy_hal_internal::into_ref;
|
||||
|
||||
use crate::gpio::sealed::AFType;
|
||||
use crate::gpio::Speed;
|
||||
pub use crate::pac::rcc::vals::{Mco1 as Mco1Source, Mco2 as Mco2Source};
|
||||
use crate::pac::RCC;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
pub(crate) mod sealed {
|
||||
pub trait McoInstance {
|
||||
type Source;
|
||||
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait McoInstance: sealed::McoInstance + 'static {}
|
||||
|
||||
pin_trait!(McoPin, McoInstance);
|
||||
|
||||
macro_rules! impl_peri {
|
||||
($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => {
|
||||
impl sealed::McoInstance for peripherals::$peri {
|
||||
type Source = $source;
|
||||
|
||||
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) {
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.$set_source(source);
|
||||
w.$set_prescaler(prescaler);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl McoInstance for peripherals::$peri {}
|
||||
};
|
||||
}
|
||||
|
||||
impl_peri!(MCO1, Mco1Source, set_mco1, set_mco1pre);
|
||||
impl_peri!(MCO2, Mco2Source, set_mco2, set_mco2pre);
|
||||
|
||||
pub struct Mco<'d, T: McoInstance> {
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
|
||||
impl<'d, T: McoInstance> Mco<'d, T> {
|
||||
/// Create a new MCO instance.
|
||||
///
|
||||
/// `prescaler` must be between 1 and 15.
|
||||
pub fn new(
|
||||
_peri: impl Peripheral<P = T> + 'd,
|
||||
pin: impl Peripheral<P = impl McoPin<T>> + 'd,
|
||||
source: T::Source,
|
||||
prescaler: u8,
|
||||
) -> Self {
|
||||
into_ref!(pin);
|
||||
|
||||
assert!(
|
||||
1 <= prescaler && prescaler <= 15,
|
||||
"Mco prescaler must be between 1 and 15. Refer to the reference manual for more information."
|
||||
);
|
||||
|
||||
critical_section::with(|_| unsafe {
|
||||
T::apply_clock_settings(source, prescaler);
|
||||
pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
|
||||
pin.set_speed(Speed::VeryHigh);
|
||||
});
|
||||
|
||||
Self { phantom: PhantomData }
|
||||
}
|
||||
}
|
@ -1,11 +1,17 @@
|
||||
#![macro_use]
|
||||
|
||||
pub mod common;
|
||||
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
pub use crate::rcc::bd::RtcClockSource;
|
||||
use crate::time::Hertz;
|
||||
|
||||
pub(crate) mod bd;
|
||||
mod bus;
|
||||
#[cfg(any(stm32h5, stm32h7))]
|
||||
mod mco;
|
||||
#[cfg(any(stm32h5, stm32h7))]
|
||||
pub use mco::*;
|
||||
|
||||
#[cfg_attr(rcc_f0, path = "f0.rs")]
|
||||
#[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")]
|
||||
#[cfg_attr(rcc_f2, path = "f2.rs")]
|
||||
@ -15,20 +21,30 @@ use crate::time::Hertz;
|
||||
#[cfg_attr(rcc_c0, path = "c0.rs")]
|
||||
#[cfg_attr(rcc_g0, path = "g0.rs")]
|
||||
#[cfg_attr(rcc_g4, path = "g4.rs")]
|
||||
#[cfg_attr(any(rcc_h7, rcc_h7ab), path = "h7.rs")]
|
||||
#[cfg_attr(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab), path = "h.rs")]
|
||||
#[cfg_attr(rcc_l0, path = "l0.rs")]
|
||||
#[cfg_attr(rcc_l1, path = "l1.rs")]
|
||||
#[cfg_attr(rcc_l4, path = "l4.rs")]
|
||||
#[cfg_attr(rcc_l5, path = "l5.rs")]
|
||||
#[cfg_attr(rcc_u5, path = "u5.rs")]
|
||||
#[cfg_attr(rcc_wb, path = "wb.rs")]
|
||||
#[cfg_attr(rcc_wba, path = "wba.rs")]
|
||||
#[cfg_attr(any(rcc_wl5, rcc_wle), path = "wl.rs")]
|
||||
#[cfg_attr(any(rcc_h5, rcc_h50), path = "h5.rs")]
|
||||
mod _version;
|
||||
pub use _version::*;
|
||||
#[cfg(feature = "low-power")]
|
||||
use atomic_polyfill::{AtomicU32, Ordering};
|
||||
|
||||
// Model Clock Configuration
|
||||
//
|
||||
// pub struct Clocks {
|
||||
// hse: Option<Hertz>,
|
||||
// hsi: bool,
|
||||
// lse: Option<Hertz>,
|
||||
// lsi: bool,
|
||||
// rtc: RtcSource,
|
||||
// }
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Clocks {
|
||||
@ -41,16 +57,18 @@ pub struct Clocks {
|
||||
pub apb2: Hertz,
|
||||
#[cfg(not(any(rcc_c0, rcc_g0)))]
|
||||
pub apb2_tim: Hertz,
|
||||
#[cfg(any(rcc_wl5, rcc_wle, rcc_h5, rcc_h50, rcc_u5))]
|
||||
#[cfg(any(rcc_wl5, rcc_wle, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_u5))]
|
||||
pub apb3: Hertz,
|
||||
#[cfg(any(rcc_h7, rcc_h7ab))]
|
||||
pub apb4: Hertz,
|
||||
#[cfg(any(rcc_wba))]
|
||||
pub apb7: Hertz,
|
||||
|
||||
// AHB
|
||||
pub ahb1: Hertz,
|
||||
#[cfg(any(
|
||||
rcc_l4, rcc_l5, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_g4, rcc_u5, rcc_wb,
|
||||
rcc_wl5, rcc_wle
|
||||
rcc_wba, rcc_wl5, rcc_wle
|
||||
))]
|
||||
pub ahb2: Hertz,
|
||||
#[cfg(any(
|
||||
@ -58,7 +76,7 @@ pub struct Clocks {
|
||||
rcc_wle
|
||||
))]
|
||||
pub ahb3: Hertz,
|
||||
#[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))]
|
||||
#[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_wba))]
|
||||
pub ahb4: Hertz,
|
||||
|
||||
#[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))]
|
||||
@ -70,15 +88,22 @@ pub struct Clocks {
|
||||
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
|
||||
pub pllsai: Option<Hertz>,
|
||||
|
||||
#[cfg(stm32f1)]
|
||||
pub adc: Hertz,
|
||||
|
||||
#[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab))]
|
||||
#[cfg(any(rcc_f1, rcc_f100, rcc_f1cl, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_f3, rcc_g4))]
|
||||
pub adc: Option<Hertz>,
|
||||
|
||||
#[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
|
||||
/// Set only if the lsi or lse is configured
|
||||
#[cfg(any(rcc_f3, rcc_g4))]
|
||||
pub adc34: Option<Hertz>,
|
||||
|
||||
#[cfg(stm32f334)]
|
||||
pub hrtim: Option<Hertz>,
|
||||
|
||||
#[cfg(any(rcc_wb, rcc_f4, rcc_f410, rcc_f7))]
|
||||
/// Set only if the lsi or lse is configured, indicates stop is supported
|
||||
pub rtc: Option<Hertz>,
|
||||
|
||||
#[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
|
||||
/// Set if the hse is configured, indicates stop is not supported
|
||||
pub rtc_hse: Option<Hertz>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "low-power")]
|
||||
@ -86,6 +111,8 @@ static CLOCK_REFCOUNT: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
#[cfg(feature = "low-power")]
|
||||
pub fn low_power_ready() -> bool {
|
||||
trace!("clock refcount: {}", CLOCK_REFCOUNT.load(Ordering::SeqCst));
|
||||
|
||||
CLOCK_REFCOUNT.load(Ordering::SeqCst) == 0
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use stm32_metapac::rcc::vals::{Msirange, Msirgsel, Pllm, Pllsrc, Sw};
|
||||
|
||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
@ -11,7 +11,7 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
|
||||
pub use super::common::VoltageScale;
|
||||
pub use crate::pac::pwr::vals::Vos as VoltageScale;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ClockSrc {
|
||||
@ -119,53 +119,13 @@ impl Into<Pllm> for PllM {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for AHBPrescaler {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
AHBPrescaler::NotDivided => 1,
|
||||
AHBPrescaler::Div2 => 0x08,
|
||||
AHBPrescaler::Div4 => 0x09,
|
||||
AHBPrescaler::Div8 => 0x0a,
|
||||
AHBPrescaler::Div16 => 0x0b,
|
||||
AHBPrescaler::Div64 => 0x0c,
|
||||
AHBPrescaler::Div128 => 0x0d,
|
||||
AHBPrescaler::Div256 => 0x0e,
|
||||
AHBPrescaler::Div512 => 0x0f,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AHBPrescaler {
|
||||
fn default() -> Self {
|
||||
AHBPrescaler::NotDivided
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for APBPrescaler {
|
||||
fn default() -> Self {
|
||||
APBPrescaler::NotDivided
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for APBPrescaler {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
APBPrescaler::NotDivided => 1,
|
||||
APBPrescaler::Div2 => 0x04,
|
||||
APBPrescaler::Div4 => 0x05,
|
||||
APBPrescaler::Div8 => 0x06,
|
||||
APBPrescaler::Div16 => 0x07,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Sw> for ClockSrc {
|
||||
fn into(self) -> Sw {
|
||||
match self {
|
||||
ClockSrc::MSI(..) => Sw::MSIS,
|
||||
ClockSrc::HSE(..) => Sw::HSE,
|
||||
ClockSrc::HSI16 => Sw::HSI16,
|
||||
ClockSrc::PLL1R(..) => Sw::PLL1R,
|
||||
ClockSrc::PLL1R(..) => Sw::PLL1_R,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -239,10 +199,10 @@ impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
mux: ClockSrc::MSI(MSIRange::default()),
|
||||
ahb_pre: Default::default(),
|
||||
apb1_pre: Default::default(),
|
||||
apb2_pre: Default::default(),
|
||||
apb3_pre: Default::default(),
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
apb3_pre: APBPrescaler::DIV1,
|
||||
hsi48: false,
|
||||
}
|
||||
}
|
||||
@ -326,12 +286,12 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
}
|
||||
|
||||
// TODO make configurable
|
||||
let power_vos = VoltageScale::Scale3;
|
||||
let power_vos = VoltageScale::RANGE3;
|
||||
|
||||
// states and programming delay
|
||||
let wait_states = match power_vos {
|
||||
// VOS 0 range VCORE 1.26V - 1.40V
|
||||
VoltageScale::Scale0 => {
|
||||
// VOS 1 range VCORE 1.26V - 1.40V
|
||||
VoltageScale::RANGE1 => {
|
||||
if sys_clk < 32_000_000 {
|
||||
0
|
||||
} else if sys_clk < 64_000_000 {
|
||||
@ -344,8 +304,8 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
4
|
||||
}
|
||||
}
|
||||
// VOS 1 range VCORE 1.15V - 1.26V
|
||||
VoltageScale::Scale1 => {
|
||||
// VOS 2 range VCORE 1.15V - 1.26V
|
||||
VoltageScale::RANGE2 => {
|
||||
if sys_clk < 30_000_000 {
|
||||
0
|
||||
} else if sys_clk < 60_000_000 {
|
||||
@ -356,8 +316,8 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
3
|
||||
}
|
||||
}
|
||||
// VOS 2 range VCORE 1.05V - 1.15V
|
||||
VoltageScale::Scale2 => {
|
||||
// VOS 3 range VCORE 1.05V - 1.15V
|
||||
VoltageScale::RANGE3 => {
|
||||
if sys_clk < 24_000_000 {
|
||||
0
|
||||
} else if sys_clk < 48_000_000 {
|
||||
@ -366,8 +326,8 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
2
|
||||
}
|
||||
}
|
||||
// VOS 3 range VCORE 0.95V - 1.05V
|
||||
VoltageScale::Scale3 => {
|
||||
// VOS 4 range VCORE 0.95V - 1.05V
|
||||
VoltageScale::RANGE4 => {
|
||||
if sys_clk < 12_000_000 {
|
||||
0
|
||||
} else {
|
||||
@ -395,7 +355,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
});
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
AHBPrescaler::DIV1 => sys_clk,
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre = 1 << (pre as u32 - 7);
|
||||
@ -404,7 +364,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
@ -414,7 +374,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
@ -424,7 +384,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
};
|
||||
|
||||
let (apb3_freq, _apb3_tim_freq) = match config.apb3_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
|
@ -1,6 +1,6 @@
|
||||
pub use super::common::{AHBPrescaler, APBPrescaler};
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::rcc::bd::{BackupDomain, RtcClockSource};
|
||||
use crate::rcc::Clocks;
|
||||
use crate::rtc::{Rtc, RtcClockSource};
|
||||
use crate::time::{khz, mhz, Hertz};
|
||||
|
||||
/// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC,
|
||||
@ -108,6 +108,7 @@ pub struct Pll {
|
||||
pub struct Config {
|
||||
pub hse: Option<Hse>,
|
||||
pub lse: Option<Hertz>,
|
||||
pub lsi: bool,
|
||||
pub sys: Sysclk,
|
||||
pub mux: Option<PllMux>,
|
||||
pub pll48: Option<Pll48Source>,
|
||||
@ -135,7 +136,8 @@ pub const WPAN_DEFAULT: Config = Config {
|
||||
prediv: 2,
|
||||
}),
|
||||
pll48: None,
|
||||
rtc: None,
|
||||
rtc: Some(RtcClockSource::LSE),
|
||||
lsi: false,
|
||||
|
||||
pll: Some(Pll {
|
||||
mul: 12,
|
||||
@ -145,11 +147,11 @@ pub const WPAN_DEFAULT: Config = Config {
|
||||
}),
|
||||
pllsai: None,
|
||||
|
||||
ahb1_pre: AHBPrescaler::NotDivided,
|
||||
ahb2_pre: AHBPrescaler::Div2,
|
||||
ahb3_pre: AHBPrescaler::NotDivided,
|
||||
apb1_pre: APBPrescaler::NotDivided,
|
||||
apb2_pre: APBPrescaler::NotDivided,
|
||||
ahb1_pre: AHBPrescaler::DIV1,
|
||||
ahb2_pre: AHBPrescaler::DIV2,
|
||||
ahb3_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
};
|
||||
|
||||
impl Default for Config {
|
||||
@ -164,12 +166,13 @@ impl Default for Config {
|
||||
pll: None,
|
||||
pllsai: None,
|
||||
rtc: None,
|
||||
lsi: false,
|
||||
|
||||
ahb1_pre: AHBPrescaler::NotDivided,
|
||||
ahb2_pre: AHBPrescaler::NotDivided,
|
||||
ahb3_pre: AHBPrescaler::NotDivided,
|
||||
apb1_pre: APBPrescaler::NotDivided,
|
||||
apb2_pre: APBPrescaler::NotDivided,
|
||||
ahb1_pre: AHBPrescaler::DIV1,
|
||||
ahb2_pre: AHBPrescaler::DIV1,
|
||||
ahb3_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -209,7 +212,7 @@ pub(crate) fn compute_clocks(config: &Config) -> Clocks {
|
||||
};
|
||||
|
||||
let ahb1_clk = match config.ahb1_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
AHBPrescaler::DIV1 => sys_clk,
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre = 1u32 << (pre as u32 - 7);
|
||||
@ -218,7 +221,7 @@ pub(crate) fn compute_clocks(config: &Config) -> Clocks {
|
||||
};
|
||||
|
||||
let ahb2_clk = match config.ahb2_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
AHBPrescaler::DIV1 => sys_clk,
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre = 1u32 << (pre as u32 - 7);
|
||||
@ -227,7 +230,7 @@ pub(crate) fn compute_clocks(config: &Config) -> Clocks {
|
||||
};
|
||||
|
||||
let ahb3_clk = match config.ahb3_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
AHBPrescaler::DIV1 => sys_clk,
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre = 1u32 << (pre as u32 - 7);
|
||||
@ -236,7 +239,7 @@ pub(crate) fn compute_clocks(config: &Config) -> Clocks {
|
||||
};
|
||||
|
||||
let (apb1_clk, apb1_tim_clk) = match config.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb1_clk, ahb1_clk),
|
||||
APBPrescaler::DIV1 => (ahb1_clk, ahb1_clk),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
@ -246,7 +249,7 @@ pub(crate) fn compute_clocks(config: &Config) -> Clocks {
|
||||
};
|
||||
|
||||
let (apb2_clk, apb2_tim_clk) = match config.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb1_clk, ahb1_clk),
|
||||
APBPrescaler::DIV1 => (ahb1_clk, ahb1_clk),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
@ -271,11 +274,11 @@ pub(crate) fn compute_clocks(config: &Config) -> Clocks {
|
||||
apb1_tim: apb1_tim_clk,
|
||||
apb2_tim: apb2_tim_clk,
|
||||
rtc: rtc_clk,
|
||||
rtc_hse: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn configure_clocks(config: &Config) {
|
||||
let pwr = crate::pac::PWR;
|
||||
let rcc = crate::pac::RCC;
|
||||
|
||||
let needs_hsi = if let Some(pll_mux) = &config.mux {
|
||||
@ -292,29 +295,13 @@ pub(crate) fn configure_clocks(config: &Config) {
|
||||
while !rcc.cr().read().hsirdy() {}
|
||||
}
|
||||
|
||||
let needs_lsi = if let Some(rtc_mux) = &config.rtc {
|
||||
*rtc_mux == RtcClockSource::LSI
|
||||
} else {
|
||||
false
|
||||
};
|
||||
rcc.cfgr().modify(|w| w.set_stopwuck(true));
|
||||
|
||||
if needs_lsi {
|
||||
rcc.csr().modify(|w| w.set_lsi1on(true));
|
||||
|
||||
while !rcc.csr().read().lsi1rdy() {}
|
||||
}
|
||||
|
||||
match &config.lse {
|
||||
Some(_) => {
|
||||
rcc.cfgr().modify(|w| w.set_stopwuck(true));
|
||||
|
||||
pwr.cr1().modify(|w| w.set_dbp(true));
|
||||
pwr.cr1().modify(|w| w.set_dbp(true));
|
||||
|
||||
rcc.bdcr().modify(|w| w.set_lseon(true));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
BackupDomain::configure_ls(
|
||||
config.rtc.unwrap_or(RtcClockSource::NOCLOCK),
|
||||
config.lsi,
|
||||
config.lse.map(|_| Default::default()),
|
||||
);
|
||||
|
||||
match &config.hse {
|
||||
Some(hse) => {
|
||||
@ -374,6 +361,4 @@ pub(crate) fn configure_clocks(config: &Config) {
|
||||
w.set_c2hpre(config.ahb2_pre.into());
|
||||
w.set_shdhpre(config.ahb3_pre.into());
|
||||
});
|
||||
|
||||
config.rtc.map(|clock_source| Rtc::set_clock_source(clock_source));
|
||||
}
|
||||
|
154
embassy-stm32/src/rcc/wba.rs
Normal file
154
embassy-stm32/src/rcc/wba.rs
Normal file
@ -0,0 +1,154 @@
|
||||
use stm32_metapac::rcc::vals::{Pllsrc, Sw};
|
||||
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
|
||||
|
||||
/// LSI speed
|
||||
pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
|
||||
pub use crate::pac::pwr::vals::Vos as VoltageScale;
|
||||
pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Ppre as APBPrescaler};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ClockSrc {
|
||||
HSE(Hertz),
|
||||
HSI16,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum PllSrc {
|
||||
HSE(Hertz),
|
||||
HSI16,
|
||||
}
|
||||
|
||||
impl Into<Pllsrc> for PllSrc {
|
||||
fn into(self) -> Pllsrc {
|
||||
match self {
|
||||
PllSrc::HSE(..) => Pllsrc::HSE32,
|
||||
PllSrc::HSI16 => Pllsrc::HSI16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Sw> for ClockSrc {
|
||||
fn into(self) -> Sw {
|
||||
match self {
|
||||
ClockSrc::HSE(..) => Sw::HSE32,
|
||||
ClockSrc::HSI16 => Sw::HSI16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Config {
|
||||
pub mux: ClockSrc,
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb1_pre: APBPrescaler,
|
||||
pub apb2_pre: APBPrescaler,
|
||||
pub apb7_pre: APBPrescaler,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
mux: ClockSrc::HSI16,
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
apb7_pre: APBPrescaler::DIV1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let sys_clk = match config.mux {
|
||||
ClockSrc::HSE(freq) => {
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
freq
|
||||
}
|
||||
ClockSrc::HSI16 => {
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
HSI_FREQ
|
||||
}
|
||||
};
|
||||
|
||||
// TODO make configurable
|
||||
let power_vos = VoltageScale::RANGE1;
|
||||
|
||||
// states and programming delay
|
||||
let wait_states = match power_vos {
|
||||
VoltageScale::RANGE1 => match sys_clk.0 {
|
||||
..=32_000_000 => 0,
|
||||
..=64_000_000 => 1,
|
||||
..=96_000_000 => 2,
|
||||
..=100_000_000 => 3,
|
||||
_ => 4,
|
||||
},
|
||||
VoltageScale::RANGE2 => match sys_clk.0 {
|
||||
..=8_000_000 => 0,
|
||||
..=16_000_000 => 1,
|
||||
_ => 2,
|
||||
},
|
||||
};
|
||||
|
||||
FLASH.acr().modify(|w| {
|
||||
w.set_latency(wait_states);
|
||||
});
|
||||
|
||||
RCC.cfgr1().modify(|w| {
|
||||
w.set_sw(config.mux.into());
|
||||
});
|
||||
|
||||
RCC.cfgr2().modify(|w| {
|
||||
w.set_hpre(config.ahb_pre.into());
|
||||
w.set_ppre1(config.apb1_pre.into());
|
||||
w.set_ppre2(config.apb2_pre.into());
|
||||
});
|
||||
|
||||
RCC.cfgr3().modify(|w| {
|
||||
w.set_ppre7(config.apb7_pre.into());
|
||||
});
|
||||
|
||||
let ahb_freq = sys_clk / config.ahb_pre;
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, freq * 2u32)
|
||||
}
|
||||
};
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, freq * 2u32)
|
||||
}
|
||||
};
|
||||
let (apb7_freq, _apb7_tim_freq) = match config.apb7_pre {
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, freq * 2u32)
|
||||
}
|
||||
};
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: sys_clk,
|
||||
ahb1: ahb_freq,
|
||||
ahb2: ahb_freq,
|
||||
ahb4: ahb_freq,
|
||||
apb1: apb1_freq,
|
||||
apb2: apb2_freq,
|
||||
apb7: apb7_freq,
|
||||
apb1_tim: apb1_tim_freq,
|
||||
apb2_tim: apb2_tim_freq,
|
||||
});
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
pub use super::common::{AHBPrescaler, APBPrescaler, VoltageScale};
|
||||
use crate::pac::pwr::vals::Dbp;
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
pub use crate::pac::pwr::vals::Vos as VoltageScale;
|
||||
use crate::pac::rcc::vals::Adcsel;
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::bd::{BackupDomain, RtcClockSource};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::rtc::{Rtc, RtcClockSource as RCS};
|
||||
use crate::time::Hertz;
|
||||
|
||||
/// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC,
|
||||
@ -75,9 +76,9 @@ impl MSIRange {
|
||||
|
||||
fn vos(&self) -> VoltageScale {
|
||||
if self > &MSIRange::Range8 {
|
||||
VoltageScale::Scale0
|
||||
VoltageScale::RANGE1
|
||||
} else {
|
||||
VoltageScale::Scale1
|
||||
VoltageScale::RANGE2
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -107,6 +108,29 @@ impl Into<u8> for MSIRange {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AdcClockSource {
|
||||
HSI16,
|
||||
PLLPCLK,
|
||||
SYSCLK,
|
||||
}
|
||||
|
||||
impl AdcClockSource {
|
||||
pub fn adcsel(&self) -> Adcsel {
|
||||
match self {
|
||||
AdcClockSource::HSI16 => Adcsel::HSI16,
|
||||
AdcClockSource::PLLPCLK => Adcsel::PLLPCLK,
|
||||
AdcClockSource::SYSCLK => Adcsel::SYSCLK,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AdcClockSource {
|
||||
fn default() -> Self {
|
||||
Self::HSI16
|
||||
}
|
||||
}
|
||||
|
||||
/// Clocks configutation
|
||||
pub struct Config {
|
||||
pub mux: ClockSrc,
|
||||
@ -114,9 +138,10 @@ pub struct Config {
|
||||
pub shd_ahb_pre: AHBPrescaler,
|
||||
pub apb1_pre: APBPrescaler,
|
||||
pub apb2_pre: APBPrescaler,
|
||||
pub enable_lsi: bool,
|
||||
pub enable_rtc_apb: bool,
|
||||
pub rtc_mux: RtcClockSource,
|
||||
pub lse: Option<Hertz>,
|
||||
pub lsi: bool,
|
||||
pub adc_clock_source: AdcClockSource,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -124,22 +149,18 @@ impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
mux: ClockSrc::MSI(MSIRange::default()),
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
shd_ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb1_pre: APBPrescaler::NotDivided,
|
||||
apb2_pre: APBPrescaler::NotDivided,
|
||||
enable_lsi: false,
|
||||
enable_rtc_apb: false,
|
||||
rtc_mux: RtcClockSource::LSI32,
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
shd_ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
rtc_mux: RtcClockSource::LSI,
|
||||
lsi: true,
|
||||
lse: None,
|
||||
adc_clock_source: AdcClockSource::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum RtcClockSource {
|
||||
LSE32,
|
||||
LSI32,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum Lsedrv {
|
||||
Low = 0,
|
||||
@ -150,13 +171,13 @@ pub enum Lsedrv {
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let (sys_clk, sw, vos) = match config.mux {
|
||||
ClockSrc::HSI16 => (HSI_FREQ.0, 0x01, VoltageScale::Scale1),
|
||||
ClockSrc::HSE32 => (HSE32_FREQ.0, 0x02, VoltageScale::Scale0),
|
||||
ClockSrc::HSI16 => (HSI_FREQ.0, 0x01, VoltageScale::RANGE2),
|
||||
ClockSrc::HSE32 => (HSE32_FREQ.0, 0x02, VoltageScale::RANGE1),
|
||||
ClockSrc::MSI(range) => (range.freq(), 0x00, range.vos()),
|
||||
};
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
AHBPrescaler::DIV1 => sys_clk,
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre = 1 << (pre as u32 - 7);
|
||||
@ -165,7 +186,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
};
|
||||
|
||||
let shd_ahb_freq: u32 = match config.shd_ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
AHBPrescaler::DIV1 => sys_clk,
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre = 1 << (pre as u32 - 7);
|
||||
@ -174,7 +195,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
@ -184,7 +205,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
@ -196,16 +217,17 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
// Adjust flash latency
|
||||
let flash_clk_src_freq: u32 = shd_ahb_freq;
|
||||
let ws = match vos {
|
||||
VoltageScale::Scale0 => match flash_clk_src_freq {
|
||||
VoltageScale::RANGE1 => match flash_clk_src_freq {
|
||||
0..=18_000_000 => 0b000,
|
||||
18_000_001..=36_000_000 => 0b001,
|
||||
_ => 0b010,
|
||||
},
|
||||
VoltageScale::Scale1 => match flash_clk_src_freq {
|
||||
VoltageScale::RANGE2 => match flash_clk_src_freq {
|
||||
0..=6_000_000 => 0b000,
|
||||
6_000_001..=12_000_000 => 0b001,
|
||||
_ => 0b010,
|
||||
},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
FLASH.acr().modify(|w| {
|
||||
@ -214,35 +236,8 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
|
||||
while FLASH.acr().read().latency() != ws {}
|
||||
|
||||
match config.rtc_mux {
|
||||
RtcClockSource::LSE32 => {
|
||||
// 1. Unlock the backup domain
|
||||
PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED));
|
||||
|
||||
// 2. Setup the LSE
|
||||
RCC.bdcr().modify(|w| {
|
||||
// Enable LSE
|
||||
w.set_lseon(true);
|
||||
// Max drive strength
|
||||
// TODO: should probably be settable
|
||||
w.set_lsedrv(Lsedrv::High as u8); //---// PAM - should not be commented
|
||||
});
|
||||
|
||||
// Wait until LSE is running
|
||||
while !RCC.bdcr().read().lserdy() {}
|
||||
|
||||
Rtc::set_clock_source(RCS::LSE);
|
||||
}
|
||||
RtcClockSource::LSI32 => {
|
||||
// Turn on the internal 32 kHz LSI oscillator
|
||||
RCC.csr().modify(|w| w.set_lsion(true));
|
||||
|
||||
// Wait until LSI is running
|
||||
while !RCC.csr().read().lsirdy() {}
|
||||
|
||||
Rtc::set_clock_source(RCS::LSI);
|
||||
}
|
||||
}
|
||||
// Enables the LSI if configured
|
||||
BackupDomain::configure_ls(config.rtc_mux, config.lsi, config.lse.map(|_| Default::default()));
|
||||
|
||||
match config.mux {
|
||||
ClockSrc::HSI16 => {
|
||||
@ -266,7 +261,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
w.set_msirange(range.into());
|
||||
w.set_msion(true);
|
||||
|
||||
if let RtcClockSource::LSE32 = config.rtc_mux {
|
||||
if let RtcClockSource::LSE = config.rtc_mux {
|
||||
// If LSE is enabled, enable calibration of MSI
|
||||
w.set_msipllen(true);
|
||||
} else {
|
||||
@ -277,16 +272,8 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
}
|
||||
}
|
||||
|
||||
if config.enable_rtc_apb {
|
||||
// enable peripheral clock for communication
|
||||
crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
|
||||
|
||||
// read to allow the pwr clock to enable
|
||||
crate::pac::PWR.cr1().read();
|
||||
}
|
||||
|
||||
RCC.extcfgr().modify(|w| {
|
||||
if config.shd_ahb_pre == AHBPrescaler::NotDivided {
|
||||
if config.shd_ahb_pre == AHBPrescaler::DIV1 {
|
||||
w.set_shdhpre(0);
|
||||
} else {
|
||||
w.set_shdhpre(config.shd_ahb_pre.into());
|
||||
@ -295,24 +282,15 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(sw.into());
|
||||
if config.ahb_pre == AHBPrescaler::NotDivided {
|
||||
w.set_hpre(0);
|
||||
} else {
|
||||
w.set_hpre(config.ahb_pre.into());
|
||||
}
|
||||
w.set_hpre(config.ahb_pre);
|
||||
w.set_ppre1(config.apb1_pre.into());
|
||||
w.set_ppre2(config.apb2_pre.into());
|
||||
});
|
||||
|
||||
// TODO: switch voltage range
|
||||
// ADC clock MUX
|
||||
RCC.ccipr().modify(|w| w.set_adcsel(config.adc_clock_source.adcsel()));
|
||||
|
||||
if config.enable_lsi {
|
||||
let csr = RCC.csr().read();
|
||||
if !csr.lsion() {
|
||||
RCC.csr().modify(|w| w.set_lsion(true));
|
||||
while !RCC.csr().read().lsirdy() {}
|
||||
}
|
||||
}
|
||||
// TODO: switch voltage range
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sys_clk),
|
||||
|
Reference in New Issue
Block a user