566: stm32: more RCC cleanups. r=Dirbaio a=Dirbaio



Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
This commit is contained in:
bors[bot] 2022-01-05 12:34:10 +00:00 committed by GitHub
commit da0c25227f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 4212 additions and 4860 deletions

View File

@ -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()

View File

@ -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);
};
}
});
}
}

View File

@ -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();

View File

@ -1 +0,0 @@

View File

@ -1 +0,0 @@

View File

@ -1 +0,0 @@

View File

@ -1 +0,0 @@

View File

@ -1 +0,0 @@

View File

@ -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 }
}
}
}

View File

@ -1 +0,0 @@

View File

@ -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::*;

View File

@ -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,
}
}
}

View File

@ -1 +0,0 @@

174
embassy-stm32/src/rcc/f0.rs Normal file
View 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),
});
}

View File

@ -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
View 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),
});
}

View File

@ -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
View 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
}

View File

@ -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
View 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;
}

View File

@ -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;

View File

@ -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
View 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;
}

View File

@ -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;

View File

@ -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
View 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(),
});
}

View File

@ -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
View 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(),
});
}

View File

@ -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
View 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)
}
}
}
}

View File

@ -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),
});
}

View File

@ -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
View 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(),
});
}

View File

@ -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
View 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(),
});
}

View File

@ -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
View 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(),
});
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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
View 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(),
});
}

View File

@ -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);
}

View 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(),
});
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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,

View File

@ -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);

View File

@ -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

View File

@ -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);