mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-25 16:23:10 +00:00
Merge #566
566: stm32: more RCC cleanups. r=Dirbaio a=Dirbaio Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
This commit is contained in:
commit
da0c25227f
@ -370,16 +370,14 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
|||||||
::core::mem::transmute(t)
|
::core::mem::transmute(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut executor = #embassy_path::executor::Executor::new();
|
|
||||||
|
|
||||||
let executor = unsafe { make_static(&mut executor) };
|
|
||||||
|
|
||||||
#chip_setup
|
#chip_setup
|
||||||
|
|
||||||
|
let mut executor = #embassy_path::executor::Executor::new();
|
||||||
|
let executor = unsafe { make_static(&mut executor) };
|
||||||
|
|
||||||
executor.run(|spawner| {
|
executor.run(|spawner| {
|
||||||
spawner.must_spawn(__embassy_main(spawner, p));
|
spawner.must_spawn(__embassy_main(spawner, p));
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
result.into()
|
result.into()
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
pub struct Dbgmcu {}
|
|
||||||
|
|
||||||
impl Dbgmcu {
|
|
||||||
pub unsafe fn enable_all() {
|
|
||||||
crate::pac::DBGMCU.cr().modify(|cr| {
|
|
||||||
crate::pac::dbgmcu! {
|
|
||||||
(cr, $fn_name:ident) => {
|
|
||||||
cr.$fn_name(true);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -29,8 +29,6 @@ pub mod adc;
|
|||||||
pub mod can;
|
pub mod can;
|
||||||
#[cfg(dac)]
|
#[cfg(dac)]
|
||||||
pub mod dac;
|
pub mod dac;
|
||||||
#[cfg(dbgmcu)]
|
|
||||||
pub mod dbgmcu;
|
|
||||||
#[cfg(dcmi)]
|
#[cfg(dcmi)]
|
||||||
pub mod dcmi;
|
pub mod dcmi;
|
||||||
#[cfg(all(eth, feature = "net"))]
|
#[cfg(all(eth, feature = "net"))]
|
||||||
@ -43,8 +41,6 @@ pub mod i2c;
|
|||||||
#[cfg(crc)]
|
#[cfg(crc)]
|
||||||
pub mod crc;
|
pub mod crc;
|
||||||
pub mod pwm;
|
pub mod pwm;
|
||||||
#[cfg(pwr)]
|
|
||||||
pub mod pwr;
|
|
||||||
#[cfg(rng)]
|
#[cfg(rng)]
|
||||||
pub mod rng;
|
pub mod rng;
|
||||||
#[cfg(sdmmc)]
|
#[cfg(sdmmc)]
|
||||||
@ -92,7 +88,13 @@ pub fn init(config: Config) -> Peripherals {
|
|||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
if config.enable_debug_during_sleep {
|
if config.enable_debug_during_sleep {
|
||||||
dbgmcu::Dbgmcu::enable_all();
|
crate::pac::DBGMCU.cr().modify(|cr| {
|
||||||
|
crate::pac::dbgmcu! {
|
||||||
|
(cr, $fn_name:ident) => {
|
||||||
|
cr.$fn_name(true);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
gpio::init();
|
gpio::init();
|
||||||
|
@ -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,12 +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")]
|
|
||||||
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 @@
|
|||||||
|
|
174
embassy-stm32/src/rcc/f0.rs
Normal file
174
embassy-stm32/src/rcc/f0.rs
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
use crate::pac::rcc::vals::{Hpre, Hsebyp, Pllmul, Pllsrc, Ppre, Sw, Usbsw};
|
||||||
|
use crate::pac::{FLASH, RCC};
|
||||||
|
use crate::time::Hertz;
|
||||||
|
|
||||||
|
use super::{set_freqs, Clocks};
|
||||||
|
|
||||||
|
const HSI: u32 = 8_000_000;
|
||||||
|
|
||||||
|
/// Configuration of the clocks
|
||||||
|
///
|
||||||
|
/// hse takes precedence over hsi48 if both are enabled
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Config {
|
||||||
|
pub hse: Option<Hertz>,
|
||||||
|
pub bypass_hse: bool,
|
||||||
|
pub usb_pll: bool,
|
||||||
|
|
||||||
|
#[cfg(rcc_f0)]
|
||||||
|
pub hsi48: bool,
|
||||||
|
|
||||||
|
pub sys_ck: Option<Hertz>,
|
||||||
|
pub hclk: Option<Hertz>,
|
||||||
|
pub pclk: Option<Hertz>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
|
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,208 +0,0 @@
|
|||||||
use core::marker::PhantomData;
|
|
||||||
|
|
||||||
use embassy::util::Unborrow;
|
|
||||||
|
|
||||||
use crate::pac::{FLASH, RCC};
|
|
||||||
use crate::peripherals;
|
|
||||||
use crate::time::Hertz;
|
|
||||||
|
|
||||||
use super::{set_freqs, Clocks};
|
|
||||||
|
|
||||||
const HSI: u32 = 8_000_000;
|
|
||||||
|
|
||||||
/// Configuration of the clocks
|
|
||||||
///
|
|
||||||
/// hse takes precedence over hsi48 if both are enabled
|
|
||||||
#[non_exhaustive]
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Config {
|
|
||||||
pub hse: Option<Hertz>,
|
|
||||||
pub bypass_hse: bool,
|
|
||||||
pub usb_pll: bool,
|
|
||||||
|
|
||||||
#[cfg(rcc_f0)]
|
|
||||||
pub hsi48: bool,
|
|
||||||
|
|
||||||
pub sys_ck: Option<Hertz>,
|
|
||||||
pub hclk: Option<Hertz>,
|
|
||||||
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 unsafe fn init(config: Config) {
|
|
||||||
let rcc = Rcc::new(<peripherals::RCC as embassy::util::Steal>::steal(), config);
|
|
||||||
let clocks = rcc.freeze();
|
|
||||||
set_freqs(clocks);
|
|
||||||
}
|
|
179
embassy-stm32/src/rcc/f1.rs
Normal file
179
embassy-stm32/src/rcc/f1.rs
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
use core::convert::TryFrom;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// Configuration of the clocks
|
||||||
|
///
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Config {
|
||||||
|
pub hse: Option<Hertz>,
|
||||||
|
|
||||||
|
pub sys_ck: Option<Hertz>,
|
||||||
|
pub hclk: Option<Hertz>,
|
||||||
|
pub pclk1: Option<Hertz>,
|
||||||
|
pub pclk2: Option<Hertz>,
|
||||||
|
pub adcclk: Option<Hertz>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
|
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,214 +0,0 @@
|
|||||||
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};
|
|
||||||
|
|
||||||
const HSI: u32 = 8_000_000;
|
|
||||||
|
|
||||||
/// Configuration of the clocks
|
|
||||||
///
|
|
||||||
#[non_exhaustive]
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Config {
|
|
||||||
pub hse: Option<Hertz>,
|
|
||||||
|
|
||||||
pub sys_ck: Option<Hertz>,
|
|
||||||
pub hclk: Option<Hertz>,
|
|
||||||
pub pclk1: Option<Hertz>,
|
|
||||||
pub pclk2: Option<Hertz>,
|
|
||||||
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 unsafe fn init(config: Config) {
|
|
||||||
let rcc = Rcc::new(<peripherals::RCC as embassy::util::Steal>::steal(), config);
|
|
||||||
let clocks = rcc.freeze();
|
|
||||||
set_freqs(clocks);
|
|
||||||
}
|
|
331
embassy-stm32/src/rcc/f3.rs
Normal file
331
embassy-stm32/src/rcc/f3.rs
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/// Clocks configutation
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Config {
|
||||||
|
/// Frequency of HSE oscillator
|
||||||
|
/// 4MHz to 32MHz
|
||||||
|
pub hse: Option<Hertz>,
|
||||||
|
/// Bypass HSE for an external clock
|
||||||
|
pub bypass_hse: bool,
|
||||||
|
/// Frequency of the System Clock
|
||||||
|
pub sysclk: Option<Hertz>,
|
||||||
|
/// Frequency of AHB bus
|
||||||
|
pub hclk: Option<Hertz>,
|
||||||
|
/// Frequency of APB1 bus
|
||||||
|
/// - Max frequency 36MHz
|
||||||
|
pub pclk1: Option<Hertz>,
|
||||||
|
/// Frequency of APB2 bus
|
||||||
|
/// - Max frequency with HSE is 72MHz
|
||||||
|
/// - Max frequency without HSE is 64MHz
|
||||||
|
pub pclk2: Option<Hertz>,
|
||||||
|
/// USB clock setup
|
||||||
|
/// It is valid only when,
|
||||||
|
/// - HSE is enabled,
|
||||||
|
/// - The System clock frequency is either 48MHz or 72MHz
|
||||||
|
/// - APB1 clock has a minimum frequency of 10MHz
|
||||||
|
pub pll48: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Information required to setup the PLL clock
|
||||||
|
struct PllConfig {
|
||||||
|
pll_src: Pllsrc,
|
||||||
|
pll_mul: Pllmul,
|
||||||
|
pll_div: Option<Prediv>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize and Set the clock frequencies
|
||||||
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
|
// 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),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 {
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function assumes cases when multiplier is one and it
|
||||||
|
// being greater than 16 is made impossible
|
||||||
|
#[inline]
|
||||||
|
fn into_pll_mul(multiplier: u32) -> Pllmul {
|
||||||
|
match multiplier {
|
||||||
|
2 => Pllmul::MUL2,
|
||||||
|
3 => Pllmul::MUL3,
|
||||||
|
4 => Pllmul::MUL4,
|
||||||
|
5 => Pllmul::MUL5,
|
||||||
|
6 => Pllmul::MUL6,
|
||||||
|
7 => Pllmul::MUL7,
|
||||||
|
8 => Pllmul::MUL8,
|
||||||
|
9 => Pllmul::MUL9,
|
||||||
|
10 => Pllmul::MUL10,
|
||||||
|
11 => Pllmul::MUL11,
|
||||||
|
12 => Pllmul::MUL12,
|
||||||
|
13 => Pllmul::MUL13,
|
||||||
|
14 => Pllmul::MUL14,
|
||||||
|
15 => Pllmul::MUL15,
|
||||||
|
16 => Pllmul::MUL16,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function assumes the incoming divisor cannot be greater
|
||||||
|
// than 16
|
||||||
|
#[inline]
|
||||||
|
fn into_pre_div(divisor: u32) -> Prediv {
|
||||||
|
match divisor {
|
||||||
|
1 => Prediv::DIV1,
|
||||||
|
2 => Prediv::DIV2,
|
||||||
|
3 => Prediv::DIV3,
|
||||||
|
4 => Prediv::DIV4,
|
||||||
|
5 => Prediv::DIV5,
|
||||||
|
6 => Prediv::DIV6,
|
||||||
|
7 => Prediv::DIV7,
|
||||||
|
8 => Prediv::DIV8,
|
||||||
|
9 => Prediv::DIV9,
|
||||||
|
10 => Prediv::DIV10,
|
||||||
|
11 => Prediv::DIV11,
|
||||||
|
12 => Prediv::DIV12,
|
||||||
|
13 => Prediv::DIV13,
|
||||||
|
14 => Prediv::DIV14,
|
||||||
|
15 => Prediv::DIV15,
|
||||||
|
16 => Prediv::DIV16,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine GCD using Euclidean algorithm
|
||||||
|
#[inline]
|
||||||
|
fn gcd(mut a: u32, mut b: u32) -> u32 {
|
||||||
|
while b != 0 {
|
||||||
|
let r = a % b;
|
||||||
|
a = b;
|
||||||
|
b = r;
|
||||||
|
}
|
||||||
|
a
|
||||||
|
}
|
@ -1,374 +0,0 @@
|
|||||||
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::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)]
|
|
||||||
pub struct Config {
|
|
||||||
/// Frequency of HSE oscillator
|
|
||||||
/// 4MHz to 32MHz
|
|
||||||
pub hse: Option<Hertz>,
|
|
||||||
/// Bypass HSE for an external clock
|
|
||||||
pub bypass_hse: bool,
|
|
||||||
/// Frequency of the System Clock
|
|
||||||
pub sysclk: Option<Hertz>,
|
|
||||||
/// Frequency of AHB bus
|
|
||||||
pub hclk: Option<Hertz>,
|
|
||||||
/// Frequency of APB1 bus
|
|
||||||
/// - Max frequency 36MHz
|
|
||||||
pub pclk1: Option<Hertz>,
|
|
||||||
/// Frequency of APB2 bus
|
|
||||||
/// - Max frequency with HSE is 72MHz
|
|
||||||
/// - Max frequency without HSE is 64MHz
|
|
||||||
pub pclk2: Option<Hertz>,
|
|
||||||
/// USB clock setup
|
|
||||||
/// It is valid only when,
|
|
||||||
/// - HSE is enabled,
|
|
||||||
/// - The System clock frequency is either 48MHz or 72MHz
|
|
||||||
/// - APB1 clock has a minimum frequency of 10MHz
|
|
||||||
pub pll48: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Information required to setup the PLL clock
|
|
||||||
struct PllConfig {
|
|
||||||
pll_src: Pllsrc,
|
|
||||||
pll_mul: Pllmul,
|
|
||||||
pll_div: Option<Prediv>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize and Set the clock frequencies
|
|
||||||
pub unsafe fn init(config: Config) {
|
|
||||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
|
||||||
let clocks = Rcc::new(r, config).freeze();
|
|
||||||
set_freqs(clocks);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d> Rcc<'d> {
|
|
||||||
pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self {
|
|
||||||
Self {
|
|
||||||
config,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
} 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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(
|
|
||||||
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"
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function assumes cases when multiplier is one and it
|
|
||||||
// being greater than 16 is made impossible
|
|
||||||
#[inline]
|
|
||||||
fn into_pll_mul(multiplier: u32) -> Pllmul {
|
|
||||||
match multiplier {
|
|
||||||
2 => Pllmul::MUL2,
|
|
||||||
3 => Pllmul::MUL3,
|
|
||||||
4 => Pllmul::MUL4,
|
|
||||||
5 => Pllmul::MUL5,
|
|
||||||
6 => Pllmul::MUL6,
|
|
||||||
7 => Pllmul::MUL7,
|
|
||||||
8 => Pllmul::MUL8,
|
|
||||||
9 => Pllmul::MUL9,
|
|
||||||
10 => Pllmul::MUL10,
|
|
||||||
11 => Pllmul::MUL11,
|
|
||||||
12 => Pllmul::MUL12,
|
|
||||||
13 => Pllmul::MUL13,
|
|
||||||
14 => Pllmul::MUL14,
|
|
||||||
15 => Pllmul::MUL15,
|
|
||||||
16 => Pllmul::MUL16,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function assumes the incoming divisor cannot be greater
|
|
||||||
// than 16
|
|
||||||
#[inline]
|
|
||||||
fn into_pre_div(divisor: u32) -> Prediv {
|
|
||||||
match divisor {
|
|
||||||
1 => Prediv::DIV1,
|
|
||||||
2 => Prediv::DIV2,
|
|
||||||
3 => Prediv::DIV3,
|
|
||||||
4 => Prediv::DIV4,
|
|
||||||
5 => Prediv::DIV5,
|
|
||||||
6 => Prediv::DIV6,
|
|
||||||
7 => Prediv::DIV7,
|
|
||||||
8 => Prediv::DIV8,
|
|
||||||
9 => Prediv::DIV9,
|
|
||||||
10 => Prediv::DIV10,
|
|
||||||
11 => Prediv::DIV11,
|
|
||||||
12 => Prediv::DIV12,
|
|
||||||
13 => Prediv::DIV13,
|
|
||||||
14 => Prediv::DIV14,
|
|
||||||
15 => Prediv::DIV15,
|
|
||||||
16 => Prediv::DIV16,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine GCD using Euclidean algorithm
|
|
||||||
#[inline]
|
|
||||||
fn gcd(mut a: u32, mut b: u32) -> u32 {
|
|
||||||
while b != 0 {
|
|
||||||
let r = a % b;
|
|
||||||
a = b;
|
|
||||||
b = r;
|
|
||||||
}
|
|
||||||
a
|
|
||||||
}
|
|
288
embassy-stm32/src/rcc/f4.rs
Normal file
288
embassy-stm32/src/rcc/f4.rs
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
use super::sealed::RccPeripheral;
|
||||||
|
use crate::pac::rcc::vals::{Hpre, Hsebyp, Ppre, Sw};
|
||||||
|
use crate::pac::{FLASH, PWR, RCC};
|
||||||
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
|
use crate::time::Hertz;
|
||||||
|
|
||||||
|
const HSI: u32 = 16_000_000;
|
||||||
|
|
||||||
|
/// Clocks configuration
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Config {
|
||||||
|
pub hse: Option<Hertz>,
|
||||||
|
pub bypass_hse: bool,
|
||||||
|
pub hclk: Option<Hertz>,
|
||||||
|
pub sys_ck: Option<Hertz>,
|
||||||
|
pub pclk1: Option<Hertz>,
|
||||||
|
pub pclk2: Option<Hertz>,
|
||||||
|
|
||||||
|
pub pll48: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe 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(|_| {
|
||||||
|
FLASH
|
||||||
|
.acr()
|
||||||
|
.modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
|
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 {
|
||||||
|
use_pll: bool,
|
||||||
|
pllsysclk: Option<u32>,
|
||||||
|
pll48clk: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
mod max {
|
||||||
|
#[cfg(stm32f401)]
|
||||||
|
pub(crate) const SYSCLK_MAX: u32 = 84_000_000;
|
||||||
|
#[cfg(any(stm32f405, stm32f407, stm32f415, stm32f417,))]
|
||||||
|
pub(crate) const SYSCLK_MAX: u32 = 168_000_000;
|
||||||
|
#[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))]
|
||||||
|
pub(crate) const SYSCLK_MAX: u32 = 100_000_000;
|
||||||
|
#[cfg(any(
|
||||||
|
stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479,
|
||||||
|
))]
|
||||||
|
pub(crate) const SYSCLK_MAX: u32 = 180_000_000;
|
||||||
|
|
||||||
|
pub(crate) const HCLK_OVERDRIVE_FREQUENCY: u32 = 168_000_000;
|
||||||
|
|
||||||
|
pub(crate) const PCLK1_MAX: u32 = PCLK2_MAX / 2;
|
||||||
|
|
||||||
|
#[cfg(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))]
|
||||||
|
pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX;
|
||||||
|
#[cfg(not(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,)))]
|
||||||
|
pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX / 2;
|
||||||
|
|
||||||
|
pub(crate) const PLL_48_CLK: u32 = 48_000_000;
|
||||||
|
pub(crate) const PLL_48_TOLERANCE: u32 = 120_000;
|
||||||
|
}
|
@ -1,21 +0,0 @@
|
|||||||
#[cfg(stm32f401)]
|
|
||||||
pub(crate) const SYSCLK_MAX: u32 = 84_000_000;
|
|
||||||
|
|
||||||
#[cfg(any(stm32f405, stm32f407, stm32f415, stm32f417,))]
|
|
||||||
pub(crate) const SYSCLK_MAX: u32 = 168_000_000;
|
|
||||||
|
|
||||||
#[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))]
|
|
||||||
pub(crate) const SYSCLK_MAX: u32 = 100_000_000;
|
|
||||||
|
|
||||||
#[cfg(any(
|
|
||||||
stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479,
|
|
||||||
))]
|
|
||||||
pub(crate) const SYSCLK_MAX: u32 = 180_000_000;
|
|
||||||
|
|
||||||
#[cfg(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))]
|
|
||||||
pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX;
|
|
||||||
|
|
||||||
#[cfg(not(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,)))]
|
|
||||||
pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX / 2;
|
|
||||||
|
|
||||||
pub(crate) const PCLK1_MAX: u32 = PCLK2_MAX / 2;
|
|
@ -1,311 +0,0 @@
|
|||||||
use crate::pac::{FLASH, PWR, RCC};
|
|
||||||
use crate::peripherals;
|
|
||||||
use crate::rcc::{get_freqs, set_freqs, Clocks};
|
|
||||||
use crate::time::Hertz;
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
use embassy::util::Unborrow;
|
|
||||||
|
|
||||||
mod max;
|
|
||||||
use max::{PCLK1_MAX, PCLK2_MAX};
|
|
||||||
|
|
||||||
const HSI: u32 = 16_000_000;
|
|
||||||
|
|
||||||
/// Clocks configutation
|
|
||||||
#[non_exhaustive]
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Config {
|
|
||||||
pub hse: Option<Hertz>,
|
|
||||||
pub bypass_hse: bool,
|
|
||||||
pub pll48: bool,
|
|
||||||
pub sys_ck: Option<Hertz>,
|
|
||||||
pub hclk: Option<Hertz>,
|
|
||||||
pub pclk1: Option<Hertz>,
|
|
||||||
pub pclk2: Option<Hertz>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// RCC peripheral
|
|
||||||
pub struct Rcc<'d> {
|
|
||||||
config: Config,
|
|
||||||
phantom: PhantomData<&'d mut peripherals::RCC>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d> Rcc<'d> {
|
|
||||||
pub fn new(_rcc: impl Unborrow<Target = peripherals::RCC> + 'd, config: Config) -> Self {
|
|
||||||
Self {
|
|
||||||
config,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn freeze(mut self) -> Clocks {
|
|
||||||
use super::sealed::RccPeripheral;
|
|
||||||
use crate::pac::rcc::vals::{Hpre, Hsebyp, Ppre, Sw};
|
|
||||||
|
|
||||||
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 {
|
|
||||||
assert!(
|
|
||||||
// USB specification allows +-0.25%
|
|
||||||
plls.pll48clk
|
|
||||||
.map(|freq| (48_000_000 - freq as i32).abs() <= 120_000)
|
|
||||||
.unwrap_or(false)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let sysclk = if sysclk_on_pll {
|
|
||||||
unwrap!(plls.pllsysclk)
|
|
||||||
} else {
|
|
||||||
sysclk
|
|
||||||
};
|
|
||||||
|
|
||||||
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(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 <= PCLK1_MAX);
|
|
||||||
|
|
||||||
let pclk2 = self
|
|
||||||
.config
|
|
||||||
.pclk2
|
|
||||||
.map(|p| p.0)
|
|
||||||
.unwrap_or_else(|| core::cmp::min(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 <= 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 > 168_000_000 {
|
|
||||||
peripherals::PWR::enable();
|
|
||||||
|
|
||||||
PWR.cr().modify(|w| w.set_oden(true));
|
|
||||||
while !PWR.csr().read().odrdy() {}
|
|
||||||
|
|
||||||
PWR.cr().modify(|w| w.set_odswen(true));
|
|
||||||
while !PWR.csr().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)));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn init(config: Config) {
|
|
||||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
|
||||||
let clocks = Rcc::new(r, config).freeze();
|
|
||||||
set_freqs(clocks);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PllResults {
|
|
||||||
use_pll: bool,
|
|
||||||
pllsysclk: Option<u32>,
|
|
||||||
pll48clk: Option<u32>,
|
|
||||||
}
|
|
321
embassy-stm32/src/rcc/f7.rs
Normal file
321
embassy-stm32/src/rcc/f7.rs
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
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::rcc::{set_freqs, Clocks};
|
||||||
|
use crate::time::Hertz;
|
||||||
|
|
||||||
|
const HSI: u32 = 16_000_000;
|
||||||
|
|
||||||
|
/// Clocks configuration
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Config {
|
||||||
|
pub hse: Option<Hertz>,
|
||||||
|
pub bypass_hse: bool,
|
||||||
|
pub hclk: Option<Hertz>,
|
||||||
|
pub sys_ck: Option<Hertz>,
|
||||||
|
pub pclk1: Option<Hertz>,
|
||||||
|
pub pclk2: Option<Hertz>,
|
||||||
|
|
||||||
|
pub pll48: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe 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(|_| {
|
||||||
|
FLASH
|
||||||
|
.acr()
|
||||||
|
.modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
|
crate::peripherals::PWR::enable();
|
||||||
|
|
||||||
|
if let Some(hse) = config.hse {
|
||||||
|
if config.bypass_hse {
|
||||||
|
assert!((max::HSE_BYPASS_MIN..=max::HSE_BYPASS_MAX).contains(&hse.0));
|
||||||
|
} 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 {
|
||||||
|
use_pll: bool,
|
||||||
|
pllsysclk: Option<u32>,
|
||||||
|
pll48clk: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
mod max {
|
||||||
|
pub(crate) const HSE_OSC_MIN: u32 = 4_000_000;
|
||||||
|
pub(crate) const HSE_OSC_MAX: u32 = 26_000_000;
|
||||||
|
pub(crate) const HSE_BYPASS_MIN: u32 = 1_000_000;
|
||||||
|
pub(crate) const HSE_BYPASS_MAX: u32 = 50_000_000;
|
||||||
|
|
||||||
|
pub(crate) const HCLK_MAX: u32 = 216_000_000;
|
||||||
|
pub(crate) const HCLK_OVERDRIVE_FREQUENCY: u32 = 180_000_000;
|
||||||
|
|
||||||
|
pub(crate) const SYSCLK_MIN: u32 = 12_500_000;
|
||||||
|
pub(crate) const SYSCLK_MAX: u32 = 216_000_000;
|
||||||
|
|
||||||
|
pub(crate) const PCLK1_MIN: u32 = SYSCLK_MIN;
|
||||||
|
pub(crate) const PCLK1_MAX: u32 = SYSCLK_MAX / 4;
|
||||||
|
|
||||||
|
pub(crate) const PCLK2_MIN: u32 = SYSCLK_MIN;
|
||||||
|
pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX / 2;
|
||||||
|
|
||||||
|
// USB specification allows +-0.25%
|
||||||
|
pub(crate) const PLL_48_CLK: u32 = 48_000_000;
|
||||||
|
pub(crate) const PLL_48_TOLERANCE: u32 = 120_000;
|
||||||
|
}
|
@ -1,19 +0,0 @@
|
|||||||
pub(crate) const HSE_OSC_MIN: u32 = 4_000_000;
|
|
||||||
pub(crate) const HSE_OSC_MAX: u32 = 26_000_000;
|
|
||||||
pub(crate) const HSE_BYPASS_MIN: u32 = 1_000_000;
|
|
||||||
pub(crate) const HSE_BYPASS_MAX: u32 = 50_000_000;
|
|
||||||
|
|
||||||
pub(crate) const HCLK_MAX: u32 = 216_000_000;
|
|
||||||
pub(crate) const HCLK_OVERDRIVE_FREQUENCY: u32 = 180_000_000;
|
|
||||||
|
|
||||||
pub(crate) const SYSCLK_MIN: u32 = 12_500_000;
|
|
||||||
pub(crate) const SYSCLK_MAX: u32 = 216_000_000;
|
|
||||||
|
|
||||||
pub(crate) const PCLK1_MIN: u32 = SYSCLK_MIN;
|
|
||||||
pub(crate) const PCLK1_MAX: u32 = SYSCLK_MAX / 4;
|
|
||||||
|
|
||||||
pub(crate) const PCLK2_MIN: u32 = SYSCLK_MIN;
|
|
||||||
pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX / 2;
|
|
||||||
|
|
||||||
pub(crate) const PLL_48_CLK: u32 = 48_000_000;
|
|
||||||
pub(crate) const PLL_48_TOLERANCE: u32 = 120_000;
|
|
@ -1,347 +0,0 @@
|
|||||||
mod max;
|
|
||||||
|
|
||||||
use crate::pac::{FLASH, PWR, RCC};
|
|
||||||
use crate::peripherals;
|
|
||||||
use crate::rcc::{get_freqs, set_freqs, Clocks};
|
|
||||||
use crate::time::Hertz;
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
use embassy::util::Unborrow;
|
|
||||||
|
|
||||||
const HSI: u32 = 16_000_000;
|
|
||||||
|
|
||||||
#[non_exhaustive]
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Config {
|
|
||||||
pub hse: Option<Hertz>,
|
|
||||||
pub bypass_hse: bool,
|
|
||||||
pub hclk: Option<Hertz>,
|
|
||||||
pub sys_ck: Option<Hertz>,
|
|
||||||
pub pclk1: Option<Hertz>,
|
|
||||||
pub pclk2: Option<Hertz>,
|
|
||||||
|
|
||||||
pub pll48: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// RCC peripheral
|
|
||||||
pub struct Rcc<'d> {
|
|
||||||
config: Config,
|
|
||||||
phantom: PhantomData<&'d mut peripherals::RCC>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn freeze(mut self) -> Clocks {
|
|
||||||
use super::sealed::RccPeripheral;
|
|
||||||
use crate::pac::pwr::vals::Vos;
|
|
||||||
use crate::pac::rcc::vals::{Hpre, Hsebyp, Ppre, Sw};
|
|
||||||
|
|
||||||
let base_clock = self.config.hse.map(|hse| hse.0).unwrap_or(HSI);
|
|
||||||
let sysclk = self.config.sys_ck.map(|sys| sys.0).unwrap_or(base_clock);
|
|
||||||
let sysclk_on_pll = sysclk != base_clock;
|
|
||||||
|
|
||||||
assert!((max::SYSCLK_MIN..=max::SYSCLK_MAX).contains(&sysclk));
|
|
||||||
|
|
||||||
let plls = self.setup_pll(
|
|
||||||
base_clock,
|
|
||||||
self.config.hse.is_some(),
|
|
||||||
if sysclk_on_pll { Some(sysclk) } else { None },
|
|
||||||
self.config.pll48,
|
|
||||||
);
|
|
||||||
|
|
||||||
if self.config.pll48 {
|
|
||||||
assert!(
|
|
||||||
// USB specification allows +-0.25%
|
|
||||||
plls.pll48clk
|
|
||||||
.map(|freq| (max::PLL_48_CLK as i32 - freq as i32).abs()
|
|
||||||
<= max::PLL_48_TOLERANCE as i32)
|
|
||||||
.unwrap_or(false)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
while !RCC.cr().read().pllrdy() {}
|
|
||||||
|
|
||||||
if hclk > max::HCLK_OVERDRIVE_FREQUENCY {
|
|
||||||
peripherals::PWR::enable();
|
|
||||||
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn init(config: Config) {
|
|
||||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
|
||||||
let clocks = Rcc::new(r, config).freeze();
|
|
||||||
set_freqs(clocks);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PllResults {
|
|
||||||
use_pll: bool,
|
|
||||||
pllsysclk: Option<u32>,
|
|
||||||
pll48clk: Option<u32>,
|
|
||||||
}
|
|
183
embassy-stm32/src/rcc/g0.rs
Normal file
183
embassy-stm32/src/rcc/g0.rs
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
use crate::pac::{PWR, RCC};
|
||||||
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
|
use crate::time::Hertz;
|
||||||
|
use crate::time::U32Ext;
|
||||||
|
|
||||||
|
/// HSI speed
|
||||||
|
pub const HSI_FREQ: u32 = 16_000_000;
|
||||||
|
|
||||||
|
/// LSI speed
|
||||||
|
pub const LSI_FREQ: u32 = 32_000;
|
||||||
|
|
||||||
|
/// System clock mux source
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum ClockSrc {
|
||||||
|
HSE(Hertz),
|
||||||
|
HSI16(HSI16Prescaler),
|
||||||
|
LSI,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum HSI16Prescaler {
|
||||||
|
NotDivided,
|
||||||
|
Div2,
|
||||||
|
Div4,
|
||||||
|
Div8,
|
||||||
|
Div16,
|
||||||
|
Div32,
|
||||||
|
Div64,
|
||||||
|
Div128,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<u8> for HSI16Prescaler {
|
||||||
|
fn into(self) -> u8 {
|
||||||
|
match self {
|
||||||
|
HSI16Prescaler::NotDivided => 0x00,
|
||||||
|
HSI16Prescaler::Div2 => 0x01,
|
||||||
|
HSI16Prescaler::Div4 => 0x02,
|
||||||
|
HSI16Prescaler::Div8 => 0x03,
|
||||||
|
HSI16Prescaler::Div16 => 0x04,
|
||||||
|
HSI16Prescaler::Div32 => 0x05,
|
||||||
|
HSI16Prescaler::Div64 => 0x06,
|
||||||
|
HSI16Prescaler::Div128 => 0x07,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// AHB prescaler
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
pub enum AHBPrescaler {
|
||||||
|
NotDivided,
|
||||||
|
Div2,
|
||||||
|
Div4,
|
||||||
|
Div8,
|
||||||
|
Div16,
|
||||||
|
Div64,
|
||||||
|
Div128,
|
||||||
|
Div256,
|
||||||
|
Div512,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// APB prescaler
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum APBPrescaler {
|
||||||
|
NotDivided,
|
||||||
|
Div2,
|
||||||
|
Div4,
|
||||||
|
Div8,
|
||||||
|
Div16,
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clocks configutation
|
||||||
|
pub struct Config {
|
||||||
|
pub mux: ClockSrc,
|
||||||
|
pub ahb_pre: AHBPrescaler,
|
||||||
|
pub apb_pre: APBPrescaler,
|
||||||
|
pub low_power_run: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Config {
|
||||||
|
Config {
|
||||||
|
mux: ClockSrc::HSI16(HSI16Prescaler::NotDivided),
|
||||||
|
ahb_pre: AHBPrescaler::NotDivided,
|
||||||
|
apb_pre: APBPrescaler::NotDivided,
|
||||||
|
low_power_run: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
|
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,234 +0,0 @@
|
|||||||
use crate::pac;
|
|
||||||
use crate::peripherals::{self, RCC};
|
|
||||||
use crate::rcc::{get_freqs, 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;
|
|
||||||
|
|
||||||
/// LSI speed
|
|
||||||
pub const LSI_FREQ: u32 = 32_000;
|
|
||||||
|
|
||||||
/// System clock mux source
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum ClockSrc {
|
|
||||||
HSE(Hertz),
|
|
||||||
HSI16(HSI16Prescaler),
|
|
||||||
LSI,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum HSI16Prescaler {
|
|
||||||
NotDivided,
|
|
||||||
Div2,
|
|
||||||
Div4,
|
|
||||||
Div8,
|
|
||||||
Div16,
|
|
||||||
Div32,
|
|
||||||
Div64,
|
|
||||||
Div128,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<u8> for HSI16Prescaler {
|
|
||||||
fn into(self) -> u8 {
|
|
||||||
match self {
|
|
||||||
HSI16Prescaler::NotDivided => 0x00,
|
|
||||||
HSI16Prescaler::Div2 => 0x01,
|
|
||||||
HSI16Prescaler::Div4 => 0x02,
|
|
||||||
HSI16Prescaler::Div8 => 0x03,
|
|
||||||
HSI16Prescaler::Div16 => 0x04,
|
|
||||||
HSI16Prescaler::Div32 => 0x05,
|
|
||||||
HSI16Prescaler::Div64 => 0x06,
|
|
||||||
HSI16Prescaler::Div128 => 0x07,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// AHB prescaler
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
|
||||||
pub enum AHBPrescaler {
|
|
||||||
NotDivided,
|
|
||||||
Div2,
|
|
||||||
Div4,
|
|
||||||
Div8,
|
|
||||||
Div16,
|
|
||||||
Div64,
|
|
||||||
Div128,
|
|
||||||
Div256,
|
|
||||||
Div512,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// APB prescaler
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum APBPrescaler {
|
|
||||||
NotDivided,
|
|
||||||
Div2,
|
|
||||||
Div4,
|
|
||||||
Div8,
|
|
||||||
Div16,
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clocks configutation
|
|
||||||
pub struct Config {
|
|
||||||
pub mux: ClockSrc,
|
|
||||||
pub ahb_pre: AHBPrescaler,
|
|
||||||
pub apb_pre: APBPrescaler,
|
|
||||||
pub low_power_run: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Config {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Config {
|
|
||||||
Config {
|
|
||||||
mux: ClockSrc::HSI16(HSI16Prescaler::NotDivided),
|
|
||||||
ahb_pre: AHBPrescaler::NotDivided,
|
|
||||||
apb_pre: APBPrescaler::NotDivided,
|
|
||||||
low_power_run: 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() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 unsafe fn init(config: Config) {
|
|
||||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
|
||||||
let clocks = r.freeze(config);
|
|
||||||
set_freqs(clocks);
|
|
||||||
}
|
|
161
embassy-stm32/src/rcc/g4.rs
Normal file
161
embassy-stm32/src/rcc/g4.rs
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
use crate::pac::{PWR, RCC};
|
||||||
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
|
use crate::time::Hertz;
|
||||||
|
use crate::time::U32Ext;
|
||||||
|
|
||||||
|
/// HSI speed
|
||||||
|
pub const HSI_FREQ: u32 = 16_000_000;
|
||||||
|
|
||||||
|
/// LSI speed
|
||||||
|
pub const LSI_FREQ: u32 = 32_000;
|
||||||
|
|
||||||
|
/// System clock mux source
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum ClockSrc {
|
||||||
|
HSE(Hertz),
|
||||||
|
HSI16,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// AHB prescaler
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
pub enum AHBPrescaler {
|
||||||
|
NotDivided,
|
||||||
|
Div2,
|
||||||
|
Div4,
|
||||||
|
Div8,
|
||||||
|
Div16,
|
||||||
|
Div64,
|
||||||
|
Div128,
|
||||||
|
Div256,
|
||||||
|
Div512,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// APB prescaler
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum APBPrescaler {
|
||||||
|
NotDivided,
|
||||||
|
Div2,
|
||||||
|
Div4,
|
||||||
|
Div8,
|
||||||
|
Div16,
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clocks configutation
|
||||||
|
pub struct Config {
|
||||||
|
pub mux: ClockSrc,
|
||||||
|
pub ahb_pre: AHBPrescaler,
|
||||||
|
pub apb1_pre: APBPrescaler,
|
||||||
|
pub apb2_pre: APBPrescaler,
|
||||||
|
pub low_power_run: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Config {
|
||||||
|
Config {
|
||||||
|
mux: ClockSrc::HSI16,
|
||||||
|
ahb_pre: AHBPrescaler::NotDivided,
|
||||||
|
apb1_pre: APBPrescaler::NotDivided,
|
||||||
|
apb2_pre: APBPrescaler::NotDivided,
|
||||||
|
low_power_run: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
|
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(),
|
||||||
|
});
|
||||||
|
}
|
@ -1,210 +0,0 @@
|
|||||||
use crate::pac;
|
|
||||||
use crate::peripherals::{self, RCC};
|
|
||||||
use crate::rcc::{get_freqs, 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;
|
|
||||||
|
|
||||||
/// LSI speed
|
|
||||||
pub const LSI_FREQ: u32 = 32_000;
|
|
||||||
|
|
||||||
/// System clock mux source
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum ClockSrc {
|
|
||||||
HSE(Hertz),
|
|
||||||
HSI16,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// AHB prescaler
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
|
||||||
pub enum AHBPrescaler {
|
|
||||||
NotDivided,
|
|
||||||
Div2,
|
|
||||||
Div4,
|
|
||||||
Div8,
|
|
||||||
Div16,
|
|
||||||
Div64,
|
|
||||||
Div128,
|
|
||||||
Div256,
|
|
||||||
Div512,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// APB prescaler
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum APBPrescaler {
|
|
||||||
NotDivided,
|
|
||||||
Div2,
|
|
||||||
Div4,
|
|
||||||
Div8,
|
|
||||||
Div16,
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clocks configutation
|
|
||||||
pub struct Config {
|
|
||||||
pub mux: ClockSrc,
|
|
||||||
pub ahb_pre: AHBPrescaler,
|
|
||||||
pub apb1_pre: APBPrescaler,
|
|
||||||
pub apb2_pre: APBPrescaler,
|
|
||||||
pub low_power_run: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Config {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Config {
|
|
||||||
Config {
|
|
||||||
mux: ClockSrc::HSI16,
|
|
||||||
ahb_pre: AHBPrescaler::NotDivided,
|
|
||||||
apb1_pre: APBPrescaler::NotDivided,
|
|
||||||
apb2_pre: APBPrescaler::NotDivided,
|
|
||||||
low_power_run: 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() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 unsafe fn init(config: Config) {
|
|
||||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
|
||||||
let clocks = r.freeze(config);
|
|
||||||
set_freqs(clocks);
|
|
||||||
}
|
|
875
embassy-stm32/src/rcc/h7.rs
Normal file
875
embassy-stm32/src/rcc/h7.rs
Normal file
@ -0,0 +1,875 @@
|
|||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use embassy::util::Unborrow;
|
||||||
|
use embassy_hal_common::unborrow;
|
||||||
|
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::vals::{Ckpersel, Dppre, Hpre, Hsebyp, Hsidiv, Pllsrc, Sw};
|
||||||
|
use crate::pac::{PWR, RCC, SYSCFG};
|
||||||
|
use crate::peripherals;
|
||||||
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
|
use crate::time::Hertz;
|
||||||
|
|
||||||
|
pub use pll::PllConfig;
|
||||||
|
|
||||||
|
const HSI: Hertz = Hertz(64_000_000);
|
||||||
|
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 {
|
||||||
|
pub hclk: Hertz,
|
||||||
|
pub pclk1: Hertz,
|
||||||
|
pub pclk2: Hertz,
|
||||||
|
pub pclk3: Hertz,
|
||||||
|
pub pclk4: Hertz,
|
||||||
|
pub ppre1: u8,
|
||||||
|
pub ppre2: u8,
|
||||||
|
pub ppre3: u8,
|
||||||
|
pub ppre4: u8,
|
||||||
|
pub csi_ck: Option<Hertz>,
|
||||||
|
pub hsi_ck: Option<Hertz>,
|
||||||
|
pub hsi48_ck: Option<Hertz>,
|
||||||
|
pub lsi_ck: Option<Hertz>,
|
||||||
|
pub per_ck: Option<Hertz>,
|
||||||
|
pub hse_ck: Option<Hertz>,
|
||||||
|
pub pll1_p_ck: Option<Hertz>,
|
||||||
|
pub pll1_q_ck: Option<Hertz>,
|
||||||
|
pub pll1_r_ck: Option<Hertz>,
|
||||||
|
pub pll2_p_ck: Option<Hertz>,
|
||||||
|
pub pll2_q_ck: Option<Hertz>,
|
||||||
|
pub pll2_r_ck: Option<Hertz>,
|
||||||
|
pub pll3_p_ck: Option<Hertz>,
|
||||||
|
pub pll3_q_ck: Option<Hertz>,
|
||||||
|
pub pll3_r_ck: Option<Hertz>,
|
||||||
|
pub timx_ker_ck: Option<Hertz>,
|
||||||
|
pub timy_ker_ck: Option<Hertz>,
|
||||||
|
pub sys_ck: Hertz,
|
||||||
|
pub c_ck: Hertz,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configuration of the core clocks
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Config {
|
||||||
|
pub hse: Option<Hertz>,
|
||||||
|
pub bypass_hse: bool,
|
||||||
|
pub sys_ck: Option<Hertz>,
|
||||||
|
pub per_ck: Option<Hertz>,
|
||||||
|
rcc_hclk: Option<Hertz>,
|
||||||
|
pub hclk: Option<Hertz>,
|
||||||
|
pub pclk1: Option<Hertz>,
|
||||||
|
pub pclk2: Option<Hertz>,
|
||||||
|
pub pclk3: Option<Hertz>,
|
||||||
|
pub pclk4: Option<Hertz>,
|
||||||
|
pub pll1: PllConfig,
|
||||||
|
pub pll2: PllConfig,
|
||||||
|
pub pll3: PllConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Setup traceclk
|
||||||
|
/// Returns a pll1_r_ck
|
||||||
|
fn traceclk_setup(config: &mut Config, sys_use_pll1_p: bool) {
|
||||||
|
let pll1_r_ck = match (sys_use_pll1_p, config.pll1.r_ck) {
|
||||||
|
// pll1_p_ck selected as system clock but pll1_r_ck not
|
||||||
|
// set. The traceclk mux is synchronous with the system
|
||||||
|
// clock mux, but has pll1_r_ck as an input. In order to
|
||||||
|
// keep traceclk running, we force a pll1_r_ck.
|
||||||
|
(true, None) => Some(Hertz(unwrap!(config.pll1.p_ck).0 / 2)),
|
||||||
|
|
||||||
|
// Either pll1 not selected as system clock, free choice
|
||||||
|
// of pll1_r_ck. Or pll1 is selected, assume user has set
|
||||||
|
// a suitable pll1_r_ck frequency.
|
||||||
|
_ => config.pll1.r_ck,
|
||||||
|
};
|
||||||
|
config.pll1.r_ck = pll1_r_ck;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Divider calculator for pclk 1 - 4
|
||||||
|
///
|
||||||
|
/// Returns real pclk, bits, ppre and the timer kernel clock
|
||||||
|
fn ppre_calculate(
|
||||||
|
requested_pclk: u32,
|
||||||
|
hclk: u32,
|
||||||
|
max_pclk: u32,
|
||||||
|
tim_pre: Option<Timpre>,
|
||||||
|
) -> (u32, u8, u8, Option<u32>) {
|
||||||
|
let (bits, ppre) = match (hclk + requested_pclk - 1) / requested_pclk {
|
||||||
|
0 => panic!(),
|
||||||
|
1 => (0b000, 1),
|
||||||
|
2 => (0b100, 2),
|
||||||
|
3..=5 => (0b101, 4),
|
||||||
|
6..=11 => (0b110, 8),
|
||||||
|
_ => (0b111, 16),
|
||||||
|
};
|
||||||
|
let real_pclk = hclk / u32::from(ppre);
|
||||||
|
assert!(real_pclk <= max_pclk);
|
||||||
|
|
||||||
|
let tim_ker_clk = if let Some(tim_pre) = tim_pre {
|
||||||
|
let clk = match (bits, tim_pre) {
|
||||||
|
(0b101, Timpre::DEFAULTX2) => hclk / 2,
|
||||||
|
(0b110, Timpre::DEFAULTX4) => hclk / 2,
|
||||||
|
(0b110, Timpre::DEFAULTX2) => hclk / 4,
|
||||||
|
(0b111, Timpre::DEFAULTX4) => hclk / 4,
|
||||||
|
(0b111, Timpre::DEFAULTX2) => hclk / 8,
|
||||||
|
_ => hclk,
|
||||||
|
};
|
||||||
|
Some(clk)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
(real_pclk, bits, ppre, tim_ker_clk)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Setup sys_ck
|
||||||
|
/// Returns sys_ck frequency, and a pll1_p_ck
|
||||||
|
fn sys_ck_setup(config: &mut Config, srcclk: Hertz) -> (Hertz, bool) {
|
||||||
|
// Compare available with wanted clocks
|
||||||
|
let sys_ck = config.sys_ck.unwrap_or(srcclk);
|
||||||
|
|
||||||
|
if sys_ck != srcclk {
|
||||||
|
// The requested system clock is not the immediately available
|
||||||
|
// HSE/HSI clock. Perhaps there are other ways of obtaining
|
||||||
|
// the requested system clock (such as `HSIDIV`) but we will
|
||||||
|
// ignore those for now.
|
||||||
|
//
|
||||||
|
// Therefore we must use pll1_p_ck
|
||||||
|
let pll1_p_ck = match config.pll1.p_ck {
|
||||||
|
Some(p_ck) => {
|
||||||
|
assert!(p_ck == sys_ck,
|
||||||
|
"Error: Cannot set pll1_p_ck independently as it must be used to generate sys_ck");
|
||||||
|
Some(p_ck)
|
||||||
|
}
|
||||||
|
None => Some(sys_ck),
|
||||||
|
};
|
||||||
|
config.pll1.p_ck = pll1_p_ck;
|
||||||
|
|
||||||
|
(sys_ck, true)
|
||||||
|
} else {
|
||||||
|
// sys_ck is derived directly from a source clock
|
||||||
|
// (HSE/HSI). pll1_p_ck can be as requested
|
||||||
|
(sys_ck, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flash_setup(rcc_aclk: u32, vos: VoltageScale) {
|
||||||
|
use crate::pac::FLASH;
|
||||||
|
|
||||||
|
// ACLK in MHz, round down and subtract 1 from integers. eg.
|
||||||
|
// 61_999_999 -> 61MHz
|
||||||
|
// 62_000_000 -> 61MHz
|
||||||
|
// 62_000_001 -> 62MHz
|
||||||
|
let rcc_aclk_mhz = (rcc_aclk - 1) / 1_000_000;
|
||||||
|
|
||||||
|
// See RM0433 Rev 7 Table 17. FLASH recommended number of wait
|
||||||
|
// states and programming delay
|
||||||
|
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,
|
||||||
|
Divided(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl McoClock {
|
||||||
|
fn into_raw(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
McoClock::Disabled => 0,
|
||||||
|
McoClock::Bypassed => 1,
|
||||||
|
McoClock::Divided(divisor) => {
|
||||||
|
if *divisor > 15 {
|
||||||
|
panic!("Mco divisor must be less than 15. Refer to the reference manual for more information.")
|
||||||
|
}
|
||||||
|
*divisor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Mco1Source {
|
||||||
|
Hsi,
|
||||||
|
Lse,
|
||||||
|
Hse,
|
||||||
|
Pll1Q,
|
||||||
|
Hsi48,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Mco1Source {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Hsi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait McoSource {
|
||||||
|
type Raw;
|
||||||
|
|
||||||
|
fn into_raw(&self) -> Self::Raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl McoSource for Mco1Source {
|
||||||
|
type Raw = Mco1;
|
||||||
|
fn into_raw(&self) -> Self::Raw {
|
||||||
|
match self {
|
||||||
|
Mco1Source::Hsi => Mco1::HSI,
|
||||||
|
Mco1Source::Lse => Mco1::LSE,
|
||||||
|
Mco1Source::Hse => Mco1::HSE,
|
||||||
|
Mco1Source::Pll1Q => Mco1::PLL1_Q,
|
||||||
|
Mco1Source::Hsi48 => Mco1::HSI48,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Mco2Source {
|
||||||
|
SysClk,
|
||||||
|
Pll2Q,
|
||||||
|
Hse,
|
||||||
|
Pll1Q,
|
||||||
|
Csi,
|
||||||
|
Lsi,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Mco2Source {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::SysClk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl McoSource for Mco2Source {
|
||||||
|
type Raw = Mco2;
|
||||||
|
fn into_raw(&self) -> Self::Raw {
|
||||||
|
match self {
|
||||||
|
Mco2Source::SysClk => Mco2::SYSCLK,
|
||||||
|
Mco2Source::Pll2Q => Mco2::PLL2_P,
|
||||||
|
Mco2Source::Hse => Mco2::HSE,
|
||||||
|
Mco2Source::Pll1Q => Mco2::PLL1_P,
|
||||||
|
Mco2Source::Csi => Mco2::CSI,
|
||||||
|
Mco2Source::Lsi => Mco2::LSI,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) mod sealed {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub trait McoInstance {
|
||||||
|
type Source;
|
||||||
|
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait McoPin<T: McoInstance>: Pin {
|
||||||
|
fn configure(&mut self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait McoInstance: sealed::McoInstance + 'static {}
|
||||||
|
|
||||||
|
pub trait McoPin<T: McoInstance>: sealed::McoPin<T> + 'static {}
|
||||||
|
|
||||||
|
macro_rules! impl_peri {
|
||||||
|
($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => {
|
||||||
|
impl sealed::McoInstance for peripherals::$peri {
|
||||||
|
type Source = $source;
|
||||||
|
|
||||||
|
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) {
|
||||||
|
RCC.cfgr().modify(|w| {
|
||||||
|
w.$set_source(source);
|
||||||
|
w.$set_prescaler(prescaler);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl McoInstance for peripherals::$peri {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre);
|
||||||
|
impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre);
|
||||||
|
|
||||||
|
macro_rules! impl_pin {
|
||||||
|
($peri:ident, $pin:ident, $af:expr) => {
|
||||||
|
impl McoPin<peripherals::$peri> for peripherals::$pin {}
|
||||||
|
|
||||||
|
impl sealed::McoPin<peripherals::$peri> for peripherals::$pin {
|
||||||
|
fn configure(&mut self) {
|
||||||
|
critical_section::with(|_| unsafe {
|
||||||
|
self.set_as_af($af, crate::gpio::sealed::AFType::OutputPushPull);
|
||||||
|
self.block().ospeedr().modify(|w| {
|
||||||
|
w.set_ospeedr(
|
||||||
|
self.pin() as usize,
|
||||||
|
crate::pac::gpio::vals::Ospeedr::VERYHIGHSPEED,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::pac::peripheral_pins!(
|
||||||
|
($inst:ident, rcc, RCC, $pin:ident, MCO_1, $af:expr) => {
|
||||||
|
impl_pin!(MCO1, $pin, $af);
|
||||||
|
};
|
||||||
|
($inst:ident, rcc, RCC, $pin:ident, MCO_2, $af:expr) => {
|
||||||
|
impl_pin!(MCO2, $pin, $af);
|
||||||
|
};
|
||||||
|
);
|
||||||
|
|
||||||
|
pub struct Mco<'d, T: McoInstance> {
|
||||||
|
phantom: PhantomData<&'d mut T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: McoInstance> Mco<'d, T> {
|
||||||
|
pub fn new(
|
||||||
|
_peri: impl Unborrow<Target = T> + 'd,
|
||||||
|
pin: impl Unborrow<Target = impl McoPin<T>> + 'd,
|
||||||
|
source: impl McoSource<Raw = T::Source>,
|
||||||
|
prescaler: McoClock,
|
||||||
|
) -> Self {
|
||||||
|
unborrow!(pin);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
T::apply_clock_settings(source.into_raw(), prescaler.into_raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
pin.configure();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn init(mut config: Config) {
|
||||||
|
// TODO make configurable?
|
||||||
|
let enable_overdrive = false;
|
||||||
|
|
||||||
|
// NB. The lower bytes of CR3 can only be written once after
|
||||||
|
// POR, and must be written with a valid combination. Refer to
|
||||||
|
// RM0433 Rev 7 6.8.4. This is partially enforced by dropping
|
||||||
|
// `self` at the end of this method, but of course we cannot
|
||||||
|
// know what happened between the previous POR and here.
|
||||||
|
#[cfg(pwr_h7)]
|
||||||
|
PWR.cr3().modify(|w| {
|
||||||
|
w.set_scuen(true);
|
||||||
|
w.set_ldoen(true);
|
||||||
|
w.set_bypass(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(pwr_h7smps)]
|
||||||
|
PWR.cr3().modify(|w| {
|
||||||
|
// hardcode "Direct SPMS" for now, this is what works on nucleos with the
|
||||||
|
// default solderbridge configuration.
|
||||||
|
w.set_sden(true);
|
||||||
|
w.set_ldoen(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Validate the supply configuration. If you are stuck here, it is
|
||||||
|
// because the voltages on your board do not match those specified
|
||||||
|
// in the D3CR.VOS and CR3.SDLEVEL fields. By default after reset
|
||||||
|
// VOS = Scale 3, so check that the voltage on the VCAP pins =
|
||||||
|
// 1.0V.
|
||||||
|
while !PWR.csr1().read().actvosrdy() {}
|
||||||
|
|
||||||
|
// Go to Scale 1
|
||||||
|
PWR.d3cr().modify(|w| w.set_vos(0b11));
|
||||||
|
while !PWR.d3cr().read().vosrdy() {}
|
||||||
|
|
||||||
|
let pwr_vos = if !enable_overdrive {
|
||||||
|
VoltageScale::Scale1
|
||||||
|
} else {
|
||||||
|
critical_section::with(|_| {
|
||||||
|
RCC.apb4enr().modify(|w| w.set_syscfgen(true));
|
||||||
|
|
||||||
|
SYSCFG.pwrcr().modify(|w| w.set_oden(1));
|
||||||
|
});
|
||||||
|
while !PWR.d3cr().read().vosrdy() {}
|
||||||
|
VoltageScale::Scale0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Freeze the core clocks, returning a Core Clocks Distribution
|
||||||
|
// and Reset (CCDR) structure. The actual frequency of the clocks
|
||||||
|
// configured is returned in the `clocks` member of the CCDR
|
||||||
|
// structure.
|
||||||
|
//
|
||||||
|
// Note that `freeze` will never result in a clock _faster_ than
|
||||||
|
// that specified. It may result in a clock that is a factor of [1,
|
||||||
|
// 2) slower.
|
||||||
|
//
|
||||||
|
// `syscfg` is required to enable the I/O compensation cell.
|
||||||
|
//
|
||||||
|
// # Panics
|
||||||
|
//
|
||||||
|
// If a clock specification cannot be achieved within the
|
||||||
|
// hardware specification then this function will panic. This
|
||||||
|
// function may also panic if a clock specification can be
|
||||||
|
// achieved, but the mechanism for doing so is not yet
|
||||||
|
// implemented here.
|
||||||
|
|
||||||
|
let srcclk = config.hse.unwrap_or(HSI); // 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,
|
||||||
|
ahb2: core_clocks.hclk,
|
||||||
|
ahb3: core_clocks.hclk,
|
||||||
|
ahb4: core_clocks.hclk,
|
||||||
|
apb1: core_clocks.pclk1,
|
||||||
|
apb2: core_clocks.pclk2,
|
||||||
|
apb4: core_clocks.pclk4,
|
||||||
|
apb1_tim: core_clocks.timx_ker_ck.unwrap_or(core_clocks.pclk1),
|
||||||
|
apb2_tim: core_clocks.timy_ker_ck.unwrap_or(core_clocks.pclk2),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mod pll {
|
||||||
|
use super::{Hertz, RCC};
|
||||||
|
|
||||||
|
const VCO_MIN: u32 = 150_000_000;
|
||||||
|
const VCO_MAX: u32 = 420_000_000;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct PllConfig {
|
||||||
|
pub p_ck: Option<Hertz>,
|
||||||
|
pub q_ck: Option<Hertz>,
|
||||||
|
pub r_ck: Option<Hertz>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct PllConfigResults {
|
||||||
|
pub ref_x_ck: u32,
|
||||||
|
pub pll_x_m: u32,
|
||||||
|
pub pll_x_p: u32,
|
||||||
|
pub vco_ck_target: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vco_output_divider_setup(output: u32, plln: usize) -> (u32, u32) {
|
||||||
|
let pll_x_p = if plln == 0 {
|
||||||
|
if output > VCO_MAX / 2 {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
((VCO_MAX / output) | 1) - 1 // Must be even or unity
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Specific to PLL2/3, will subtract 1 later
|
||||||
|
if output > VCO_MAX / 2 {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
VCO_MAX / output
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let vco_ck = output + pll_x_p;
|
||||||
|
|
||||||
|
assert!(pll_x_p < 128);
|
||||||
|
assert!(vco_ck >= VCO_MIN);
|
||||||
|
assert!(vco_ck <= VCO_MAX);
|
||||||
|
|
||||||
|
(vco_ck, pll_x_p)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Must have exclusive access to the RCC register block
|
||||||
|
unsafe fn vco_setup(pll_src: u32, requested_output: u32, plln: usize) -> PllConfigResults {
|
||||||
|
use crate::pac::rcc::vals::{Pllrge, Pllvcosel};
|
||||||
|
|
||||||
|
let (vco_ck_target, pll_x_p) = vco_output_divider_setup(requested_output, plln);
|
||||||
|
|
||||||
|
// Input divisor, resulting in a reference clock in the range
|
||||||
|
// 1 to 2 MHz. Choose the highest reference clock (lowest m)
|
||||||
|
let pll_x_m = (pll_src + 1_999_999) / 2_000_000;
|
||||||
|
assert!(pll_x_m < 64);
|
||||||
|
|
||||||
|
// Calculate resulting reference clock
|
||||||
|
let ref_x_ck = pll_src / pll_x_m;
|
||||||
|
assert!((1_000_000..=2_000_000).contains(&ref_x_ck));
|
||||||
|
|
||||||
|
RCC.pllcfgr().modify(|w| {
|
||||||
|
w.set_pllvcosel(plln, Pllvcosel::MEDIUMVCO);
|
||||||
|
w.set_pllrge(plln, Pllrge::RANGE1);
|
||||||
|
});
|
||||||
|
PllConfigResults {
|
||||||
|
ref_x_ck,
|
||||||
|
pll_x_m,
|
||||||
|
pll_x_p,
|
||||||
|
vco_ck_target,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Must have exclusive access to the RCC register block
|
||||||
|
pub(super) unsafe fn pll_setup(
|
||||||
|
pll_src: u32,
|
||||||
|
config: &PllConfig,
|
||||||
|
plln: usize,
|
||||||
|
) -> (Option<u32>, Option<u32>, Option<u32>) {
|
||||||
|
use crate::pac::rcc::vals::Divp;
|
||||||
|
|
||||||
|
match config.p_ck {
|
||||||
|
Some(requested_output) => {
|
||||||
|
let config_results = vco_setup(pll_src, requested_output.0, plln);
|
||||||
|
let PllConfigResults {
|
||||||
|
ref_x_ck,
|
||||||
|
pll_x_m,
|
||||||
|
pll_x_p,
|
||||||
|
vco_ck_target,
|
||||||
|
} = config_results;
|
||||||
|
|
||||||
|
RCC.pllckselr().modify(|w| w.set_divm(plln, pll_x_m as u8));
|
||||||
|
|
||||||
|
// Feedback divider. Integer only
|
||||||
|
let pll_x_n = vco_ck_target / ref_x_ck;
|
||||||
|
assert!(pll_x_n >= 4);
|
||||||
|
assert!(pll_x_n <= 512);
|
||||||
|
RCC.plldivr(plln)
|
||||||
|
.modify(|w| w.set_divn1((pll_x_n - 1) as u16));
|
||||||
|
|
||||||
|
// No FRACN
|
||||||
|
RCC.pllcfgr().modify(|w| w.set_pllfracen(plln, false));
|
||||||
|
let vco_ck = ref_x_ck * pll_x_n;
|
||||||
|
|
||||||
|
RCC.plldivr(plln)
|
||||||
|
.modify(|w| w.set_divp1(Divp((pll_x_p - 1) as u8)));
|
||||||
|
RCC.pllcfgr().modify(|w| w.set_divpen(plln, true));
|
||||||
|
|
||||||
|
// Calulate additional output dividers
|
||||||
|
let q_ck = match config.q_ck {
|
||||||
|
Some(Hertz(ck)) if ck > 0 => {
|
||||||
|
let div = (vco_ck + ck - 1) / ck;
|
||||||
|
RCC.plldivr(plln).modify(|w| w.set_divq1((div - 1) as u8));
|
||||||
|
RCC.pllcfgr().modify(|w| w.set_divqen(plln, true));
|
||||||
|
Some(vco_ck / div)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
let r_ck = match config.r_ck {
|
||||||
|
Some(Hertz(ck)) if ck > 0 => {
|
||||||
|
let div = (vco_ck + ck - 1) / ck;
|
||||||
|
RCC.plldivr(plln).modify(|w| w.set_divr1((div - 1) as u8));
|
||||||
|
RCC.pllcfgr().modify(|w| w.set_divren(plln, true));
|
||||||
|
Some(vco_ck / div)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
(Some(vco_ck / pll_x_p), q_ck, r_ck)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
assert!(
|
||||||
|
config.q_ck.is_none(),
|
||||||
|
"Must set PLL P clock for Q clock to take effect!"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
config.r_ck.is_none(),
|
||||||
|
"Must set PLL P clock for R clock to take effect!"
|
||||||
|
);
|
||||||
|
(None, None, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,702 +0,0 @@
|
|||||||
use core::marker::PhantomData;
|
|
||||||
|
|
||||||
use embassy::util::Unborrow;
|
|
||||||
use embassy_hal_common::unborrow;
|
|
||||||
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::peripherals;
|
|
||||||
use crate::pwr::{Power, VoltageScale};
|
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
|
||||||
use crate::time::Hertz;
|
|
||||||
|
|
||||||
mod pll;
|
|
||||||
use pll::pll_setup;
|
|
||||||
pub use pll::PllConfig;
|
|
||||||
|
|
||||||
const HSI: Hertz = Hertz(64_000_000);
|
|
||||||
const CSI: Hertz = Hertz(4_000_000);
|
|
||||||
const HSI48: Hertz = Hertz(48_000_000);
|
|
||||||
const LSI: Hertz = Hertz(32_000);
|
|
||||||
|
|
||||||
/// Core clock frequencies
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct CoreClocks {
|
|
||||||
pub hclk: Hertz,
|
|
||||||
pub pclk1: Hertz,
|
|
||||||
pub pclk2: Hertz,
|
|
||||||
pub pclk3: Hertz,
|
|
||||||
pub pclk4: Hertz,
|
|
||||||
pub ppre1: u8,
|
|
||||||
pub ppre2: u8,
|
|
||||||
pub ppre3: u8,
|
|
||||||
pub ppre4: u8,
|
|
||||||
pub csi_ck: Option<Hertz>,
|
|
||||||
pub hsi_ck: Option<Hertz>,
|
|
||||||
pub hsi48_ck: Option<Hertz>,
|
|
||||||
pub lsi_ck: Option<Hertz>,
|
|
||||||
pub per_ck: Option<Hertz>,
|
|
||||||
pub hse_ck: Option<Hertz>,
|
|
||||||
pub pll1_p_ck: Option<Hertz>,
|
|
||||||
pub pll1_q_ck: Option<Hertz>,
|
|
||||||
pub pll1_r_ck: Option<Hertz>,
|
|
||||||
pub pll2_p_ck: Option<Hertz>,
|
|
||||||
pub pll2_q_ck: Option<Hertz>,
|
|
||||||
pub pll2_r_ck: Option<Hertz>,
|
|
||||||
pub pll3_p_ck: Option<Hertz>,
|
|
||||||
pub pll3_q_ck: Option<Hertz>,
|
|
||||||
pub pll3_r_ck: Option<Hertz>,
|
|
||||||
pub timx_ker_ck: Option<Hertz>,
|
|
||||||
pub timy_ker_ck: Option<Hertz>,
|
|
||||||
pub sys_ck: Hertz,
|
|
||||||
pub c_ck: Hertz,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configuration of the core clocks
|
|
||||||
#[non_exhaustive]
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Config {
|
|
||||||
pub hse: Option<Hertz>,
|
|
||||||
pub bypass_hse: bool,
|
|
||||||
pub sys_ck: Option<Hertz>,
|
|
||||||
pub per_ck: Option<Hertz>,
|
|
||||||
rcc_hclk: Option<Hertz>,
|
|
||||||
pub hclk: Option<Hertz>,
|
|
||||||
pub pclk1: Option<Hertz>,
|
|
||||||
pub pclk2: Option<Hertz>,
|
|
||||||
pub pclk3: Option<Hertz>,
|
|
||||||
pub pclk4: Option<Hertz>,
|
|
||||||
pub pll1: PllConfig,
|
|
||||||
pub pll2: PllConfig,
|
|
||||||
pub pll3: PllConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub 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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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_setup(srcclk.0, &self.config.pll1, 0) };
|
|
||||||
let (pll2_p_ck, pll2_q_ck, pll2_r_ck) =
|
|
||||||
unsafe { pll_setup(srcclk.0, &self.config.pll2, 1) };
|
|
||||||
let (pll3_p_ck, pll3_q_ck, pll3_r_ck) =
|
|
||||||
unsafe { 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
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
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,
|
|
||||||
"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),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
Divided(u8),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl McoClock {
|
|
||||||
fn into_raw(&self) -> u8 {
|
|
||||||
match self {
|
|
||||||
McoClock::Disabled => 0,
|
|
||||||
McoClock::Bypassed => 1,
|
|
||||||
McoClock::Divided(divisor) => {
|
|
||||||
if *divisor > 15 {
|
|
||||||
panic!("Mco divisor must be less than 15. Refer to the reference manual for more information.")
|
|
||||||
}
|
|
||||||
*divisor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub enum Mco1Source {
|
|
||||||
Hsi,
|
|
||||||
Lse,
|
|
||||||
Hse,
|
|
||||||
Pll1Q,
|
|
||||||
Hsi48,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Mco1Source {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Hsi
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait McoSource {
|
|
||||||
type Raw;
|
|
||||||
|
|
||||||
fn into_raw(&self) -> Self::Raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl McoSource for Mco1Source {
|
|
||||||
type Raw = Mco1;
|
|
||||||
fn into_raw(&self) -> Self::Raw {
|
|
||||||
match self {
|
|
||||||
Mco1Source::Hsi => Mco1::HSI,
|
|
||||||
Mco1Source::Lse => Mco1::LSE,
|
|
||||||
Mco1Source::Hse => Mco1::HSE,
|
|
||||||
Mco1Source::Pll1Q => Mco1::PLL1_Q,
|
|
||||||
Mco1Source::Hsi48 => Mco1::HSI48,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub enum Mco2Source {
|
|
||||||
SysClk,
|
|
||||||
Pll2Q,
|
|
||||||
Hse,
|
|
||||||
Pll1Q,
|
|
||||||
Csi,
|
|
||||||
Lsi,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Mco2Source {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::SysClk
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl McoSource for Mco2Source {
|
|
||||||
type Raw = Mco2;
|
|
||||||
fn into_raw(&self) -> Self::Raw {
|
|
||||||
match self {
|
|
||||||
Mco2Source::SysClk => Mco2::SYSCLK,
|
|
||||||
Mco2Source::Pll2Q => Mco2::PLL2_P,
|
|
||||||
Mco2Source::Hse => Mco2::HSE,
|
|
||||||
Mco2Source::Pll1Q => Mco2::PLL1_P,
|
|
||||||
Mco2Source::Csi => Mco2::CSI,
|
|
||||||
Mco2Source::Lsi => Mco2::LSI,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub trait McoInstance {
|
|
||||||
type Source;
|
|
||||||
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait McoPin<T: McoInstance>: Pin {
|
|
||||||
fn configure(&mut self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait McoInstance: sealed::McoInstance + 'static {}
|
|
||||||
|
|
||||||
pub trait McoPin<T: McoInstance>: sealed::McoPin<T> + 'static {}
|
|
||||||
|
|
||||||
macro_rules! impl_peri {
|
|
||||||
($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => {
|
|
||||||
impl sealed::McoInstance for peripherals::$peri {
|
|
||||||
type Source = $source;
|
|
||||||
|
|
||||||
unsafe fn apply_clock_settings(source: Self::Source, prescaler: u8) {
|
|
||||||
RCC.cfgr().modify(|w| {
|
|
||||||
w.$set_source(source);
|
|
||||||
w.$set_prescaler(prescaler);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl McoInstance for peripherals::$peri {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_peri!(MCO1, Mco1, set_mco1, set_mco1pre);
|
|
||||||
impl_peri!(MCO2, Mco2, set_mco2, set_mco2pre);
|
|
||||||
|
|
||||||
macro_rules! impl_pin {
|
|
||||||
($peri:ident, $pin:ident, $af:expr) => {
|
|
||||||
impl McoPin<peripherals::$peri> for peripherals::$pin {}
|
|
||||||
|
|
||||||
impl sealed::McoPin<peripherals::$peri> for peripherals::$pin {
|
|
||||||
fn configure(&mut self) {
|
|
||||||
critical_section::with(|_| unsafe {
|
|
||||||
self.set_as_af($af, crate::gpio::sealed::AFType::OutputPushPull);
|
|
||||||
self.block().ospeedr().modify(|w| {
|
|
||||||
w.set_ospeedr(
|
|
||||||
self.pin() as usize,
|
|
||||||
crate::pac::gpio::vals::Ospeedr::VERYHIGHSPEED,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
crate::pac::peripheral_pins!(
|
|
||||||
($inst:ident, rcc, RCC, $pin:ident, MCO_1, $af:expr) => {
|
|
||||||
impl_pin!(MCO1, $pin, $af);
|
|
||||||
};
|
|
||||||
($inst:ident, rcc, RCC, $pin:ident, MCO_2, $af:expr) => {
|
|
||||||
impl_pin!(MCO2, $pin, $af);
|
|
||||||
};
|
|
||||||
);
|
|
||||||
|
|
||||||
pub struct Mco<'d, T: McoInstance> {
|
|
||||||
phantom: PhantomData<&'d mut T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, T: McoInstance> Mco<'d, T> {
|
|
||||||
pub fn new(
|
|
||||||
_peri: impl Unborrow<Target = T> + 'd,
|
|
||||||
pin: impl Unborrow<Target = impl McoPin<T>> + 'd,
|
|
||||||
source: impl McoSource<Raw = T::Source>,
|
|
||||||
prescaler: McoClock,
|
|
||||||
) -> Self {
|
|
||||||
unborrow!(pin);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
T::apply_clock_settings(source.into_raw(), prescaler.into_raw());
|
|
||||||
}
|
|
||||||
|
|
||||||
pin.configure();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub 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);
|
|
||||||
set_freqs(Clocks {
|
|
||||||
sys: core_clocks.c_ck,
|
|
||||||
ahb1: core_clocks.hclk,
|
|
||||||
ahb2: core_clocks.hclk,
|
|
||||||
ahb3: core_clocks.hclk,
|
|
||||||
ahb4: core_clocks.hclk,
|
|
||||||
apb1: core_clocks.pclk1,
|
|
||||||
apb2: core_clocks.pclk2,
|
|
||||||
apb4: core_clocks.pclk4,
|
|
||||||
apb1_tim: core_clocks.timx_ker_ck.unwrap_or(core_clocks.pclk1),
|
|
||||||
apb2_tim: core_clocks.timy_ker_ck.unwrap_or(core_clocks.pclk2),
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,145 +0,0 @@
|
|||||||
use super::{Hertz, RCC};
|
|
||||||
|
|
||||||
const VCO_MIN: u32 = 150_000_000;
|
|
||||||
const VCO_MAX: u32 = 420_000_000;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct PllConfig {
|
|
||||||
pub p_ck: Option<Hertz>,
|
|
||||||
pub q_ck: Option<Hertz>,
|
|
||||||
pub r_ck: Option<Hertz>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) struct PllConfigResults {
|
|
||||||
pub ref_x_ck: u32,
|
|
||||||
pub pll_x_m: u32,
|
|
||||||
pub pll_x_p: u32,
|
|
||||||
pub vco_ck_target: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vco_output_divider_setup(output: u32, plln: usize) -> (u32, u32) {
|
|
||||||
let pll_x_p = if plln == 0 {
|
|
||||||
if output > VCO_MAX / 2 {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
((VCO_MAX / output) | 1) - 1 // Must be even or unity
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Specific to PLL2/3, will subtract 1 later
|
|
||||||
if output > VCO_MAX / 2 {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
VCO_MAX / output
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let vco_ck = output + pll_x_p;
|
|
||||||
|
|
||||||
assert!(pll_x_p < 128);
|
|
||||||
assert!(vco_ck >= VCO_MIN);
|
|
||||||
assert!(vco_ck <= VCO_MAX);
|
|
||||||
|
|
||||||
(vco_ck, pll_x_p)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Must have exclusive access to the RCC register block
|
|
||||||
unsafe fn vco_setup(pll_src: u32, requested_output: u32, plln: usize) -> PllConfigResults {
|
|
||||||
use crate::pac::rcc::vals::{Pllrge, Pllvcosel};
|
|
||||||
|
|
||||||
let (vco_ck_target, pll_x_p) = vco_output_divider_setup(requested_output, plln);
|
|
||||||
|
|
||||||
// Input divisor, resulting in a reference clock in the range
|
|
||||||
// 1 to 2 MHz. Choose the highest reference clock (lowest m)
|
|
||||||
let pll_x_m = (pll_src + 1_999_999) / 2_000_000;
|
|
||||||
assert!(pll_x_m < 64);
|
|
||||||
|
|
||||||
// Calculate resulting reference clock
|
|
||||||
let ref_x_ck = pll_src / pll_x_m;
|
|
||||||
assert!((1_000_000..=2_000_000).contains(&ref_x_ck));
|
|
||||||
|
|
||||||
RCC.pllcfgr().modify(|w| {
|
|
||||||
w.set_pllvcosel(plln, Pllvcosel::MEDIUMVCO);
|
|
||||||
w.set_pllrge(plln, Pllrge::RANGE1);
|
|
||||||
});
|
|
||||||
PllConfigResults {
|
|
||||||
ref_x_ck,
|
|
||||||
pll_x_m,
|
|
||||||
pll_x_p,
|
|
||||||
vco_ck_target,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Must have exclusive access to the RCC register block
|
|
||||||
pub(super) unsafe fn pll_setup(
|
|
||||||
pll_src: u32,
|
|
||||||
config: &PllConfig,
|
|
||||||
plln: usize,
|
|
||||||
) -> (Option<u32>, Option<u32>, Option<u32>) {
|
|
||||||
use crate::pac::rcc::vals::Divp;
|
|
||||||
|
|
||||||
match config.p_ck {
|
|
||||||
Some(requested_output) => {
|
|
||||||
let config_results = vco_setup(pll_src, requested_output.0, plln);
|
|
||||||
let PllConfigResults {
|
|
||||||
ref_x_ck,
|
|
||||||
pll_x_m,
|
|
||||||
pll_x_p,
|
|
||||||
vco_ck_target,
|
|
||||||
} = config_results;
|
|
||||||
|
|
||||||
RCC.pllckselr().modify(|w| w.set_divm(plln, pll_x_m as u8));
|
|
||||||
|
|
||||||
// Feedback divider. Integer only
|
|
||||||
let pll_x_n = vco_ck_target / ref_x_ck;
|
|
||||||
assert!(pll_x_n >= 4);
|
|
||||||
assert!(pll_x_n <= 512);
|
|
||||||
RCC.plldivr(plln)
|
|
||||||
.modify(|w| w.set_divn1((pll_x_n - 1) as u16));
|
|
||||||
|
|
||||||
// No FRACN
|
|
||||||
RCC.pllcfgr().modify(|w| w.set_pllfracen(plln, false));
|
|
||||||
let vco_ck = ref_x_ck * pll_x_n;
|
|
||||||
|
|
||||||
RCC.plldivr(plln)
|
|
||||||
.modify(|w| w.set_divp1(Divp((pll_x_p - 1) as u8)));
|
|
||||||
RCC.pllcfgr().modify(|w| w.set_divpen(plln, true));
|
|
||||||
|
|
||||||
// Calulate additional output dividers
|
|
||||||
let q_ck = match config.q_ck {
|
|
||||||
Some(Hertz(ck)) if ck > 0 => {
|
|
||||||
let div = (vco_ck + ck - 1) / ck;
|
|
||||||
RCC.plldivr(plln).modify(|w| w.set_divq1((div - 1) as u8));
|
|
||||||
RCC.pllcfgr().modify(|w| w.set_divqen(plln, true));
|
|
||||||
Some(vco_ck / div)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
let r_ck = match config.r_ck {
|
|
||||||
Some(Hertz(ck)) if ck > 0 => {
|
|
||||||
let div = (vco_ck + ck - 1) / ck;
|
|
||||||
RCC.plldivr(plln).modify(|w| w.set_divr1((div - 1) as u8));
|
|
||||||
RCC.pllcfgr().modify(|w| w.set_divren(plln, true));
|
|
||||||
Some(vco_ck / div)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
(Some(vco_ck / pll_x_p), q_ck, r_ck)
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
assert!(
|
|
||||||
config.q_ck.is_none(),
|
|
||||||
"Must set PLL P clock for Q clock to take effect!"
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
config.r_ck.is_none(),
|
|
||||||
"Must set PLL P clock for R clock to take effect!"
|
|
||||||
);
|
|
||||||
(None, None, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
362
embassy-stm32/src/rcc/l0.rs
Normal file
362
embassy-stm32/src/rcc/l0.rs
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/// HSI16 speed
|
||||||
|
pub const HSI16_FREQ: u32 = 16_000_000;
|
||||||
|
|
||||||
|
/// System clock mux source
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum ClockSrc {
|
||||||
|
MSI(MSIRange),
|
||||||
|
PLL(PLLSource, PLLMul, PLLDiv),
|
||||||
|
HSE(Hertz),
|
||||||
|
HSI16,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// MSI Clock Range
|
||||||
|
///
|
||||||
|
/// These ranges control the frequency of the MSI. Internally, these ranges map
|
||||||
|
/// to the `MSIRANGE` bits in the `RCC_ICSCR` register.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum MSIRange {
|
||||||
|
/// Around 65.536 kHz
|
||||||
|
Range0,
|
||||||
|
/// Around 131.072 kHz
|
||||||
|
Range1,
|
||||||
|
/// Around 262.144 kHz
|
||||||
|
Range2,
|
||||||
|
/// Around 524.288 kHz
|
||||||
|
Range3,
|
||||||
|
/// Around 1.048 MHz
|
||||||
|
Range4,
|
||||||
|
/// Around 2.097 MHz (reset value)
|
||||||
|
Range5,
|
||||||
|
/// Around 4.194 MHz
|
||||||
|
Range6,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MSIRange {
|
||||||
|
fn default() -> MSIRange {
|
||||||
|
MSIRange::Range5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
||||||
|
NotDivided,
|
||||||
|
Div2,
|
||||||
|
Div4,
|
||||||
|
Div8,
|
||||||
|
Div16,
|
||||||
|
Div64,
|
||||||
|
Div128,
|
||||||
|
Div256,
|
||||||
|
Div512,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// APB prescaler
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum APBPrescaler {
|
||||||
|
NotDivided,
|
||||||
|
Div2,
|
||||||
|
Div4,
|
||||||
|
Div8,
|
||||||
|
Div16,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PLL clock input source
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum PLLSource {
|
||||||
|
HSI16,
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 From<PLLSource> for Pllsrc {
|
||||||
|
fn from(val: PLLSource) -> Pllsrc {
|
||||||
|
match val {
|
||||||
|
PLLSource::HSI16 => Pllsrc::HSI16,
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clocks configutation
|
||||||
|
pub struct Config {
|
||||||
|
pub mux: ClockSrc,
|
||||||
|
pub ahb_pre: AHBPrescaler,
|
||||||
|
pub apb1_pre: APBPrescaler,
|
||||||
|
pub apb2_pre: APBPrescaler,
|
||||||
|
pub enable_hsi48: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Config {
|
||||||
|
Config {
|
||||||
|
mux: ClockSrc::MSI(MSIRange::default()),
|
||||||
|
ahb_pre: AHBPrescaler::NotDivided,
|
||||||
|
apb1_pre: APBPrescaler::NotDivided,
|
||||||
|
apb2_pre: APBPrescaler::NotDivided,
|
||||||
|
enable_hsi48: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
|
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,437 +0,0 @@
|
|||||||
use crate::pac;
|
|
||||||
use crate::peripherals::{self, CRS, RCC, SYSCFG};
|
|
||||||
use crate::rcc::{get_freqs, 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;
|
|
||||||
|
|
||||||
/// System clock mux source
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum ClockSrc {
|
|
||||||
MSI(MSIRange),
|
|
||||||
PLL(PLLSource, PLLMul, PLLDiv),
|
|
||||||
HSE(Hertz),
|
|
||||||
HSI16,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// MSI Clock Range
|
|
||||||
///
|
|
||||||
/// These ranges control the frequency of the MSI. Internally, these ranges map
|
|
||||||
/// to the `MSIRANGE` bits in the `RCC_ICSCR` register.
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum MSIRange {
|
|
||||||
/// Around 65.536 kHz
|
|
||||||
Range0,
|
|
||||||
/// Around 131.072 kHz
|
|
||||||
Range1,
|
|
||||||
/// Around 262.144 kHz
|
|
||||||
Range2,
|
|
||||||
/// Around 524.288 kHz
|
|
||||||
Range3,
|
|
||||||
/// Around 1.048 MHz
|
|
||||||
Range4,
|
|
||||||
/// Around 2.097 MHz (reset value)
|
|
||||||
Range5,
|
|
||||||
/// Around 4.194 MHz
|
|
||||||
Range6,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for MSIRange {
|
|
||||||
fn default() -> MSIRange {
|
|
||||||
MSIRange::Range5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 {
|
|
||||||
NotDivided,
|
|
||||||
Div2,
|
|
||||||
Div4,
|
|
||||||
Div8,
|
|
||||||
Div16,
|
|
||||||
Div64,
|
|
||||||
Div128,
|
|
||||||
Div256,
|
|
||||||
Div512,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// APB prescaler
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum APBPrescaler {
|
|
||||||
NotDivided,
|
|
||||||
Div2,
|
|
||||||
Div4,
|
|
||||||
Div8,
|
|
||||||
Div16,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// PLL clock input source
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum PLLSource {
|
|
||||||
HSI16,
|
|
||||||
HSE(Hertz),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<Pllmul> for PLLMul {
|
|
||||||
fn into(self) -> Pllmul {
|
|
||||||
match self {
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<Plldiv> for PLLDiv {
|
|
||||||
fn into(self) -> Plldiv {
|
|
||||||
match self {
|
|
||||||
PLLDiv::Div2 => Plldiv::DIV2,
|
|
||||||
PLLDiv::Div3 => Plldiv::DIV3,
|
|
||||||
PLLDiv::Div4 => Plldiv::DIV4,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<Pllsrc> for PLLSource {
|
|
||||||
fn into(self) -> Pllsrc {
|
|
||||||
match self {
|
|
||||||
PLLSource::HSI16 => Pllsrc::HSI16,
|
|
||||||
PLLSource::HSE(_) => Pllsrc::HSE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<Ppre> for APBPrescaler {
|
|
||||||
fn into(self) -> Ppre {
|
|
||||||
match self {
|
|
||||||
APBPrescaler::NotDivided => Ppre::DIV1,
|
|
||||||
APBPrescaler::Div2 => Ppre::DIV2,
|
|
||||||
APBPrescaler::Div4 => Ppre::DIV4,
|
|
||||||
APBPrescaler::Div8 => Ppre::DIV8,
|
|
||||||
APBPrescaler::Div16 => Ppre::DIV16,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<Hpre> for AHBPrescaler {
|
|
||||||
fn into(self) -> Hpre {
|
|
||||||
match self {
|
|
||||||
AHBPrescaler::NotDivided => Hpre::DIV1,
|
|
||||||
AHBPrescaler::Div2 => Hpre::DIV2,
|
|
||||||
AHBPrescaler::Div4 => Hpre::DIV4,
|
|
||||||
AHBPrescaler::Div8 => Hpre::DIV8,
|
|
||||||
AHBPrescaler::Div16 => Hpre::DIV16,
|
|
||||||
AHBPrescaler::Div64 => Hpre::DIV64,
|
|
||||||
AHBPrescaler::Div128 => Hpre::DIV128,
|
|
||||||
AHBPrescaler::Div256 => Hpre::DIV256,
|
|
||||||
AHBPrescaler::Div512 => Hpre::DIV512,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<Msirange> for MSIRange {
|
|
||||||
fn into(self) -> Msirange {
|
|
||||||
match self {
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clocks configutation
|
|
||||||
pub struct Config {
|
|
||||||
pub mux: ClockSrc,
|
|
||||||
pub ahb_pre: AHBPrescaler,
|
|
||||||
pub apb1_pre: APBPrescaler,
|
|
||||||
pub apb2_pre: APBPrescaler,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Config {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Config {
|
|
||||||
Config {
|
|
||||||
mux: ClockSrc::MSI(MSIRange::default()),
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 unsafe fn init(config: Config) {
|
|
||||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
|
||||||
let clocks = r.freeze(config);
|
|
||||||
set_freqs(clocks);
|
|
||||||
}
|
|
321
embassy-stm32/src/rcc/l1.rs
Normal file
321
embassy-stm32/src/rcc/l1.rs
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/// HSI speed
|
||||||
|
pub const HSI_FREQ: u32 = 16_000_000;
|
||||||
|
|
||||||
|
/// System clock mux source
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum ClockSrc {
|
||||||
|
MSI(MSIRange),
|
||||||
|
PLL(PLLSource, PLLMul, PLLDiv),
|
||||||
|
HSE(Hertz),
|
||||||
|
HSI,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// MSI Clock Range
|
||||||
|
///
|
||||||
|
/// These ranges control the frequency of the MSI. Internally, these ranges map
|
||||||
|
/// to the `MSIRANGE` bits in the `RCC_ICSCR` register.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum MSIRange {
|
||||||
|
/// Around 65.536 kHz
|
||||||
|
Range0,
|
||||||
|
/// Around 131.072 kHz
|
||||||
|
Range1,
|
||||||
|
/// Around 262.144 kHz
|
||||||
|
Range2,
|
||||||
|
/// Around 524.288 kHz
|
||||||
|
Range3,
|
||||||
|
/// Around 1.048 MHz
|
||||||
|
Range4,
|
||||||
|
/// Around 2.097 MHz (reset value)
|
||||||
|
Range5,
|
||||||
|
/// Around 4.194 MHz
|
||||||
|
Range6,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MSIRange {
|
||||||
|
fn default() -> MSIRange {
|
||||||
|
MSIRange::Range5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
||||||
|
NotDivided,
|
||||||
|
Div2,
|
||||||
|
Div4,
|
||||||
|
Div8,
|
||||||
|
Div16,
|
||||||
|
Div64,
|
||||||
|
Div128,
|
||||||
|
Div256,
|
||||||
|
Div512,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// APB prescaler
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum APBPrescaler {
|
||||||
|
NotDivided,
|
||||||
|
Div2,
|
||||||
|
Div4,
|
||||||
|
Div8,
|
||||||
|
Div16,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clocks configutation
|
||||||
|
pub struct Config {
|
||||||
|
pub mux: ClockSrc,
|
||||||
|
pub ahb_pre: AHBPrescaler,
|
||||||
|
pub apb1_pre: APBPrescaler,
|
||||||
|
pub apb2_pre: APBPrescaler,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Config {
|
||||||
|
Config {
|
||||||
|
mux: ClockSrc::MSI(MSIRange::default()),
|
||||||
|
ahb_pre: AHBPrescaler::NotDivided,
|
||||||
|
apb1_pre: APBPrescaler::NotDivided,
|
||||||
|
apb2_pre: APBPrescaler::NotDivided,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
|
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,261 +0,0 @@
|
|||||||
use crate::pac;
|
|
||||||
use crate::peripherals::{self, RCC};
|
|
||||||
use crate::rcc::{get_freqs, 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;
|
|
||||||
|
|
||||||
/// System clock mux source
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum ClockSrc {
|
|
||||||
MSI(MSIRange),
|
|
||||||
HSE(Hertz),
|
|
||||||
HSI,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// MSI Clock Range
|
|
||||||
///
|
|
||||||
/// These ranges control the frequency of the MSI. Internally, these ranges map
|
|
||||||
/// to the `MSIRANGE` bits in the `RCC_ICSCR` register.
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum MSIRange {
|
|
||||||
/// Around 65.536 kHz
|
|
||||||
Range0,
|
|
||||||
/// Around 131.072 kHz
|
|
||||||
Range1,
|
|
||||||
/// Around 262.144 kHz
|
|
||||||
Range2,
|
|
||||||
/// Around 524.288 kHz
|
|
||||||
Range3,
|
|
||||||
/// Around 1.048 MHz
|
|
||||||
Range4,
|
|
||||||
/// Around 2.097 MHz (reset value)
|
|
||||||
Range5,
|
|
||||||
/// Around 4.194 MHz
|
|
||||||
Range6,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for MSIRange {
|
|
||||||
fn default() -> MSIRange {
|
|
||||||
MSIRange::Range5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// AHB prescaler
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
|
||||||
pub enum AHBPrescaler {
|
|
||||||
NotDivided,
|
|
||||||
Div2,
|
|
||||||
Div4,
|
|
||||||
Div8,
|
|
||||||
Div16,
|
|
||||||
Div64,
|
|
||||||
Div128,
|
|
||||||
Div256,
|
|
||||||
Div512,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// APB prescaler
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum APBPrescaler {
|
|
||||||
NotDivided,
|
|
||||||
Div2,
|
|
||||||
Div4,
|
|
||||||
Div8,
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clocks configutation
|
|
||||||
pub struct Config {
|
|
||||||
pub mux: ClockSrc,
|
|
||||||
pub ahb_pre: AHBPrescaler,
|
|
||||||
pub apb1_pre: APBPrescaler,
|
|
||||||
pub apb2_pre: APBPrescaler,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Config {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Config {
|
|
||||||
Config {
|
|
||||||
mux: ClockSrc::MSI(MSIRange::default()),
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 unsafe fn init(config: Config) {
|
|
||||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
|
||||||
let clocks = r.freeze(config);
|
|
||||||
set_freqs(clocks);
|
|
||||||
}
|
|
429
embassy-stm32/src/rcc/l4.rs
Normal file
429
embassy-stm32/src/rcc/l4.rs
Normal file
@ -0,0 +1,429 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/// HSI16 speed
|
||||||
|
pub const HSI16_FREQ: u32 = 16_000_000;
|
||||||
|
|
||||||
|
/// System clock mux source
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum ClockSrc {
|
||||||
|
MSI(MSIRange),
|
||||||
|
PLL(PLLSource, PLLClkDiv, PLLSrcDiv, PLLMul, Option<PLL48Div>),
|
||||||
|
HSE(Hertz),
|
||||||
|
HSI16,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// MSI Clock Range
|
||||||
|
///
|
||||||
|
/// These ranges control the frequency of the MSI. Internally, these ranges map
|
||||||
|
/// to the `MSIRANGE` bits in the `RCC_ICSCR` register.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum MSIRange {
|
||||||
|
/// Around 100 kHz
|
||||||
|
Range0,
|
||||||
|
/// Around 200 kHz
|
||||||
|
Range1,
|
||||||
|
/// Around 400 kHz
|
||||||
|
Range2,
|
||||||
|
/// Around 800 kHz
|
||||||
|
Range3,
|
||||||
|
/// Around 1 MHz
|
||||||
|
Range4,
|
||||||
|
/// Around 2 MHz
|
||||||
|
Range5,
|
||||||
|
/// Around 4 MHz (reset value)
|
||||||
|
Range6,
|
||||||
|
/// Around 8 MHz
|
||||||
|
Range7,
|
||||||
|
/// Around 16 MHz
|
||||||
|
Range8,
|
||||||
|
/// Around 24 MHz
|
||||||
|
Range9,
|
||||||
|
/// Around 32 MHz
|
||||||
|
Range10,
|
||||||
|
/// Around 48 MHz
|
||||||
|
Range11,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MSIRange {
|
||||||
|
fn default() -> MSIRange {
|
||||||
|
MSIRange::Range6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type PLL48Div = PLLClkDiv;
|
||||||
|
|
||||||
|
/// PLL divider
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum PLLDiv {
|
||||||
|
Div2,
|
||||||
|
Div3,
|
||||||
|
Div4,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// AHB prescaler
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
pub enum AHBPrescaler {
|
||||||
|
NotDivided,
|
||||||
|
Div2,
|
||||||
|
Div4,
|
||||||
|
Div8,
|
||||||
|
Div16,
|
||||||
|
Div64,
|
||||||
|
Div128,
|
||||||
|
Div256,
|
||||||
|
Div512,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// APB prescaler
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum APBPrescaler {
|
||||||
|
NotDivided,
|
||||||
|
Div2,
|
||||||
|
Div4,
|
||||||
|
Div8,
|
||||||
|
Div16,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PLL clock input source
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum PLLSource {
|
||||||
|
HSI16,
|
||||||
|
HSE(Hertz),
|
||||||
|
}
|
||||||
|
|
||||||
|
seq_macro::seq!(N in 8..=86 {
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum PLLMul {
|
||||||
|
#(
|
||||||
|
Mul#N,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PLLMul> for u8 {
|
||||||
|
fn from(val: PLLMul) -> u8 {
|
||||||
|
match val {
|
||||||
|
#(
|
||||||
|
PLLMul::Mul#N => N,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PLLMul {
|
||||||
|
pub fn to_mul(self) -> u32 {
|
||||||
|
match self {
|
||||||
|
#(
|
||||||
|
PLLMul::Mul#N => N,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum PLLClkDiv {
|
||||||
|
Div2,
|
||||||
|
Div4,
|
||||||
|
Div6,
|
||||||
|
Div8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PLLClkDiv {
|
||||||
|
pub fn to_div(self) -> u32 {
|
||||||
|
let val: u8 = self.into();
|
||||||
|
(val as u32 + 1) * 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PLLClkDiv> for u8 {
|
||||||
|
fn from(val: PLLClkDiv) -> u8 {
|
||||||
|
match val {
|
||||||
|
PLLClkDiv::Div2 => 0b00,
|
||||||
|
PLLClkDiv::Div4 => 0b01,
|
||||||
|
PLLClkDiv::Div6 => 0b10,
|
||||||
|
PLLClkDiv::Div8 => 0b11,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum PLLSrcDiv {
|
||||||
|
Div1,
|
||||||
|
Div2,
|
||||||
|
Div3,
|
||||||
|
Div4,
|
||||||
|
Div5,
|
||||||
|
Div6,
|
||||||
|
Div7,
|
||||||
|
Div8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PLLSrcDiv {
|
||||||
|
pub fn to_div(self) -> u32 {
|
||||||
|
let val: u8 = self.into();
|
||||||
|
val as u32 + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PLLSrcDiv> for u8 {
|
||||||
|
fn from(val: PLLSrcDiv) -> u8 {
|
||||||
|
match val {
|
||||||
|
PLLSrcDiv::Div1 => 0b000,
|
||||||
|
PLLSrcDiv::Div2 => 0b001,
|
||||||
|
PLLSrcDiv::Div3 => 0b010,
|
||||||
|
PLLSrcDiv::Div4 => 0b011,
|
||||||
|
PLLSrcDiv::Div5 => 0b100,
|
||||||
|
PLLSrcDiv::Div6 => 0b101,
|
||||||
|
PLLSrcDiv::Div7 => 0b110,
|
||||||
|
PLLSrcDiv::Div8 => 0b111,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PLLSource> for Pllsrc {
|
||||||
|
fn from(val: PLLSource) -> Pllsrc {
|
||||||
|
match val {
|
||||||
|
PLLSource::HSI16 => Pllsrc::HSI16,
|
||||||
|
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::RANGE100K,
|
||||||
|
MSIRange::Range1 => Msirange::RANGE200K,
|
||||||
|
MSIRange::Range2 => Msirange::RANGE400K,
|
||||||
|
MSIRange::Range3 => Msirange::RANGE800K,
|
||||||
|
MSIRange::Range4 => Msirange::RANGE1M,
|
||||||
|
MSIRange::Range5 => Msirange::RANGE2M,
|
||||||
|
MSIRange::Range6 => Msirange::RANGE4M,
|
||||||
|
MSIRange::Range7 => Msirange::RANGE8M,
|
||||||
|
MSIRange::Range8 => Msirange::RANGE16M,
|
||||||
|
MSIRange::Range9 => Msirange::RANGE24M,
|
||||||
|
MSIRange::Range10 => Msirange::RANGE32M,
|
||||||
|
MSIRange::Range11 => Msirange::RANGE48M,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clocks configutation
|
||||||
|
pub struct Config {
|
||||||
|
pub mux: ClockSrc,
|
||||||
|
pub ahb_pre: AHBPrescaler,
|
||||||
|
pub apb1_pre: APBPrescaler,
|
||||||
|
pub apb2_pre: APBPrescaler,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Config {
|
||||||
|
Config {
|
||||||
|
mux: ClockSrc::MSI(MSIRange::Range6),
|
||||||
|
ahb_pre: AHBPrescaler::NotDivided,
|
||||||
|
apb1_pre: APBPrescaler::NotDivided,
|
||||||
|
apb2_pre: APBPrescaler::NotDivided,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
|
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,489 +0,0 @@
|
|||||||
use crate::pac;
|
|
||||||
use crate::peripherals::{self, RCC};
|
|
||||||
use crate::rcc::{get_freqs, 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;
|
|
||||||
|
|
||||||
/// System clock mux source
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum ClockSrc {
|
|
||||||
PLL(PLLSource, PLLClkDiv, PLLSrcDiv, PLLMul, Option<PLL48Div>),
|
|
||||||
MSI(MSIRange),
|
|
||||||
HSE(Hertz),
|
|
||||||
HSI16,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// MSI Clock Range
|
|
||||||
///
|
|
||||||
/// These ranges control the frequency of the MSI. Internally, these ranges map
|
|
||||||
/// to the `MSIRANGE` bits in the `RCC_ICSCR` register.
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum MSIRange {
|
|
||||||
/// Around 100 kHz
|
|
||||||
Range0,
|
|
||||||
/// Around 200 kHz
|
|
||||||
Range1,
|
|
||||||
/// Around 400 kHz
|
|
||||||
Range2,
|
|
||||||
/// Around 800 kHz
|
|
||||||
Range3,
|
|
||||||
/// Around 1 MHz
|
|
||||||
Range4,
|
|
||||||
/// Around 2 MHz
|
|
||||||
Range5,
|
|
||||||
/// Around 4 MHz (reset value)
|
|
||||||
Range6,
|
|
||||||
/// Around 8 MHz
|
|
||||||
Range7,
|
|
||||||
/// Around 16 MHz
|
|
||||||
Range8,
|
|
||||||
/// Around 24 MHz
|
|
||||||
Range9,
|
|
||||||
/// Around 32 MHz
|
|
||||||
Range10,
|
|
||||||
/// Around 48 MHz
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type PLL48Div = PLLClkDiv;
|
|
||||||
|
|
||||||
/// PLL divider
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum PLLDiv {
|
|
||||||
Div2,
|
|
||||||
Div3,
|
|
||||||
Div4,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// AHB prescaler
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
|
||||||
pub enum AHBPrescaler {
|
|
||||||
NotDivided,
|
|
||||||
Div2,
|
|
||||||
Div4,
|
|
||||||
Div8,
|
|
||||||
Div16,
|
|
||||||
Div64,
|
|
||||||
Div128,
|
|
||||||
Div256,
|
|
||||||
Div512,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// APB prescaler
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum APBPrescaler {
|
|
||||||
NotDivided,
|
|
||||||
Div2,
|
|
||||||
Div4,
|
|
||||||
Div8,
|
|
||||||
Div16,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// PLL clock input source
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum PLLSource {
|
|
||||||
HSI16,
|
|
||||||
HSE(Hertz),
|
|
||||||
}
|
|
||||||
|
|
||||||
seq_macro::seq!(N in 8..=86 {
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum PLLMul {
|
|
||||||
#(
|
|
||||||
Mul#N,
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<u8> for PLLMul {
|
|
||||||
fn into(self) -> u8 {
|
|
||||||
match self {
|
|
||||||
#(
|
|
||||||
PLLMul::Mul#N => N,
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PLLMul {
|
|
||||||
pub fn to_mul(self) -> u32 {
|
|
||||||
match self {
|
|
||||||
#(
|
|
||||||
PLLMul::Mul#N => N,
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum PLLClkDiv {
|
|
||||||
Div2,
|
|
||||||
Div4,
|
|
||||||
Div6,
|
|
||||||
Div8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PLLClkDiv {
|
|
||||||
pub fn to_div(self) -> u32 {
|
|
||||||
let val: u8 = self.into();
|
|
||||||
val as u32 + 1 * 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<u8> for PLLClkDiv {
|
|
||||||
fn into(self) -> u8 {
|
|
||||||
match self {
|
|
||||||
PLLClkDiv::Div2 => 0b00,
|
|
||||||
PLLClkDiv::Div4 => 0b01,
|
|
||||||
PLLClkDiv::Div6 => 0b10,
|
|
||||||
PLLClkDiv::Div8 => 0b11,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum PLLSrcDiv {
|
|
||||||
Div1,
|
|
||||||
Div2,
|
|
||||||
Div3,
|
|
||||||
Div4,
|
|
||||||
Div5,
|
|
||||||
Div6,
|
|
||||||
Div7,
|
|
||||||
Div8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PLLSrcDiv {
|
|
||||||
pub fn to_div(self) -> u32 {
|
|
||||||
let val: u8 = self.into();
|
|
||||||
val as u32 + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<u8> for PLLSrcDiv {
|
|
||||||
fn into(self) -> u8 {
|
|
||||||
match self {
|
|
||||||
PLLSrcDiv::Div1 => 0b000,
|
|
||||||
PLLSrcDiv::Div2 => 0b001,
|
|
||||||
PLLSrcDiv::Div3 => 0b010,
|
|
||||||
PLLSrcDiv::Div4 => 0b011,
|
|
||||||
PLLSrcDiv::Div5 => 0b100,
|
|
||||||
PLLSrcDiv::Div6 => 0b101,
|
|
||||||
PLLSrcDiv::Div7 => 0b110,
|
|
||||||
PLLSrcDiv::Div8 => 0b111,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<u8> for PLLSource {
|
|
||||||
fn into(self) -> u8 {
|
|
||||||
match self {
|
|
||||||
PLLSource::HSI16 => 0b10,
|
|
||||||
PLLSource::HSE(_) => 0b11,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<Msirange> for MSIRange {
|
|
||||||
fn into(self) -> Msirange {
|
|
||||||
match self {
|
|
||||||
MSIRange::Range0 => Msirange::RANGE100K,
|
|
||||||
MSIRange::Range1 => Msirange::RANGE200K,
|
|
||||||
MSIRange::Range2 => Msirange::RANGE400K,
|
|
||||||
MSIRange::Range3 => Msirange::RANGE800K,
|
|
||||||
MSIRange::Range4 => Msirange::RANGE1M,
|
|
||||||
MSIRange::Range5 => Msirange::RANGE2M,
|
|
||||||
MSIRange::Range6 => Msirange::RANGE4M,
|
|
||||||
MSIRange::Range7 => Msirange::RANGE8M,
|
|
||||||
MSIRange::Range8 => Msirange::RANGE16M,
|
|
||||||
MSIRange::Range9 => Msirange::RANGE24M,
|
|
||||||
MSIRange::Range10 => Msirange::RANGE32M,
|
|
||||||
MSIRange::Range11 => Msirange::RANGE48M,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clocks configutation
|
|
||||||
pub struct Config {
|
|
||||||
pub mux: ClockSrc,
|
|
||||||
pub ahb_pre: AHBPrescaler,
|
|
||||||
pub apb1_pre: APBPrescaler,
|
|
||||||
pub apb2_pre: APBPrescaler,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Config {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Config {
|
|
||||||
Config {
|
|
||||||
mux: ClockSrc::MSI(MSIRange::Range6),
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 unsafe fn init(config: Config) {
|
|
||||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
|
||||||
let clocks = r.freeze(config);
|
|
||||||
set_freqs(clocks);
|
|
||||||
}
|
|
@ -4,6 +4,23 @@ use crate::peripherals;
|
|||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
use core::mem::MaybeUninit;
|
use core::mem::MaybeUninit;
|
||||||
|
|
||||||
|
#[cfg_attr(any(rcc_f0, rcc_f0x0), path = "f0.rs")]
|
||||||
|
#[cfg_attr(rcc_f1, path = "f1.rs")]
|
||||||
|
#[cfg_attr(rcc_f3, path = "f3.rs")]
|
||||||
|
#[cfg_attr(any(rcc_f4, rcc_f410), path = "f4.rs")]
|
||||||
|
#[cfg_attr(rcc_f7, path = "f7.rs")]
|
||||||
|
#[cfg_attr(rcc_g0, path = "g0.rs")]
|
||||||
|
#[cfg_attr(rcc_g4, path = "g4.rs")]
|
||||||
|
#[cfg_attr(any(rcc_h7, rcc_h7ab), path = "h7.rs")]
|
||||||
|
#[cfg_attr(rcc_l0, path = "l0.rs")]
|
||||||
|
#[cfg_attr(rcc_l1, path = "l1.rs")]
|
||||||
|
#[cfg_attr(rcc_l4, path = "l4.rs")]
|
||||||
|
#[cfg_attr(rcc_u5, path = "u5.rs")]
|
||||||
|
#[cfg_attr(rcc_wb, path = "wb.rs")]
|
||||||
|
#[cfg_attr(rcc_wl5, path = "wl5.rs")]
|
||||||
|
mod _version;
|
||||||
|
pub use _version::*;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Clocks {
|
pub struct Clocks {
|
||||||
pub sys: Hertz,
|
pub sys: Hertz,
|
||||||
@ -44,7 +61,7 @@ pub struct Clocks {
|
|||||||
#[cfg(any(rcc_h7))]
|
#[cfg(any(rcc_h7))]
|
||||||
pub apb4: Hertz,
|
pub apb4: Hertz,
|
||||||
|
|
||||||
#[cfg(rcc_f4)]
|
#[cfg(any(rcc_f4, rcc_f7))]
|
||||||
pub pll48: Option<Hertz>,
|
pub pll48: Option<Hertz>,
|
||||||
|
|
||||||
#[cfg(rcc_f1)]
|
#[cfg(rcc_f1)]
|
||||||
@ -59,61 +76,15 @@ static mut CLOCK_FREQS: MaybeUninit<Clocks> = MaybeUninit::uninit();
|
|||||||
/// Sets the clock frequencies
|
/// Sets the clock frequencies
|
||||||
///
|
///
|
||||||
/// Safety: Sets a mutable global.
|
/// Safety: Sets a mutable global.
|
||||||
pub unsafe fn set_freqs(freqs: Clocks) {
|
pub(crate) unsafe fn set_freqs(freqs: Clocks) {
|
||||||
CLOCK_FREQS.as_mut_ptr().write(freqs);
|
CLOCK_FREQS.as_mut_ptr().write(freqs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Safety: Reads a mutable global.
|
/// Safety: Reads a mutable global.
|
||||||
pub unsafe fn get_freqs() -> &'static Clocks {
|
pub(crate) unsafe fn get_freqs() -> &'static Clocks {
|
||||||
&*CLOCK_FREQS.as_ptr()
|
&*CLOCK_FREQS.as_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(rcc_h7)] {
|
|
||||||
mod h7;
|
|
||||||
pub use h7::*;
|
|
||||||
} else if #[cfg(rcc_l0)] {
|
|
||||||
mod l0;
|
|
||||||
pub use l0::*;
|
|
||||||
} else if #[cfg(rcc_l1)] {
|
|
||||||
mod l1;
|
|
||||||
pub use l1::*;
|
|
||||||
} else if #[cfg(rcc_l4)] {
|
|
||||||
mod l4;
|
|
||||||
pub use l4::*;
|
|
||||||
} else if #[cfg(rcc_f1)] {
|
|
||||||
mod f1;
|
|
||||||
pub use f1::*;
|
|
||||||
} else if #[cfg(rcc_f3)] {
|
|
||||||
mod f3;
|
|
||||||
pub use f3::*;
|
|
||||||
} else if #[cfg(rcc_f4)] {
|
|
||||||
mod f4;
|
|
||||||
pub use f4::*;
|
|
||||||
} else if #[cfg(rcc_f7)] {
|
|
||||||
mod f7;
|
|
||||||
pub use f7::*;
|
|
||||||
} else if #[cfg(rcc_wb)] {
|
|
||||||
mod wb;
|
|
||||||
pub use wb::*;
|
|
||||||
} else if #[cfg(rcc_wl5)] {
|
|
||||||
mod wl5x;
|
|
||||||
pub use wl5x::*;
|
|
||||||
} else if #[cfg(any(rcc_f0, rcc_f0x0))] {
|
|
||||||
mod f0;
|
|
||||||
pub use f0::*;
|
|
||||||
} else if #[cfg(any(rcc_g0))] {
|
|
||||||
mod g0;
|
|
||||||
pub use g0::*;
|
|
||||||
} else if #[cfg(any(rcc_g4))] {
|
|
||||||
mod g4;
|
|
||||||
pub use g4::*;
|
|
||||||
} else if #[cfg(any(rcc_u5))] {
|
|
||||||
mod u5;
|
|
||||||
pub use u5::*;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
pub trait RccPeripheral {
|
pub trait RccPeripheral {
|
||||||
fn frequency() -> crate::time::Hertz;
|
fn frequency() -> crate::time::Hertz;
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
use crate::pac;
|
use crate::pac::{FLASH, RCC};
|
||||||
use crate::peripherals::{self, RCC};
|
|
||||||
use crate::pwr::{Power, VoltageScale};
|
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
use crate::time::{Hertz, U32Ext};
|
use crate::time::{Hertz, U32Ext};
|
||||||
use stm32_metapac::rcc::vals::{Hpre, Msirange, Msirgsel, Pllm, Pllsrc, Ppre, Sw};
|
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
|
/// HSI16 speed
|
||||||
pub const HSI16_FREQ: u32 = 16_000_000;
|
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)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum ClockSrc {
|
pub enum ClockSrc {
|
||||||
MSI(MSIRange),
|
MSI(MSIRange),
|
||||||
@ -293,218 +305,188 @@ impl Default for Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extension trait that freezes the `RCC` peripheral with provided clocks configuration
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
pub trait RccExt {
|
let sys_clk = match config.mux {
|
||||||
fn freeze(self, config: Config, power: &Power) -> Clocks;
|
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() {}
|
||||||
|
|
||||||
impl RccExt for RCC {
|
range.into()
|
||||||
#[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);
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
ClockSrc::HSE(freq) => {
|
||||||
|
RCC.cr().write(|w| w.set_hseon(true));
|
||||||
|
while !RCC.cr().read().hserdy() {}
|
||||||
|
|
||||||
unsafe {
|
freq.0
|
||||||
rcc.cfgr1().modify(|w| {
|
}
|
||||||
w.set_sw(cfgr.mux.into());
|
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.cfgr2().modify(|w| {
|
RCC.pll1divr().modify(|w| {
|
||||||
w.set_hpre(cfgr.ahb_pre.into());
|
w.set_pllr(div.to_div());
|
||||||
w.set_ppre1(cfgr.apb1_pre.into());
|
w.set_plln(n.to_mul());
|
||||||
w.set_ppre2(cfgr.apb2_pre.into());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
rcc.cfgr3().modify(|w| {
|
// Enable PLL
|
||||||
w.set_ppre3(cfgr.apb3_pre.into());
|
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
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let ahb_freq: u32 = match cfgr.ahb_pre {
|
// TODO make configurable
|
||||||
AHBPrescaler::NotDivided => sys_clk,
|
let power_vos = VoltageScale::Range4;
|
||||||
pre => {
|
|
||||||
let pre: u8 = pre.into();
|
// states and programming delay
|
||||||
let pre = 1 << (pre as u32 - 7);
|
let wait_states = match power_vos {
|
||||||
sys_clk / pre
|
// 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
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
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(),
|
|
||||||
}
|
}
|
||||||
}
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub unsafe fn init(config: Config) {
|
FLASH.acr().modify(|w| {
|
||||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
w.set_latency(wait_states);
|
||||||
let power = Power::new(<peripherals::PWR as embassy::util::Steal>::steal());
|
});
|
||||||
let clocks = r.freeze(config, &power);
|
|
||||||
set_freqs(clocks);
|
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(),
|
||||||
|
});
|
||||||
}
|
}
|
167
embassy-stm32/src/rcc/wb.rs
Normal file
167
embassy-stm32/src/rcc/wb.rs
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
use crate::pac::RCC;
|
||||||
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
|
use crate::time::Hertz;
|
||||||
|
use crate::time::U32Ext;
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
|
||||||
|
/// HSI speed
|
||||||
|
pub const HSI_FREQ: u32 = 16_000_000;
|
||||||
|
|
||||||
|
/// System clock mux source
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum ClockSrc {
|
||||||
|
HSE(Hertz),
|
||||||
|
HSI16,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// AHB prescaler
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
pub enum AHBPrescaler {
|
||||||
|
NotDivided,
|
||||||
|
Div2,
|
||||||
|
Div3,
|
||||||
|
Div4,
|
||||||
|
Div5,
|
||||||
|
Div6,
|
||||||
|
Div8,
|
||||||
|
Div10,
|
||||||
|
Div16,
|
||||||
|
Div32,
|
||||||
|
Div64,
|
||||||
|
Div128,
|
||||||
|
Div256,
|
||||||
|
Div512,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// APB prescaler
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum APBPrescaler {
|
||||||
|
NotDivided,
|
||||||
|
Div2,
|
||||||
|
Div4,
|
||||||
|
Div8,
|
||||||
|
Div16,
|
||||||
|
}
|
||||||
|
|
||||||
|
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::Div3 => 0x01,
|
||||||
|
AHBPrescaler::Div4 => 0x09,
|
||||||
|
AHBPrescaler::Div5 => 0x02,
|
||||||
|
AHBPrescaler::Div6 => 0x05,
|
||||||
|
AHBPrescaler::Div8 => 0x0a,
|
||||||
|
AHBPrescaler::Div10 => 0x06,
|
||||||
|
AHBPrescaler::Div16 => 0x0b,
|
||||||
|
AHBPrescaler::Div32 => 0x07,
|
||||||
|
AHBPrescaler::Div64 => 0x0c,
|
||||||
|
AHBPrescaler::Div128 => 0x0d,
|
||||||
|
AHBPrescaler::Div256 => 0x0e,
|
||||||
|
AHBPrescaler::Div512 => 0x0f,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clocks configutation
|
||||||
|
pub struct Config {
|
||||||
|
pub mux: ClockSrc,
|
||||||
|
pub ahb_pre: AHBPrescaler,
|
||||||
|
pub apb1_pre: APBPrescaler,
|
||||||
|
pub apb2_pre: APBPrescaler,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Config {
|
||||||
|
Config {
|
||||||
|
mux: ClockSrc::HSI16,
|
||||||
|
ahb_pre: AHBPrescaler::NotDivided,
|
||||||
|
apb1_pre: APBPrescaler::NotDivided,
|
||||||
|
apb2_pre: APBPrescaler::NotDivided,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
|
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,213 +0,0 @@
|
|||||||
use crate::pac;
|
|
||||||
use crate::peripherals::{self, RCC};
|
|
||||||
use crate::rcc::{get_freqs, 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.
|
|
||||||
|
|
||||||
/// Only the basic setup using the HSE and HSI clocks are supported as of now.
|
|
||||||
|
|
||||||
/// HSI speed
|
|
||||||
pub const HSI_FREQ: u32 = 16_000_000;
|
|
||||||
|
|
||||||
/// System clock mux source
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum ClockSrc {
|
|
||||||
HSE(Hertz),
|
|
||||||
HSI16,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// AHB prescaler
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
|
||||||
pub enum AHBPrescaler {
|
|
||||||
NotDivided,
|
|
||||||
Div2,
|
|
||||||
Div3,
|
|
||||||
Div4,
|
|
||||||
Div5,
|
|
||||||
Div6,
|
|
||||||
Div8,
|
|
||||||
Div10,
|
|
||||||
Div16,
|
|
||||||
Div32,
|
|
||||||
Div64,
|
|
||||||
Div128,
|
|
||||||
Div256,
|
|
||||||
Div512,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// APB prescaler
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum APBPrescaler {
|
|
||||||
NotDivided,
|
|
||||||
Div2,
|
|
||||||
Div4,
|
|
||||||
Div8,
|
|
||||||
Div16,
|
|
||||||
}
|
|
||||||
|
|
||||||
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::Div3 => 0x01,
|
|
||||||
AHBPrescaler::Div4 => 0x09,
|
|
||||||
AHBPrescaler::Div5 => 0x02,
|
|
||||||
AHBPrescaler::Div6 => 0x05,
|
|
||||||
AHBPrescaler::Div8 => 0x0a,
|
|
||||||
AHBPrescaler::Div10 => 0x06,
|
|
||||||
AHBPrescaler::Div16 => 0x0b,
|
|
||||||
AHBPrescaler::Div32 => 0x07,
|
|
||||||
AHBPrescaler::Div64 => 0x0c,
|
|
||||||
AHBPrescaler::Div128 => 0x0d,
|
|
||||||
AHBPrescaler::Div256 => 0x0e,
|
|
||||||
AHBPrescaler::Div512 => 0x0f,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clocks configutation
|
|
||||||
pub struct Config {
|
|
||||||
pub mux: ClockSrc,
|
|
||||||
pub ahb_pre: AHBPrescaler,
|
|
||||||
pub apb1_pre: APBPrescaler,
|
|
||||||
pub apb2_pre: APBPrescaler,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Config {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Config {
|
|
||||||
Config {
|
|
||||||
mux: ClockSrc::HSI16,
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 unsafe fn init(config: Config) {
|
|
||||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
|
||||||
let clocks = r.freeze(config);
|
|
||||||
set_freqs(clocks);
|
|
||||||
}
|
|
189
embassy-stm32/src/rcc/wl5.rs
Normal file
189
embassy-stm32/src/rcc/wl5.rs
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
use crate::pac::RCC;
|
||||||
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
|
use crate::time::U32Ext;
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
|
||||||
|
/// HSI speed
|
||||||
|
pub const HSI_FREQ: u32 = 16_000_000;
|
||||||
|
|
||||||
|
pub const HSE32_FREQ: u32 = 32_000_000;
|
||||||
|
|
||||||
|
/// System clock mux source
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum ClockSrc {
|
||||||
|
HSE32,
|
||||||
|
HSI16,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// AHB prescaler
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
pub enum AHBPrescaler {
|
||||||
|
NotDivided,
|
||||||
|
Div2,
|
||||||
|
Div3,
|
||||||
|
Div4,
|
||||||
|
Div5,
|
||||||
|
Div6,
|
||||||
|
Div8,
|
||||||
|
Div10,
|
||||||
|
Div16,
|
||||||
|
Div32,
|
||||||
|
Div64,
|
||||||
|
Div128,
|
||||||
|
Div256,
|
||||||
|
Div512,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// APB prescaler
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum APBPrescaler {
|
||||||
|
NotDivided,
|
||||||
|
Div2,
|
||||||
|
Div4,
|
||||||
|
Div8,
|
||||||
|
Div16,
|
||||||
|
}
|
||||||
|
|
||||||
|
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::Div3 => 0x01,
|
||||||
|
AHBPrescaler::Div4 => 0x09,
|
||||||
|
AHBPrescaler::Div5 => 0x02,
|
||||||
|
AHBPrescaler::Div6 => 0x05,
|
||||||
|
AHBPrescaler::Div8 => 0x0a,
|
||||||
|
AHBPrescaler::Div10 => 0x06,
|
||||||
|
AHBPrescaler::Div16 => 0x0b,
|
||||||
|
AHBPrescaler::Div32 => 0x07,
|
||||||
|
AHBPrescaler::Div64 => 0x0c,
|
||||||
|
AHBPrescaler::Div128 => 0x0d,
|
||||||
|
AHBPrescaler::Div256 => 0x0e,
|
||||||
|
AHBPrescaler::Div512 => 0x0f,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clocks configutation
|
||||||
|
pub struct Config {
|
||||||
|
pub mux: ClockSrc,
|
||||||
|
pub ahb_pre: AHBPrescaler,
|
||||||
|
pub apb1_pre: APBPrescaler,
|
||||||
|
pub apb2_pre: APBPrescaler,
|
||||||
|
pub enable_lsi: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Config {
|
||||||
|
Config {
|
||||||
|
mux: ClockSrc::HSI16,
|
||||||
|
ahb_pre: AHBPrescaler::NotDivided,
|
||||||
|
apb1_pre: APBPrescaler::NotDivided,
|
||||||
|
apb2_pre: APBPrescaler::NotDivided,
|
||||||
|
enable_lsi: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
|
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(),
|
||||||
|
});
|
||||||
|
}
|
@ -1,236 +0,0 @@
|
|||||||
use crate::pac;
|
|
||||||
use crate::peripherals::{self, RCC};
|
|
||||||
use crate::rcc::{get_freqs, 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.
|
|
||||||
|
|
||||||
/// Only the basic setup using the HSE and HSI clocks are supported as of now.
|
|
||||||
|
|
||||||
/// HSI speed
|
|
||||||
pub const HSI_FREQ: u32 = 16_000_000;
|
|
||||||
|
|
||||||
pub const HSE32_FREQ: u32 = 32_000_000;
|
|
||||||
|
|
||||||
/// System clock mux source
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum ClockSrc {
|
|
||||||
HSE32,
|
|
||||||
HSI16,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// AHB prescaler
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
|
||||||
pub enum AHBPrescaler {
|
|
||||||
NotDivided,
|
|
||||||
Div2,
|
|
||||||
Div3,
|
|
||||||
Div4,
|
|
||||||
Div5,
|
|
||||||
Div6,
|
|
||||||
Div8,
|
|
||||||
Div10,
|
|
||||||
Div16,
|
|
||||||
Div32,
|
|
||||||
Div64,
|
|
||||||
Div128,
|
|
||||||
Div256,
|
|
||||||
Div512,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// APB prescaler
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum APBPrescaler {
|
|
||||||
NotDivided,
|
|
||||||
Div2,
|
|
||||||
Div4,
|
|
||||||
Div8,
|
|
||||||
Div16,
|
|
||||||
}
|
|
||||||
|
|
||||||
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::Div3 => 0x01,
|
|
||||||
AHBPrescaler::Div4 => 0x09,
|
|
||||||
AHBPrescaler::Div5 => 0x02,
|
|
||||||
AHBPrescaler::Div6 => 0x05,
|
|
||||||
AHBPrescaler::Div8 => 0x0a,
|
|
||||||
AHBPrescaler::Div10 => 0x06,
|
|
||||||
AHBPrescaler::Div16 => 0x0b,
|
|
||||||
AHBPrescaler::Div32 => 0x07,
|
|
||||||
AHBPrescaler::Div64 => 0x0c,
|
|
||||||
AHBPrescaler::Div128 => 0x0d,
|
|
||||||
AHBPrescaler::Div256 => 0x0e,
|
|
||||||
AHBPrescaler::Div512 => 0x0f,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clocks configutation
|
|
||||||
pub struct Config {
|
|
||||||
pub mux: ClockSrc,
|
|
||||||
pub ahb_pre: AHBPrescaler,
|
|
||||||
pub apb1_pre: APBPrescaler,
|
|
||||||
pub apb2_pre: APBPrescaler,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Config {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Config {
|
|
||||||
Config {
|
|
||||||
mux: ClockSrc::HSI16,
|
|
||||||
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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn init(config: Config) {
|
|
||||||
let r = <peripherals::RCC as embassy::util::Steal>::steal();
|
|
||||||
let clocks = r.freeze(config);
|
|
||||||
set_freqs(clocks);
|
|
||||||
}
|
|
@ -8,16 +8,18 @@ mod example_common;
|
|||||||
use embassy::executor::Spawner;
|
use embassy::executor::Spawner;
|
||||||
use embassy_stm32::exti::ExtiInput;
|
use embassy_stm32::exti::ExtiInput;
|
||||||
use embassy_stm32::gpio::{Input, Pull};
|
use embassy_stm32::gpio::{Input, Pull};
|
||||||
use embassy_stm32::{rcc, Peripherals};
|
use embassy_stm32::Peripherals;
|
||||||
use embassy_traits::gpio::{WaitForFallingEdge, WaitForRisingEdge};
|
use embassy_traits::gpio::{WaitForFallingEdge, WaitForRisingEdge};
|
||||||
use example_common::*;
|
use example_common::*;
|
||||||
|
|
||||||
#[embassy::main]
|
fn config() -> embassy_stm32::Config {
|
||||||
async fn main(_spawner: Spawner, mut p: Peripherals) {
|
let mut config = embassy_stm32::Config::default();
|
||||||
let mut rcc = rcc::Rcc::new(p.RCC);
|
config.rcc.enable_hsi48 = true;
|
||||||
// Enables SYSCFG
|
config
|
||||||
let _ = rcc.enable_hsi48(&mut p.SYSCFG, p.CRS);
|
}
|
||||||
|
|
||||||
|
#[embassy::main(config = "config()")]
|
||||||
|
async fn main(_spawner: Spawner, p: Peripherals) {
|
||||||
let button = Input::new(p.PB2, Pull::Up);
|
let button = Input::new(p.PB2, Pull::Up);
|
||||||
let mut button = ExtiInput::new(button, p.EXTI2);
|
let mut button = ExtiInput::new(button, p.EXTI2);
|
||||||
|
|
||||||
|
@ -11,10 +11,8 @@ mod example_common;
|
|||||||
|
|
||||||
use embassy_lora::{sx127x::*, LoraTimer};
|
use embassy_lora::{sx127x::*, LoraTimer};
|
||||||
use embassy_stm32::{
|
use embassy_stm32::{
|
||||||
dbgmcu::Dbgmcu,
|
|
||||||
exti::ExtiInput,
|
exti::ExtiInput,
|
||||||
gpio::{Input, Level, Output, Pull, Speed},
|
gpio::{Input, Level, Output, Pull, Speed},
|
||||||
rcc,
|
|
||||||
rng::Rng,
|
rng::Rng,
|
||||||
spi,
|
spi,
|
||||||
time::U32Ext,
|
time::U32Ext,
|
||||||
@ -26,18 +24,12 @@ use lorawan_encoding::default_crypto::DefaultFactory as Crypto;
|
|||||||
fn config() -> embassy_stm32::Config {
|
fn config() -> embassy_stm32::Config {
|
||||||
let mut config = embassy_stm32::Config::default();
|
let mut config = embassy_stm32::Config::default();
|
||||||
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16;
|
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16;
|
||||||
|
config.rcc.enable_hsi48 = true;
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy::main(config = "config()")]
|
#[embassy::main(config = "config()")]
|
||||||
async fn main(_spawner: embassy::executor::Spawner, mut p: Peripherals) {
|
async fn main(_spawner: embassy::executor::Spawner, p: Peripherals) {
|
||||||
unsafe {
|
|
||||||
Dbgmcu::enable_all();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut rcc = rcc::Rcc::new(p.RCC);
|
|
||||||
let _ = rcc.enable_hsi48(&mut p.SYSCFG, p.CRS);
|
|
||||||
|
|
||||||
// SPI for sx127x
|
// SPI for sx127x
|
||||||
let spi = spi::Spi::new(
|
let spi = spi::Spi::new(
|
||||||
p.SPI1,
|
p.SPI1,
|
||||||
|
@ -10,10 +10,9 @@ mod example_common;
|
|||||||
|
|
||||||
use embassy_lora::{stm32wl::*, LoraTimer};
|
use embassy_lora::{stm32wl::*, LoraTimer};
|
||||||
use embassy_stm32::{
|
use embassy_stm32::{
|
||||||
dbgmcu::Dbgmcu,
|
|
||||||
dma::NoDma,
|
dma::NoDma,
|
||||||
gpio::{Level, Output, Pin, Speed},
|
gpio::{Level, Output, Pin, Speed},
|
||||||
interrupt, pac, rcc,
|
interrupt, pac,
|
||||||
rng::Rng,
|
rng::Rng,
|
||||||
subghz::*,
|
subghz::*,
|
||||||
Peripherals,
|
Peripherals,
|
||||||
@ -24,19 +23,13 @@ use lorawan_encoding::default_crypto::DefaultFactory as Crypto;
|
|||||||
fn config() -> embassy_stm32::Config {
|
fn config() -> embassy_stm32::Config {
|
||||||
let mut config = embassy_stm32::Config::default();
|
let mut config = embassy_stm32::Config::default();
|
||||||
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16;
|
config.rcc.mux = embassy_stm32::rcc::ClockSrc::HSI16;
|
||||||
|
config.rcc.enable_lsi = true;
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy::main(config = "config()")]
|
#[embassy::main(config = "config()")]
|
||||||
async fn main(_spawner: embassy::executor::Spawner, p: Peripherals) {
|
async fn main(_spawner: embassy::executor::Spawner, p: Peripherals) {
|
||||||
unsafe {
|
unsafe { pac::RCC.ccipr().modify(|w| w.set_rngsel(0b01)) }
|
||||||
Dbgmcu::enable_all();
|
|
||||||
let mut rcc = rcc::Rcc::new(p.RCC);
|
|
||||||
rcc.enable_lsi();
|
|
||||||
pac::RCC.ccipr().modify(|w| {
|
|
||||||
w.set_rngsel(0b01);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let ctrl1 = Output::new(p.PC3.degrade(), Level::High, Speed::High);
|
let ctrl1 = Output::new(p.PC3.degrade(), Level::High, Speed::High);
|
||||||
let ctrl2 = Output::new(p.PC4.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::channel::signal::Signal;
|
||||||
use embassy::interrupt::{Interrupt, InterruptExt};
|
use embassy::interrupt::{Interrupt, InterruptExt};
|
||||||
use embassy::traits::gpio::WaitForRisingEdge;
|
use embassy::traits::gpio::WaitForRisingEdge;
|
||||||
use embassy_stm32::dbgmcu::Dbgmcu;
|
|
||||||
use embassy_stm32::dma::NoDma;
|
use embassy_stm32::dma::NoDma;
|
||||||
use embassy_stm32::exti::ExtiInput;
|
use embassy_stm32::exti::ExtiInput;
|
||||||
use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
|
use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed};
|
||||||
@ -72,10 +71,6 @@ fn config() -> embassy_stm32::Config {
|
|||||||
|
|
||||||
#[embassy::main(config = "config()")]
|
#[embassy::main(config = "config()")]
|
||||||
async fn main(_spawner: embassy::executor::Spawner, p: Peripherals) {
|
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 led1 = Output::new(p.PB15, Level::High, Speed::Low);
|
||||||
let mut led2 = Output::new(p.PB9, Level::Low, Speed::Low);
|
let mut led2 = Output::new(p.PB9, Level::Low, Speed::Low);
|
||||||
let mut led3 = Output::new(p.PB11, Level::Low, Speed::Low);
|
let mut led3 = Output::new(p.PB11, Level::Low, Speed::Low);
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 27f9d6dc2c5afaa5003ce9afc06def9b16d30adb
|
Subproject commit 3fa97966f07d43a28c0031175591e1c2ff5d0831
|
@ -125,6 +125,7 @@ SECTIONS
|
|||||||
{
|
{
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
__sdata = .;
|
__sdata = .;
|
||||||
|
__edata = .;
|
||||||
*(.data .data.*);
|
*(.data .data.*);
|
||||||
. = ALIGN(4); /* 4-byte align the end (VMA) of this section */
|
. = ALIGN(4); /* 4-byte align the end (VMA) of this section */
|
||||||
} > RAM
|
} > RAM
|
||||||
@ -132,7 +133,6 @@ SECTIONS
|
|||||||
* use the .data loading mechanism by pushing __edata. Note: do not change
|
* use the .data loading mechanism by pushing __edata. Note: do not change
|
||||||
* output region or load region in those user sections! */
|
* output region or load region in those user sections! */
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
__edata = .;
|
|
||||||
|
|
||||||
/* LMA of .data */
|
/* LMA of .data */
|
||||||
__sidata = LOADADDR(.data);
|
__sidata = LOADADDR(.data);
|
||||||
|
Loading…
Reference in New Issue
Block a user