mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-25 08:12:30 +00:00
stm32/rcc: remove Rcc struct, RccExt trait.
All the RCC configuration is executed in init().
This commit is contained in:
parent
c3fd9a0f44
commit
2eb0cc5df7
@ -43,8 +43,6 @@ pub mod i2c;
|
||||
#[cfg(crc)]
|
||||
pub mod crc;
|
||||
pub mod pwm;
|
||||
#[cfg(pwr)]
|
||||
pub mod pwr;
|
||||
#[cfg(rng)]
|
||||
pub mod rng;
|
||||
#[cfg(sdmmc)]
|
||||
|
@ -1 +0,0 @@
|
||||
|
@ -1 +0,0 @@
|
||||
|
@ -1 +0,0 @@
|
||||
|
@ -1 +0,0 @@
|
||||
|
@ -1 +0,0 @@
|
||||
|
@ -1,78 +0,0 @@
|
||||
use crate::pac::{PWR, RCC, SYSCFG};
|
||||
use crate::peripherals;
|
||||
|
||||
/// Voltage Scale
|
||||
///
|
||||
/// Represents the voltage range feeding the CPU core. The maximum core
|
||||
/// clock frequency depends on this value.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum VoltageScale {
|
||||
/// VOS 0 range VCORE 1.26V - 1.40V
|
||||
Scale0,
|
||||
/// VOS 1 range VCORE 1.15V - 1.26V
|
||||
Scale1,
|
||||
/// VOS 2 range VCORE 1.05V - 1.15V
|
||||
Scale2,
|
||||
/// VOS 3 range VCORE 0.95V - 1.05V
|
||||
Scale3,
|
||||
}
|
||||
|
||||
/// Power Configuration
|
||||
///
|
||||
/// Generated when the PWR peripheral is frozen. The existence of this
|
||||
/// value indicates that the voltage scaling configuration can no
|
||||
/// longer be changed.
|
||||
pub struct Power {
|
||||
pub(crate) vos: VoltageScale,
|
||||
}
|
||||
|
||||
impl Power {
|
||||
pub fn new(_peri: peripherals::PWR, enable_overdrive: bool) -> Self {
|
||||
// NOTE(unsafe) we have the PWR singleton
|
||||
unsafe {
|
||||
// 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 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
|
||||
};
|
||||
Self { vos }
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
|
@ -1,13 +0,0 @@
|
||||
#[cfg_attr(any(pwr_h7, pwr_h7smps), path = "h7.rs")]
|
||||
#[cfg_attr(pwr_f3, path = "f3.rs")]
|
||||
#[cfg_attr(pwr_f4, path = "f4.rs")]
|
||||
#[cfg_attr(pwr_f7, path = "f7.rs")]
|
||||
#[cfg_attr(pwr_wl5, path = "wl5.rs")]
|
||||
#[cfg_attr(pwr_g0, path = "g0.rs")]
|
||||
#[cfg_attr(pwr_g4, path = "g4.rs")]
|
||||
#[cfg_attr(pwr_l1, path = "l1.rs")]
|
||||
#[cfg_attr(pwr_u5, path = "u5.rs")]
|
||||
#[cfg_attr(pwr_wb55, path = "wb55.rs")]
|
||||
mod _version;
|
||||
|
||||
pub use _version::*;
|
@ -1,32 +0,0 @@
|
||||
use crate::peripherals;
|
||||
|
||||
/// Voltage Scale
|
||||
///
|
||||
/// Represents the voltage range feeding the CPU core. The maximum core
|
||||
/// clock frequency depends on this value.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum VoltageScale {
|
||||
// Highest frequency
|
||||
Range1,
|
||||
Range2,
|
||||
Range3,
|
||||
// Lowest power
|
||||
Range4,
|
||||
}
|
||||
|
||||
/// Power Configuration
|
||||
///
|
||||
/// Generated when the PWR peripheral is frozen. The existence of this
|
||||
/// value indicates that the voltage scaling configuration can no
|
||||
/// longer be changed.
|
||||
pub struct Power {
|
||||
pub(crate) vos: VoltageScale,
|
||||
}
|
||||
|
||||
impl Power {
|
||||
pub fn new(_peri: peripherals::PWR) -> Self {
|
||||
Self {
|
||||
vos: VoltageScale::Range4,
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
|
@ -1,9 +1,5 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy::util::Unborrow;
|
||||
|
||||
use crate::pac::rcc::vals::{Hpre, Hsebyp, Pllmul, Pllsrc, Ppre, Sw, Usbsw};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::peripherals;
|
||||
use crate::time::Hertz;
|
||||
|
||||
use super::{set_freqs, Clocks};
|
||||
@ -28,181 +24,151 @@ pub struct Config {
|
||||
pub pclk: Option<Hertz>,
|
||||
}
|
||||
|
||||
pub struct Rcc<'d> {
|
||||
inner: PhantomData<&'d ()>,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl<'d> Rcc<'d> {
|
||||
pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self {
|
||||
Self {
|
||||
inner: PhantomData,
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn freeze(self) -> Clocks {
|
||||
use crate::pac::rcc::vals::{Hpre, Hsebyp, Pllmul, Pllsrc, Ppre, Sw, Usbsw};
|
||||
|
||||
let sysclk = self.config.sys_ck.map(|v| v.0).unwrap_or(HSI);
|
||||
|
||||
let (src_clk, use_hsi48) = self.config.hse.map(|v| (v.0, false)).unwrap_or_else(|| {
|
||||
#[cfg(rcc_f0)]
|
||||
if self.config.hsi48 {
|
||||
return (48_000_000, true);
|
||||
}
|
||||
(HSI, false)
|
||||
});
|
||||
|
||||
let (pllmul_bits, real_sysclk) = if sysclk == src_clk {
|
||||
(None, sysclk)
|
||||
} else {
|
||||
let prediv = if self.config.hse.is_some() { 1 } else { 2 };
|
||||
let pllmul = (2 * prediv * sysclk + src_clk) / src_clk / 2;
|
||||
let pllmul = pllmul.max(2).min(16);
|
||||
|
||||
let pllmul_bits = pllmul as u8 - 2;
|
||||
let real_sysclk = pllmul * src_clk / prediv;
|
||||
(Some(pllmul_bits), real_sysclk)
|
||||
};
|
||||
|
||||
let hpre_bits = self
|
||||
.config
|
||||
.hclk
|
||||
.map(|hclk| match real_sysclk / hclk.0 {
|
||||
0 => unreachable!(),
|
||||
1 => 0b0111,
|
||||
2 => 0b1000,
|
||||
3..=5 => 0b1001,
|
||||
6..=11 => 0b1010,
|
||||
12..=39 => 0b1011,
|
||||
40..=95 => 0b1100,
|
||||
96..=191 => 0b1101,
|
||||
192..=383 => 0b1110,
|
||||
_ => 0b1111,
|
||||
})
|
||||
.unwrap_or(0b0111);
|
||||
let hclk = real_sysclk / (1 << (hpre_bits - 0b0111));
|
||||
|
||||
let ppre_bits = self
|
||||
.config
|
||||
.pclk
|
||||
.map(|pclk| match hclk / pclk.0 {
|
||||
0 => unreachable!(),
|
||||
1 => 0b011,
|
||||
2 => 0b100,
|
||||
3..=5 => 0b101,
|
||||
6..=11 => 0b110,
|
||||
_ => 0b111,
|
||||
})
|
||||
.unwrap_or(0b011);
|
||||
|
||||
let ppre: u8 = 1 << (ppre_bits - 0b011);
|
||||
let pclk = hclk / u32::from(ppre);
|
||||
|
||||
let timer_mul = if ppre == 1 { 1 } else { 2 };
|
||||
|
||||
// NOTE(safety) Atomic write
|
||||
unsafe {
|
||||
FLASH.acr().write(|w| {
|
||||
let latency = if real_sysclk <= 24_000_000 {
|
||||
0
|
||||
} else if real_sysclk <= 48_000_000 {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
};
|
||||
w.latency().0 = latency;
|
||||
});
|
||||
}
|
||||
|
||||
// NOTE(unsafe) We have exclusive access to the RCC
|
||||
unsafe {
|
||||
match (self.config.hse.is_some(), use_hsi48) {
|
||||
(true, _) => {
|
||||
RCC.cr().modify(|w| {
|
||||
w.set_csson(true);
|
||||
w.set_hseon(true);
|
||||
|
||||
if self.config.bypass_hse {
|
||||
w.set_hsebyp(Hsebyp::BYPASSED);
|
||||
}
|
||||
});
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
if pllmul_bits.is_some() {
|
||||
RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSE_DIV_PREDIV))
|
||||
}
|
||||
}
|
||||
(false, true) => {
|
||||
// use_hsi48 will always be false for rcc_f0x0
|
||||
#[cfg(rcc_f0)]
|
||||
RCC.cr2().modify(|w| w.set_hsi48on(true));
|
||||
#[cfg(rcc_f0)]
|
||||
while !RCC.cr2().read().hsi48rdy() {}
|
||||
|
||||
#[cfg(rcc_f0)]
|
||||
if pllmul_bits.is_some() {
|
||||
RCC.cfgr()
|
||||
.modify(|w| w.set_pllsrc(Pllsrc::HSI48_DIV_PREDIV))
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
RCC.cr().modify(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
if pllmul_bits.is_some() {
|
||||
RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSI_DIV2))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.config.usb_pll {
|
||||
RCC.cfgr3().modify(|w| w.set_usbsw(Usbsw::PLLCLK));
|
||||
}
|
||||
// TODO: Option to use CRS (Clock Recovery)
|
||||
|
||||
if let Some(pllmul_bits) = pllmul_bits {
|
||||
RCC.cfgr().modify(|w| w.set_pllmul(Pllmul(pllmul_bits)));
|
||||
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_ppre(Ppre(ppre_bits));
|
||||
w.set_hpre(Hpre(hpre_bits));
|
||||
w.set_sw(Sw::PLL)
|
||||
});
|
||||
} else {
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_ppre(Ppre(ppre_bits));
|
||||
w.set_hpre(Hpre(hpre_bits));
|
||||
|
||||
if self.config.hse.is_some() {
|
||||
w.set_sw(Sw::HSE);
|
||||
} else if use_hsi48 {
|
||||
#[cfg(rcc_f0)]
|
||||
w.set_sw(Sw::HSI48);
|
||||
} else {
|
||||
w.set_sw(Sw::HSI)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Clocks {
|
||||
sys: Hertz(real_sysclk),
|
||||
apb1: Hertz(pclk),
|
||||
apb2: Hertz(pclk),
|
||||
apb1_tim: Hertz(pclk * timer_mul),
|
||||
apb2_tim: Hertz(pclk * timer_mul),
|
||||
ahb: Hertz(hclk),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let rcc = Rcc::new(<peripherals::RCC as embassy::util::Steal>::steal(), config);
|
||||
let clocks = rcc.freeze();
|
||||
set_freqs(clocks);
|
||||
let sysclk = config.sys_ck.map(|v| v.0).unwrap_or(HSI);
|
||||
|
||||
let (src_clk, use_hsi48) = config.hse.map(|v| (v.0, false)).unwrap_or_else(|| {
|
||||
#[cfg(rcc_f0)]
|
||||
if config.hsi48 {
|
||||
return (48_000_000, true);
|
||||
}
|
||||
(HSI, false)
|
||||
});
|
||||
|
||||
let (pllmul_bits, real_sysclk) = if sysclk == src_clk {
|
||||
(None, sysclk)
|
||||
} else {
|
||||
let prediv = if config.hse.is_some() { 1 } else { 2 };
|
||||
let pllmul = (2 * prediv * sysclk + src_clk) / src_clk / 2;
|
||||
let pllmul = pllmul.max(2).min(16);
|
||||
|
||||
let pllmul_bits = pllmul as u8 - 2;
|
||||
let real_sysclk = pllmul * src_clk / prediv;
|
||||
(Some(pllmul_bits), real_sysclk)
|
||||
};
|
||||
|
||||
let hpre_bits = config
|
||||
.hclk
|
||||
.map(|hclk| match real_sysclk / hclk.0 {
|
||||
0 => unreachable!(),
|
||||
1 => 0b0111,
|
||||
2 => 0b1000,
|
||||
3..=5 => 0b1001,
|
||||
6..=11 => 0b1010,
|
||||
12..=39 => 0b1011,
|
||||
40..=95 => 0b1100,
|
||||
96..=191 => 0b1101,
|
||||
192..=383 => 0b1110,
|
||||
_ => 0b1111,
|
||||
})
|
||||
.unwrap_or(0b0111);
|
||||
let hclk = real_sysclk / (1 << (hpre_bits - 0b0111));
|
||||
|
||||
let ppre_bits = config
|
||||
.pclk
|
||||
.map(|pclk| match hclk / pclk.0 {
|
||||
0 => unreachable!(),
|
||||
1 => 0b011,
|
||||
2 => 0b100,
|
||||
3..=5 => 0b101,
|
||||
6..=11 => 0b110,
|
||||
_ => 0b111,
|
||||
})
|
||||
.unwrap_or(0b011);
|
||||
|
||||
let ppre: u8 = 1 << (ppre_bits - 0b011);
|
||||
let pclk = hclk / u32::from(ppre);
|
||||
|
||||
let timer_mul = if ppre == 1 { 1 } else { 2 };
|
||||
|
||||
FLASH.acr().write(|w| {
|
||||
let latency = if real_sysclk <= 24_000_000 {
|
||||
0
|
||||
} else if real_sysclk <= 48_000_000 {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
};
|
||||
w.latency().0 = latency;
|
||||
});
|
||||
|
||||
match (config.hse.is_some(), use_hsi48) {
|
||||
(true, _) => {
|
||||
RCC.cr().modify(|w| {
|
||||
w.set_csson(true);
|
||||
w.set_hseon(true);
|
||||
|
||||
if config.bypass_hse {
|
||||
w.set_hsebyp(Hsebyp::BYPASSED);
|
||||
}
|
||||
});
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
if pllmul_bits.is_some() {
|
||||
RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSE_DIV_PREDIV))
|
||||
}
|
||||
}
|
||||
(false, true) => {
|
||||
// use_hsi48 will always be false for rcc_f0x0
|
||||
#[cfg(rcc_f0)]
|
||||
RCC.cr2().modify(|w| w.set_hsi48on(true));
|
||||
#[cfg(rcc_f0)]
|
||||
while !RCC.cr2().read().hsi48rdy() {}
|
||||
|
||||
#[cfg(rcc_f0)]
|
||||
if pllmul_bits.is_some() {
|
||||
RCC.cfgr()
|
||||
.modify(|w| w.set_pllsrc(Pllsrc::HSI48_DIV_PREDIV))
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
RCC.cr().modify(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
if pllmul_bits.is_some() {
|
||||
RCC.cfgr().modify(|w| w.set_pllsrc(Pllsrc::HSI_DIV2))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if config.usb_pll {
|
||||
RCC.cfgr3().modify(|w| w.set_usbsw(Usbsw::PLLCLK));
|
||||
}
|
||||
// TODO: Option to use CRS (Clock Recovery)
|
||||
|
||||
if let Some(pllmul_bits) = pllmul_bits {
|
||||
RCC.cfgr().modify(|w| w.set_pllmul(Pllmul(pllmul_bits)));
|
||||
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_ppre(Ppre(ppre_bits));
|
||||
w.set_hpre(Hpre(hpre_bits));
|
||||
w.set_sw(Sw::PLL)
|
||||
});
|
||||
} else {
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_ppre(Ppre(ppre_bits));
|
||||
w.set_hpre(Hpre(hpre_bits));
|
||||
|
||||
if config.hse.is_some() {
|
||||
w.set_sw(Sw::HSE);
|
||||
} else if use_hsi48 {
|
||||
#[cfg(rcc_f0)]
|
||||
w.set_sw(Sw::HSI48);
|
||||
} else {
|
||||
w.set_sw(Sw::HSI)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(real_sysclk),
|
||||
apb1: Hertz(pclk),
|
||||
apb2: Hertz(pclk),
|
||||
apb1_tim: Hertz(pclk * timer_mul),
|
||||
apb2_tim: Hertz(pclk * timer_mul),
|
||||
ahb: Hertz(hclk),
|
||||
});
|
||||
}
|
||||
|
@ -1,14 +1,10 @@
|
||||
use core::convert::TryFrom;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use embassy::util::Unborrow;
|
||||
|
||||
use crate::pac::flash::vals::Latency;
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::peripherals;
|
||||
use crate::time::Hertz;
|
||||
|
||||
use super::{set_freqs, Clocks};
|
||||
use crate::pac::flash::vals::Latency;
|
||||
use crate::pac::rcc::vals::{Adcpre, Hpre, Pllmul, Pllsrc, Ppre1, Sw, Usbpre};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::time::Hertz;
|
||||
|
||||
const HSI: u32 = 8_000_000;
|
||||
|
||||
@ -26,189 +22,158 @@ pub struct Config {
|
||||
pub adcclk: Option<Hertz>,
|
||||
}
|
||||
|
||||
pub struct Rcc<'d> {
|
||||
inner: PhantomData<&'d ()>,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl<'d> Rcc<'d> {
|
||||
pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self {
|
||||
Self {
|
||||
inner: PhantomData,
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn freeze(self) -> Clocks {
|
||||
use crate::pac::rcc::vals::{Adcpre, Hpre, Pllmul, Pllsrc, Ppre1, Sw, Usbpre};
|
||||
|
||||
let pllsrcclk = self.config.hse.map(|hse| hse.0).unwrap_or(HSI / 2);
|
||||
let sysclk = self.config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk);
|
||||
let pllmul = sysclk / pllsrcclk;
|
||||
|
||||
let (pllmul_bits, real_sysclk) = if pllmul == 1 {
|
||||
(None, self.config.hse.map(|hse| hse.0).unwrap_or(HSI))
|
||||
} else {
|
||||
let pllmul = core::cmp::min(core::cmp::max(pllmul, 1), 16);
|
||||
(Some(pllmul as u8 - 2), pllsrcclk * pllmul)
|
||||
};
|
||||
|
||||
assert!(real_sysclk <= 72_000_000);
|
||||
|
||||
let hpre_bits = self
|
||||
.config
|
||||
.hclk
|
||||
.map(|hclk| match real_sysclk / hclk.0 {
|
||||
0 => unreachable!(),
|
||||
1 => 0b0111,
|
||||
2 => 0b1000,
|
||||
3..=5 => 0b1001,
|
||||
6..=11 => 0b1010,
|
||||
12..=39 => 0b1011,
|
||||
40..=95 => 0b1100,
|
||||
96..=191 => 0b1101,
|
||||
192..=383 => 0b1110,
|
||||
_ => 0b1111,
|
||||
})
|
||||
.unwrap_or(0b0111);
|
||||
|
||||
let hclk = if hpre_bits >= 0b1100 {
|
||||
real_sysclk / (1 << (hpre_bits - 0b0110))
|
||||
} else {
|
||||
real_sysclk / (1 << (hpre_bits - 0b0111))
|
||||
};
|
||||
|
||||
assert!(hclk <= 72_000_000);
|
||||
|
||||
let ppre1_bits = self
|
||||
.config
|
||||
.pclk1
|
||||
.map(|pclk1| match hclk / pclk1.0 {
|
||||
0 => unreachable!(),
|
||||
1 => 0b011,
|
||||
2 => 0b100,
|
||||
3..=5 => 0b101,
|
||||
6..=11 => 0b110,
|
||||
_ => 0b111,
|
||||
})
|
||||
.unwrap_or(0b011);
|
||||
|
||||
let ppre1 = 1 << (ppre1_bits - 0b011);
|
||||
let pclk1 = hclk / u32::try_from(ppre1).unwrap();
|
||||
let timer_mul1 = if ppre1 == 1 { 1 } else { 2 };
|
||||
|
||||
assert!(pclk1 <= 36_000_000);
|
||||
|
||||
let ppre2_bits = self
|
||||
.config
|
||||
.pclk2
|
||||
.map(|pclk2| match hclk / pclk2.0 {
|
||||
0 => unreachable!(),
|
||||
1 => 0b011,
|
||||
2 => 0b100,
|
||||
3..=5 => 0b101,
|
||||
6..=11 => 0b110,
|
||||
_ => 0b111,
|
||||
})
|
||||
.unwrap_or(0b011);
|
||||
|
||||
let ppre2 = 1 << (ppre2_bits - 0b011);
|
||||
let pclk2 = hclk / u32::try_from(ppre2).unwrap();
|
||||
let timer_mul2 = if ppre2 == 1 { 1 } else { 2 };
|
||||
|
||||
assert!(pclk2 <= 72_000_000);
|
||||
|
||||
// Only needed for stm32f103?
|
||||
// NOTE(safety) Atomic write
|
||||
unsafe {
|
||||
FLASH.acr().write(|w| {
|
||||
w.set_latency(if real_sysclk <= 24_000_000 {
|
||||
Latency(0b000)
|
||||
} else if real_sysclk <= 48_000_000 {
|
||||
Latency(0b001)
|
||||
} else {
|
||||
Latency(0b010)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
// the USB clock is only valid if an external crystal is used, the PLL is enabled, and the
|
||||
// PLL output frequency is a supported one.
|
||||
// usbpre == false: divide clock by 1.5, otherwise no division
|
||||
let (usbpre, _usbclk_valid) = match (self.config.hse, pllmul_bits, real_sysclk) {
|
||||
(Some(_), Some(_), 72_000_000) => (false, true),
|
||||
(Some(_), Some(_), 48_000_000) => (true, true),
|
||||
_ => (true, false),
|
||||
};
|
||||
|
||||
let apre_bits: u8 = self
|
||||
.config
|
||||
.adcclk
|
||||
.map(|adcclk| match pclk2 / adcclk.0 {
|
||||
0..=2 => 0b00,
|
||||
3..=4 => 0b01,
|
||||
5..=7 => 0b10,
|
||||
_ => 0b11,
|
||||
})
|
||||
.unwrap_or(0b11);
|
||||
|
||||
let apre = (apre_bits + 1) << 1;
|
||||
let adcclk = pclk2 / unwrap!(u32::try_from(apre));
|
||||
|
||||
assert!(adcclk <= 14_000_000);
|
||||
|
||||
unsafe {
|
||||
if self.config.hse.is_some() {
|
||||
// enable HSE and wait for it to be ready
|
||||
RCC.cr().modify(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
}
|
||||
|
||||
if let Some(pllmul_bits) = pllmul_bits {
|
||||
// enable PLL and wait for it to be ready
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_pllmul(Pllmul(pllmul_bits));
|
||||
w.set_pllsrc(Pllsrc(self.config.hse.is_some() as u8));
|
||||
});
|
||||
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
}
|
||||
|
||||
// Only needed for stm32f103?
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_adcpre(Adcpre(apre_bits));
|
||||
w.set_ppre2(Ppre1(ppre2_bits));
|
||||
w.set_ppre1(Ppre1(ppre1_bits));
|
||||
w.set_hpre(Hpre(hpre_bits));
|
||||
w.set_usbpre(Usbpre(usbpre as u8));
|
||||
w.set_sw(Sw(if pllmul_bits.is_some() {
|
||||
// PLL
|
||||
0b10
|
||||
} else if self.config.hse.is_some() {
|
||||
// HSE
|
||||
0b1
|
||||
} else {
|
||||
// HSI
|
||||
0b0
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
Clocks {
|
||||
sys: Hertz(real_sysclk),
|
||||
apb1: Hertz(pclk1),
|
||||
apb2: Hertz(pclk2),
|
||||
apb1_tim: Hertz(pclk1 * timer_mul1),
|
||||
apb2_tim: Hertz(pclk2 * timer_mul2),
|
||||
ahb: Hertz(hclk),
|
||||
adc: Hertz(adcclk),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let rcc = Rcc::new(<peripherals::RCC as embassy::util::Steal>::steal(), config);
|
||||
let clocks = rcc.freeze();
|
||||
set_freqs(clocks);
|
||||
let pllsrcclk = config.hse.map(|hse| hse.0).unwrap_or(HSI / 2);
|
||||
let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk);
|
||||
let pllmul = sysclk / pllsrcclk;
|
||||
|
||||
let (pllmul_bits, real_sysclk) = if pllmul == 1 {
|
||||
(None, config.hse.map(|hse| hse.0).unwrap_or(HSI))
|
||||
} else {
|
||||
let pllmul = core::cmp::min(core::cmp::max(pllmul, 1), 16);
|
||||
(Some(pllmul as u8 - 2), pllsrcclk * pllmul)
|
||||
};
|
||||
|
||||
assert!(real_sysclk <= 72_000_000);
|
||||
|
||||
let hpre_bits = config
|
||||
.hclk
|
||||
.map(|hclk| match real_sysclk / hclk.0 {
|
||||
0 => unreachable!(),
|
||||
1 => 0b0111,
|
||||
2 => 0b1000,
|
||||
3..=5 => 0b1001,
|
||||
6..=11 => 0b1010,
|
||||
12..=39 => 0b1011,
|
||||
40..=95 => 0b1100,
|
||||
96..=191 => 0b1101,
|
||||
192..=383 => 0b1110,
|
||||
_ => 0b1111,
|
||||
})
|
||||
.unwrap_or(0b0111);
|
||||
|
||||
let hclk = if hpre_bits >= 0b1100 {
|
||||
real_sysclk / (1 << (hpre_bits - 0b0110))
|
||||
} else {
|
||||
real_sysclk / (1 << (hpre_bits - 0b0111))
|
||||
};
|
||||
|
||||
assert!(hclk <= 72_000_000);
|
||||
|
||||
let ppre1_bits = config
|
||||
.pclk1
|
||||
.map(|pclk1| match hclk / pclk1.0 {
|
||||
0 => unreachable!(),
|
||||
1 => 0b011,
|
||||
2 => 0b100,
|
||||
3..=5 => 0b101,
|
||||
6..=11 => 0b110,
|
||||
_ => 0b111,
|
||||
})
|
||||
.unwrap_or(0b011);
|
||||
|
||||
let ppre1 = 1 << (ppre1_bits - 0b011);
|
||||
let pclk1 = hclk / u32::try_from(ppre1).unwrap();
|
||||
let timer_mul1 = if ppre1 == 1 { 1 } else { 2 };
|
||||
|
||||
assert!(pclk1 <= 36_000_000);
|
||||
|
||||
let ppre2_bits = config
|
||||
.pclk2
|
||||
.map(|pclk2| match hclk / pclk2.0 {
|
||||
0 => unreachable!(),
|
||||
1 => 0b011,
|
||||
2 => 0b100,
|
||||
3..=5 => 0b101,
|
||||
6..=11 => 0b110,
|
||||
_ => 0b111,
|
||||
})
|
||||
.unwrap_or(0b011);
|
||||
|
||||
let ppre2 = 1 << (ppre2_bits - 0b011);
|
||||
let pclk2 = hclk / u32::try_from(ppre2).unwrap();
|
||||
let timer_mul2 = if ppre2 == 1 { 1 } else { 2 };
|
||||
|
||||
assert!(pclk2 <= 72_000_000);
|
||||
|
||||
// Only needed for stm32f103?
|
||||
FLASH.acr().write(|w| {
|
||||
w.set_latency(if real_sysclk <= 24_000_000 {
|
||||
Latency(0b000)
|
||||
} else if real_sysclk <= 48_000_000 {
|
||||
Latency(0b001)
|
||||
} else {
|
||||
Latency(0b010)
|
||||
});
|
||||
});
|
||||
|
||||
// the USB clock is only valid if an external crystal is used, the PLL is enabled, and the
|
||||
// PLL output frequency is a supported one.
|
||||
// usbpre == false: divide clock by 1.5, otherwise no division
|
||||
let (usbpre, _usbclk_valid) = match (config.hse, pllmul_bits, real_sysclk) {
|
||||
(Some(_), Some(_), 72_000_000) => (false, true),
|
||||
(Some(_), Some(_), 48_000_000) => (true, true),
|
||||
_ => (true, false),
|
||||
};
|
||||
|
||||
let apre_bits: u8 = config
|
||||
.adcclk
|
||||
.map(|adcclk| match pclk2 / adcclk.0 {
|
||||
0..=2 => 0b00,
|
||||
3..=4 => 0b01,
|
||||
5..=7 => 0b10,
|
||||
_ => 0b11,
|
||||
})
|
||||
.unwrap_or(0b11);
|
||||
|
||||
let apre = (apre_bits + 1) << 1;
|
||||
let adcclk = pclk2 / unwrap!(u32::try_from(apre));
|
||||
|
||||
assert!(adcclk <= 14_000_000);
|
||||
|
||||
if config.hse.is_some() {
|
||||
// enable HSE and wait for it to be ready
|
||||
RCC.cr().modify(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
}
|
||||
|
||||
if let Some(pllmul_bits) = pllmul_bits {
|
||||
// enable PLL and wait for it to be ready
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_pllmul(Pllmul(pllmul_bits));
|
||||
w.set_pllsrc(Pllsrc(config.hse.is_some() as u8));
|
||||
});
|
||||
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
}
|
||||
|
||||
// Only needed for stm32f103?
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_adcpre(Adcpre(apre_bits));
|
||||
w.set_ppre2(Ppre1(ppre2_bits));
|
||||
w.set_ppre1(Ppre1(ppre1_bits));
|
||||
w.set_hpre(Hpre(hpre_bits));
|
||||
w.set_usbpre(Usbpre(usbpre as u8));
|
||||
w.set_sw(Sw(if pllmul_bits.is_some() {
|
||||
// PLL
|
||||
0b10
|
||||
} else if config.hse.is_some() {
|
||||
// HSE
|
||||
0b1
|
||||
} else {
|
||||
// HSI
|
||||
0b0
|
||||
}));
|
||||
});
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(real_sysclk),
|
||||
apb1: Hertz(pclk1),
|
||||
apb2: Hertz(pclk2),
|
||||
apb1_tim: Hertz(pclk1 * timer_mul1),
|
||||
apb2_tim: Hertz(pclk2 * timer_mul2),
|
||||
ahb: Hertz(hclk),
|
||||
adc: Hertz(adcclk),
|
||||
});
|
||||
}
|
||||
|
@ -1,23 +1,11 @@
|
||||
use core::marker::PhantomData;
|
||||
use embassy::util::Unborrow;
|
||||
|
||||
use crate::pac::{
|
||||
flash::vals::Latency,
|
||||
rcc::vals::{Hpre, Hsebyp, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre},
|
||||
FLASH, RCC,
|
||||
};
|
||||
use crate::peripherals;
|
||||
use crate::pac::flash::vals::Latency;
|
||||
use crate::pac::rcc::vals::{Hpre, Hsebyp, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
|
||||
const HSI: u32 = 8_000_000;
|
||||
|
||||
/// RCC peripheral
|
||||
pub struct Rcc<'d> {
|
||||
config: Config,
|
||||
phantom: PhantomData<&'d mut peripherals::RCC>,
|
||||
}
|
||||
|
||||
/// Clocks configutation
|
||||
#[non_exhaustive]
|
||||
#[derive(Default)]
|
||||
@ -55,259 +43,228 @@ struct PllConfig {
|
||||
|
||||
/// Initialize and Set the clock frequencies
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
||||
let clocks = Rcc::new(r, config).freeze();
|
||||
set_freqs(clocks);
|
||||
// Calculate the real System clock, and PLL configuration if applicable
|
||||
let (Hertz(sysclk), pll_config) = get_sysclk(&config);
|
||||
assert!(sysclk <= 72_000_000);
|
||||
|
||||
// Calculate real AHB clock
|
||||
let hclk = config.hclk.map(|h| h.0).unwrap_or(sysclk);
|
||||
let (hpre_bits, hpre_div) = match sysclk / hclk {
|
||||
0 => unreachable!(),
|
||||
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),
|
||||
};
|
||||
let hclk = sysclk / hpre_div;
|
||||
assert!(hclk <= 72_000_000);
|
||||
|
||||
// Calculate real APB1 clock
|
||||
let pclk1 = config.pclk1.map(|p| p.0).unwrap_or(hclk);
|
||||
let (ppre1_bits, ppre1) = match hclk / pclk1 {
|
||||
0 => unreachable!(),
|
||||
1 => (Ppre::DIV1, 1),
|
||||
2 => (Ppre::DIV2, 2),
|
||||
3..=5 => (Ppre::DIV4, 4),
|
||||
6..=11 => (Ppre::DIV8, 8),
|
||||
_ => (Ppre::DIV16, 16),
|
||||
};
|
||||
let timer_mul1 = if ppre1 == 1 { 1 } else { 2 };
|
||||
let pclk1 = hclk / ppre1;
|
||||
assert!(pclk1 <= 36_000_000);
|
||||
|
||||
// Calculate real APB2 clock
|
||||
let pclk2 = config.pclk2.map(|p| p.0).unwrap_or(hclk);
|
||||
let (ppre2_bits, ppre2) = match hclk / pclk2 {
|
||||
0 => unreachable!(),
|
||||
1 => (Ppre::DIV1, 1),
|
||||
2 => (Ppre::DIV2, 2),
|
||||
3..=5 => (Ppre::DIV4, 4),
|
||||
6..=11 => (Ppre::DIV8, 8),
|
||||
_ => (Ppre::DIV16, 16),
|
||||
};
|
||||
let timer_mul2 = if ppre2 == 1 { 1 } else { 2 };
|
||||
let pclk2 = hclk / ppre2;
|
||||
assert!(pclk2 <= 72_000_000);
|
||||
|
||||
// Set latency based on HCLK frquency
|
||||
FLASH.acr().write(|w| {
|
||||
w.set_latency(if hclk <= 24_000_000 {
|
||||
Latency::WS0
|
||||
} else if hclk <= 48_000_000 {
|
||||
Latency::WS1
|
||||
} else {
|
||||
Latency::WS2
|
||||
});
|
||||
});
|
||||
|
||||
// Enable HSE
|
||||
if config.hse.is_some() {
|
||||
RCC.cr().write(|w| {
|
||||
w.set_hsebyp(if config.bypass_hse {
|
||||
Hsebyp::BYPASSED
|
||||
} else {
|
||||
Hsebyp::NOTBYPASSED
|
||||
});
|
||||
// We turn on clock security to switch to HSI when HSE fails
|
||||
w.set_csson(true);
|
||||
w.set_hseon(true);
|
||||
});
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
}
|
||||
|
||||
// Enable PLL
|
||||
if let Some(ref pll_config) = pll_config {
|
||||
RCC.cfgr().write(|w| {
|
||||
w.set_pllmul(pll_config.pll_mul);
|
||||
w.set_pllsrc(pll_config.pll_src);
|
||||
});
|
||||
if let Some(pll_div) = pll_config.pll_div {
|
||||
RCC.cfgr2().write(|w| w.set_prediv(pll_div));
|
||||
}
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
}
|
||||
|
||||
if config.pll48 {
|
||||
let usb_pre = get_usb_pre(&config, sysclk, pclk1, &pll_config);
|
||||
RCC.cfgr().write(|w| {
|
||||
w.set_usbpre(usb_pre);
|
||||
});
|
||||
}
|
||||
|
||||
// Set prescalers
|
||||
RCC.cfgr().write(|w| {
|
||||
w.set_ppre2(ppre2_bits);
|
||||
w.set_ppre1(ppre1_bits);
|
||||
w.set_hpre(hpre_bits);
|
||||
});
|
||||
|
||||
// Wait for the new prescalers to kick in
|
||||
// "The clocks are divided with the new prescaler factor from
|
||||
// 1 to 16 AHB cycles after write"
|
||||
cortex_m::asm::delay(16);
|
||||
|
||||
RCC.cfgr().write(|w| {
|
||||
w.set_sw(match (pll_config, config.hse) {
|
||||
(Some(_), _) => Sw::PLL,
|
||||
(None, Some(_)) => Sw::HSE,
|
||||
(None, None) => Sw::HSI,
|
||||
})
|
||||
});
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sysclk),
|
||||
apb1: Hertz(pclk1),
|
||||
apb2: Hertz(pclk2),
|
||||
apb1_tim: Hertz(pclk1 * timer_mul1),
|
||||
apb2_tim: Hertz(pclk2 * timer_mul2),
|
||||
ahb: Hertz(hclk),
|
||||
});
|
||||
}
|
||||
|
||||
impl<'d> Rcc<'d> {
|
||||
pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self {
|
||||
Self {
|
||||
config,
|
||||
phantom: PhantomData,
|
||||
#[inline]
|
||||
fn get_sysclk(config: &Config) -> (Hertz, Option<PllConfig>) {
|
||||
match (config.sysclk, config.hse) {
|
||||
(Some(sysclk), Some(hse)) if sysclk == hse => (hse, None),
|
||||
(Some(sysclk), None) if sysclk.0 == HSI => (Hertz(HSI), None),
|
||||
// If the user selected System clock is different from HSI or HSE
|
||||
// we will have to setup PLL clock source
|
||||
(Some(sysclk), _) => {
|
||||
let (sysclk, pll_config) = calc_pll(config, sysclk);
|
||||
(sysclk, Some(pll_config))
|
||||
}
|
||||
(None, Some(hse)) => (hse, None),
|
||||
(None, None) => (Hertz(HSI), None),
|
||||
}
|
||||
}
|
||||
|
||||
fn freeze(self) -> Clocks {
|
||||
// Calculate the real System clock, and PLL configuration if applicable
|
||||
let (Hertz(sysclk), pll_config) = self.get_sysclk();
|
||||
assert!(sysclk <= 72_000_000);
|
||||
|
||||
// Calculate real AHB clock
|
||||
let hclk = self.config.hclk.map(|h| h.0).unwrap_or(sysclk);
|
||||
let (hpre_bits, hpre_div) = match sysclk / hclk {
|
||||
0 => unreachable!(),
|
||||
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),
|
||||
};
|
||||
let hclk = sysclk / hpre_div;
|
||||
assert!(hclk <= 72_000_000);
|
||||
|
||||
// Calculate real APB1 clock
|
||||
let pclk1 = self.config.pclk1.map(|p| p.0).unwrap_or(hclk);
|
||||
let (ppre1_bits, ppre1) = match hclk / pclk1 {
|
||||
0 => unreachable!(),
|
||||
1 => (Ppre::DIV1, 1),
|
||||
2 => (Ppre::DIV2, 2),
|
||||
3..=5 => (Ppre::DIV4, 4),
|
||||
6..=11 => (Ppre::DIV8, 8),
|
||||
_ => (Ppre::DIV16, 16),
|
||||
};
|
||||
let timer_mul1 = if ppre1 == 1 { 1 } else { 2 };
|
||||
let pclk1 = hclk / ppre1;
|
||||
assert!(pclk1 <= 36_000_000);
|
||||
|
||||
// Calculate real APB2 clock
|
||||
let pclk2 = self.config.pclk2.map(|p| p.0).unwrap_or(hclk);
|
||||
let (ppre2_bits, ppre2) = match hclk / pclk2 {
|
||||
0 => unreachable!(),
|
||||
1 => (Ppre::DIV1, 1),
|
||||
2 => (Ppre::DIV2, 2),
|
||||
3..=5 => (Ppre::DIV4, 4),
|
||||
6..=11 => (Ppre::DIV8, 8),
|
||||
_ => (Ppre::DIV16, 16),
|
||||
};
|
||||
let timer_mul2 = if ppre2 == 1 { 1 } else { 2 };
|
||||
let pclk2 = hclk / ppre2;
|
||||
assert!(pclk2 <= 72_000_000);
|
||||
|
||||
// Set latency based on HCLK frquency
|
||||
// NOTE(safety) Atomic write
|
||||
unsafe {
|
||||
FLASH.acr().write(|w| {
|
||||
w.set_latency(if hclk <= 24_000_000 {
|
||||
Latency::WS0
|
||||
} else if hclk <= 48_000_000 {
|
||||
Latency::WS1
|
||||
#[inline]
|
||||
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;
|
||||
// Minimum PLL multiplier is two
|
||||
if multiplier == 1 {
|
||||
multiplier *= 2;
|
||||
divisor *= 2;
|
||||
}
|
||||
assert!(multiplier <= 16);
|
||||
assert!(divisor <= 16);
|
||||
(multiplier, divisor)
|
||||
};
|
||||
// Based on the source of Pll, we calculate the actual system clock
|
||||
// frequency, PLL's source identifier, multiplier and divisor
|
||||
let (act_sysclk, pll_src, pll_mul, pll_div) = match config.hse {
|
||||
Some(Hertz(hse)) => {
|
||||
let (multiplier, divisor) = get_mul_div(sysclk, hse);
|
||||
(
|
||||
Hertz((hse / divisor) * multiplier),
|
||||
Pllsrc::HSE_DIV_PREDIV,
|
||||
into_pll_mul(multiplier),
|
||||
Some(into_pre_div(divisor)),
|
||||
)
|
||||
}
|
||||
None => {
|
||||
cfg_if::cfg_if! {
|
||||
// For some chips PREDIV is always two, and cannot be changed
|
||||
if #[cfg(any(
|
||||
feature="stm32f302xd", feature="stm32f302xe", feature="stm32f303xd",
|
||||
feature="stm32f303xe", feature="stm32f398xe"
|
||||
))] {
|
||||
let (multiplier, divisor) = get_mul_div(sysclk, HSI);
|
||||
(
|
||||
Hertz((hse / divisor) * multiplier),
|
||||
Pllsrc::HSI_DIV_PREDIV,
|
||||
into_pll_mul(multiplier),
|
||||
Some(into_pre_div(divisor)),
|
||||
)
|
||||
} else {
|
||||
Latency::WS2
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
// Enable HSE
|
||||
if self.config.hse.is_some() {
|
||||
// NOTE(unsafe) We own the peripheral block
|
||||
unsafe {
|
||||
RCC.cr().write(|w| {
|
||||
w.set_hsebyp(if self.config.bypass_hse {
|
||||
Hsebyp::BYPASSED
|
||||
} else {
|
||||
Hsebyp::NOTBYPASSED
|
||||
});
|
||||
// We turn on clock security to switch to HSI when HSE fails
|
||||
w.set_csson(true);
|
||||
w.set_hseon(true);
|
||||
});
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
}
|
||||
}
|
||||
|
||||
// Enable PLL
|
||||
if let Some(ref pll_config) = pll_config {
|
||||
// NOTE(unsafe) We own the peripheral block
|
||||
unsafe {
|
||||
RCC.cfgr().write(|w| {
|
||||
w.set_pllmul(pll_config.pll_mul);
|
||||
w.set_pllsrc(pll_config.pll_src);
|
||||
});
|
||||
if let Some(pll_div) = pll_config.pll_div {
|
||||
RCC.cfgr2().write(|w| w.set_prediv(pll_div));
|
||||
}
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
}
|
||||
}
|
||||
|
||||
if self.config.pll48 {
|
||||
let usb_pre = self.get_usb_pre(sysclk, pclk1, &pll_config);
|
||||
// NOTE(unsafe) We own the peripheral block
|
||||
unsafe {
|
||||
RCC.cfgr().write(|w| {
|
||||
w.set_usbpre(usb_pre);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Set prescalers
|
||||
unsafe {
|
||||
// NOTE(unsafe) We own the peripheral block
|
||||
RCC.cfgr().write(|w| {
|
||||
w.set_ppre2(ppre2_bits);
|
||||
w.set_ppre1(ppre1_bits);
|
||||
w.set_hpre(hpre_bits);
|
||||
});
|
||||
|
||||
// Wait for the new prescalers to kick in
|
||||
// "The clocks are divided with the new prescaler factor from
|
||||
// 1 to 16 AHB cycles after write"
|
||||
cortex_m::asm::delay(16);
|
||||
|
||||
// NOTE(unsafe) We own the peripheral block
|
||||
RCC.cfgr().write(|w| {
|
||||
w.set_sw(match (pll_config, self.config.hse) {
|
||||
(Some(_), _) => Sw::PLL,
|
||||
(None, Some(_)) => Sw::HSE,
|
||||
(None, None) => Sw::HSI,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
Clocks {
|
||||
sys: Hertz(sysclk),
|
||||
apb1: Hertz(pclk1),
|
||||
apb2: Hertz(pclk2),
|
||||
apb1_tim: Hertz(pclk1 * timer_mul1),
|
||||
apb2_tim: Hertz(pclk2 * timer_mul2),
|
||||
ahb: Hertz(hclk),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_sysclk(&self) -> (Hertz, Option<PllConfig>) {
|
||||
match (self.config.sysclk, self.config.hse) {
|
||||
(Some(sysclk), Some(hse)) if sysclk == hse => (hse, None),
|
||||
(Some(sysclk), None) if sysclk.0 == HSI => (Hertz(HSI), None),
|
||||
// If the user selected System clock is different from HSI or HSE
|
||||
// we will have to setup PLL clock source
|
||||
(Some(sysclk), _) => {
|
||||
let (sysclk, pll_config) = self.calc_pll(sysclk);
|
||||
(sysclk, Some(pll_config))
|
||||
}
|
||||
(None, Some(hse)) => (hse, None),
|
||||
(None, None) => (Hertz(HSI), None),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn calc_pll(&self, 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;
|
||||
// Minimum PLL multiplier is two
|
||||
if multiplier == 1 {
|
||||
multiplier *= 2;
|
||||
divisor *= 2;
|
||||
}
|
||||
assert!(multiplier <= 16);
|
||||
assert!(divisor <= 16);
|
||||
(multiplier, divisor)
|
||||
};
|
||||
// Based on the source of Pll, we calculate the actual system clock
|
||||
// frequency, PLL's source identifier, multiplier and divisor
|
||||
let (act_sysclk, pll_src, pll_mul, pll_div) = match self.config.hse {
|
||||
Some(Hertz(hse)) => {
|
||||
let (multiplier, divisor) = get_mul_div(sysclk, hse);
|
||||
(
|
||||
Hertz((hse / divisor) * multiplier),
|
||||
Pllsrc::HSE_DIV_PREDIV,
|
||||
into_pll_mul(multiplier),
|
||||
Some(into_pre_div(divisor)),
|
||||
)
|
||||
}
|
||||
None => {
|
||||
cfg_if::cfg_if! {
|
||||
// For some chips PREDIV is always two, and cannot be changed
|
||||
if #[cfg(any(
|
||||
feature="stm32f302xd", feature="stm32f302xe", feature="stm32f303xd",
|
||||
feature="stm32f303xe", feature="stm32f398xe"
|
||||
))] {
|
||||
let (multiplier, divisor) = get_mul_div(sysclk, HSI);
|
||||
(
|
||||
Hertz((hse / divisor) * multiplier),
|
||||
Pllsrc::HSI_DIV_PREDIV,
|
||||
into_pll_mul(multiplier),
|
||||
Some(into_pre_div(divisor)),
|
||||
)
|
||||
} else {
|
||||
let pllsrcclk = HSI / 2;
|
||||
let multiplier = sysclk / pllsrcclk;
|
||||
assert!(multiplier <= 16);
|
||||
(
|
||||
Hertz(pllsrcclk * multiplier),
|
||||
Pllsrc::HSI_DIV2,
|
||||
into_pll_mul(multiplier),
|
||||
None,
|
||||
)
|
||||
}
|
||||
let pllsrcclk = HSI / 2;
|
||||
let multiplier = sysclk / pllsrcclk;
|
||||
assert!(multiplier <= 16);
|
||||
(
|
||||
Hertz(pllsrcclk * multiplier),
|
||||
Pllsrc::HSI_DIV2,
|
||||
into_pll_mul(multiplier),
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
act_sysclk,
|
||||
PllConfig {
|
||||
pll_src,
|
||||
pll_mul,
|
||||
pll_div,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
act_sysclk,
|
||||
PllConfig {
|
||||
pll_src,
|
||||
pll_mul,
|
||||
pll_div,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_usb_pre(&self, sysclk: u32, pclk1: u32, pll_config: &Option<PllConfig>) -> Usbpre {
|
||||
cfg_if::cfg_if! {
|
||||
// Some chips do not have USB
|
||||
if #[cfg(any(stm32f301, stm32f318, stm32f334))] {
|
||||
panic!("USB clock not supported by the chip");
|
||||
} else {
|
||||
let usb_ok = self.config.hse.is_some() && pll_config.is_some() && (pclk1 >= 10_000_000);
|
||||
match (usb_ok, sysclk) {
|
||||
(true, 72_000_000) => Usbpre::DIV1_5,
|
||||
(true, 48_000_000) => Usbpre::DIV1,
|
||||
_ => panic!(
|
||||
"USB clock is only valid if the PLL output frequency is either 48MHz or 72MHz"
|
||||
),
|
||||
}
|
||||
#[inline]
|
||||
fn get_usb_pre(config: &Config, sysclk: u32, pclk1: u32, pll_config: &Option<PllConfig>) -> Usbpre {
|
||||
cfg_if::cfg_if! {
|
||||
// Some chips do not have USB
|
||||
if #[cfg(any(stm32f301, stm32f318, stm32f334))] {
|
||||
panic!("USB clock not supported by the chip");
|
||||
} else {
|
||||
let usb_ok = config.hse.is_some() && pll_config.is_some() && (pclk1 >= 10_000_000);
|
||||
match (usb_ok, sysclk) {
|
||||
(true, 72_000_000) => Usbpre::DIV1_5,
|
||||
(true, 48_000_000) => Usbpre::DIV1,
|
||||
_ => panic!(
|
||||
"USB clock is only valid if the PLL output frequency is either 48MHz or 72MHz"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
use super::sealed::RccPeripheral;
|
||||
use crate::pac::rcc::vals::{Hpre, Hsebyp, Ppre, Sw};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::peripherals;
|
||||
use crate::rcc::{get_freqs, set_freqs, Clocks};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
use core::marker::PhantomData;
|
||||
use embassy::util::Unborrow;
|
||||
|
||||
const HSI: u32 = 16_000_000;
|
||||
|
||||
@ -21,284 +20,240 @@ pub struct Config {
|
||||
pub pll48: bool,
|
||||
}
|
||||
|
||||
/// RCC peripheral
|
||||
pub struct Rcc<'d> {
|
||||
config: Config,
|
||||
phantom: PhantomData<&'d mut peripherals::RCC>,
|
||||
unsafe fn setup_pll(
|
||||
pllsrcclk: u32,
|
||||
use_hse: bool,
|
||||
pllsysclk: Option<u32>,
|
||||
pll48clk: bool,
|
||||
) -> PllResults {
|
||||
use crate::pac::rcc::vals::{Pllp, Pllsrc};
|
||||
|
||||
let sysclk = pllsysclk.unwrap_or(pllsrcclk);
|
||||
if pllsysclk.is_none() && !pll48clk {
|
||||
RCC.pllcfgr()
|
||||
.modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8)));
|
||||
|
||||
return PllResults {
|
||||
use_pll: false,
|
||||
pllsysclk: None,
|
||||
pll48clk: None,
|
||||
};
|
||||
}
|
||||
// Input divisor from PLL source clock, must result to frequency in
|
||||
// the range from 1 to 2 MHz
|
||||
let pllm_min = (pllsrcclk + 1_999_999) / 2_000_000;
|
||||
let pllm_max = pllsrcclk / 1_000_000;
|
||||
|
||||
// Sysclk output divisor must be one of 2, 4, 6 or 8
|
||||
let sysclk_div = core::cmp::min(8, (432_000_000 / sysclk) & !1);
|
||||
|
||||
let target_freq = if pll48clk {
|
||||
48_000_000
|
||||
} else {
|
||||
sysclk * sysclk_div
|
||||
};
|
||||
|
||||
// Find the lowest pllm value that minimize the difference between
|
||||
// target frequency and the real vco_out frequency.
|
||||
let pllm = unwrap!((pllm_min..=pllm_max).min_by_key(|pllm| {
|
||||
let vco_in = pllsrcclk / pllm;
|
||||
let plln = target_freq / vco_in;
|
||||
target_freq - vco_in * plln
|
||||
}));
|
||||
|
||||
let vco_in = pllsrcclk / pllm;
|
||||
assert!((1_000_000..=2_000_000).contains(&vco_in));
|
||||
|
||||
// Main scaler, must result in >= 100MHz (>= 192MHz for F401)
|
||||
// and <= 432MHz, min 50, max 432
|
||||
let plln = if pll48clk {
|
||||
// try the different valid pllq according to the valid
|
||||
// main scaller values, and take the best
|
||||
let pllq = unwrap!((4..=9).min_by_key(|pllq| {
|
||||
let plln = 48_000_000 * pllq / vco_in;
|
||||
let pll48_diff = 48_000_000 - vco_in * plln / pllq;
|
||||
let sysclk_diff = (sysclk as i32 - (vco_in * plln / sysclk_div) as i32).abs();
|
||||
(pll48_diff, sysclk_diff)
|
||||
}));
|
||||
48_000_000 * pllq / vco_in
|
||||
} else {
|
||||
sysclk * sysclk_div / vco_in
|
||||
};
|
||||
|
||||
let pllp = (sysclk_div / 2) - 1;
|
||||
|
||||
let pllq = (vco_in * plln + 47_999_999) / 48_000_000;
|
||||
let real_pll48clk = vco_in * plln / pllq;
|
||||
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllm(pllm as u8);
|
||||
w.set_plln(plln as u16);
|
||||
w.set_pllp(Pllp(pllp as u8));
|
||||
w.set_pllq(pllq as u8);
|
||||
w.set_pllsrc(Pllsrc(use_hse as u8));
|
||||
});
|
||||
|
||||
let real_pllsysclk = vco_in * plln / sysclk_div;
|
||||
|
||||
PllResults {
|
||||
use_pll: true,
|
||||
pllsysclk: Some(real_pllsysclk),
|
||||
pll48clk: if pll48clk { Some(real_pll48clk) } else { None },
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Rcc<'d> {
|
||||
pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self {
|
||||
Self {
|
||||
config,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
unsafe fn flash_setup(sysclk: u32) {
|
||||
use crate::pac::flash::vals::Latency;
|
||||
|
||||
fn freeze(mut self) -> Clocks {
|
||||
use super::sealed::RccPeripheral;
|
||||
use crate::pac::rcc::vals::{Hpre, Hsebyp, Ppre, Sw};
|
||||
// Be conservative with voltage ranges
|
||||
const FLASH_LATENCY_STEP: u32 = 30_000_000;
|
||||
|
||||
peripherals::PWR::enable();
|
||||
|
||||
let pllsrcclk = self.config.hse.map(|hse| hse.0).unwrap_or(HSI);
|
||||
let sysclk = self.config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk);
|
||||
let sysclk_on_pll = sysclk != pllsrcclk;
|
||||
|
||||
let plls = self.setup_pll(
|
||||
pllsrcclk,
|
||||
self.config.hse.is_some(),
|
||||
if sysclk_on_pll { Some(sysclk) } else { None },
|
||||
self.config.pll48,
|
||||
);
|
||||
|
||||
if self.config.pll48 {
|
||||
let freq = unwrap!(plls.pll48clk);
|
||||
|
||||
assert!((max::PLL_48_CLK as i32 - freq as i32).abs() <= max::PLL_48_TOLERANCE as i32);
|
||||
}
|
||||
|
||||
let sysclk = if sysclk_on_pll {
|
||||
unwrap!(plls.pllsysclk)
|
||||
} else {
|
||||
sysclk
|
||||
};
|
||||
|
||||
// AHB prescaler
|
||||
let hclk = self.config.hclk.map(|h| h.0).unwrap_or(sysclk);
|
||||
let (hpre_bits, hpre_div) = match (sysclk + hclk - 1) / hclk {
|
||||
0 => unreachable!(),
|
||||
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 AHB clock
|
||||
let hclk = sysclk / hpre_div;
|
||||
|
||||
let pclk1 = self
|
||||
.config
|
||||
.pclk1
|
||||
.map(|p| p.0)
|
||||
.unwrap_or_else(|| core::cmp::min(max::PCLK1_MAX, hclk));
|
||||
|
||||
let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 {
|
||||
0 => unreachable!(),
|
||||
1 => (0b000, 1),
|
||||
2 => (0b100, 2),
|
||||
3..=5 => (0b101, 4),
|
||||
6..=11 => (0b110, 8),
|
||||
_ => (0b111, 16),
|
||||
};
|
||||
let timer_mul1 = if ppre1 == 1 { 1 } else { 2 };
|
||||
|
||||
// Calculate real APB1 clock
|
||||
let pclk1 = hclk / ppre1;
|
||||
assert!(pclk1 <= max::PCLK1_MAX);
|
||||
|
||||
let pclk2 = self
|
||||
.config
|
||||
.pclk2
|
||||
.map(|p| p.0)
|
||||
.unwrap_or_else(|| core::cmp::min(max::PCLK2_MAX, hclk));
|
||||
let (ppre2_bits, ppre2) = match (hclk + pclk2 - 1) / pclk2 {
|
||||
0 => unreachable!(),
|
||||
1 => (0b000, 1),
|
||||
2 => (0b100, 2),
|
||||
3..=5 => (0b101, 4),
|
||||
6..=11 => (0b110, 8),
|
||||
_ => (0b111, 16),
|
||||
};
|
||||
let timer_mul2 = if ppre2 == 1 { 1 } else { 2 };
|
||||
|
||||
// Calculate real APB2 clock
|
||||
let pclk2 = hclk / ppre2;
|
||||
assert!(pclk2 <= max::PCLK2_MAX);
|
||||
|
||||
Self::flash_setup(sysclk);
|
||||
|
||||
if self.config.hse.is_some() {
|
||||
// NOTE(unsafe) We own the peripheral block
|
||||
unsafe {
|
||||
RCC.cr().modify(|w| {
|
||||
w.set_hsebyp(Hsebyp(self.config.bypass_hse as u8));
|
||||
w.set_hseon(true);
|
||||
});
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
}
|
||||
}
|
||||
|
||||
if plls.use_pll {
|
||||
unsafe {
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
|
||||
if hclk > max::HCLK_OVERDRIVE_FREQUENCY {
|
||||
PWR.cr1().modify(|w| w.set_oden(true));
|
||||
while !PWR.csr1().read().odrdy() {}
|
||||
|
||||
PWR.cr1().modify(|w| w.set_odswen(true));
|
||||
while !PWR.csr1().read().odswrdy() {}
|
||||
}
|
||||
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_ppre2(Ppre(ppre2_bits));
|
||||
w.set_ppre1(Ppre(ppre1_bits));
|
||||
w.set_hpre(hpre_bits);
|
||||
});
|
||||
|
||||
// Wait for the new prescalers to kick in
|
||||
// "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write"
|
||||
cortex_m::asm::delay(16);
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(if sysclk_on_pll {
|
||||
Sw::PLL
|
||||
} else if self.config.hse.is_some() {
|
||||
Sw::HSE
|
||||
} else {
|
||||
Sw::HSI
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
Clocks {
|
||||
sys: Hertz(sysclk),
|
||||
apb1: Hertz(pclk1),
|
||||
apb2: Hertz(pclk2),
|
||||
|
||||
apb1_tim: Hertz(pclk1 * timer_mul1),
|
||||
apb2_tim: Hertz(pclk2 * timer_mul2),
|
||||
|
||||
ahb1: Hertz(hclk),
|
||||
ahb2: Hertz(hclk),
|
||||
ahb3: Hertz(hclk),
|
||||
|
||||
pll48: plls.pll48clk.map(Hertz),
|
||||
}
|
||||
}
|
||||
|
||||
// Safety: RCC init must have been called
|
||||
pub fn clocks(&self) -> &'static Clocks {
|
||||
unsafe { get_freqs() }
|
||||
}
|
||||
|
||||
fn setup_pll(
|
||||
&mut self,
|
||||
pllsrcclk: u32,
|
||||
use_hse: bool,
|
||||
pllsysclk: Option<u32>,
|
||||
pll48clk: bool,
|
||||
) -> PllResults {
|
||||
use crate::pac::rcc::vals::{Pllp, Pllsrc};
|
||||
|
||||
let sysclk = pllsysclk.unwrap_or(pllsrcclk);
|
||||
if pllsysclk.is_none() && !pll48clk {
|
||||
// NOTE(unsafe) We have a mutable borrow to the owner of the RegBlock
|
||||
unsafe {
|
||||
RCC.pllcfgr()
|
||||
.modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8)));
|
||||
}
|
||||
|
||||
return PllResults {
|
||||
use_pll: false,
|
||||
pllsysclk: None,
|
||||
pll48clk: None,
|
||||
};
|
||||
}
|
||||
// Input divisor from PLL source clock, must result to frequency in
|
||||
// the range from 1 to 2 MHz
|
||||
let pllm_min = (pllsrcclk + 1_999_999) / 2_000_000;
|
||||
let pllm_max = pllsrcclk / 1_000_000;
|
||||
|
||||
// Sysclk output divisor must be one of 2, 4, 6 or 8
|
||||
let sysclk_div = core::cmp::min(8, (432_000_000 / sysclk) & !1);
|
||||
|
||||
let target_freq = if pll48clk {
|
||||
48_000_000
|
||||
} else {
|
||||
sysclk * sysclk_div
|
||||
};
|
||||
|
||||
// Find the lowest pllm value that minimize the difference between
|
||||
// target frequency and the real vco_out frequency.
|
||||
let pllm = unwrap!((pllm_min..=pllm_max).min_by_key(|pllm| {
|
||||
let vco_in = pllsrcclk / pllm;
|
||||
let plln = target_freq / vco_in;
|
||||
target_freq - vco_in * plln
|
||||
}));
|
||||
|
||||
let vco_in = pllsrcclk / pllm;
|
||||
assert!((1_000_000..=2_000_000).contains(&vco_in));
|
||||
|
||||
// Main scaler, must result in >= 100MHz (>= 192MHz for F401)
|
||||
// and <= 432MHz, min 50, max 432
|
||||
let plln = if pll48clk {
|
||||
// try the different valid pllq according to the valid
|
||||
// main scaller values, and take the best
|
||||
let pllq = unwrap!((4..=9).min_by_key(|pllq| {
|
||||
let plln = 48_000_000 * pllq / vco_in;
|
||||
let pll48_diff = 48_000_000 - vco_in * plln / pllq;
|
||||
let sysclk_diff = (sysclk as i32 - (vco_in * plln / sysclk_div) as i32).abs();
|
||||
(pll48_diff, sysclk_diff)
|
||||
}));
|
||||
48_000_000 * pllq / vco_in
|
||||
} else {
|
||||
sysclk * sysclk_div / vco_in
|
||||
};
|
||||
|
||||
let pllp = (sysclk_div / 2) - 1;
|
||||
|
||||
let pllq = (vco_in * plln + 47_999_999) / 48_000_000;
|
||||
let real_pll48clk = vco_in * plln / pllq;
|
||||
|
||||
unsafe {
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllm(pllm as u8);
|
||||
w.set_plln(plln as u16);
|
||||
w.set_pllp(Pllp(pllp as u8));
|
||||
w.set_pllq(pllq as u8);
|
||||
w.set_pllsrc(Pllsrc(use_hse as u8));
|
||||
});
|
||||
}
|
||||
|
||||
let real_pllsysclk = vco_in * plln / sysclk_div;
|
||||
|
||||
PllResults {
|
||||
use_pll: true,
|
||||
pllsysclk: Some(real_pllsysclk),
|
||||
pll48clk: if pll48clk { Some(real_pll48clk) } else { None },
|
||||
}
|
||||
}
|
||||
|
||||
fn flash_setup(sysclk: u32) {
|
||||
use crate::pac::flash::vals::Latency;
|
||||
|
||||
// Be conservative with voltage ranges
|
||||
const FLASH_LATENCY_STEP: u32 = 30_000_000;
|
||||
|
||||
critical_section::with(|_| unsafe {
|
||||
FLASH
|
||||
.acr()
|
||||
.modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
|
||||
});
|
||||
}
|
||||
critical_section::with(|_| {
|
||||
FLASH
|
||||
.acr()
|
||||
.modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
||||
let clocks = Rcc::new(r, config).freeze();
|
||||
set_freqs(clocks);
|
||||
crate::peripherals::PWR::enable();
|
||||
|
||||
let pllsrcclk = config.hse.map(|hse| hse.0).unwrap_or(HSI);
|
||||
let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk);
|
||||
let sysclk_on_pll = sysclk != pllsrcclk;
|
||||
|
||||
let plls = setup_pll(
|
||||
pllsrcclk,
|
||||
config.hse.is_some(),
|
||||
if sysclk_on_pll { Some(sysclk) } else { None },
|
||||
config.pll48,
|
||||
);
|
||||
|
||||
if config.pll48 {
|
||||
let freq = unwrap!(plls.pll48clk);
|
||||
|
||||
assert!((max::PLL_48_CLK as i32 - freq as i32).abs() <= max::PLL_48_TOLERANCE as i32);
|
||||
}
|
||||
|
||||
let sysclk = if sysclk_on_pll {
|
||||
unwrap!(plls.pllsysclk)
|
||||
} else {
|
||||
sysclk
|
||||
};
|
||||
|
||||
// AHB prescaler
|
||||
let hclk = config.hclk.map(|h| h.0).unwrap_or(sysclk);
|
||||
let (hpre_bits, hpre_div) = match (sysclk + hclk - 1) / hclk {
|
||||
0 => unreachable!(),
|
||||
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 AHB clock
|
||||
let hclk = sysclk / hpre_div;
|
||||
|
||||
let pclk1 = config
|
||||
.pclk1
|
||||
.map(|p| p.0)
|
||||
.unwrap_or_else(|| core::cmp::min(max::PCLK1_MAX, hclk));
|
||||
|
||||
let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 {
|
||||
0 => unreachable!(),
|
||||
1 => (0b000, 1),
|
||||
2 => (0b100, 2),
|
||||
3..=5 => (0b101, 4),
|
||||
6..=11 => (0b110, 8),
|
||||
_ => (0b111, 16),
|
||||
};
|
||||
let timer_mul1 = if ppre1 == 1 { 1 } else { 2 };
|
||||
|
||||
// Calculate real APB1 clock
|
||||
let pclk1 = hclk / ppre1;
|
||||
assert!(pclk1 <= max::PCLK1_MAX);
|
||||
|
||||
let pclk2 = config
|
||||
.pclk2
|
||||
.map(|p| p.0)
|
||||
.unwrap_or_else(|| core::cmp::min(max::PCLK2_MAX, hclk));
|
||||
let (ppre2_bits, ppre2) = match (hclk + pclk2 - 1) / pclk2 {
|
||||
0 => unreachable!(),
|
||||
1 => (0b000, 1),
|
||||
2 => (0b100, 2),
|
||||
3..=5 => (0b101, 4),
|
||||
6..=11 => (0b110, 8),
|
||||
_ => (0b111, 16),
|
||||
};
|
||||
let timer_mul2 = if ppre2 == 1 { 1 } else { 2 };
|
||||
|
||||
// Calculate real APB2 clock
|
||||
let pclk2 = hclk / ppre2;
|
||||
assert!(pclk2 <= max::PCLK2_MAX);
|
||||
|
||||
flash_setup(sysclk);
|
||||
|
||||
if config.hse.is_some() {
|
||||
RCC.cr().modify(|w| {
|
||||
w.set_hsebyp(Hsebyp(config.bypass_hse as u8));
|
||||
w.set_hseon(true);
|
||||
});
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
}
|
||||
|
||||
if plls.use_pll {
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
|
||||
if hclk > max::HCLK_OVERDRIVE_FREQUENCY {
|
||||
PWR.cr1().modify(|w| w.set_oden(true));
|
||||
while !PWR.csr1().read().odrdy() {}
|
||||
|
||||
PWR.cr1().modify(|w| w.set_odswen(true));
|
||||
while !PWR.csr1().read().odswrdy() {}
|
||||
}
|
||||
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
}
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_ppre2(Ppre(ppre2_bits));
|
||||
w.set_ppre1(Ppre(ppre1_bits));
|
||||
w.set_hpre(hpre_bits);
|
||||
});
|
||||
|
||||
// Wait for the new prescalers to kick in
|
||||
// "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write"
|
||||
cortex_m::asm::delay(16);
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(if sysclk_on_pll {
|
||||
Sw::PLL
|
||||
} else if config.hse.is_some() {
|
||||
Sw::HSE
|
||||
} else {
|
||||
Sw::HSI
|
||||
})
|
||||
});
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sysclk),
|
||||
apb1: Hertz(pclk1),
|
||||
apb2: Hertz(pclk2),
|
||||
|
||||
apb1_tim: Hertz(pclk1 * timer_mul1),
|
||||
apb2_tim: Hertz(pclk2 * timer_mul2),
|
||||
|
||||
ahb1: Hertz(hclk),
|
||||
ahb2: Hertz(hclk),
|
||||
ahb3: Hertz(hclk),
|
||||
|
||||
pll48: plls.pll48clk.map(Hertz),
|
||||
});
|
||||
}
|
||||
|
||||
struct PllResults {
|
||||
|
@ -1,9 +1,9 @@
|
||||
use super::sealed::RccPeripheral;
|
||||
use crate::pac::pwr::vals::Vos;
|
||||
use crate::pac::rcc::vals::{Hpre, Hsebyp, Ppre, Sw};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::peripherals;
|
||||
use crate::rcc::{get_freqs, set_freqs, Clocks};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
use core::marker::PhantomData;
|
||||
use embassy::util::Unborrow;
|
||||
|
||||
const HSI: u32 = 16_000_000;
|
||||
|
||||
@ -21,318 +21,274 @@ pub struct Config {
|
||||
pub pll48: bool,
|
||||
}
|
||||
|
||||
/// RCC peripheral
|
||||
pub struct Rcc<'d> {
|
||||
config: Config,
|
||||
phantom: PhantomData<&'d mut peripherals::RCC>,
|
||||
unsafe fn setup_pll(
|
||||
pllsrcclk: u32,
|
||||
use_hse: bool,
|
||||
pllsysclk: Option<u32>,
|
||||
pll48clk: bool,
|
||||
) -> PllResults {
|
||||
use crate::pac::rcc::vals::{Pllp, Pllsrc};
|
||||
|
||||
let sysclk = pllsysclk.unwrap_or(pllsrcclk);
|
||||
if pllsysclk.is_none() && !pll48clk {
|
||||
RCC.pllcfgr()
|
||||
.modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8)));
|
||||
|
||||
return PllResults {
|
||||
use_pll: false,
|
||||
pllsysclk: None,
|
||||
pll48clk: None,
|
||||
};
|
||||
}
|
||||
// Input divisor from PLL source clock, must result to frequency in
|
||||
// the range from 1 to 2 MHz
|
||||
let pllm_min = (pllsrcclk + 1_999_999) / 2_000_000;
|
||||
let pllm_max = pllsrcclk / 1_000_000;
|
||||
|
||||
// Sysclk output divisor must be one of 2, 4, 6 or 8
|
||||
let sysclk_div = core::cmp::min(8, (432_000_000 / sysclk) & !1);
|
||||
|
||||
let target_freq = if pll48clk {
|
||||
48_000_000
|
||||
} else {
|
||||
sysclk * sysclk_div
|
||||
};
|
||||
|
||||
// Find the lowest pllm value that minimize the difference between
|
||||
// target frequency and the real vco_out frequency.
|
||||
let pllm = unwrap!((pllm_min..=pllm_max).min_by_key(|pllm| {
|
||||
let vco_in = pllsrcclk / pllm;
|
||||
let plln = target_freq / vco_in;
|
||||
target_freq - vco_in * plln
|
||||
}));
|
||||
|
||||
let vco_in = pllsrcclk / pllm;
|
||||
assert!((1_000_000..=2_000_000).contains(&vco_in));
|
||||
|
||||
// Main scaler, must result in >= 100MHz (>= 192MHz for F401)
|
||||
// and <= 432MHz, min 50, max 432
|
||||
let plln = if pll48clk {
|
||||
// try the different valid pllq according to the valid
|
||||
// main scaller values, and take the best
|
||||
let pllq = unwrap!((4..=9).min_by_key(|pllq| {
|
||||
let plln = 48_000_000 * pllq / vco_in;
|
||||
let pll48_diff = 48_000_000 - vco_in * plln / pllq;
|
||||
let sysclk_diff = (sysclk as i32 - (vco_in * plln / sysclk_div) as i32).abs();
|
||||
(pll48_diff, sysclk_diff)
|
||||
}));
|
||||
48_000_000 * pllq / vco_in
|
||||
} else {
|
||||
sysclk * sysclk_div / vco_in
|
||||
};
|
||||
|
||||
let pllp = (sysclk_div / 2) - 1;
|
||||
|
||||
let pllq = (vco_in * plln + 47_999_999) / 48_000_000;
|
||||
let real_pll48clk = vco_in * plln / pllq;
|
||||
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllm(pllm as u8);
|
||||
w.set_plln(plln as u16);
|
||||
w.set_pllp(Pllp(pllp as u8));
|
||||
w.set_pllq(pllq as u8);
|
||||
w.set_pllsrc(Pllsrc(use_hse as u8));
|
||||
});
|
||||
|
||||
let real_pllsysclk = vco_in * plln / sysclk_div;
|
||||
|
||||
PllResults {
|
||||
use_pll: true,
|
||||
pllsysclk: Some(real_pllsysclk),
|
||||
pll48clk: if pll48clk { Some(real_pll48clk) } else { None },
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Rcc<'d> {
|
||||
pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self {
|
||||
if let Some(hse) = config.hse {
|
||||
if config.bypass_hse {
|
||||
assert!((max::HSE_BYPASS_MIN..=max::HSE_BYPASS_MAX).contains(&hse.0));
|
||||
} else {
|
||||
assert!((max::HSE_OSC_MIN..=max::HSE_OSC_MAX).contains(&hse.0));
|
||||
}
|
||||
}
|
||||
Self {
|
||||
config,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
unsafe fn flash_setup(sysclk: u32) {
|
||||
use crate::pac::flash::vals::Latency;
|
||||
|
||||
fn freeze(mut self) -> Clocks {
|
||||
use super::sealed::RccPeripheral;
|
||||
use crate::pac::pwr::vals::Vos;
|
||||
use crate::pac::rcc::vals::{Hpre, Hsebyp, Ppre, Sw};
|
||||
// Be conservative with voltage ranges
|
||||
const FLASH_LATENCY_STEP: u32 = 30_000_000;
|
||||
|
||||
peripherals::PWR::enable();
|
||||
|
||||
let pllsrcclk = self.config.hse.map(|hse| hse.0).unwrap_or(HSI);
|
||||
let sysclk = self.config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk);
|
||||
let sysclk_on_pll = sysclk != pllsrcclk;
|
||||
|
||||
assert!((max::SYSCLK_MIN..=max::SYSCLK_MAX).contains(&sysclk));
|
||||
|
||||
let plls = self.setup_pll(
|
||||
pllsrcclk,
|
||||
self.config.hse.is_some(),
|
||||
if sysclk_on_pll { Some(sysclk) } else { None },
|
||||
self.config.pll48,
|
||||
);
|
||||
|
||||
if self.config.pll48 {
|
||||
let freq = unwrap!(plls.pll48clk);
|
||||
|
||||
assert!((max::PLL_48_CLK as i32 - freq as i32).abs() <= max::PLL_48_TOLERANCE as i32);
|
||||
}
|
||||
|
||||
let sysclk = if sysclk_on_pll {
|
||||
unwrap!(plls.pllsysclk)
|
||||
} else {
|
||||
sysclk
|
||||
};
|
||||
|
||||
// AHB prescaler
|
||||
let hclk = self.config.hclk.map(|h| h.0).unwrap_or(sysclk);
|
||||
let (hpre_bits, hpre_div) = match (sysclk + hclk - 1) / hclk {
|
||||
0 => unreachable!(),
|
||||
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 AHB clock
|
||||
let hclk = sysclk / hpre_div;
|
||||
|
||||
assert!(hclk < max::HCLK_MAX);
|
||||
|
||||
let pclk1 = self
|
||||
.config
|
||||
.pclk1
|
||||
.map(|p| p.0)
|
||||
.unwrap_or_else(|| core::cmp::min(max::PCLK1_MAX, hclk));
|
||||
|
||||
let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 {
|
||||
0 => unreachable!(),
|
||||
1 => (0b000, 1),
|
||||
2 => (0b100, 2),
|
||||
3..=5 => (0b101, 4),
|
||||
6..=11 => (0b110, 8),
|
||||
_ => (0b111, 16),
|
||||
};
|
||||
let timer_mul1 = if ppre1 == 1 { 1 } else { 2 };
|
||||
|
||||
// Calculate real APB1 clock
|
||||
let pclk1 = hclk / ppre1;
|
||||
assert!((max::PCLK1_MIN..=max::PCLK1_MAX).contains(&pclk1));
|
||||
|
||||
let pclk2 = self
|
||||
.config
|
||||
.pclk2
|
||||
.map(|p| p.0)
|
||||
.unwrap_or_else(|| core::cmp::min(max::PCLK2_MAX, hclk));
|
||||
let (ppre2_bits, ppre2) = match (hclk + pclk2 - 1) / pclk2 {
|
||||
0 => unreachable!(),
|
||||
1 => (0b000, 1),
|
||||
2 => (0b100, 2),
|
||||
3..=5 => (0b101, 4),
|
||||
6..=11 => (0b110, 8),
|
||||
_ => (0b111, 16),
|
||||
};
|
||||
let timer_mul2 = if ppre2 == 1 { 1 } else { 2 };
|
||||
|
||||
// Calculate real APB2 clock
|
||||
let pclk2 = hclk / ppre2;
|
||||
assert!((max::PCLK2_MIN..=max::PCLK2_MAX).contains(&pclk2));
|
||||
|
||||
Self::flash_setup(sysclk);
|
||||
|
||||
if self.config.hse.is_some() {
|
||||
// NOTE(unsafe) We own the peripheral block
|
||||
unsafe {
|
||||
RCC.cr().modify(|w| {
|
||||
w.set_hsebyp(Hsebyp(self.config.bypass_hse as u8));
|
||||
w.set_hseon(true);
|
||||
});
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
}
|
||||
}
|
||||
|
||||
if plls.use_pll {
|
||||
unsafe {
|
||||
RCC.cr().modify(|w| w.set_pllon(false));
|
||||
|
||||
// enable PWR and setup VOSScale
|
||||
|
||||
RCC.apb1enr().modify(|w| w.set_pwren(true));
|
||||
|
||||
let vos_scale = if sysclk <= 144_000_000 {
|
||||
3
|
||||
} else if sysclk <= 168_000_000 {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
};
|
||||
PWR.cr1().modify(|w| {
|
||||
w.set_vos(match vos_scale {
|
||||
3 => Vos::SCALE3,
|
||||
2 => Vos::SCALE2,
|
||||
1 => Vos::SCALE1,
|
||||
_ => panic!("Invalid VOS Scale."),
|
||||
})
|
||||
});
|
||||
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
|
||||
if hclk > max::HCLK_OVERDRIVE_FREQUENCY {
|
||||
PWR.cr1().modify(|w| w.set_oden(true));
|
||||
while !PWR.csr1().read().odrdy() {}
|
||||
|
||||
PWR.cr1().modify(|w| w.set_odswen(true));
|
||||
while !PWR.csr1().read().odswrdy() {}
|
||||
}
|
||||
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_ppre2(Ppre(ppre2_bits));
|
||||
w.set_ppre1(Ppre(ppre1_bits));
|
||||
w.set_hpre(hpre_bits);
|
||||
});
|
||||
|
||||
// Wait for the new prescalers to kick in
|
||||
// "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write"
|
||||
cortex_m::asm::delay(16);
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(if sysclk_on_pll {
|
||||
Sw::PLL
|
||||
} else if self.config.hse.is_some() {
|
||||
Sw::HSE
|
||||
} else {
|
||||
Sw::HSI
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
Clocks {
|
||||
sys: Hertz(sysclk),
|
||||
apb1: Hertz(pclk1),
|
||||
apb2: Hertz(pclk2),
|
||||
|
||||
apb1_tim: Hertz(pclk1 * timer_mul1),
|
||||
apb2_tim: Hertz(pclk2 * timer_mul2),
|
||||
|
||||
ahb1: Hertz(hclk),
|
||||
ahb2: Hertz(hclk),
|
||||
ahb3: Hertz(hclk),
|
||||
|
||||
pll48: plls.pll48clk.map(Hertz),
|
||||
}
|
||||
}
|
||||
|
||||
// Safety: RCC init must have been called
|
||||
pub fn clocks(&self) -> &'static Clocks {
|
||||
unsafe { get_freqs() }
|
||||
}
|
||||
|
||||
fn setup_pll(
|
||||
&mut self,
|
||||
pllsrcclk: u32,
|
||||
use_hse: bool,
|
||||
pllsysclk: Option<u32>,
|
||||
pll48clk: bool,
|
||||
) -> PllResults {
|
||||
use crate::pac::rcc::vals::{Pllp, Pllsrc};
|
||||
|
||||
let sysclk = pllsysclk.unwrap_or(pllsrcclk);
|
||||
if pllsysclk.is_none() && !pll48clk {
|
||||
// NOTE(unsafe) We have a mutable borrow to the owner of the RegBlock
|
||||
unsafe {
|
||||
RCC.pllcfgr()
|
||||
.modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8)));
|
||||
}
|
||||
|
||||
return PllResults {
|
||||
use_pll: false,
|
||||
pllsysclk: None,
|
||||
pll48clk: None,
|
||||
};
|
||||
}
|
||||
// Input divisor from PLL source clock, must result to frequency in
|
||||
// the range from 1 to 2 MHz
|
||||
let pllm_min = (pllsrcclk + 1_999_999) / 2_000_000;
|
||||
let pllm_max = pllsrcclk / 1_000_000;
|
||||
|
||||
// Sysclk output divisor must be one of 2, 4, 6 or 8
|
||||
let sysclk_div = core::cmp::min(8, (432_000_000 / sysclk) & !1);
|
||||
|
||||
let target_freq = if pll48clk {
|
||||
48_000_000
|
||||
} else {
|
||||
sysclk * sysclk_div
|
||||
};
|
||||
|
||||
// Find the lowest pllm value that minimize the difference between
|
||||
// target frequency and the real vco_out frequency.
|
||||
let pllm = unwrap!((pllm_min..=pllm_max).min_by_key(|pllm| {
|
||||
let vco_in = pllsrcclk / pllm;
|
||||
let plln = target_freq / vco_in;
|
||||
target_freq - vco_in * plln
|
||||
}));
|
||||
|
||||
let vco_in = pllsrcclk / pllm;
|
||||
assert!((1_000_000..=2_000_000).contains(&vco_in));
|
||||
|
||||
// Main scaler, must result in >= 100MHz (>= 192MHz for F401)
|
||||
// and <= 432MHz, min 50, max 432
|
||||
let plln = if pll48clk {
|
||||
// try the different valid pllq according to the valid
|
||||
// main scaller values, and take the best
|
||||
let pllq = unwrap!((4..=9).min_by_key(|pllq| {
|
||||
let plln = 48_000_000 * pllq / vco_in;
|
||||
let pll48_diff = 48_000_000 - vco_in * plln / pllq;
|
||||
let sysclk_diff = (sysclk as i32 - (vco_in * plln / sysclk_div) as i32).abs();
|
||||
(pll48_diff, sysclk_diff)
|
||||
}));
|
||||
48_000_000 * pllq / vco_in
|
||||
} else {
|
||||
sysclk * sysclk_div / vco_in
|
||||
};
|
||||
|
||||
let pllp = (sysclk_div / 2) - 1;
|
||||
|
||||
let pllq = (vco_in * plln + 47_999_999) / 48_000_000;
|
||||
let real_pll48clk = vco_in * plln / pllq;
|
||||
|
||||
unsafe {
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllm(pllm as u8);
|
||||
w.set_plln(plln as u16);
|
||||
w.set_pllp(Pllp(pllp as u8));
|
||||
w.set_pllq(pllq as u8);
|
||||
w.set_pllsrc(Pllsrc(use_hse as u8));
|
||||
});
|
||||
}
|
||||
|
||||
let real_pllsysclk = vco_in * plln / sysclk_div;
|
||||
|
||||
PllResults {
|
||||
use_pll: true,
|
||||
pllsysclk: Some(real_pllsysclk),
|
||||
pll48clk: if pll48clk { Some(real_pll48clk) } else { None },
|
||||
}
|
||||
}
|
||||
|
||||
fn flash_setup(sysclk: u32) {
|
||||
use crate::pac::flash::vals::Latency;
|
||||
|
||||
// Be conservative with voltage ranges
|
||||
const FLASH_LATENCY_STEP: u32 = 30_000_000;
|
||||
|
||||
critical_section::with(|_| unsafe {
|
||||
FLASH
|
||||
.acr()
|
||||
.modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
|
||||
});
|
||||
}
|
||||
critical_section::with(|_| {
|
||||
FLASH
|
||||
.acr()
|
||||
.modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
||||
let clocks = Rcc::new(r, config).freeze();
|
||||
set_freqs(clocks);
|
||||
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));
|
||||
} else {
|
||||
assert!((max::HSE_OSC_MIN..=max::HSE_OSC_MAX).contains(&hse.0));
|
||||
}
|
||||
}
|
||||
|
||||
let pllsrcclk = config.hse.map(|hse| hse.0).unwrap_or(HSI);
|
||||
let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk);
|
||||
let sysclk_on_pll = sysclk != pllsrcclk;
|
||||
|
||||
assert!((max::SYSCLK_MIN..=max::SYSCLK_MAX).contains(&sysclk));
|
||||
|
||||
let plls = setup_pll(
|
||||
pllsrcclk,
|
||||
config.hse.is_some(),
|
||||
if sysclk_on_pll { Some(sysclk) } else { None },
|
||||
config.pll48,
|
||||
);
|
||||
|
||||
if config.pll48 {
|
||||
let freq = unwrap!(plls.pll48clk);
|
||||
|
||||
assert!((max::PLL_48_CLK as i32 - freq as i32).abs() <= max::PLL_48_TOLERANCE as i32);
|
||||
}
|
||||
|
||||
let sysclk = if sysclk_on_pll {
|
||||
unwrap!(plls.pllsysclk)
|
||||
} else {
|
||||
sysclk
|
||||
};
|
||||
|
||||
// AHB prescaler
|
||||
let hclk = config.hclk.map(|h| h.0).unwrap_or(sysclk);
|
||||
let (hpre_bits, hpre_div) = match (sysclk + hclk - 1) / hclk {
|
||||
0 => unreachable!(),
|
||||
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 AHB clock
|
||||
let hclk = sysclk / hpre_div;
|
||||
|
||||
assert!(hclk < max::HCLK_MAX);
|
||||
|
||||
let pclk1 = config
|
||||
.pclk1
|
||||
.map(|p| p.0)
|
||||
.unwrap_or_else(|| core::cmp::min(max::PCLK1_MAX, hclk));
|
||||
|
||||
let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 {
|
||||
0 => unreachable!(),
|
||||
1 => (0b000, 1),
|
||||
2 => (0b100, 2),
|
||||
3..=5 => (0b101, 4),
|
||||
6..=11 => (0b110, 8),
|
||||
_ => (0b111, 16),
|
||||
};
|
||||
let timer_mul1 = if ppre1 == 1 { 1 } else { 2 };
|
||||
|
||||
// Calculate real APB1 clock
|
||||
let pclk1 = hclk / ppre1;
|
||||
assert!((max::PCLK1_MIN..=max::PCLK1_MAX).contains(&pclk1));
|
||||
|
||||
let pclk2 = config
|
||||
.pclk2
|
||||
.map(|p| p.0)
|
||||
.unwrap_or_else(|| core::cmp::min(max::PCLK2_MAX, hclk));
|
||||
let (ppre2_bits, ppre2) = match (hclk + pclk2 - 1) / pclk2 {
|
||||
0 => unreachable!(),
|
||||
1 => (0b000, 1),
|
||||
2 => (0b100, 2),
|
||||
3..=5 => (0b101, 4),
|
||||
6..=11 => (0b110, 8),
|
||||
_ => (0b111, 16),
|
||||
};
|
||||
let timer_mul2 = if ppre2 == 1 { 1 } else { 2 };
|
||||
|
||||
// Calculate real APB2 clock
|
||||
let pclk2 = hclk / ppre2;
|
||||
assert!((max::PCLK2_MIN..=max::PCLK2_MAX).contains(&pclk2));
|
||||
|
||||
flash_setup(sysclk);
|
||||
|
||||
if config.hse.is_some() {
|
||||
RCC.cr().modify(|w| {
|
||||
w.set_hsebyp(Hsebyp(config.bypass_hse as u8));
|
||||
w.set_hseon(true);
|
||||
});
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
let vos_scale = if sysclk <= 144_000_000 {
|
||||
3
|
||||
} else if sysclk <= 168_000_000 {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
};
|
||||
PWR.cr1().modify(|w| {
|
||||
w.set_vos(match vos_scale {
|
||||
3 => Vos::SCALE3,
|
||||
2 => Vos::SCALE2,
|
||||
1 => Vos::SCALE1,
|
||||
_ => panic!("Invalid VOS Scale."),
|
||||
})
|
||||
});
|
||||
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
|
||||
if hclk > max::HCLK_OVERDRIVE_FREQUENCY {
|
||||
PWR.cr1().modify(|w| w.set_oden(true));
|
||||
while !PWR.csr1().read().odrdy() {}
|
||||
|
||||
PWR.cr1().modify(|w| w.set_odswen(true));
|
||||
while !PWR.csr1().read().odswrdy() {}
|
||||
}
|
||||
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
}
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_ppre2(Ppre(ppre2_bits));
|
||||
w.set_ppre1(Ppre(ppre1_bits));
|
||||
w.set_hpre(hpre_bits);
|
||||
});
|
||||
|
||||
// Wait for the new prescalers to kick in
|
||||
// "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write"
|
||||
cortex_m::asm::delay(16);
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(if sysclk_on_pll {
|
||||
Sw::PLL
|
||||
} else if config.hse.is_some() {
|
||||
Sw::HSE
|
||||
} else {
|
||||
Sw::HSI
|
||||
})
|
||||
});
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sysclk),
|
||||
apb1: Hertz(pclk1),
|
||||
apb2: Hertz(pclk2),
|
||||
|
||||
apb1_tim: Hertz(pclk1 * timer_mul1),
|
||||
apb2_tim: Hertz(pclk2 * timer_mul2),
|
||||
|
||||
ahb1: Hertz(hclk),
|
||||
ahb2: Hertz(hclk),
|
||||
ahb3: Hertz(hclk),
|
||||
|
||||
pll48: plls.pll48clk.map(Hertz),
|
||||
});
|
||||
}
|
||||
|
||||
struct PllResults {
|
||||
|
@ -1,11 +1,7 @@
|
||||
use crate::pac;
|
||||
use crate::peripherals::{self, RCC};
|
||||
use crate::rcc::{get_freqs, set_freqs, Clocks};
|
||||
use crate::pac::{PWR, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
use crate::time::U32Ext;
|
||||
use core::marker::PhantomData;
|
||||
use embassy::util::Unborrow;
|
||||
use embassy_hal_common::unborrow;
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: u32 = 16_000_000;
|
||||
@ -120,115 +116,68 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// RCC peripheral
|
||||
pub struct Rcc<'d> {
|
||||
_rb: peripherals::RCC,
|
||||
phantom: PhantomData<&'d mut peripherals::RCC>,
|
||||
}
|
||||
|
||||
impl<'d> Rcc<'d> {
|
||||
pub fn new(rcc: impl Unborrow<Target = peripherals::RCC> + 'd) -> Self {
|
||||
unborrow!(rcc);
|
||||
Self {
|
||||
_rb: rcc,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
// Safety: RCC init must have been called
|
||||
pub fn clocks(&self) -> &'static Clocks {
|
||||
unsafe { get_freqs() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait that freezes the `RCC` peripheral with provided clocks configuration
|
||||
pub trait RccExt {
|
||||
fn freeze(self, config: Config) -> Clocks;
|
||||
}
|
||||
|
||||
impl RccExt for RCC {
|
||||
#[inline]
|
||||
fn freeze(self, cfgr: Config) -> Clocks {
|
||||
let rcc = pac::RCC;
|
||||
let (sys_clk, sw) = match cfgr.mux {
|
||||
ClockSrc::HSI16(div) => {
|
||||
// Enable HSI16
|
||||
let div: u8 = div.into();
|
||||
unsafe {
|
||||
rcc.cr().write(|w| {
|
||||
w.set_hsidiv(div);
|
||||
w.set_hsion(true)
|
||||
});
|
||||
while !rcc.cr().read().hsirdy() {}
|
||||
}
|
||||
|
||||
(HSI_FREQ >> div, 0x00)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_hseon(true));
|
||||
while !rcc.cr().read().hserdy() {}
|
||||
}
|
||||
|
||||
(freq.0, 0x01)
|
||||
}
|
||||
ClockSrc::LSI => {
|
||||
// Enable LSI
|
||||
unsafe {
|
||||
rcc.csr().write(|w| w.set_lsion(true));
|
||||
while !rcc.csr().read().lsirdy() {}
|
||||
}
|
||||
(LSI_FREQ, 0x03)
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
rcc.cfgr().modify(|w| {
|
||||
w.set_sw(sw.into());
|
||||
w.set_hpre(cfgr.ahb_pre.into());
|
||||
w.set_ppre(cfgr.apb_pre.into());
|
||||
});
|
||||
}
|
||||
|
||||
let ahb_freq: u32 = match cfgr.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre = 1 << (pre as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
|
||||
let (apb_freq, apb_tim_freq) = match cfgr.apb_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let pwr = pac::PWR;
|
||||
if cfgr.low_power_run {
|
||||
assert!(sys_clk.hz() <= 2_000_000.hz());
|
||||
unsafe {
|
||||
pwr.cr1().modify(|w| w.set_lpr(true));
|
||||
}
|
||||
}
|
||||
|
||||
Clocks {
|
||||
sys: sys_clk.hz(),
|
||||
ahb: ahb_freq.hz(),
|
||||
apb: apb_freq.hz(),
|
||||
apb_tim: apb_tim_freq.hz(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
||||
let clocks = r.freeze(config);
|
||||
set_freqs(clocks);
|
||||
let (sys_clk, sw) = match config.mux {
|
||||
ClockSrc::HSI16(div) => {
|
||||
// Enable HSI16
|
||||
let div: u8 = div.into();
|
||||
RCC.cr().write(|w| {
|
||||
w.set_hsidiv(div);
|
||||
w.set_hsion(true)
|
||||
});
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
(HSI_FREQ >> div, 0x00)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
(freq.0, 0x01)
|
||||
}
|
||||
ClockSrc::LSI => {
|
||||
// Enable LSI
|
||||
RCC.csr().write(|w| w.set_lsion(true));
|
||||
while !RCC.csr().read().lsirdy() {}
|
||||
(LSI_FREQ, 0x03)
|
||||
}
|
||||
};
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(sw.into());
|
||||
w.set_hpre(config.ahb_pre.into());
|
||||
w.set_ppre(config.apb_pre.into());
|
||||
});
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre = 1 << (pre as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
|
||||
let (apb_freq, apb_tim_freq) = match config.apb_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
if config.low_power_run {
|
||||
assert!(sys_clk.hz() <= 2_000_000.hz());
|
||||
PWR.cr1().modify(|w| w.set_lpr(true));
|
||||
}
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: sys_clk.hz(),
|
||||
ahb: ahb_freq.hz(),
|
||||
apb: apb_freq.hz(),
|
||||
apb_tim: apb_tim_freq.hz(),
|
||||
});
|
||||
}
|
||||
|
@ -1,11 +1,7 @@
|
||||
use crate::pac;
|
||||
use crate::peripherals::{self, RCC};
|
||||
use crate::rcc::{get_freqs, set_freqs, Clocks};
|
||||
use crate::pac::{PWR, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
use crate::time::U32Ext;
|
||||
use core::marker::PhantomData;
|
||||
use embassy::util::Unborrow;
|
||||
use embassy_hal_common::unborrow;
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: u32 = 16_000_000;
|
||||
@ -94,117 +90,72 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// RCC peripheral
|
||||
pub struct Rcc<'d> {
|
||||
_rb: peripherals::RCC,
|
||||
phantom: PhantomData<&'d mut peripherals::RCC>,
|
||||
}
|
||||
|
||||
impl<'d> Rcc<'d> {
|
||||
pub fn new(rcc: impl Unborrow<Target = peripherals::RCC> + 'd) -> Self {
|
||||
unborrow!(rcc);
|
||||
Self {
|
||||
_rb: rcc,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
// Safety: RCC init must have been called
|
||||
pub fn clocks(&self) -> &'static Clocks {
|
||||
unsafe { get_freqs() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait that freezes the `RCC` peripheral with provided clocks configuration
|
||||
pub trait RccExt {
|
||||
fn freeze(self, config: Config) -> Clocks;
|
||||
}
|
||||
|
||||
impl RccExt for RCC {
|
||||
#[inline]
|
||||
fn freeze(self, cfgr: Config) -> Clocks {
|
||||
let rcc = pac::RCC;
|
||||
let (sys_clk, sw) = match cfgr.mux {
|
||||
ClockSrc::HSI16 => {
|
||||
// Enable HSI16
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_hsion(true));
|
||||
while !rcc.cr().read().hsirdy() {}
|
||||
}
|
||||
|
||||
(HSI_FREQ, 0x01)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_hseon(true));
|
||||
while !rcc.cr().read().hserdy() {}
|
||||
}
|
||||
|
||||
(freq.0, 0x02)
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
rcc.cfgr().modify(|w| {
|
||||
w.set_sw(sw.into());
|
||||
w.set_hpre(cfgr.ahb_pre.into());
|
||||
w.set_ppre1(cfgr.apb1_pre.into());
|
||||
w.set_ppre2(cfgr.apb2_pre.into());
|
||||
});
|
||||
}
|
||||
|
||||
let ahb_freq: u32 = match cfgr.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre = 1 << (pre as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let pwr = pac::PWR;
|
||||
if cfgr.low_power_run {
|
||||
assert!(sys_clk.hz() <= 2_000_000.hz());
|
||||
unsafe {
|
||||
pwr.cr1().modify(|w| w.set_lpr(true));
|
||||
}
|
||||
}
|
||||
|
||||
Clocks {
|
||||
sys: sys_clk.hz(),
|
||||
ahb1: ahb_freq.hz(),
|
||||
ahb2: ahb_freq.hz(),
|
||||
apb1: apb1_freq.hz(),
|
||||
apb1_tim: apb1_tim_freq.hz(),
|
||||
apb2: apb2_freq.hz(),
|
||||
apb2_tim: apb2_tim_freq.hz(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
||||
let clocks = r.freeze(config);
|
||||
set_freqs(clocks);
|
||||
let (sys_clk, sw) = match config.mux {
|
||||
ClockSrc::HSI16 => {
|
||||
// Enable HSI16
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
(HSI_FREQ, 0x01)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
(freq.0, 0x02)
|
||||
}
|
||||
};
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(sw.into());
|
||||
w.set_hpre(config.ahb_pre.into());
|
||||
w.set_ppre1(config.apb1_pre.into());
|
||||
w.set_ppre2(config.apb2_pre.into());
|
||||
});
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre = 1 << (pre as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
if config.low_power_run {
|
||||
assert!(sys_clk.hz() <= 2_000_000.hz());
|
||||
PWR.cr1().modify(|w| w.set_lpr(true));
|
||||
}
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: sys_clk.hz(),
|
||||
ahb1: ahb_freq.hz(),
|
||||
ahb2: ahb_freq.hz(),
|
||||
apb1: apb1_freq.hz(),
|
||||
apb1_tim: apb1_tim_freq.hz(),
|
||||
apb2: apb2_freq.hz(),
|
||||
apb2_tim: apb2_tim_freq.hz(),
|
||||
});
|
||||
}
|
||||
|
@ -7,9 +7,9 @@ use stm32_metapac::rcc::vals::{Mco1, Mco2};
|
||||
use crate::gpio::sealed::Pin as __GpioPin;
|
||||
use crate::gpio::Pin;
|
||||
use crate::pac::rcc::vals::Timpre;
|
||||
use crate::pac::{RCC, SYSCFG};
|
||||
use crate::pac::rcc::vals::{Ckpersel, Dppre, Hpre, Hsebyp, Hsidiv, Pllsrc, Sw};
|
||||
use crate::pac::{PWR, RCC, SYSCFG};
|
||||
use crate::peripherals;
|
||||
use crate::pwr::{Power, VoltageScale};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
|
||||
@ -20,6 +20,22 @@ const CSI: Hertz = Hertz(4_000_000);
|
||||
const HSI48: Hertz = Hertz(48_000_000);
|
||||
const LSI: Hertz = Hertz(32_000);
|
||||
|
||||
/// Voltage Scale
|
||||
///
|
||||
/// Represents the voltage range feeding the CPU core. The maximum core
|
||||
/// clock frequency depends on this value.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum VoltageScale {
|
||||
/// VOS 0 range VCORE 1.26V - 1.40V
|
||||
Scale0,
|
||||
/// VOS 1 range VCORE 1.15V - 1.26V
|
||||
Scale1,
|
||||
/// VOS 2 range VCORE 1.05V - 1.15V
|
||||
Scale2,
|
||||
/// VOS 3 range VCORE 0.95V - 1.05V
|
||||
Scale3,
|
||||
}
|
||||
|
||||
/// Core clock frequencies
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CoreClocks {
|
||||
@ -72,439 +88,151 @@ pub struct Config {
|
||||
pub pll3: PllConfig,
|
||||
}
|
||||
|
||||
pub struct Rcc<'d> {
|
||||
inner: PhantomData<&'d ()>,
|
||||
config: Config,
|
||||
/// 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;
|
||||
}
|
||||
|
||||
impl<'d> Rcc<'d> {
|
||||
pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self {
|
||||
Self {
|
||||
inner: PhantomData,
|
||||
config,
|
||||
}
|
||||
}
|
||||
/// 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);
|
||||
|
||||
/// 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.
|
||||
pub fn freeze(mut self, pwr: &Power) -> CoreClocks {
|
||||
use crate::pac::rcc::vals::{Ckpersel, Dppre, Hpre, Hsebyp, Hsidiv, Pllsrc, Sw};
|
||||
|
||||
let srcclk = self.config.hse.unwrap_or(HSI); // Available clocks
|
||||
let (sys_ck, sys_use_pll1_p) = self.sys_ck_setup(srcclk);
|
||||
|
||||
// Configure traceclk from PLL if needed
|
||||
self.traceclk_setup(sys_use_pll1_p);
|
||||
|
||||
// NOTE(unsafe) We have exclusive access to the RCC
|
||||
let (pll1_p_ck, pll1_q_ck, pll1_r_ck) =
|
||||
unsafe { pll::pll_setup(srcclk.0, &self.config.pll1, 0) };
|
||||
let (pll2_p_ck, pll2_q_ck, pll2_r_ck) =
|
||||
unsafe { pll::pll_setup(srcclk.0, &self.config.pll2, 1) };
|
||||
let (pll3_p_ck, pll3_q_ck, pll3_r_ck) =
|
||||
unsafe { pll::pll_setup(srcclk.0, &self.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
|
||||
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)
|
||||
}
|
||||
|
||||
// NOTE(unsafe) We own the regblock
|
||||
unsafe {
|
||||
// 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);
|
||||
/// 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);
|
||||
|
||||
RCC.csr().modify(|w| w.set_lsion(true));
|
||||
while !RCC.csr().read().lsirdy() {}
|
||||
}
|
||||
|
||||
// per_ck from HSI by default
|
||||
let (per_ck, ckpersel) = match (self.config.per_ck == self.config.hse, self.config.per_ck) {
|
||||
(true, Some(hse)) => (hse, Ckpersel::HSE), // HSE
|
||||
(_, Some(CSI)) => (CSI, Ckpersel::CSI), // CSI
|
||||
_ => (HSI, 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 = self
|
||||
.config
|
||||
.rcc_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 = self
|
||||
.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) =
|
||||
Self::ppre_calculate(requested_pclk1, rcc_hclk, pclk_max, Some(timpre));
|
||||
|
||||
let requested_pclk2 = self
|
||||
.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) =
|
||||
Self::ppre_calculate(requested_pclk2, rcc_hclk, pclk_max, Some(timpre));
|
||||
|
||||
let requested_pclk3 = self
|
||||
.config
|
||||
.pclk3
|
||||
.map(|v| v.0)
|
||||
.unwrap_or_else(|| pclk_max.min(rcc_hclk / 2));
|
||||
let (rcc_pclk3, ppre3_bits, ppre3, _) =
|
||||
Self::ppre_calculate(requested_pclk3, rcc_hclk, pclk_max, None);
|
||||
|
||||
let requested_pclk4 = self
|
||||
.config
|
||||
.pclk4
|
||||
.map(|v| v.0)
|
||||
.unwrap_or_else(|| pclk_max.min(rcc_hclk / 2));
|
||||
let (rcc_pclk4, ppre4_bits, ppre4, _) =
|
||||
Self::ppre_calculate(requested_pclk4, rcc_hclk, pclk_max, None);
|
||||
|
||||
Self::flash_setup(rcc_aclk, pwr.vos);
|
||||
|
||||
// Start switching clocks -------------------
|
||||
// NOTE(unsafe) We have the RCC singleton
|
||||
unsafe {
|
||||
// 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 self.config.hse {
|
||||
Some(hse) => {
|
||||
// Ensure HSE is on and stable
|
||||
RCC.cr().modify(|w| {
|
||||
w.set_hseon(true);
|
||||
w.set_hsebyp(if self.config.bypass_hse {
|
||||
Hsebyp::BYPASSED
|
||||
} else {
|
||||
Hsebyp::NOTBYPASSED
|
||||
});
|
||||
});
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
Some(hse)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let pllsrc = if self.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(d1cpre_bits));
|
||||
w.set_d1ppre(Dppre(ppre3_bits));
|
||||
w.set_hpre(hpre_bits)
|
||||
});
|
||||
// Ensure core prescaler value is valid before future lower
|
||||
// core voltage
|
||||
while RCC.d1cfgr().read().d1cpre().0 != d1cpre_bits {}
|
||||
|
||||
// APB1 / APB2 Prescaler
|
||||
RCC.d2cfgr().modify(|w| {
|
||||
w.set_d2ppre1(Dppre(ppre1_bits));
|
||||
w.set_d2ppre2(Dppre(ppre2_bits));
|
||||
});
|
||||
|
||||
// APB4 Prescaler
|
||||
RCC.d3cfgr().modify(|w| w.set_d3ppre(Dppre(ppre4_bits)));
|
||||
|
||||
// Peripheral Clock (per_ck)
|
||||
RCC.d1ccipr().modify(|w| w.set_ckpersel(ckpersel));
|
||||
|
||||
// Set timer clocks prescaler setting
|
||||
RCC.cfgr().modify(|w| w.set_timpre(timpre));
|
||||
|
||||
// Select system clock source
|
||||
let sw = match (sys_use_pll1_p, self.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() != sw.0 {}
|
||||
|
||||
// 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() {}
|
||||
|
||||
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),
|
||||
hsi_ck: Some(HSI),
|
||||
hsi48_ck: Some(HSI48),
|
||||
lsi_ck: Some(LSI),
|
||||
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),
|
||||
sys_ck,
|
||||
c_ck: Hertz(sys_d1cpre_ck),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Setup traceclk
|
||||
/// Returns a pll1_r_ck
|
||||
fn traceclk_setup(&mut self, sys_use_pll1_p: bool) {
|
||||
let pll1_r_ck = match (sys_use_pll1_p, self.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!(self.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.
|
||||
_ => self.config.pll1.r_ck,
|
||||
};
|
||||
self.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(&mut self, srcclk: Hertz) -> (Hertz, bool) {
|
||||
// Compare available with wanted clocks
|
||||
let sys_ck = self.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 self.config.pll1.p_ck {
|
||||
Some(p_ck) => {
|
||||
assert!(p_ck == sys_ck,
|
||||
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),
|
||||
};
|
||||
self.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
|
||||
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),
|
||||
},
|
||||
Some(p_ck)
|
||||
}
|
||||
None => Some(sys_ck),
|
||||
};
|
||||
config.pll1.p_ck = pll1_p_ck;
|
||||
|
||||
// NOTE(unsafe) Atomic write
|
||||
unsafe {
|
||||
FLASH.acr().write(|w| {
|
||||
w.set_wrhighfreq(progr_delay);
|
||||
w.set_latency(wait_states)
|
||||
});
|
||||
while FLASH.acr().read().latency() != wait_states {}
|
||||
}
|
||||
(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
|
||||
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),
|
||||
},
|
||||
};
|
||||
|
||||
// NOTE(unsafe) Atomic write
|
||||
unsafe {
|
||||
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,
|
||||
@ -681,10 +409,309 @@ impl<'d, T: McoInstance> Mco<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let mut power = Power::new(<peripherals::PWR as embassy::util::Steal>::steal(), false);
|
||||
let rcc = Rcc::new(<peripherals::RCC as embassy::util::Steal>::steal(), config);
|
||||
let core_clocks = rcc.freeze(&mut power);
|
||||
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); // 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);
|
||||
|
||||
// NOTE(unsafe) We have exclusive access to the RCC
|
||||
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)) => (CSI, Ckpersel::CSI), // CSI
|
||||
_ => (HSI, 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.rcc_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);
|
||||
|
||||
flash_setup(rcc_aclk, pwr_vos);
|
||||
|
||||
// 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(if config.bypass_hse {
|
||||
Hsebyp::BYPASSED
|
||||
} else {
|
||||
Hsebyp::NOTBYPASSED
|
||||
});
|
||||
});
|
||||
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(d1cpre_bits));
|
||||
w.set_d1ppre(Dppre(ppre3_bits));
|
||||
w.set_hpre(hpre_bits)
|
||||
});
|
||||
// Ensure core prescaler value is valid before future lower
|
||||
// core voltage
|
||||
while RCC.d1cfgr().read().d1cpre().0 != d1cpre_bits {}
|
||||
|
||||
// APB1 / APB2 Prescaler
|
||||
RCC.d2cfgr().modify(|w| {
|
||||
w.set_d2ppre1(Dppre(ppre1_bits));
|
||||
w.set_d2ppre2(Dppre(ppre2_bits));
|
||||
});
|
||||
|
||||
// APB4 Prescaler
|
||||
RCC.d3cfgr().modify(|w| w.set_d3ppre(Dppre(ppre4_bits)));
|
||||
|
||||
// Peripheral Clock (per_ck)
|
||||
RCC.d1ccipr().modify(|w| w.set_ckpersel(ckpersel));
|
||||
|
||||
// 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() != sw.0 {}
|
||||
|
||||
// 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),
|
||||
hsi_ck: Some(HSI),
|
||||
hsi48_ck: Some(HSI48),
|
||||
lsi_ck: Some(LSI),
|
||||
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),
|
||||
sys_ck,
|
||||
c_ck: Hertz(sys_d1cpre_ck),
|
||||
};
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: core_clocks.c_ck,
|
||||
ahb1: core_clocks.hclk,
|
||||
|
@ -1,18 +1,11 @@
|
||||
use crate::pac;
|
||||
use crate::peripherals::{self, CRS, RCC, SYSCFG};
|
||||
use crate::rcc::{get_freqs, set_freqs, Clocks};
|
||||
use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw};
|
||||
use crate::pac::{CRS, RCC, SYSCFG};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
use crate::time::U32Ext;
|
||||
use core::marker::PhantomData;
|
||||
use embassy::util::Unborrow;
|
||||
use embassy_hal_common::unborrow;
|
||||
use pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw};
|
||||
|
||||
/// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC,
|
||||
/// and with the addition of the init function to configure a system clock.
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: u32 = 16_000_000;
|
||||
/// HSI16 speed
|
||||
pub const HSI16_FREQ: u32 = 16_000_000;
|
||||
|
||||
/// System clock mux source
|
||||
#[derive(Clone, Copy)]
|
||||
@ -104,9 +97,9 @@ pub enum PLLSource {
|
||||
HSE(Hertz),
|
||||
}
|
||||
|
||||
impl Into<Pllmul> for PLLMul {
|
||||
fn into(self) -> Pllmul {
|
||||
match self {
|
||||
impl From<PLLMul> for Pllmul {
|
||||
fn from(val: PLLMul) -> Pllmul {
|
||||
match val {
|
||||
PLLMul::Mul3 => Pllmul::MUL3,
|
||||
PLLMul::Mul4 => Pllmul::MUL4,
|
||||
PLLMul::Mul6 => Pllmul::MUL6,
|
||||
@ -120,9 +113,9 @@ impl Into<Pllmul> for PLLMul {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Plldiv> for PLLDiv {
|
||||
fn into(self) -> Plldiv {
|
||||
match self {
|
||||
impl From<PLLDiv> for Plldiv {
|
||||
fn from(val: PLLDiv) -> Plldiv {
|
||||
match val {
|
||||
PLLDiv::Div2 => Plldiv::DIV2,
|
||||
PLLDiv::Div3 => Plldiv::DIV3,
|
||||
PLLDiv::Div4 => Plldiv::DIV4,
|
||||
@ -130,18 +123,18 @@ impl Into<Plldiv> for PLLDiv {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Pllsrc> for PLLSource {
|
||||
fn into(self) -> Pllsrc {
|
||||
match self {
|
||||
impl From<PLLSource> for Pllsrc {
|
||||
fn from(val: PLLSource) -> Pllsrc {
|
||||
match val {
|
||||
PLLSource::HSI16 => Pllsrc::HSI16,
|
||||
PLLSource::HSE(_) => Pllsrc::HSE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Ppre> for APBPrescaler {
|
||||
fn into(self) -> Ppre {
|
||||
match self {
|
||||
impl From<APBPrescaler> for Ppre {
|
||||
fn from(val: APBPrescaler) -> Ppre {
|
||||
match val {
|
||||
APBPrescaler::NotDivided => Ppre::DIV1,
|
||||
APBPrescaler::Div2 => Ppre::DIV2,
|
||||
APBPrescaler::Div4 => Ppre::DIV4,
|
||||
@ -151,9 +144,9 @@ impl Into<Ppre> for APBPrescaler {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Hpre> for AHBPrescaler {
|
||||
fn into(self) -> Hpre {
|
||||
match self {
|
||||
impl From<AHBPrescaler> for Hpre {
|
||||
fn from(val: AHBPrescaler) -> Hpre {
|
||||
match val {
|
||||
AHBPrescaler::NotDivided => Hpre::DIV1,
|
||||
AHBPrescaler::Div2 => Hpre::DIV2,
|
||||
AHBPrescaler::Div4 => Hpre::DIV4,
|
||||
@ -167,9 +160,9 @@ impl Into<Hpre> for AHBPrescaler {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Msirange> for MSIRange {
|
||||
fn into(self) -> Msirange {
|
||||
match self {
|
||||
impl From<MSIRange> for Msirange {
|
||||
fn from(val: MSIRange) -> Msirange {
|
||||
match val {
|
||||
MSIRange::Range0 => Msirange::RANGE0,
|
||||
MSIRange::Range1 => Msirange::RANGE1,
|
||||
MSIRange::Range2 => Msirange::RANGE2,
|
||||
@ -187,6 +180,7 @@ pub struct Config {
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb1_pre: APBPrescaler,
|
||||
pub apb2_pre: APBPrescaler,
|
||||
pub enable_hsi48: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -197,241 +191,172 @@ impl Default for Config {
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb1_pre: APBPrescaler::NotDivided,
|
||||
apb2_pre: APBPrescaler::NotDivided,
|
||||
enable_hsi48: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// RCC peripheral
|
||||
pub struct Rcc<'d> {
|
||||
_rb: peripherals::RCC,
|
||||
phantom: PhantomData<&'d mut peripherals::RCC>,
|
||||
}
|
||||
|
||||
impl<'d> Rcc<'d> {
|
||||
pub fn new(rcc: impl Unborrow<Target = peripherals::RCC> + 'd) -> Self {
|
||||
unborrow!(rcc);
|
||||
Self {
|
||||
_rb: rcc,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
// Safety: RCC init must have been called
|
||||
pub fn clocks(&self) -> &'static Clocks {
|
||||
unsafe { get_freqs() }
|
||||
}
|
||||
|
||||
pub fn enable_hsi48(&mut self, _syscfg: &mut SYSCFG, _crs: CRS) -> HSI48 {
|
||||
let rcc = pac::RCC;
|
||||
unsafe {
|
||||
// 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));
|
||||
|
||||
// Enable CRS peripheral
|
||||
rcc.apb1enr().modify(|w| w.set_crsen(true));
|
||||
|
||||
// Initialize CRS
|
||||
let crs = pac::CRS;
|
||||
crs.cfgr().write(|w|
|
||||
|
||||
// Select LSE as synchronization source
|
||||
w.set_syncsrc(0b01));
|
||||
crs.cr().modify(|w| {
|
||||
w.set_autotrimen(true);
|
||||
w.set_cen(true);
|
||||
});
|
||||
|
||||
// Enable VREFINT reference for HSI48 oscillator
|
||||
let syscfg = pac::SYSCFG;
|
||||
syscfg.cfgr3().modify(|w| {
|
||||
w.set_enref_hsi48(true);
|
||||
w.set_en_vrefint(true);
|
||||
});
|
||||
|
||||
// Select HSI48 as USB clock
|
||||
rcc.ccipr().modify(|w| w.set_hsi48msel(true));
|
||||
|
||||
// Enable dedicated USB clock
|
||||
rcc.crrcr().modify(|w| w.set_hsi48on(true));
|
||||
while !rcc.crrcr().read().hsi48rdy() {}
|
||||
}
|
||||
|
||||
HSI48(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait that freezes the `RCC` peripheral with provided clocks configuration
|
||||
pub trait RccExt {
|
||||
fn freeze(self, config: Config) -> Clocks;
|
||||
}
|
||||
|
||||
impl RccExt for RCC {
|
||||
// `cfgr` is almost always a constant, so make sure it can be constant-propagated properly by
|
||||
// marking this function and all `Config` constructors and setters as `#[inline]`.
|
||||
// This saves ~900 Bytes for the `pwr.rs` example.
|
||||
#[inline]
|
||||
fn freeze(self, cfgr: Config) -> Clocks {
|
||||
let rcc = pac::RCC;
|
||||
let (sys_clk, sw) = match cfgr.mux {
|
||||
ClockSrc::MSI(range) => {
|
||||
// Set MSI range
|
||||
unsafe {
|
||||
rcc.icscr().write(|w| w.set_msirange(range.into()));
|
||||
}
|
||||
|
||||
// Enable MSI
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_msion(true));
|
||||
while !rcc.cr().read().msirdy() {}
|
||||
}
|
||||
|
||||
let freq = 32_768 * (1 << (range as u8 + 1));
|
||||
(freq, Sw::MSI)
|
||||
}
|
||||
ClockSrc::HSI16 => {
|
||||
// Enable HSI16
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_hsi16on(true));
|
||||
while !rcc.cr().read().hsi16rdyf() {}
|
||||
}
|
||||
|
||||
(HSI_FREQ, Sw::HSI16)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_hseon(true));
|
||||
while !rcc.cr().read().hserdy() {}
|
||||
}
|
||||
|
||||
(freq.0, Sw::HSE)
|
||||
}
|
||||
ClockSrc::PLL(src, mul, div) => {
|
||||
let freq = match src {
|
||||
PLLSource::HSE(freq) => {
|
||||
// Enable HSE
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_hseon(true));
|
||||
while !rcc.cr().read().hserdy() {}
|
||||
}
|
||||
freq.0
|
||||
}
|
||||
PLLSource::HSI16 => {
|
||||
// Enable HSI
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_hsi16on(true));
|
||||
while !rcc.cr().read().hsi16rdyf() {}
|
||||
}
|
||||
HSI_FREQ
|
||||
}
|
||||
};
|
||||
|
||||
// Disable PLL
|
||||
unsafe {
|
||||
rcc.cr().modify(|w| w.set_pllon(false));
|
||||
while rcc.cr().read().pllrdy() {}
|
||||
}
|
||||
|
||||
let freq = match mul {
|
||||
PLLMul::Mul3 => freq * 3,
|
||||
PLLMul::Mul4 => freq * 4,
|
||||
PLLMul::Mul6 => freq * 6,
|
||||
PLLMul::Mul8 => freq * 8,
|
||||
PLLMul::Mul12 => freq * 12,
|
||||
PLLMul::Mul16 => freq * 16,
|
||||
PLLMul::Mul24 => freq * 24,
|
||||
PLLMul::Mul32 => freq * 32,
|
||||
PLLMul::Mul48 => freq * 48,
|
||||
};
|
||||
|
||||
let freq = match div {
|
||||
PLLDiv::Div2 => freq / 2,
|
||||
PLLDiv::Div3 => freq / 3,
|
||||
PLLDiv::Div4 => freq / 4,
|
||||
};
|
||||
assert!(freq <= 32_u32.mhz().0);
|
||||
|
||||
unsafe {
|
||||
rcc.cfgr().write(move |w| {
|
||||
w.set_pllmul(mul.into());
|
||||
w.set_plldiv(div.into());
|
||||
w.set_pllsrc(src.into());
|
||||
});
|
||||
|
||||
// Enable PLL
|
||||
rcc.cr().modify(|w| w.set_pllon(true));
|
||||
while !rcc.cr().read().pllrdy() {}
|
||||
}
|
||||
|
||||
(freq, Sw::PLL)
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
rcc.cfgr().modify(|w| {
|
||||
w.set_sw(sw.into());
|
||||
w.set_hpre(cfgr.ahb_pre.into());
|
||||
w.set_ppre1(cfgr.apb1_pre.into());
|
||||
w.set_ppre2(cfgr.apb2_pre.into());
|
||||
});
|
||||
}
|
||||
|
||||
let ahb_freq: u32 = match cfgr.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: Hpre = pre.into();
|
||||
let pre = 1 << (pre.0 as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.0 - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.0 - 3);
|
||||
let freq = ahb_freq / (1 << (pre as u8 - 3));
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
Clocks {
|
||||
sys: sys_clk.hz(),
|
||||
ahb: ahb_freq.hz(),
|
||||
apb1: apb1_freq.hz(),
|
||||
apb2: apb2_freq.hz(),
|
||||
apb1_tim: apb1_tim_freq.hz(),
|
||||
apb2_tim: apb2_tim_freq.hz(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Token that exists only, if the HSI48 clock has been enabled
|
||||
///
|
||||
/// You can get an instance of this struct by calling [`Rcc::enable_hsi48`].
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct HSI48(());
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
||||
let clocks = r.freeze(config);
|
||||
set_freqs(clocks);
|
||||
let (sys_clk, sw) = match config.mux {
|
||||
ClockSrc::MSI(range) => {
|
||||
// Set MSI range
|
||||
RCC.icscr().write(|w| w.set_msirange(range.into()));
|
||||
|
||||
// Enable MSI
|
||||
RCC.cr().write(|w| w.set_msion(true));
|
||||
while !RCC.cr().read().msirdy() {}
|
||||
|
||||
let freq = 32_768 * (1 << (range as u8 + 1));
|
||||
(freq, Sw::MSI)
|
||||
}
|
||||
ClockSrc::HSI16 => {
|
||||
// Enable HSI16
|
||||
RCC.cr().write(|w| w.set_hsi16on(true));
|
||||
while !RCC.cr().read().hsi16rdyf() {}
|
||||
|
||||
(HSI16_FREQ, Sw::HSI16)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
(freq.0, Sw::HSE)
|
||||
}
|
||||
ClockSrc::PLL(src, mul, div) => {
|
||||
let freq = match src {
|
||||
PLLSource::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
freq.0
|
||||
}
|
||||
PLLSource::HSI16 => {
|
||||
// Enable HSI
|
||||
RCC.cr().write(|w| w.set_hsi16on(true));
|
||||
while !RCC.cr().read().hsi16rdyf() {}
|
||||
HSI16_FREQ
|
||||
}
|
||||
};
|
||||
|
||||
// Disable PLL
|
||||
RCC.cr().modify(|w| w.set_pllon(false));
|
||||
while RCC.cr().read().pllrdy() {}
|
||||
|
||||
let freq = match mul {
|
||||
PLLMul::Mul3 => freq * 3,
|
||||
PLLMul::Mul4 => freq * 4,
|
||||
PLLMul::Mul6 => freq * 6,
|
||||
PLLMul::Mul8 => freq * 8,
|
||||
PLLMul::Mul12 => freq * 12,
|
||||
PLLMul::Mul16 => freq * 16,
|
||||
PLLMul::Mul24 => freq * 24,
|
||||
PLLMul::Mul32 => freq * 32,
|
||||
PLLMul::Mul48 => freq * 48,
|
||||
};
|
||||
|
||||
let freq = match div {
|
||||
PLLDiv::Div2 => freq / 2,
|
||||
PLLDiv::Div3 => freq / 3,
|
||||
PLLDiv::Div4 => freq / 4,
|
||||
};
|
||||
assert!(freq <= 32_u32.mhz().0);
|
||||
|
||||
RCC.cfgr().write(move |w| {
|
||||
w.set_pllmul(mul.into());
|
||||
w.set_plldiv(div.into());
|
||||
w.set_pllsrc(src.into());
|
||||
});
|
||||
|
||||
// Enable PLL
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
|
||||
(freq, Sw::PLL)
|
||||
}
|
||||
};
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(sw);
|
||||
w.set_hpre(config.ahb_pre.into());
|
||||
w.set_ppre1(config.apb1_pre.into());
|
||||
w.set_ppre2(config.apb2_pre.into());
|
||||
});
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: Hpre = pre.into();
|
||||
let pre = 1 << (pre.0 as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.0 - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.0 - 3);
|
||||
let freq = ahb_freq / (1 << (pre as u8 - 3));
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
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));
|
||||
|
||||
// Enable CRS peripheral
|
||||
RCC.apb1enr().modify(|w| w.set_crsen(true));
|
||||
|
||||
// Initialize CRS
|
||||
CRS.cfgr().write(|w|
|
||||
|
||||
// Select LSE as synchronization source
|
||||
w.set_syncsrc(0b01));
|
||||
CRS.cr().modify(|w| {
|
||||
w.set_autotrimen(true);
|
||||
w.set_cen(true);
|
||||
});
|
||||
|
||||
// Enable VREFINT reference for HSI48 oscillator
|
||||
SYSCFG.cfgr3().modify(|w| {
|
||||
w.set_enref_hsi48(true);
|
||||
w.set_en_vrefint(true);
|
||||
});
|
||||
|
||||
// Select HSI48 as USB clock
|
||||
RCC.ccipr().modify(|w| w.set_hsi48msel(true));
|
||||
|
||||
// Enable dedicated USB clock
|
||||
RCC.crrcr().modify(|w| w.set_hsi48on(true));
|
||||
while !RCC.crrcr().read().hsi48rdy() {}
|
||||
}
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: sys_clk.hz(),
|
||||
ahb: ahb_freq.hz(),
|
||||
apb1: apb1_freq.hz(),
|
||||
apb2: apb2_freq.hz(),
|
||||
apb1_tim: apb1_tim_freq.hz(),
|
||||
apb2_tim: apb2_tim_freq.hz(),
|
||||
});
|
||||
}
|
||||
|
@ -1,13 +1,8 @@
|
||||
use crate::pac;
|
||||
use crate::peripherals::{self, RCC};
|
||||
use crate::rcc::{get_freqs, set_freqs, Clocks};
|
||||
use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw};
|
||||
use crate::pac::RCC;
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
use crate::time::U32Ext;
|
||||
use core::marker::PhantomData;
|
||||
use embassy::util::Unborrow;
|
||||
use embassy_hal_common::unborrow;
|
||||
|
||||
/// Most of clock setup is copied from rcc/l0
|
||||
|
||||
/// HSI speed
|
||||
pub const HSI_FREQ: u32 = 16_000_000;
|
||||
@ -16,6 +11,7 @@ pub const HSI_FREQ: u32 = 16_000_000;
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ClockSrc {
|
||||
MSI(MSIRange),
|
||||
PLL(PLLSource, PLLMul, PLLDiv),
|
||||
HSE(Hertz),
|
||||
HSI,
|
||||
}
|
||||
@ -48,6 +44,28 @@ impl Default for MSIRange {
|
||||
}
|
||||
}
|
||||
|
||||
/// PLL divider
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PLLDiv {
|
||||
Div2,
|
||||
Div3,
|
||||
Div4,
|
||||
}
|
||||
|
||||
/// PLL multiplier
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PLLMul {
|
||||
Mul3,
|
||||
Mul4,
|
||||
Mul6,
|
||||
Mul8,
|
||||
Mul12,
|
||||
Mul16,
|
||||
Mul24,
|
||||
Mul32,
|
||||
Mul48,
|
||||
}
|
||||
|
||||
/// AHB prescaler
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum AHBPrescaler {
|
||||
@ -72,46 +90,86 @@ pub enum APBPrescaler {
|
||||
Div16,
|
||||
}
|
||||
|
||||
type Ppre = u8;
|
||||
impl Into<Ppre> for APBPrescaler {
|
||||
fn into(self) -> Ppre {
|
||||
match self {
|
||||
APBPrescaler::NotDivided => 0b000,
|
||||
APBPrescaler::Div2 => 0b100,
|
||||
APBPrescaler::Div4 => 0b101,
|
||||
APBPrescaler::Div8 => 0b110,
|
||||
APBPrescaler::Div16 => 0b111,
|
||||
/// PLL clock input source
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PLLSource {
|
||||
HSI,
|
||||
HSE(Hertz),
|
||||
}
|
||||
|
||||
impl From<PLLMul> for Pllmul {
|
||||
fn from(val: PLLMul) -> Pllmul {
|
||||
match val {
|
||||
PLLMul::Mul3 => Pllmul::MUL3,
|
||||
PLLMul::Mul4 => Pllmul::MUL4,
|
||||
PLLMul::Mul6 => Pllmul::MUL6,
|
||||
PLLMul::Mul8 => Pllmul::MUL8,
|
||||
PLLMul::Mul12 => Pllmul::MUL12,
|
||||
PLLMul::Mul16 => Pllmul::MUL16,
|
||||
PLLMul::Mul24 => Pllmul::MUL24,
|
||||
PLLMul::Mul32 => Pllmul::MUL32,
|
||||
PLLMul::Mul48 => Pllmul::MUL48,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Hpre = u8;
|
||||
impl Into<Hpre> for AHBPrescaler {
|
||||
fn into(self) -> Hpre {
|
||||
match self {
|
||||
AHBPrescaler::NotDivided => 0b0000,
|
||||
AHBPrescaler::Div2 => 0b1000,
|
||||
AHBPrescaler::Div4 => 0b1001,
|
||||
AHBPrescaler::Div8 => 0b1010,
|
||||
AHBPrescaler::Div16 => 0b1011,
|
||||
AHBPrescaler::Div64 => 0b1100,
|
||||
AHBPrescaler::Div128 => 0b1101,
|
||||
AHBPrescaler::Div256 => 0b1110,
|
||||
AHBPrescaler::Div512 => 0b1111,
|
||||
impl From<PLLDiv> for Plldiv {
|
||||
fn from(val: PLLDiv) -> Plldiv {
|
||||
match val {
|
||||
PLLDiv::Div2 => Plldiv::DIV2,
|
||||
PLLDiv::Div3 => Plldiv::DIV3,
|
||||
PLLDiv::Div4 => Plldiv::DIV4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for MSIRange {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
MSIRange::Range0 => 0b000,
|
||||
MSIRange::Range1 => 0b001,
|
||||
MSIRange::Range2 => 0b010,
|
||||
MSIRange::Range3 => 0b011,
|
||||
MSIRange::Range4 => 0b100,
|
||||
MSIRange::Range5 => 0b101,
|
||||
MSIRange::Range6 => 0b110,
|
||||
impl From<PLLSource> for Pllsrc {
|
||||
fn from(val: PLLSource) -> Pllsrc {
|
||||
match val {
|
||||
PLLSource::HSI => Pllsrc::HSI,
|
||||
PLLSource::HSE(_) => Pllsrc::HSE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<APBPrescaler> for Ppre {
|
||||
fn from(val: APBPrescaler) -> Ppre {
|
||||
match val {
|
||||
APBPrescaler::NotDivided => Ppre::DIV1,
|
||||
APBPrescaler::Div2 => Ppre::DIV2,
|
||||
APBPrescaler::Div4 => Ppre::DIV4,
|
||||
APBPrescaler::Div8 => Ppre::DIV8,
|
||||
APBPrescaler::Div16 => Ppre::DIV16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AHBPrescaler> for Hpre {
|
||||
fn from(val: AHBPrescaler) -> Hpre {
|
||||
match val {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MSIRange> for Msirange {
|
||||
fn from(val: MSIRange) -> Msirange {
|
||||
match val {
|
||||
MSIRange::Range0 => Msirange::RANGE0,
|
||||
MSIRange::Range1 => Msirange::RANGE1,
|
||||
MSIRange::Range2 => Msirange::RANGE2,
|
||||
MSIRange::Range3 => Msirange::RANGE3,
|
||||
MSIRange::Range4 => Msirange::RANGE4,
|
||||
MSIRange::Range5 => Msirange::RANGE5,
|
||||
MSIRange::Range6 => Msirange::RANGE6,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -136,126 +194,128 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// RCC peripheral
|
||||
pub struct Rcc<'d> {
|
||||
_rb: peripherals::RCC,
|
||||
phantom: PhantomData<&'d mut peripherals::RCC>,
|
||||
}
|
||||
|
||||
impl<'d> Rcc<'d> {
|
||||
pub fn new(rcc: impl Unborrow<Target = peripherals::RCC> + 'd) -> Self {
|
||||
unborrow!(rcc);
|
||||
Self {
|
||||
_rb: rcc,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
// Safety: RCC init must have been called
|
||||
pub fn clocks(&self) -> &'static Clocks {
|
||||
unsafe { get_freqs() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait that freezes the `RCC` peripheral with provided clocks configuration
|
||||
pub trait RccExt {
|
||||
fn freeze(self, config: Config) -> Clocks;
|
||||
}
|
||||
|
||||
impl RccExt for RCC {
|
||||
// `cfgr` is almost always a constant, so make sure it can be constant-propagated properly by
|
||||
// marking this function and all `Config` constructors and setters as `#[inline]`.
|
||||
// This saves ~900 Bytes for the `pwr.rs` example.
|
||||
#[inline]
|
||||
fn freeze(self, cfgr: Config) -> Clocks {
|
||||
let rcc = pac::RCC;
|
||||
let (sys_clk, sw) = match cfgr.mux {
|
||||
ClockSrc::MSI(range) => {
|
||||
// Set MSI range
|
||||
unsafe {
|
||||
rcc.icscr().write(|w| w.set_msirange(range.into()));
|
||||
}
|
||||
|
||||
// Enable MSI
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_msion(true));
|
||||
while !rcc.cr().read().msirdy() {}
|
||||
}
|
||||
|
||||
let freq = 32_768 * (1 << (range as u8 + 1));
|
||||
(freq, 0b00)
|
||||
}
|
||||
ClockSrc::HSI => {
|
||||
// Enable HSI
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_hsion(true));
|
||||
while !rcc.cr().read().hsirdy() {}
|
||||
}
|
||||
|
||||
(HSI_FREQ, 0b01)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_hseon(true));
|
||||
while !rcc.cr().read().hserdy() {}
|
||||
}
|
||||
|
||||
(freq.0, 0b10)
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
rcc.cfgr().modify(|w| {
|
||||
w.set_sw(sw.into());
|
||||
w.set_hpre(cfgr.ahb_pre.into());
|
||||
w.set_ppre1(cfgr.apb1_pre.into());
|
||||
w.set_ppre2(cfgr.apb2_pre.into());
|
||||
});
|
||||
}
|
||||
|
||||
let ahb_freq: u32 = match cfgr.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: Hpre = pre.into();
|
||||
let pre = 1 << (pre as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / (1 << (pre as u8 - 3));
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
Clocks {
|
||||
sys: sys_clk.hz(),
|
||||
ahb: ahb_freq.hz(),
|
||||
apb1: apb1_freq.hz(),
|
||||
apb2: apb2_freq.hz(),
|
||||
apb1_tim: apb1_tim_freq.hz(),
|
||||
apb2_tim: apb2_tim_freq.hz(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
||||
let clocks = r.freeze(config);
|
||||
set_freqs(clocks);
|
||||
let (sys_clk, sw) = match config.mux {
|
||||
ClockSrc::MSI(range) => {
|
||||
// Set MSI range
|
||||
RCC.icscr().write(|w| w.set_msirange(range.into()));
|
||||
|
||||
// Enable MSI
|
||||
RCC.cr().write(|w| w.set_msion(true));
|
||||
while !RCC.cr().read().msirdy() {}
|
||||
|
||||
let freq = 32_768 * (1 << (range as u8 + 1));
|
||||
(freq, Sw::MSI)
|
||||
}
|
||||
ClockSrc::HSI => {
|
||||
// Enable HSI
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
(HSI_FREQ, Sw::HSI)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
(freq.0, Sw::HSE)
|
||||
}
|
||||
ClockSrc::PLL(src, mul, div) => {
|
||||
let freq = match src {
|
||||
PLLSource::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
freq.0
|
||||
}
|
||||
PLLSource::HSI => {
|
||||
// Enable HSI
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
HSI_FREQ
|
||||
}
|
||||
};
|
||||
|
||||
// Disable PLL
|
||||
RCC.cr().modify(|w| w.set_pllon(false));
|
||||
while RCC.cr().read().pllrdy() {}
|
||||
|
||||
let freq = match mul {
|
||||
PLLMul::Mul3 => freq * 3,
|
||||
PLLMul::Mul4 => freq * 4,
|
||||
PLLMul::Mul6 => freq * 6,
|
||||
PLLMul::Mul8 => freq * 8,
|
||||
PLLMul::Mul12 => freq * 12,
|
||||
PLLMul::Mul16 => freq * 16,
|
||||
PLLMul::Mul24 => freq * 24,
|
||||
PLLMul::Mul32 => freq * 32,
|
||||
PLLMul::Mul48 => freq * 48,
|
||||
};
|
||||
|
||||
let freq = match div {
|
||||
PLLDiv::Div2 => freq / 2,
|
||||
PLLDiv::Div3 => freq / 3,
|
||||
PLLDiv::Div4 => freq / 4,
|
||||
};
|
||||
assert!(freq <= 32_u32.mhz().0);
|
||||
|
||||
RCC.cfgr().write(move |w| {
|
||||
w.set_pllmul(mul.into());
|
||||
w.set_plldiv(div.into());
|
||||
w.set_pllsrc(src.into());
|
||||
});
|
||||
|
||||
// Enable PLL
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
|
||||
(freq, Sw::PLL)
|
||||
}
|
||||
};
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(sw);
|
||||
w.set_hpre(config.ahb_pre.into());
|
||||
w.set_ppre1(config.apb1_pre.into());
|
||||
w.set_ppre2(config.apb2_pre.into());
|
||||
});
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: Hpre = pre.into();
|
||||
let pre = 1 << (pre.0 as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.0 - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.0 - 3);
|
||||
let freq = ahb_freq / (1 << (pre as u8 - 3));
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: sys_clk.hz(),
|
||||
ahb: ahb_freq.hz(),
|
||||
apb1: apb1_freq.hz(),
|
||||
apb2: apb2_freq.hz(),
|
||||
apb1_tim: apb1_tim_freq.hz(),
|
||||
apb2_tim: apb2_tim_freq.hz(),
|
||||
});
|
||||
}
|
||||
|
@ -1,17 +1,8 @@
|
||||
use crate::pac;
|
||||
use crate::peripherals::{self, RCC};
|
||||
use crate::rcc::{get_freqs, set_freqs, Clocks};
|
||||
use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
use crate::time::U32Ext;
|
||||
use core::marker::PhantomData;
|
||||
use embassy::util::Unborrow;
|
||||
use embassy_hal_common::unborrow;
|
||||
use stm32_metapac::rcc::vals::Msirange;
|
||||
|
||||
/// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC,
|
||||
/// and with the addition of the init function to configure a system clock.
|
||||
|
||||
/// Only the basic setup using the HSE and HSI clocks are supported as of now.
|
||||
|
||||
/// HSI16 speed
|
||||
pub const HSI16_FREQ: u32 = 16_000_000;
|
||||
@ -19,8 +10,8 @@ pub const HSI16_FREQ: u32 = 16_000_000;
|
||||
/// System clock mux source
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ClockSrc {
|
||||
PLL(PLLSource, PLLClkDiv, PLLSrcDiv, PLLMul, Option<PLL48Div>),
|
||||
MSI(MSIRange),
|
||||
PLL(PLLSource, PLLClkDiv, PLLSrcDiv, PLLMul, Option<PLL48Div>),
|
||||
HSE(Hertz),
|
||||
HSI16,
|
||||
}
|
||||
@ -57,25 +48,6 @@ pub enum MSIRange {
|
||||
Range11,
|
||||
}
|
||||
|
||||
impl Into<u32> for MSIRange {
|
||||
fn into(self) -> u32 {
|
||||
match self {
|
||||
MSIRange::Range0 => 100_000,
|
||||
MSIRange::Range1 => 200_000,
|
||||
MSIRange::Range2 => 400_000,
|
||||
MSIRange::Range3 => 800_000,
|
||||
MSIRange::Range4 => 1_000_000,
|
||||
MSIRange::Range5 => 2_000_000,
|
||||
MSIRange::Range6 => 4_000_000,
|
||||
MSIRange::Range7 => 8_000_000,
|
||||
MSIRange::Range8 => 16_000_000,
|
||||
MSIRange::Range9 => 24_000_000,
|
||||
MSIRange::Range10 => 32_000_000,
|
||||
MSIRange::Range11 => 48_000_000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MSIRange {
|
||||
fn default() -> MSIRange {
|
||||
MSIRange::Range6
|
||||
@ -131,9 +103,9 @@ seq_macro::seq!(N in 8..=86 {
|
||||
)*
|
||||
}
|
||||
|
||||
impl Into<u8> for PLLMul {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
impl From<PLLMul> for u8 {
|
||||
fn from(val: PLLMul) -> u8 {
|
||||
match val {
|
||||
#(
|
||||
PLLMul::Mul#N => N,
|
||||
)*
|
||||
@ -163,13 +135,13 @@ pub enum PLLClkDiv {
|
||||
impl PLLClkDiv {
|
||||
pub fn to_div(self) -> u32 {
|
||||
let val: u8 = self.into();
|
||||
val as u32 + 1 * 2
|
||||
(val as u32 + 1) * 2
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for PLLClkDiv {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
impl From<PLLClkDiv> for u8 {
|
||||
fn from(val: PLLClkDiv) -> u8 {
|
||||
match val {
|
||||
PLLClkDiv::Div2 => 0b00,
|
||||
PLLClkDiv::Div4 => 0b01,
|
||||
PLLClkDiv::Div6 => 0b10,
|
||||
@ -197,9 +169,9 @@ impl PLLSrcDiv {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for PLLSrcDiv {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
impl From<PLLSrcDiv> for u8 {
|
||||
fn from(val: PLLSrcDiv) -> u8 {
|
||||
match val {
|
||||
PLLSrcDiv::Div1 => 0b000,
|
||||
PLLSrcDiv::Div2 => 0b001,
|
||||
PLLSrcDiv::Div3 => 0b010,
|
||||
@ -212,18 +184,46 @@ impl Into<u8> for PLLSrcDiv {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for PLLSource {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
PLLSource::HSI16 => 0b10,
|
||||
PLLSource::HSE(_) => 0b11,
|
||||
impl From<PLLSource> for Pllsrc {
|
||||
fn from(val: PLLSource) -> Pllsrc {
|
||||
match val {
|
||||
PLLSource::HSI16 => Pllsrc::HSI16,
|
||||
PLLSource::HSE(_) => Pllsrc::HSE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Msirange> for MSIRange {
|
||||
fn into(self) -> Msirange {
|
||||
match self {
|
||||
impl From<APBPrescaler> for Ppre {
|
||||
fn from(val: APBPrescaler) -> Ppre {
|
||||
match val {
|
||||
APBPrescaler::NotDivided => Ppre::DIV1,
|
||||
APBPrescaler::Div2 => Ppre::DIV2,
|
||||
APBPrescaler::Div4 => Ppre::DIV4,
|
||||
APBPrescaler::Div8 => Ppre::DIV8,
|
||||
APBPrescaler::Div16 => Ppre::DIV16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AHBPrescaler> for Hpre {
|
||||
fn from(val: AHBPrescaler) -> Hpre {
|
||||
match val {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MSIRange> for Msirange {
|
||||
fn from(val: MSIRange) -> Msirange {
|
||||
match val {
|
||||
MSIRange::Range0 => Msirange::RANGE100K,
|
||||
MSIRange::Range1 => Msirange::RANGE200K,
|
||||
MSIRange::Range2 => Msirange::RANGE400K,
|
||||
@ -239,30 +239,22 @@ impl Into<Msirange> for MSIRange {
|
||||
}
|
||||
}
|
||||
}
|
||||
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<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 From<MSIRange> for u32 {
|
||||
fn from(val: MSIRange) -> u32 {
|
||||
match val {
|
||||
MSIRange::Range0 => 100_000,
|
||||
MSIRange::Range1 => 200_000,
|
||||
MSIRange::Range2 => 400_000,
|
||||
MSIRange::Range3 => 800_000,
|
||||
MSIRange::Range4 => 1_000_000,
|
||||
MSIRange::Range5 => 2_000_000,
|
||||
MSIRange::Range6 => 4_000_000,
|
||||
MSIRange::Range7 => 8_000_000,
|
||||
MSIRange::Range8 => 16_000_000,
|
||||
MSIRange::Range9 => 24_000_000,
|
||||
MSIRange::Range10 => 32_000_000,
|
||||
MSIRange::Range11 => 48_000_000,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -287,203 +279,151 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// RCC peripheral
|
||||
pub struct Rcc<'d> {
|
||||
_rb: peripherals::RCC,
|
||||
phantom: PhantomData<&'d mut peripherals::RCC>,
|
||||
}
|
||||
|
||||
impl<'d> Rcc<'d> {
|
||||
pub fn new(rcc: impl Unborrow<Target = peripherals::RCC> + 'd) -> Self {
|
||||
unborrow!(rcc);
|
||||
Self {
|
||||
_rb: rcc,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
// Safety: RCC init must have been called
|
||||
pub fn clocks(&self) -> &'static Clocks {
|
||||
unsafe { get_freqs() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait that freezes the `RCC` peripheral with provided clocks configuration
|
||||
pub trait RccExt {
|
||||
fn freeze(self, config: Config) -> Clocks;
|
||||
}
|
||||
|
||||
impl RccExt for RCC {
|
||||
#[inline]
|
||||
fn freeze(self, cfgr: Config) -> Clocks {
|
||||
let rcc = pac::RCC;
|
||||
let (sys_clk, sw) = match cfgr.mux {
|
||||
ClockSrc::HSI16 => {
|
||||
// Enable HSI16
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_hsion(true));
|
||||
while !rcc.cr().read().hsirdy() {}
|
||||
}
|
||||
|
||||
(HSI16_FREQ, 0b01)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_hseon(true));
|
||||
while !rcc.cr().read().hserdy() {}
|
||||
}
|
||||
|
||||
(freq.0, 0b10)
|
||||
}
|
||||
ClockSrc::MSI(range) => {
|
||||
// Enable MSI
|
||||
unsafe {
|
||||
rcc.cr().write(|w| {
|
||||
let bits: Msirange = range.into();
|
||||
w.set_msirange(bits);
|
||||
w.set_msipllen(false);
|
||||
w.set_msirgsel(true);
|
||||
w.set_msion(true);
|
||||
});
|
||||
while !rcc.cr().read().msirdy() {}
|
||||
|
||||
// Enable as clock source for USB, RNG if running at 48 MHz
|
||||
if let MSIRange::Range11 = range {
|
||||
rcc.ccipr().modify(|w| {
|
||||
w.set_clk48sel(0b11);
|
||||
});
|
||||
}
|
||||
}
|
||||
(range.into(), 0b00)
|
||||
}
|
||||
ClockSrc::PLL(src, div, prediv, mul, pll48div) => {
|
||||
let freq = match src {
|
||||
PLLSource::HSE(freq) => {
|
||||
// Enable HSE
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_hseon(true));
|
||||
while !rcc.cr().read().hserdy() {}
|
||||
}
|
||||
freq.0
|
||||
}
|
||||
PLLSource::HSI16 => {
|
||||
// Enable HSI
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_hsion(true));
|
||||
while !rcc.cr().read().hsirdy() {}
|
||||
}
|
||||
HSI16_FREQ
|
||||
}
|
||||
};
|
||||
|
||||
// Disable PLL
|
||||
unsafe {
|
||||
rcc.cr().modify(|w| w.set_pllon(false));
|
||||
while rcc.cr().read().pllrdy() {}
|
||||
}
|
||||
|
||||
let freq = (freq / prediv.to_div() * mul.to_mul()) / div.to_div();
|
||||
|
||||
assert!(freq <= 80_000_000);
|
||||
|
||||
unsafe {
|
||||
rcc.pllcfgr().write(move |w| {
|
||||
w.set_plln(mul.into());
|
||||
w.set_pllm(prediv.into());
|
||||
w.set_pllr(div.into());
|
||||
if let Some(pll48div) = pll48div {
|
||||
w.set_pllq(pll48div.into());
|
||||
w.set_pllqen(true);
|
||||
}
|
||||
w.set_pllsrc(src.into());
|
||||
});
|
||||
|
||||
// Enable as clock source for USB, RNG if PLL48 divisor is provided
|
||||
if pll48div.is_some() {
|
||||
rcc.ccipr().modify(|w| {
|
||||
w.set_clk48sel(0b10);
|
||||
});
|
||||
}
|
||||
|
||||
// Enable PLL
|
||||
rcc.cr().modify(|w| w.set_pllon(true));
|
||||
while !rcc.cr().read().pllrdy() {}
|
||||
rcc.pllcfgr().modify(|w| w.set_pllren(true));
|
||||
}
|
||||
(freq, 0b11)
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
// Set flash wait states
|
||||
pac::FLASH.acr().modify(|w| {
|
||||
w.set_latency(if sys_clk <= 16_000_000 {
|
||||
0b000
|
||||
} else if sys_clk <= 32_000_000 {
|
||||
0b001
|
||||
} else if sys_clk <= 48_000_000 {
|
||||
0b010
|
||||
} else if sys_clk <= 64_000_000 {
|
||||
0b011
|
||||
} else {
|
||||
0b100
|
||||
});
|
||||
});
|
||||
|
||||
// Switch active clocks to new clock source
|
||||
rcc.cfgr().modify(|w| {
|
||||
w.set_sw(sw.into());
|
||||
w.set_hpre(cfgr.ahb_pre.into());
|
||||
w.set_ppre1(cfgr.apb1_pre.into());
|
||||
w.set_ppre2(cfgr.apb2_pre.into());
|
||||
});
|
||||
}
|
||||
|
||||
let ahb_freq: u32 = match cfgr.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre = 1 << (pre as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / (1 << (pre as u8 - 3));
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
Clocks {
|
||||
sys: sys_clk.hz(),
|
||||
ahb1: ahb_freq.hz(),
|
||||
ahb2: ahb_freq.hz(),
|
||||
ahb3: ahb_freq.hz(),
|
||||
apb1: apb1_freq.hz(),
|
||||
apb2: apb2_freq.hz(),
|
||||
apb1_tim: apb1_tim_freq.hz(),
|
||||
apb2_tim: apb2_tim_freq.hz(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
||||
let clocks = r.freeze(config);
|
||||
set_freqs(clocks);
|
||||
let (sys_clk, sw) = match config.mux {
|
||||
ClockSrc::MSI(range) => {
|
||||
// Enable MSI
|
||||
RCC.cr().write(|w| {
|
||||
let bits: Msirange = range.into();
|
||||
w.set_msirange(bits);
|
||||
w.set_msipllen(false);
|
||||
w.set_msirgsel(true);
|
||||
w.set_msion(true);
|
||||
});
|
||||
while !RCC.cr().read().msirdy() {}
|
||||
|
||||
// Enable as clock source for USB, RNG if running at 48 MHz
|
||||
if let MSIRange::Range11 = range {
|
||||
RCC.ccipr().modify(|w| {
|
||||
w.set_clk48sel(0b11);
|
||||
});
|
||||
}
|
||||
(range.into(), Sw::MSI)
|
||||
}
|
||||
ClockSrc::HSI16 => {
|
||||
// Enable HSI16
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
(HSI16_FREQ, Sw::HSI16)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
(freq.0, Sw::HSE)
|
||||
}
|
||||
ClockSrc::PLL(src, div, prediv, mul, pll48div) => {
|
||||
let freq = match src {
|
||||
PLLSource::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
freq.0
|
||||
}
|
||||
PLLSource::HSI16 => {
|
||||
// Enable HSI
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
HSI16_FREQ
|
||||
}
|
||||
};
|
||||
|
||||
// Disable PLL
|
||||
RCC.cr().modify(|w| w.set_pllon(false));
|
||||
while RCC.cr().read().pllrdy() {}
|
||||
|
||||
let freq = (freq / prediv.to_div() * mul.to_mul()) / div.to_div();
|
||||
|
||||
assert!(freq <= 80_000_000);
|
||||
|
||||
RCC.pllcfgr().write(move |w| {
|
||||
w.set_plln(mul.into());
|
||||
w.set_pllm(prediv.into());
|
||||
w.set_pllr(div.into());
|
||||
if let Some(pll48div) = pll48div {
|
||||
w.set_pllq(pll48div.into());
|
||||
w.set_pllqen(true);
|
||||
}
|
||||
w.set_pllsrc(src.into());
|
||||
});
|
||||
|
||||
// Enable as clock source for USB, RNG if PLL48 divisor is provided
|
||||
if pll48div.is_some() {
|
||||
RCC.ccipr().modify(|w| {
|
||||
w.set_clk48sel(0b10);
|
||||
});
|
||||
}
|
||||
|
||||
// Enable PLL
|
||||
RCC.cr().modify(|w| w.set_pllon(true));
|
||||
while !RCC.cr().read().pllrdy() {}
|
||||
RCC.pllcfgr().modify(|w| w.set_pllren(true));
|
||||
|
||||
(freq, Sw::PLL)
|
||||
}
|
||||
};
|
||||
|
||||
// Set flash wait states
|
||||
FLASH.acr().modify(|w| {
|
||||
w.set_latency(if sys_clk <= 16_000_000 {
|
||||
0b000
|
||||
} else if sys_clk <= 32_000_000 {
|
||||
0b001
|
||||
} else if sys_clk <= 48_000_000 {
|
||||
0b010
|
||||
} else if sys_clk <= 64_000_000 {
|
||||
0b011
|
||||
} else {
|
||||
0b100
|
||||
});
|
||||
});
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(sw);
|
||||
w.set_hpre(config.ahb_pre.into());
|
||||
w.set_ppre1(config.apb1_pre.into());
|
||||
w.set_ppre2(config.apb2_pre.into());
|
||||
});
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: Hpre = pre.into();
|
||||
let pre = 1 << (pre.0 as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.0 - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.0 - 3);
|
||||
let freq = ahb_freq / (1 << (pre as u8 - 3));
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: sys_clk.hz(),
|
||||
ahb1: ahb_freq.hz(),
|
||||
ahb2: ahb_freq.hz(),
|
||||
ahb3: ahb_freq.hz(),
|
||||
apb1: apb1_freq.hz(),
|
||||
apb2: apb2_freq.hz(),
|
||||
apb1_tim: apb1_tim_freq.hz(),
|
||||
apb2_tim: apb2_tim_freq.hz(),
|
||||
});
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
use crate::pac;
|
||||
use crate::peripherals::{self, RCC};
|
||||
use crate::pwr::{Power, VoltageScale};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::{Hertz, U32Ext};
|
||||
use stm32_metapac::rcc::vals::{Hpre, Msirange, Msirgsel, Pllm, Pllsrc, Ppre, Sw};
|
||||
@ -8,6 +6,20 @@ use stm32_metapac::rcc::vals::{Hpre, Msirange, Msirgsel, Pllm, Pllsrc, Ppre, Sw}
|
||||
/// HSI16 speed
|
||||
pub const HSI16_FREQ: u32 = 16_000_000;
|
||||
|
||||
/// Voltage Scale
|
||||
///
|
||||
/// Represents the voltage range feeding the CPU core. The maximum core
|
||||
/// clock frequency depends on this value.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum VoltageScale {
|
||||
// Highest frequency
|
||||
Range1,
|
||||
Range2,
|
||||
Range3,
|
||||
// Lowest power
|
||||
Range4,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ClockSrc {
|
||||
MSI(MSIRange),
|
||||
@ -293,218 +305,188 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait that freezes the `RCC` peripheral with provided clocks configuration
|
||||
pub trait RccExt {
|
||||
fn freeze(self, config: Config, power: &Power) -> Clocks;
|
||||
}
|
||||
|
||||
impl RccExt for RCC {
|
||||
#[inline]
|
||||
fn freeze(self, cfgr: Config, power: &Power) -> Clocks {
|
||||
let rcc = pac::RCC;
|
||||
|
||||
let sys_clk = match cfgr.mux {
|
||||
ClockSrc::MSI(range) => {
|
||||
unsafe {
|
||||
rcc.icscr1().modify(|w| {
|
||||
let bits: Msirange = range.into();
|
||||
w.set_msisrange(bits);
|
||||
w.set_msirgsel(Msirgsel::RCC_ICSCR1);
|
||||
});
|
||||
rcc.cr().write(|w| {
|
||||
w.set_msipllen(false);
|
||||
w.set_msison(true);
|
||||
w.set_msison(true);
|
||||
});
|
||||
while !rcc.cr().read().msisrdy() {}
|
||||
}
|
||||
|
||||
range.into()
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_hseon(true));
|
||||
while !rcc.cr().read().hserdy() {}
|
||||
}
|
||||
|
||||
freq.0
|
||||
}
|
||||
ClockSrc::HSI16 => {
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_hsion(true));
|
||||
while !rcc.cr().read().hsirdy() {}
|
||||
}
|
||||
|
||||
HSI16_FREQ
|
||||
}
|
||||
ClockSrc::PLL1R(src, m, n, div) => {
|
||||
let freq = match src {
|
||||
PllSrc::MSI(_) => MSIRange::default().into(),
|
||||
PllSrc::HSE(hertz) => hertz.0,
|
||||
PllSrc::HSI16 => HSI16_FREQ,
|
||||
};
|
||||
|
||||
// disable
|
||||
unsafe {
|
||||
rcc.cr().modify(|w| w.set_pllon(0, false));
|
||||
while rcc.cr().read().pllrdy(0) {}
|
||||
}
|
||||
|
||||
let vco = freq * n as u8 as u32;
|
||||
let pll_ck = vco / (div as u8 as u32 + 1);
|
||||
|
||||
unsafe {
|
||||
rcc.pll1cfgr().write(|w| {
|
||||
w.set_pllm(m.into());
|
||||
w.set_pllsrc(src.into());
|
||||
});
|
||||
|
||||
rcc.pll1divr().modify(|w| {
|
||||
w.set_pllr(div.to_div());
|
||||
w.set_plln(n.to_mul());
|
||||
});
|
||||
|
||||
// Enable PLL
|
||||
rcc.cr().modify(|w| w.set_pllon(0, true));
|
||||
while !rcc.cr().read().pllrdy(0) {}
|
||||
rcc.pll1cfgr().modify(|w| w.set_pllren(true));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_pllon(0, true));
|
||||
while !rcc.cr().read().pllrdy(0) {}
|
||||
}
|
||||
|
||||
pll_ck
|
||||
}
|
||||
};
|
||||
|
||||
// states and programming delay
|
||||
let wait_states = match power.vos {
|
||||
// VOS 0 range VCORE 1.26V - 1.40V
|
||||
VoltageScale::Range1 => {
|
||||
if sys_clk < 32_000_000 {
|
||||
0
|
||||
} else if sys_clk < 64_000_000 {
|
||||
1
|
||||
} else if sys_clk < 96_000_000 {
|
||||
2
|
||||
} else if sys_clk < 128_000_000 {
|
||||
3
|
||||
} else {
|
||||
4
|
||||
}
|
||||
}
|
||||
// VOS 1 range VCORE 1.15V - 1.26V
|
||||
VoltageScale::Range2 => {
|
||||
if sys_clk < 30_000_000 {
|
||||
0
|
||||
} else if sys_clk < 60_000_000 {
|
||||
1
|
||||
} else if sys_clk < 90_000_000 {
|
||||
2
|
||||
} else {
|
||||
3
|
||||
}
|
||||
}
|
||||
// VOS 2 range VCORE 1.05V - 1.15V
|
||||
VoltageScale::Range3 => {
|
||||
if sys_clk < 24_000_000 {
|
||||
0
|
||||
} else if sys_clk < 48_000_000 {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
||||
}
|
||||
// VOS 3 range VCORE 0.95V - 1.05V
|
||||
VoltageScale::Range4 => {
|
||||
if sys_clk < 12_000_000 {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
pac::FLASH.acr().modify(|w| {
|
||||
w.set_latency(wait_states);
|
||||
})
|
||||
}
|
||||
|
||||
unsafe {
|
||||
rcc.cfgr1().modify(|w| {
|
||||
w.set_sw(cfgr.mux.into());
|
||||
});
|
||||
|
||||
rcc.cfgr2().modify(|w| {
|
||||
w.set_hpre(cfgr.ahb_pre.into());
|
||||
w.set_ppre1(cfgr.apb1_pre.into());
|
||||
w.set_ppre2(cfgr.apb2_pre.into());
|
||||
});
|
||||
|
||||
rcc.cfgr3().modify(|w| {
|
||||
w.set_ppre3(cfgr.apb3_pre.into());
|
||||
});
|
||||
}
|
||||
|
||||
let ahb_freq: u32 = match cfgr.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre = 1 << (pre as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / (1 << (pre as u8 - 3));
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb3_freq, _apb3_tim_freq) = match cfgr.apb3_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / (1 << (pre as u8 - 3));
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
Clocks {
|
||||
sys: sys_clk.hz(),
|
||||
ahb1: ahb_freq.hz(),
|
||||
ahb2: ahb_freq.hz(),
|
||||
ahb3: ahb_freq.hz(),
|
||||
apb1: apb1_freq.hz(),
|
||||
apb2: apb2_freq.hz(),
|
||||
apb3: apb3_freq.hz(),
|
||||
apb1_tim: apb1_tim_freq.hz(),
|
||||
apb2_tim: apb2_tim_freq.hz(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
||||
let power = Power::new(<peripherals::PWR as embassy::util::Steal>::steal());
|
||||
let clocks = r.freeze(config, &power);
|
||||
set_freqs(clocks);
|
||||
let sys_clk = match config.mux {
|
||||
ClockSrc::MSI(range) => {
|
||||
RCC.icscr1().modify(|w| {
|
||||
let bits: Msirange = range.into();
|
||||
w.set_msisrange(bits);
|
||||
w.set_msirgsel(Msirgsel::RCC_ICSCR1);
|
||||
});
|
||||
RCC.cr().write(|w| {
|
||||
w.set_msipllen(false);
|
||||
w.set_msison(true);
|
||||
w.set_msison(true);
|
||||
});
|
||||
while !RCC.cr().read().msisrdy() {}
|
||||
|
||||
range.into()
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
freq.0
|
||||
}
|
||||
ClockSrc::HSI16 => {
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
HSI16_FREQ
|
||||
}
|
||||
ClockSrc::PLL1R(src, m, n, div) => {
|
||||
let freq = match src {
|
||||
PllSrc::MSI(_) => MSIRange::default().into(),
|
||||
PllSrc::HSE(hertz) => hertz.0,
|
||||
PllSrc::HSI16 => HSI16_FREQ,
|
||||
};
|
||||
|
||||
// disable
|
||||
RCC.cr().modify(|w| w.set_pllon(0, false));
|
||||
while RCC.cr().read().pllrdy(0) {}
|
||||
|
||||
let vco = freq * n as u8 as u32;
|
||||
let pll_ck = vco / (div as u8 as u32 + 1);
|
||||
|
||||
RCC.pll1cfgr().write(|w| {
|
||||
w.set_pllm(m.into());
|
||||
w.set_pllsrc(src.into());
|
||||
});
|
||||
|
||||
RCC.pll1divr().modify(|w| {
|
||||
w.set_pllr(div.to_div());
|
||||
w.set_plln(n.to_mul());
|
||||
});
|
||||
|
||||
// Enable PLL
|
||||
RCC.cr().modify(|w| w.set_pllon(0, true));
|
||||
while !RCC.cr().read().pllrdy(0) {}
|
||||
RCC.pll1cfgr().modify(|w| w.set_pllren(true));
|
||||
|
||||
RCC.cr().write(|w| w.set_pllon(0, true));
|
||||
while !RCC.cr().read().pllrdy(0) {}
|
||||
|
||||
pll_ck
|
||||
}
|
||||
};
|
||||
|
||||
// TODO make configurable
|
||||
let power_vos = VoltageScale::Range4;
|
||||
|
||||
// states and programming delay
|
||||
let wait_states = match power_vos {
|
||||
// VOS 0 range VCORE 1.26V - 1.40V
|
||||
VoltageScale::Range1 => {
|
||||
if sys_clk < 32_000_000 {
|
||||
0
|
||||
} else if sys_clk < 64_000_000 {
|
||||
1
|
||||
} else if sys_clk < 96_000_000 {
|
||||
2
|
||||
} else if sys_clk < 128_000_000 {
|
||||
3
|
||||
} else {
|
||||
4
|
||||
}
|
||||
}
|
||||
// VOS 1 range VCORE 1.15V - 1.26V
|
||||
VoltageScale::Range2 => {
|
||||
if sys_clk < 30_000_000 {
|
||||
0
|
||||
} else if sys_clk < 60_000_000 {
|
||||
1
|
||||
} else if sys_clk < 90_000_000 {
|
||||
2
|
||||
} else {
|
||||
3
|
||||
}
|
||||
}
|
||||
// VOS 2 range VCORE 1.05V - 1.15V
|
||||
VoltageScale::Range3 => {
|
||||
if sys_clk < 24_000_000 {
|
||||
0
|
||||
} else if sys_clk < 48_000_000 {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
||||
}
|
||||
// VOS 3 range VCORE 0.95V - 1.05V
|
||||
VoltageScale::Range4 => {
|
||||
if sys_clk < 12_000_000 {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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_ppre3(config.apb3_pre.into());
|
||||
});
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre = 1 << (pre as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / (1 << (pre as u8 - 3));
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb3_freq, _apb3_tim_freq) = match config.apb3_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / (1 << (pre as u8 - 3));
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: sys_clk.hz(),
|
||||
ahb1: ahb_freq.hz(),
|
||||
ahb2: ahb_freq.hz(),
|
||||
ahb3: ahb_freq.hz(),
|
||||
apb1: apb1_freq.hz(),
|
||||
apb2: apb2_freq.hz(),
|
||||
apb3: apb3_freq.hz(),
|
||||
apb1_tim: apb1_tim_freq.hz(),
|
||||
apb2_tim: apb2_tim_freq.hz(),
|
||||
});
|
||||
}
|
||||
|
@ -1,11 +1,7 @@
|
||||
use crate::pac;
|
||||
use crate::peripherals::{self, RCC};
|
||||
use crate::rcc::{get_freqs, set_freqs, Clocks};
|
||||
use crate::pac::RCC;
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
use crate::time::U32Ext;
|
||||
use core::marker::PhantomData;
|
||||
use embassy::util::Unborrow;
|
||||
use embassy_hal_common::unborrow;
|
||||
|
||||
/// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC,
|
||||
/// and with the addition of the init function to configure a system clock.
|
||||
@ -104,110 +100,68 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// RCC peripheral
|
||||
pub struct Rcc<'d> {
|
||||
_rb: peripherals::RCC,
|
||||
phantom: PhantomData<&'d mut peripherals::RCC>,
|
||||
}
|
||||
|
||||
impl<'d> Rcc<'d> {
|
||||
pub fn new(rcc: impl Unborrow<Target = peripherals::RCC> + 'd) -> Self {
|
||||
unborrow!(rcc);
|
||||
Self {
|
||||
_rb: rcc,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
// Safety: RCC init must have been called
|
||||
pub fn clocks(&self) -> &'static Clocks {
|
||||
unsafe { get_freqs() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait that freezes the `RCC` peripheral with provided clocks configuration
|
||||
pub trait RccExt {
|
||||
fn freeze(self, config: Config) -> Clocks;
|
||||
}
|
||||
|
||||
impl RccExt for RCC {
|
||||
#[inline]
|
||||
fn freeze(self, cfgr: Config) -> Clocks {
|
||||
let rcc = pac::RCC;
|
||||
let (sys_clk, sw) = match cfgr.mux {
|
||||
ClockSrc::HSI16 => {
|
||||
// Enable HSI16
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_hsion(true));
|
||||
while !rcc.cr().read().hsirdy() {}
|
||||
}
|
||||
|
||||
(HSI_FREQ, 0x01)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_hseon(true));
|
||||
while !rcc.cr().read().hserdy() {}
|
||||
}
|
||||
|
||||
(freq.0, 0x02)
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
rcc.cfgr().modify(|w| {
|
||||
w.set_sw(sw.into());
|
||||
w.set_hpre(cfgr.ahb_pre.into());
|
||||
w.set_ppre1(cfgr.apb1_pre.into());
|
||||
w.set_ppre2(cfgr.apb2_pre.into());
|
||||
});
|
||||
}
|
||||
|
||||
let ahb_freq: u32 = match cfgr.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre = 1 << (pre as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / (1 << (pre as u8 - 3));
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
Clocks {
|
||||
sys: sys_clk.hz(),
|
||||
ahb1: ahb_freq.hz(),
|
||||
ahb2: ahb_freq.hz(),
|
||||
ahb3: ahb_freq.hz(),
|
||||
apb1: apb1_freq.hz(),
|
||||
apb2: apb2_freq.hz(),
|
||||
apb1_tim: apb1_tim_freq.hz(),
|
||||
apb2_tim: apb2_tim_freq.hz(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
||||
let clocks = r.freeze(config);
|
||||
set_freqs(clocks);
|
||||
let (sys_clk, sw) = match config.mux {
|
||||
ClockSrc::HSI16 => {
|
||||
// Enable HSI16
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
(HSI_FREQ, 0x01)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
(freq.0, 0x02)
|
||||
}
|
||||
};
|
||||
|
||||
RCC.cfgr().modify(|w| {
|
||||
w.set_sw(sw.into());
|
||||
w.set_hpre(config.ahb_pre.into());
|
||||
w.set_ppre1(config.apb1_pre.into());
|
||||
w.set_ppre2(config.apb2_pre.into());
|
||||
});
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre = 1 << (pre as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / (1 << (pre as u8 - 3));
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: sys_clk.hz(),
|
||||
ahb1: ahb_freq.hz(),
|
||||
ahb2: ahb_freq.hz(),
|
||||
ahb3: ahb_freq.hz(),
|
||||
apb1: apb1_freq.hz(),
|
||||
apb2: apb2_freq.hz(),
|
||||
apb1_tim: apb1_tim_freq.hz(),
|
||||
apb2_tim: apb2_tim_freq.hz(),
|
||||
});
|
||||
}
|
||||
|
@ -1,10 +1,6 @@
|
||||
use crate::pac;
|
||||
use crate::peripherals::{self, RCC};
|
||||
use crate::rcc::{get_freqs, set_freqs, Clocks};
|
||||
use crate::pac::RCC;
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::U32Ext;
|
||||
use core::marker::PhantomData;
|
||||
use embassy::util::Unborrow;
|
||||
use embassy_hal_common::unborrow;
|
||||
|
||||
/// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC,
|
||||
/// and with the addition of the init function to configure a system clock.
|
||||
@ -91,6 +87,7 @@ pub struct Config {
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb1_pre: APBPrescaler,
|
||||
pub apb2_pre: APBPrescaler,
|
||||
pub enable_lsi: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@ -101,136 +98,92 @@ impl Default for Config {
|
||||
ahb_pre: AHBPrescaler::NotDivided,
|
||||
apb1_pre: APBPrescaler::NotDivided,
|
||||
apb2_pre: APBPrescaler::NotDivided,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// RCC peripheral
|
||||
pub struct Rcc<'d> {
|
||||
_rb: peripherals::RCC,
|
||||
phantom: PhantomData<&'d mut peripherals::RCC>,
|
||||
}
|
||||
|
||||
impl<'d> Rcc<'d> {
|
||||
pub fn new(rcc: impl Unborrow<Target = peripherals::RCC> + 'd) -> Self {
|
||||
unborrow!(rcc);
|
||||
Self {
|
||||
_rb: rcc,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable_lsi(&mut self) {
|
||||
let rcc = pac::RCC;
|
||||
unsafe {
|
||||
let csr = rcc.csr().read();
|
||||
if !csr.lsion() {
|
||||
rcc.csr().modify(|w| w.set_lsion(true));
|
||||
while !rcc.csr().read().lsirdy() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Safety: RCC init must have been called
|
||||
pub fn clocks(&self) -> &'static Clocks {
|
||||
unsafe { get_freqs() }
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait that freezes the `RCC` peripheral with provided clocks configuration
|
||||
pub trait RccExt {
|
||||
fn freeze(self, config: Config) -> Clocks;
|
||||
}
|
||||
|
||||
impl RccExt for RCC {
|
||||
#[inline]
|
||||
fn freeze(self, cfgr: Config) -> Clocks {
|
||||
let rcc = pac::RCC;
|
||||
let (sys_clk, sw) = match cfgr.mux {
|
||||
ClockSrc::HSI16 => {
|
||||
// Enable HSI16
|
||||
unsafe {
|
||||
rcc.cr().write(|w| w.set_hsion(true));
|
||||
while !rcc.cr().read().hsirdy() {}
|
||||
}
|
||||
|
||||
(HSI_FREQ, 0x01)
|
||||
}
|
||||
ClockSrc::HSE32 => {
|
||||
// Enable HSE32
|
||||
unsafe {
|
||||
rcc.cr().write(|w| {
|
||||
w.set_hsebyppwr(true);
|
||||
w.set_hseon(true);
|
||||
});
|
||||
while !rcc.cr().read().hserdy() {}
|
||||
}
|
||||
|
||||
(HSE32_FREQ, 0x02)
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
rcc.cfgr().modify(|w| {
|
||||
w.set_sw(sw.into());
|
||||
if cfgr.ahb_pre == AHBPrescaler::NotDivided {
|
||||
w.set_hpre(0);
|
||||
} else {
|
||||
w.set_hpre(cfgr.ahb_pre.into());
|
||||
}
|
||||
w.set_ppre1(cfgr.apb1_pre.into());
|
||||
w.set_ppre2(cfgr.apb2_pre.into());
|
||||
});
|
||||
}
|
||||
|
||||
let ahb_freq: u32 = match cfgr.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre = 1 << (pre as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / (1 << (pre as u8 - 3));
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: completely untested
|
||||
let apb3_freq = ahb_freq;
|
||||
|
||||
Clocks {
|
||||
sys: sys_clk.hz(),
|
||||
ahb1: ahb_freq.hz(),
|
||||
ahb2: ahb_freq.hz(),
|
||||
ahb3: ahb_freq.hz(),
|
||||
apb1: apb1_freq.hz(),
|
||||
apb2: apb2_freq.hz(),
|
||||
apb3: apb3_freq.hz(),
|
||||
apb1_tim: apb1_tim_freq.hz(),
|
||||
apb2_tim: apb2_tim_freq.hz(),
|
||||
enable_lsi: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
||||
let clocks = r.freeze(config);
|
||||
set_freqs(clocks);
|
||||
let (sys_clk, sw) = match config.mux {
|
||||
ClockSrc::HSI16 => {
|
||||
// Enable HSI16
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
(HSI_FREQ, 0x01)
|
||||
}
|
||||
ClockSrc::HSE32 => {
|
||||
// Enable HSE32
|
||||
RCC.cr().write(|w| {
|
||||
w.set_hsebyppwr(true);
|
||||
w.set_hseon(true);
|
||||
});
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
(HSE32_FREQ, 0x02)
|
||||
}
|
||||
};
|
||||
|
||||
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_ppre1(config.apb1_pre.into());
|
||||
w.set_ppre2(config.apb2_pre.into());
|
||||
});
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::NotDivided => sys_clk,
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre = 1 << (pre as u32 - 7);
|
||||
sys_clk / pre
|
||||
}
|
||||
};
|
||||
|
||||
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
|
||||
APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / (1 << (pre as u8 - 3));
|
||||
(freq, freq * 2)
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: completely untested
|
||||
let apb3_freq = ahb_freq;
|
||||
|
||||
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() {}
|
||||
}
|
||||
}
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: sys_clk.hz(),
|
||||
ahb1: ahb_freq.hz(),
|
||||
ahb2: ahb_freq.hz(),
|
||||
ahb3: ahb_freq.hz(),
|
||||
apb1: apb1_freq.hz(),
|
||||
apb2: apb2_freq.hz(),
|
||||
apb3: apb3_freq.hz(),
|
||||
apb1_tim: apb1_tim_freq.hz(),
|
||||
apb2_tim: apb2_tim_freq.hz(),
|
||||
});
|
||||
}
|
||||
|
@ -8,16 +8,18 @@ mod example_common;
|
||||
use embassy::executor::Spawner;
|
||||
use embassy_stm32::exti::ExtiInput;
|
||||
use embassy_stm32::gpio::{Input, Pull};
|
||||
use embassy_stm32::{rcc, Peripherals};
|
||||
use embassy_stm32::Peripherals;
|
||||
use embassy_traits::gpio::{WaitForFallingEdge, WaitForRisingEdge};
|
||||
use example_common::*;
|
||||
|
||||
#[embassy::main]
|
||||
async fn main(_spawner: Spawner, mut p: Peripherals) {
|
||||
let mut rcc = rcc::Rcc::new(p.RCC);
|
||||
// Enables SYSCFG
|
||||
let _ = rcc.enable_hsi48(&mut p.SYSCFG, p.CRS);
|
||||
fn config() -> embassy_stm32::Config {
|
||||
let mut config = embassy_stm32::Config::default();
|
||||
config.rcc.enable_hsi48 = true;
|
||||
config
|
||||
}
|
||||
|
||||
#[embassy::main(config = "config()")]
|
||||
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||
let button = Input::new(p.PB2, Pull::Up);
|
||||
let mut button = ExtiInput::new(button, p.EXTI2);
|
||||
|
||||
|
@ -11,10 +11,8 @@ mod example_common;
|
||||
|
||||
use embassy_lora::{sx127x::*, LoraTimer};
|
||||
use embassy_stm32::{
|
||||
dbgmcu::Dbgmcu,
|
||||
exti::ExtiInput,
|
||||
gpio::{Input, Level, Output, Pull, Speed},
|
||||
rcc,
|
||||
rng::Rng,
|
||||
spi,
|
||||
time::U32Ext,
|
||||
@ -26,18 +24,12 @@ use lorawan_encoding::default_crypto::DefaultFactory as Crypto;
|
||||
fn config() -> embassy_stm32::Config {
|
||||
let mut config = embassy_stm32::Config::default();
|
||||
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16;
|
||||
config.rcc.enable_hsi48 = true;
|
||||
config
|
||||
}
|
||||
|
||||
#[embassy::main(config = "config()")]
|
||||
async fn main(_spawner: embassy::executor::Spawner, mut p: Peripherals) {
|
||||
unsafe {
|
||||
Dbgmcu::enable_all();
|
||||
}
|
||||
|
||||
let mut rcc = rcc::Rcc::new(p.RCC);
|
||||
let _ = rcc.enable_hsi48(&mut p.SYSCFG, p.CRS);
|
||||
|
||||
async fn main(_spawner: embassy::executor::Spawner, p: Peripherals) {
|
||||
// SPI for sx127x
|
||||
let spi = spi::Spi::new(
|
||||
p.SPI1,
|
||||
|
@ -10,10 +10,9 @@ mod example_common;
|
||||
|
||||
use embassy_lora::{stm32wl::*, LoraTimer};
|
||||
use embassy_stm32::{
|
||||
dbgmcu::Dbgmcu,
|
||||
dma::NoDma,
|
||||
gpio::{Level, Output, Pin, Speed},
|
||||
interrupt, pac, rcc,
|
||||
interrupt, pac,
|
||||
rng::Rng,
|
||||
subghz::*,
|
||||
Peripherals,
|
||||
@ -24,19 +23,13 @@ use lorawan_encoding::default_crypto::DefaultFactory as Crypto;
|
||||
fn config() -> embassy_stm32::Config {
|
||||
let mut config = embassy_stm32::Config::default();
|
||||
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16;
|
||||
config.rcc.enable_lsi = true;
|
||||
config
|
||||
}
|
||||
|
||||
#[embassy::main(config = "config()")]
|
||||
async fn main(_spawner: embassy::executor::Spawner, p: Peripherals) {
|
||||
unsafe {
|
||||
Dbgmcu::enable_all();
|
||||
let mut rcc = rcc::Rcc::new(p.RCC);
|
||||
rcc.enable_lsi();
|
||||
pac::RCC.ccipr().modify(|w| {
|
||||
w.set_rngsel(0b01);
|
||||
});
|
||||
}
|
||||
unsafe { pac::RCC.ccipr().modify(|w| w.set_rngsel(0b01)) }
|
||||
|
||||
let ctrl1 = Output::new(p.PC3.degrade(), Level::High, Speed::High);
|
||||
let ctrl2 = Output::new(p.PC4.degrade(), Level::High, Speed::High);
|
||||
|
@ -11,7 +11,6 @@ mod example_common;
|
||||
use embassy::channel::signal::Signal;
|
||||
use embassy::interrupt::{Interrupt, InterruptExt};
|
||||
use embassy::traits::gpio::WaitForRisingEdge;
|
||||
use embassy_stm32::dbgmcu::Dbgmcu;
|
||||
use embassy_stm32::dma::NoDma;
|
||||
use embassy_stm32::exti::ExtiInput;
|
||||
use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
|
||||
@ -72,10 +71,6 @@ fn config() -> embassy_stm32::Config {
|
||||
|
||||
#[embassy::main(config = "config()")]
|
||||
async fn main(_spawner: embassy::executor::Spawner, p: Peripherals) {
|
||||
unsafe {
|
||||
Dbgmcu::enable_all();
|
||||
}
|
||||
|
||||
let mut led1 = Output::new(p.PB15, Level::High, Speed::Low);
|
||||
let mut led2 = Output::new(p.PB9, Level::Low, Speed::Low);
|
||||
let mut led3 = Output::new(p.PB11, Level::Low, Speed::Low);
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 8530a19ffdcdcbc608a97b40895827d09e670eb7
|
||||
Subproject commit 3fa97966f07d43a28c0031175591e1c2ff5d0831
|
Loading…
Reference in New Issue
Block a user