stm32/rcc: remove Rcc struct, RccExt trait.

All the RCC configuration is executed in init().
This commit is contained in:
Dario Nieuwenhuis 2022-01-04 23:58:13 +01:00
parent c3fd9a0f44
commit 2eb0cc5df7
31 changed files with 2586 additions and 3196 deletions

View File

@ -43,8 +43,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)]

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,13 +0,0 @@
#[cfg_attr(any(pwr_h7, pwr_h7smps), path = "h7.rs")]
#[cfg_attr(pwr_f3, path = "f3.rs")]
#[cfg_attr(pwr_f4, path = "f4.rs")]
#[cfg_attr(pwr_f7, path = "f7.rs")]
#[cfg_attr(pwr_wl5, path = "wl5.rs")]
#[cfg_attr(pwr_g0, path = "g0.rs")]
#[cfg_attr(pwr_g4, path = "g4.rs")]
#[cfg_attr(pwr_l1, path = "l1.rs")]
#[cfg_attr(pwr_u5, path = "u5.rs")]
#[cfg_attr(pwr_wb55, path = "wb55.rs")]
mod _version;
pub use _version::*;

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

View File

@ -1,9 +1,5 @@
use core::marker::PhantomData; use crate::pac::rcc::vals::{Hpre, Hsebyp, Pllmul, Pllsrc, Ppre, Sw, Usbsw};
use embassy::util::Unborrow;
use crate::pac::{FLASH, RCC}; use crate::pac::{FLASH, RCC};
use crate::peripherals;
use crate::time::Hertz; use crate::time::Hertz;
use super::{set_freqs, Clocks}; use super::{set_freqs, Clocks};
@ -28,27 +24,12 @@ pub struct Config {
pub pclk: Option<Hertz>, pub pclk: Option<Hertz>,
} }
pub struct Rcc<'d> { pub(crate) unsafe fn init(config: Config) {
inner: PhantomData<&'d ()>, let sysclk = config.sys_ck.map(|v| v.0).unwrap_or(HSI);
config: Config,
}
impl<'d> Rcc<'d> { let (src_clk, use_hsi48) = config.hse.map(|v| (v.0, false)).unwrap_or_else(|| {
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)] #[cfg(rcc_f0)]
if self.config.hsi48 { if config.hsi48 {
return (48_000_000, true); return (48_000_000, true);
} }
(HSI, false) (HSI, false)
@ -57,7 +38,7 @@ impl<'d> Rcc<'d> {
let (pllmul_bits, real_sysclk) = if sysclk == src_clk { let (pllmul_bits, real_sysclk) = if sysclk == src_clk {
(None, sysclk) (None, sysclk)
} else { } else {
let prediv = if self.config.hse.is_some() { 1 } else { 2 }; let prediv = if config.hse.is_some() { 1 } else { 2 };
let pllmul = (2 * prediv * sysclk + src_clk) / src_clk / 2; let pllmul = (2 * prediv * sysclk + src_clk) / src_clk / 2;
let pllmul = pllmul.max(2).min(16); let pllmul = pllmul.max(2).min(16);
@ -66,8 +47,7 @@ impl<'d> Rcc<'d> {
(Some(pllmul_bits), real_sysclk) (Some(pllmul_bits), real_sysclk)
}; };
let hpre_bits = self let hpre_bits = config
.config
.hclk .hclk
.map(|hclk| match real_sysclk / hclk.0 { .map(|hclk| match real_sysclk / hclk.0 {
0 => unreachable!(), 0 => unreachable!(),
@ -84,8 +64,7 @@ impl<'d> Rcc<'d> {
.unwrap_or(0b0111); .unwrap_or(0b0111);
let hclk = real_sysclk / (1 << (hpre_bits - 0b0111)); let hclk = real_sysclk / (1 << (hpre_bits - 0b0111));
let ppre_bits = self let ppre_bits = config
.config
.pclk .pclk
.map(|pclk| match hclk / pclk.0 { .map(|pclk| match hclk / pclk.0 {
0 => unreachable!(), 0 => unreachable!(),
@ -102,8 +81,6 @@ impl<'d> Rcc<'d> {
let timer_mul = if ppre == 1 { 1 } else { 2 }; let timer_mul = if ppre == 1 { 1 } else { 2 };
// NOTE(safety) Atomic write
unsafe {
FLASH.acr().write(|w| { FLASH.acr().write(|w| {
let latency = if real_sysclk <= 24_000_000 { let latency = if real_sysclk <= 24_000_000 {
0 0
@ -114,17 +91,14 @@ impl<'d> Rcc<'d> {
}; };
w.latency().0 = latency; w.latency().0 = latency;
}); });
}
// NOTE(unsafe) We have exclusive access to the RCC match (config.hse.is_some(), use_hsi48) {
unsafe {
match (self.config.hse.is_some(), use_hsi48) {
(true, _) => { (true, _) => {
RCC.cr().modify(|w| { RCC.cr().modify(|w| {
w.set_csson(true); w.set_csson(true);
w.set_hseon(true); w.set_hseon(true);
if self.config.bypass_hse { if config.bypass_hse {
w.set_hsebyp(Hsebyp::BYPASSED); w.set_hsebyp(Hsebyp::BYPASSED);
} }
}); });
@ -157,7 +131,7 @@ impl<'d> Rcc<'d> {
} }
} }
if self.config.usb_pll { if config.usb_pll {
RCC.cfgr3().modify(|w| w.set_usbsw(Usbsw::PLLCLK)); RCC.cfgr3().modify(|w| w.set_usbsw(Usbsw::PLLCLK));
} }
// TODO: Option to use CRS (Clock Recovery) // TODO: Option to use CRS (Clock Recovery)
@ -178,7 +152,7 @@ impl<'d> Rcc<'d> {
w.set_ppre(Ppre(ppre_bits)); w.set_ppre(Ppre(ppre_bits));
w.set_hpre(Hpre(hpre_bits)); w.set_hpre(Hpre(hpre_bits));
if self.config.hse.is_some() { if config.hse.is_some() {
w.set_sw(Sw::HSE); w.set_sw(Sw::HSE);
} else if use_hsi48 { } else if use_hsi48 {
#[cfg(rcc_f0)] #[cfg(rcc_f0)]
@ -188,21 +162,13 @@ impl<'d> Rcc<'d> {
} }
}) })
} }
}
Clocks { set_freqs(Clocks {
sys: Hertz(real_sysclk), sys: Hertz(real_sysclk),
apb1: Hertz(pclk), apb1: Hertz(pclk),
apb2: Hertz(pclk), apb2: Hertz(pclk),
apb1_tim: Hertz(pclk * timer_mul), apb1_tim: Hertz(pclk * timer_mul),
apb2_tim: Hertz(pclk * timer_mul), apb2_tim: Hertz(pclk * timer_mul),
ahb: Hertz(hclk), ahb: Hertz(hclk),
} });
}
}
pub(crate) unsafe fn init(config: Config) {
let rcc = Rcc::new(<peripherals::RCC as embassy::util::Steal>::steal(), config);
let clocks = rcc.freeze();
set_freqs(clocks);
} }

View File

@ -1,14 +1,10 @@
use core::convert::TryFrom; use core::convert::TryFrom;
use core::marker::PhantomData;
use embassy::util::Unborrow;
use crate::pac::flash::vals::Latency;
use crate::pac::{FLASH, RCC};
use crate::peripherals;
use crate::time::Hertz;
use super::{set_freqs, Clocks}; use 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; const HSI: u32 = 8_000_000;
@ -26,28 +22,13 @@ pub struct Config {
pub adcclk: Option<Hertz>, pub adcclk: Option<Hertz>,
} }
pub struct Rcc<'d> { pub(crate) unsafe fn init(config: Config) {
inner: PhantomData<&'d ()>, let pllsrcclk = config.hse.map(|hse| hse.0).unwrap_or(HSI / 2);
config: Config, let sysclk = config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk);
}
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 = sysclk / pllsrcclk;
let (pllmul_bits, real_sysclk) = if pllmul == 1 { let (pllmul_bits, real_sysclk) = if pllmul == 1 {
(None, self.config.hse.map(|hse| hse.0).unwrap_or(HSI)) (None, config.hse.map(|hse| hse.0).unwrap_or(HSI))
} else { } else {
let pllmul = core::cmp::min(core::cmp::max(pllmul, 1), 16); let pllmul = core::cmp::min(core::cmp::max(pllmul, 1), 16);
(Some(pllmul as u8 - 2), pllsrcclk * pllmul) (Some(pllmul as u8 - 2), pllsrcclk * pllmul)
@ -55,8 +36,7 @@ impl<'d> Rcc<'d> {
assert!(real_sysclk <= 72_000_000); assert!(real_sysclk <= 72_000_000);
let hpre_bits = self let hpre_bits = config
.config
.hclk .hclk
.map(|hclk| match real_sysclk / hclk.0 { .map(|hclk| match real_sysclk / hclk.0 {
0 => unreachable!(), 0 => unreachable!(),
@ -80,8 +60,7 @@ impl<'d> Rcc<'d> {
assert!(hclk <= 72_000_000); assert!(hclk <= 72_000_000);
let ppre1_bits = self let ppre1_bits = config
.config
.pclk1 .pclk1
.map(|pclk1| match hclk / pclk1.0 { .map(|pclk1| match hclk / pclk1.0 {
0 => unreachable!(), 0 => unreachable!(),
@ -99,8 +78,7 @@ impl<'d> Rcc<'d> {
assert!(pclk1 <= 36_000_000); assert!(pclk1 <= 36_000_000);
let ppre2_bits = self let ppre2_bits = config
.config
.pclk2 .pclk2
.map(|pclk2| match hclk / pclk2.0 { .map(|pclk2| match hclk / pclk2.0 {
0 => unreachable!(), 0 => unreachable!(),
@ -119,8 +97,6 @@ impl<'d> Rcc<'d> {
assert!(pclk2 <= 72_000_000); assert!(pclk2 <= 72_000_000);
// Only needed for stm32f103? // Only needed for stm32f103?
// NOTE(safety) Atomic write
unsafe {
FLASH.acr().write(|w| { FLASH.acr().write(|w| {
w.set_latency(if real_sysclk <= 24_000_000 { w.set_latency(if real_sysclk <= 24_000_000 {
Latency(0b000) Latency(0b000)
@ -129,20 +105,18 @@ impl<'d> Rcc<'d> {
} else { } else {
Latency(0b010) Latency(0b010)
}); });
}) });
}
// the USB clock is only valid if an external crystal is used, the PLL is enabled, and the // 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. // PLL output frequency is a supported one.
// usbpre == false: divide clock by 1.5, otherwise no division // usbpre == false: divide clock by 1.5, otherwise no division
let (usbpre, _usbclk_valid) = match (self.config.hse, pllmul_bits, real_sysclk) { let (usbpre, _usbclk_valid) = match (config.hse, pllmul_bits, real_sysclk) {
(Some(_), Some(_), 72_000_000) => (false, true), (Some(_), Some(_), 72_000_000) => (false, true),
(Some(_), Some(_), 48_000_000) => (true, true), (Some(_), Some(_), 48_000_000) => (true, true),
_ => (true, false), _ => (true, false),
}; };
let apre_bits: u8 = self let apre_bits: u8 = config
.config
.adcclk .adcclk
.map(|adcclk| match pclk2 / adcclk.0 { .map(|adcclk| match pclk2 / adcclk.0 {
0..=2 => 0b00, 0..=2 => 0b00,
@ -157,8 +131,7 @@ impl<'d> Rcc<'d> {
assert!(adcclk <= 14_000_000); assert!(adcclk <= 14_000_000);
unsafe { if config.hse.is_some() {
if self.config.hse.is_some() {
// enable HSE and wait for it to be ready // enable HSE and wait for it to be ready
RCC.cr().modify(|w| w.set_hseon(true)); RCC.cr().modify(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {} while !RCC.cr().read().hserdy() {}
@ -168,7 +141,7 @@ impl<'d> Rcc<'d> {
// enable PLL and wait for it to be ready // enable PLL and wait for it to be ready
RCC.cfgr().modify(|w| { RCC.cfgr().modify(|w| {
w.set_pllmul(Pllmul(pllmul_bits)); w.set_pllmul(Pllmul(pllmul_bits));
w.set_pllsrc(Pllsrc(self.config.hse.is_some() as u8)); w.set_pllsrc(Pllsrc(config.hse.is_some() as u8));
}); });
RCC.cr().modify(|w| w.set_pllon(true)); RCC.cr().modify(|w| w.set_pllon(true));
@ -185,7 +158,7 @@ impl<'d> Rcc<'d> {
w.set_sw(Sw(if pllmul_bits.is_some() { w.set_sw(Sw(if pllmul_bits.is_some() {
// PLL // PLL
0b10 0b10
} else if self.config.hse.is_some() { } else if config.hse.is_some() {
// HSE // HSE
0b1 0b1
} else { } else {
@ -193,9 +166,8 @@ impl<'d> Rcc<'d> {
0b0 0b0
})); }));
}); });
}
Clocks { set_freqs(Clocks {
sys: Hertz(real_sysclk), sys: Hertz(real_sysclk),
apb1: Hertz(pclk1), apb1: Hertz(pclk1),
apb2: Hertz(pclk2), apb2: Hertz(pclk2),
@ -203,12 +175,5 @@ impl<'d> Rcc<'d> {
apb2_tim: Hertz(pclk2 * timer_mul2), apb2_tim: Hertz(pclk2 * timer_mul2),
ahb: Hertz(hclk), ahb: Hertz(hclk),
adc: Hertz(adcclk), adc: Hertz(adcclk),
} });
}
}
pub(crate) unsafe fn init(config: Config) {
let rcc = Rcc::new(<peripherals::RCC as embassy::util::Steal>::steal(), config);
let clocks = rcc.freeze();
set_freqs(clocks);
} }

View File

@ -1,23 +1,11 @@
use core::marker::PhantomData; use crate::pac::flash::vals::Latency;
use embassy::util::Unborrow; use crate::pac::rcc::vals::{Hpre, Hsebyp, Pllmul, Pllsrc, Ppre, Prediv, Sw, Usbpre};
use crate::pac::{FLASH, RCC};
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::rcc::{set_freqs, Clocks};
use crate::time::Hertz; use crate::time::Hertz;
const HSI: u32 = 8_000_000; const HSI: u32 = 8_000_000;
/// RCC peripheral
pub struct Rcc<'d> {
config: Config,
phantom: PhantomData<&'d mut peripherals::RCC>,
}
/// Clocks configutation /// Clocks configutation
#[non_exhaustive] #[non_exhaustive]
#[derive(Default)] #[derive(Default)]
@ -55,26 +43,12 @@ struct PllConfig {
/// Initialize and Set the clock frequencies /// Initialize and Set the clock frequencies
pub(crate) unsafe fn init(config: Config) { pub(crate) unsafe fn init(config: Config) {
let r = <peripherals::RCC as embassy::util::Steal>::steal();
let clocks = Rcc::new(r, config).freeze();
set_freqs(clocks);
}
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 // Calculate the real System clock, and PLL configuration if applicable
let (Hertz(sysclk), pll_config) = self.get_sysclk(); let (Hertz(sysclk), pll_config) = get_sysclk(&config);
assert!(sysclk <= 72_000_000); assert!(sysclk <= 72_000_000);
// Calculate real AHB clock // Calculate real AHB clock
let hclk = self.config.hclk.map(|h| h.0).unwrap_or(sysclk); let hclk = config.hclk.map(|h| h.0).unwrap_or(sysclk);
let (hpre_bits, hpre_div) = match sysclk / hclk { let (hpre_bits, hpre_div) = match sysclk / hclk {
0 => unreachable!(), 0 => unreachable!(),
1 => (Hpre::DIV1, 1), 1 => (Hpre::DIV1, 1),
@ -91,7 +65,7 @@ impl<'d> Rcc<'d> {
assert!(hclk <= 72_000_000); assert!(hclk <= 72_000_000);
// Calculate real APB1 clock // Calculate real APB1 clock
let pclk1 = self.config.pclk1.map(|p| p.0).unwrap_or(hclk); let pclk1 = config.pclk1.map(|p| p.0).unwrap_or(hclk);
let (ppre1_bits, ppre1) = match hclk / pclk1 { let (ppre1_bits, ppre1) = match hclk / pclk1 {
0 => unreachable!(), 0 => unreachable!(),
1 => (Ppre::DIV1, 1), 1 => (Ppre::DIV1, 1),
@ -105,7 +79,7 @@ impl<'d> Rcc<'d> {
assert!(pclk1 <= 36_000_000); assert!(pclk1 <= 36_000_000);
// Calculate real APB2 clock // Calculate real APB2 clock
let pclk2 = self.config.pclk2.map(|p| p.0).unwrap_or(hclk); let pclk2 = config.pclk2.map(|p| p.0).unwrap_or(hclk);
let (ppre2_bits, ppre2) = match hclk / pclk2 { let (ppre2_bits, ppre2) = match hclk / pclk2 {
0 => unreachable!(), 0 => unreachable!(),
1 => (Ppre::DIV1, 1), 1 => (Ppre::DIV1, 1),
@ -119,8 +93,6 @@ impl<'d> Rcc<'d> {
assert!(pclk2 <= 72_000_000); assert!(pclk2 <= 72_000_000);
// Set latency based on HCLK frquency // Set latency based on HCLK frquency
// NOTE(safety) Atomic write
unsafe {
FLASH.acr().write(|w| { FLASH.acr().write(|w| {
w.set_latency(if hclk <= 24_000_000 { w.set_latency(if hclk <= 24_000_000 {
Latency::WS0 Latency::WS0
@ -129,15 +101,12 @@ impl<'d> Rcc<'d> {
} else { } else {
Latency::WS2 Latency::WS2
}); });
}) });
}
// Enable HSE // Enable HSE
if self.config.hse.is_some() { if config.hse.is_some() {
// NOTE(unsafe) We own the peripheral block
unsafe {
RCC.cr().write(|w| { RCC.cr().write(|w| {
w.set_hsebyp(if self.config.bypass_hse { w.set_hsebyp(if config.bypass_hse {
Hsebyp::BYPASSED Hsebyp::BYPASSED
} else { } else {
Hsebyp::NOTBYPASSED Hsebyp::NOTBYPASSED
@ -148,12 +117,9 @@ impl<'d> Rcc<'d> {
}); });
while !RCC.cr().read().hserdy() {} while !RCC.cr().read().hserdy() {}
} }
}
// Enable PLL // Enable PLL
if let Some(ref pll_config) = pll_config { if let Some(ref pll_config) = pll_config {
// NOTE(unsafe) We own the peripheral block
unsafe {
RCC.cfgr().write(|w| { RCC.cfgr().write(|w| {
w.set_pllmul(pll_config.pll_mul); w.set_pllmul(pll_config.pll_mul);
w.set_pllsrc(pll_config.pll_src); w.set_pllsrc(pll_config.pll_src);
@ -164,21 +130,15 @@ impl<'d> Rcc<'d> {
RCC.cr().modify(|w| w.set_pllon(true)); RCC.cr().modify(|w| w.set_pllon(true));
while !RCC.cr().read().pllrdy() {} while !RCC.cr().read().pllrdy() {}
} }
}
if self.config.pll48 { if config.pll48 {
let usb_pre = self.get_usb_pre(sysclk, pclk1, &pll_config); let usb_pre = get_usb_pre(&config, sysclk, pclk1, &pll_config);
// NOTE(unsafe) We own the peripheral block
unsafe {
RCC.cfgr().write(|w| { RCC.cfgr().write(|w| {
w.set_usbpre(usb_pre); w.set_usbpre(usb_pre);
}); });
} }
}
// Set prescalers // Set prescalers
unsafe {
// NOTE(unsafe) We own the peripheral block
RCC.cfgr().write(|w| { RCC.cfgr().write(|w| {
w.set_ppre2(ppre2_bits); w.set_ppre2(ppre2_bits);
w.set_ppre1(ppre1_bits); w.set_ppre1(ppre1_bits);
@ -190,35 +150,33 @@ impl<'d> Rcc<'d> {
// 1 to 16 AHB cycles after write" // 1 to 16 AHB cycles after write"
cortex_m::asm::delay(16); cortex_m::asm::delay(16);
// NOTE(unsafe) We own the peripheral block
RCC.cfgr().write(|w| { RCC.cfgr().write(|w| {
w.set_sw(match (pll_config, self.config.hse) { w.set_sw(match (pll_config, config.hse) {
(Some(_), _) => Sw::PLL, (Some(_), _) => Sw::PLL,
(None, Some(_)) => Sw::HSE, (None, Some(_)) => Sw::HSE,
(None, None) => Sw::HSI, (None, None) => Sw::HSI,
}) })
}); });
}
Clocks { set_freqs(Clocks {
sys: Hertz(sysclk), sys: Hertz(sysclk),
apb1: Hertz(pclk1), apb1: Hertz(pclk1),
apb2: Hertz(pclk2), apb2: Hertz(pclk2),
apb1_tim: Hertz(pclk1 * timer_mul1), apb1_tim: Hertz(pclk1 * timer_mul1),
apb2_tim: Hertz(pclk2 * timer_mul2), apb2_tim: Hertz(pclk2 * timer_mul2),
ahb: Hertz(hclk), ahb: Hertz(hclk),
} });
} }
#[inline] #[inline]
fn get_sysclk(&self) -> (Hertz, Option<PllConfig>) { fn get_sysclk(config: &Config) -> (Hertz, Option<PllConfig>) {
match (self.config.sysclk, self.config.hse) { match (config.sysclk, config.hse) {
(Some(sysclk), Some(hse)) if sysclk == hse => (hse, None), (Some(sysclk), Some(hse)) if sysclk == hse => (hse, None),
(Some(sysclk), None) if sysclk.0 == HSI => (Hertz(HSI), None), (Some(sysclk), None) if sysclk.0 == HSI => (Hertz(HSI), None),
// If the user selected System clock is different from HSI or HSE // If the user selected System clock is different from HSI or HSE
// we will have to setup PLL clock source // we will have to setup PLL clock source
(Some(sysclk), _) => { (Some(sysclk), _) => {
let (sysclk, pll_config) = self.calc_pll(sysclk); let (sysclk, pll_config) = calc_pll(config, sysclk);
(sysclk, Some(pll_config)) (sysclk, Some(pll_config))
} }
(None, Some(hse)) => (hse, None), (None, Some(hse)) => (hse, None),
@ -227,7 +185,7 @@ impl<'d> Rcc<'d> {
} }
#[inline] #[inline]
fn calc_pll(&self, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) { fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) {
// Calculates the Multiplier and the Divisor to arrive at // Calculates the Multiplier and the Divisor to arrive at
// the required System clock from PLL source frequency // the required System clock from PLL source frequency
let get_mul_div = |sysclk, pllsrcclk| { let get_mul_div = |sysclk, pllsrcclk| {
@ -245,7 +203,7 @@ impl<'d> Rcc<'d> {
}; };
// Based on the source of Pll, we calculate the actual system clock // Based on the source of Pll, we calculate the actual system clock
// frequency, PLL's source identifier, multiplier and divisor // frequency, PLL's source identifier, multiplier and divisor
let (act_sysclk, pll_src, pll_mul, pll_div) = match self.config.hse { let (act_sysclk, pll_src, pll_mul, pll_div) = match config.hse {
Some(Hertz(hse)) => { Some(Hertz(hse)) => {
let (multiplier, divisor) = get_mul_div(sysclk, hse); let (multiplier, divisor) = get_mul_div(sysclk, hse);
( (
@ -294,13 +252,13 @@ impl<'d> Rcc<'d> {
} }
#[inline] #[inline]
fn get_usb_pre(&self, sysclk: u32, pclk1: u32, pll_config: &Option<PllConfig>) -> Usbpre { fn get_usb_pre(config: &Config, sysclk: u32, pclk1: u32, pll_config: &Option<PllConfig>) -> Usbpre {
cfg_if::cfg_if! { cfg_if::cfg_if! {
// Some chips do not have USB // Some chips do not have USB
if #[cfg(any(stm32f301, stm32f318, stm32f334))] { if #[cfg(any(stm32f301, stm32f318, stm32f334))] {
panic!("USB clock not supported by the chip"); panic!("USB clock not supported by the chip");
} else { } else {
let usb_ok = self.config.hse.is_some() && pll_config.is_some() && (pclk1 >= 10_000_000); let usb_ok = config.hse.is_some() && pll_config.is_some() && (pclk1 >= 10_000_000);
match (usb_ok, sysclk) { match (usb_ok, sysclk) {
(true, 72_000_000) => Usbpre::DIV1_5, (true, 72_000_000) => Usbpre::DIV1_5,
(true, 48_000_000) => Usbpre::DIV1, (true, 48_000_000) => Usbpre::DIV1,
@ -311,7 +269,6 @@ impl<'d> Rcc<'d> {
} }
} }
} }
}
// This function assumes cases when multiplier is one and it // This function assumes cases when multiplier is one and it
// being greater than 16 is made impossible // being greater than 16 is made impossible

View File

@ -1,9 +1,8 @@
use super::sealed::RccPeripheral;
use crate::pac::rcc::vals::{Hpre, Hsebyp, Ppre, Sw};
use crate::pac::{FLASH, PWR, RCC}; use crate::pac::{FLASH, PWR, RCC};
use crate::peripherals; use crate::rcc::{set_freqs, Clocks};
use crate::rcc::{get_freqs, set_freqs, Clocks};
use crate::time::Hertz; use crate::time::Hertz;
use core::marker::PhantomData;
use embassy::util::Unborrow;
const HSI: u32 = 16_000_000; const HSI: u32 = 16_000_000;
@ -21,180 +20,7 @@ pub struct Config {
pub pll48: bool, pub pll48: bool,
} }
/// RCC peripheral unsafe fn setup_pll(
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};
peripherals::PWR::enable();
let pllsrcclk = self.config.hse.map(|hse| hse.0).unwrap_or(HSI);
let sysclk = self.config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk);
let sysclk_on_pll = sysclk != pllsrcclk;
let plls = self.setup_pll(
pllsrcclk,
self.config.hse.is_some(),
if sysclk_on_pll { Some(sysclk) } else { None },
self.config.pll48,
);
if self.config.pll48 {
let freq = unwrap!(plls.pll48clk);
assert!((max::PLL_48_CLK as i32 - freq as i32).abs() <= max::PLL_48_TOLERANCE as i32);
}
let sysclk = if sysclk_on_pll {
unwrap!(plls.pllsysclk)
} else {
sysclk
};
// AHB prescaler
let hclk = self.config.hclk.map(|h| h.0).unwrap_or(sysclk);
let (hpre_bits, hpre_div) = match (sysclk + hclk - 1) / hclk {
0 => unreachable!(),
1 => (Hpre::DIV1, 1),
2 => (Hpre::DIV2, 2),
3..=5 => (Hpre::DIV4, 4),
6..=11 => (Hpre::DIV8, 8),
12..=39 => (Hpre::DIV16, 16),
40..=95 => (Hpre::DIV64, 64),
96..=191 => (Hpre::DIV128, 128),
192..=383 => (Hpre::DIV256, 256),
_ => (Hpre::DIV512, 512),
};
// Calculate real AHB clock
let hclk = sysclk / hpre_div;
let pclk1 = self
.config
.pclk1
.map(|p| p.0)
.unwrap_or_else(|| core::cmp::min(max::PCLK1_MAX, hclk));
let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 {
0 => unreachable!(),
1 => (0b000, 1),
2 => (0b100, 2),
3..=5 => (0b101, 4),
6..=11 => (0b110, 8),
_ => (0b111, 16),
};
let timer_mul1 = if ppre1 == 1 { 1 } else { 2 };
// Calculate real APB1 clock
let pclk1 = hclk / ppre1;
assert!(pclk1 <= max::PCLK1_MAX);
let pclk2 = self
.config
.pclk2
.map(|p| p.0)
.unwrap_or_else(|| core::cmp::min(max::PCLK2_MAX, hclk));
let (ppre2_bits, ppre2) = match (hclk + pclk2 - 1) / pclk2 {
0 => unreachable!(),
1 => (0b000, 1),
2 => (0b100, 2),
3..=5 => (0b101, 4),
6..=11 => (0b110, 8),
_ => (0b111, 16),
};
let timer_mul2 = if ppre2 == 1 { 1 } else { 2 };
// Calculate real APB2 clock
let pclk2 = hclk / ppre2;
assert!(pclk2 <= max::PCLK2_MAX);
Self::flash_setup(sysclk);
if self.config.hse.is_some() {
// NOTE(unsafe) We own the peripheral block
unsafe {
RCC.cr().modify(|w| {
w.set_hsebyp(Hsebyp(self.config.bypass_hse as u8));
w.set_hseon(true);
});
while !RCC.cr().read().hserdy() {}
}
}
if plls.use_pll {
unsafe {
RCC.cr().modify(|w| w.set_pllon(true));
if hclk > max::HCLK_OVERDRIVE_FREQUENCY {
PWR.cr1().modify(|w| w.set_oden(true));
while !PWR.csr1().read().odrdy() {}
PWR.cr1().modify(|w| w.set_odswen(true));
while !PWR.csr1().read().odswrdy() {}
}
while !RCC.cr().read().pllrdy() {}
}
}
unsafe {
RCC.cfgr().modify(|w| {
w.set_ppre2(Ppre(ppre2_bits));
w.set_ppre1(Ppre(ppre1_bits));
w.set_hpre(hpre_bits);
});
// Wait for the new prescalers to kick in
// "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write"
cortex_m::asm::delay(16);
RCC.cfgr().modify(|w| {
w.set_sw(if sysclk_on_pll {
Sw::PLL
} else if self.config.hse.is_some() {
Sw::HSE
} else {
Sw::HSI
})
});
}
Clocks {
sys: Hertz(sysclk),
apb1: Hertz(pclk1),
apb2: Hertz(pclk2),
apb1_tim: Hertz(pclk1 * timer_mul1),
apb2_tim: Hertz(pclk2 * timer_mul2),
ahb1: Hertz(hclk),
ahb2: Hertz(hclk),
ahb3: Hertz(hclk),
pll48: plls.pll48clk.map(Hertz),
}
}
// Safety: RCC init must have been called
pub fn clocks(&self) -> &'static Clocks {
unsafe { get_freqs() }
}
fn setup_pll(
&mut self,
pllsrcclk: u32, pllsrcclk: u32,
use_hse: bool, use_hse: bool,
pllsysclk: Option<u32>, pllsysclk: Option<u32>,
@ -204,11 +30,8 @@ impl<'d> Rcc<'d> {
let sysclk = pllsysclk.unwrap_or(pllsrcclk); let sysclk = pllsysclk.unwrap_or(pllsrcclk);
if pllsysclk.is_none() && !pll48clk { if pllsysclk.is_none() && !pll48clk {
// NOTE(unsafe) We have a mutable borrow to the owner of the RegBlock
unsafe {
RCC.pllcfgr() RCC.pllcfgr()
.modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8))); .modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8)));
}
return PllResults { return PllResults {
use_pll: false, use_pll: false,
@ -262,7 +85,6 @@ impl<'d> Rcc<'d> {
let pllq = (vco_in * plln + 47_999_999) / 48_000_000; let pllq = (vco_in * plln + 47_999_999) / 48_000_000;
let real_pll48clk = vco_in * plln / pllq; let real_pll48clk = vco_in * plln / pllq;
unsafe {
RCC.pllcfgr().modify(|w| { RCC.pllcfgr().modify(|w| {
w.set_pllm(pllm as u8); w.set_pllm(pllm as u8);
w.set_plln(plln as u16); w.set_plln(plln as u16);
@ -270,7 +92,6 @@ impl<'d> Rcc<'d> {
w.set_pllq(pllq as u8); w.set_pllq(pllq as u8);
w.set_pllsrc(Pllsrc(use_hse as u8)); w.set_pllsrc(Pllsrc(use_hse as u8));
}); });
}
let real_pllsysclk = vco_in * plln / sysclk_div; let real_pllsysclk = vco_in * plln / sysclk_div;
@ -281,24 +102,158 @@ impl<'d> Rcc<'d> {
} }
} }
fn flash_setup(sysclk: u32) { unsafe fn flash_setup(sysclk: u32) {
use crate::pac::flash::vals::Latency; use crate::pac::flash::vals::Latency;
// Be conservative with voltage ranges // Be conservative with voltage ranges
const FLASH_LATENCY_STEP: u32 = 30_000_000; const FLASH_LATENCY_STEP: u32 = 30_000_000;
critical_section::with(|_| unsafe { critical_section::with(|_| {
FLASH FLASH
.acr() .acr()
.modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8))); .modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
}); });
} }
}
pub(crate) unsafe fn init(config: Config) { pub(crate) unsafe fn init(config: Config) {
let r = <peripherals::RCC as embassy::util::Steal>::steal(); crate::peripherals::PWR::enable();
let clocks = Rcc::new(r, config).freeze();
set_freqs(clocks); 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 { struct PllResults {

View File

@ -1,9 +1,9 @@
use super::sealed::RccPeripheral;
use crate::pac::pwr::vals::Vos;
use crate::pac::rcc::vals::{Hpre, Hsebyp, Ppre, Sw};
use crate::pac::{FLASH, PWR, RCC}; use crate::pac::{FLASH, PWR, RCC};
use crate::peripherals; use crate::rcc::{set_freqs, Clocks};
use crate::rcc::{get_freqs, set_freqs, Clocks};
use crate::time::Hertz; use crate::time::Hertz;
use core::marker::PhantomData;
use embassy::util::Unborrow;
const HSI: u32 = 16_000_000; const HSI: u32 = 16_000_000;
@ -21,214 +21,7 @@ pub struct Config {
pub pll48: bool, pub pll48: bool,
} }
/// RCC peripheral unsafe fn setup_pll(
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};
peripherals::PWR::enable();
let pllsrcclk = self.config.hse.map(|hse| hse.0).unwrap_or(HSI);
let sysclk = self.config.sys_ck.map(|sys| sys.0).unwrap_or(pllsrcclk);
let sysclk_on_pll = sysclk != pllsrcclk;
assert!((max::SYSCLK_MIN..=max::SYSCLK_MAX).contains(&sysclk));
let plls = self.setup_pll(
pllsrcclk,
self.config.hse.is_some(),
if sysclk_on_pll { Some(sysclk) } else { None },
self.config.pll48,
);
if self.config.pll48 {
let freq = unwrap!(plls.pll48clk);
assert!((max::PLL_48_CLK as i32 - freq as i32).abs() <= max::PLL_48_TOLERANCE as i32);
}
let sysclk = if sysclk_on_pll {
unwrap!(plls.pllsysclk)
} else {
sysclk
};
// AHB prescaler
let hclk = self.config.hclk.map(|h| h.0).unwrap_or(sysclk);
let (hpre_bits, hpre_div) = match (sysclk + hclk - 1) / hclk {
0 => unreachable!(),
1 => (Hpre::DIV1, 1),
2 => (Hpre::DIV2, 2),
3..=5 => (Hpre::DIV4, 4),
6..=11 => (Hpre::DIV8, 8),
12..=39 => (Hpre::DIV16, 16),
40..=95 => (Hpre::DIV64, 64),
96..=191 => (Hpre::DIV128, 128),
192..=383 => (Hpre::DIV256, 256),
_ => (Hpre::DIV512, 512),
};
// Calculate real AHB clock
let hclk = sysclk / hpre_div;
assert!(hclk < max::HCLK_MAX);
let pclk1 = self
.config
.pclk1
.map(|p| p.0)
.unwrap_or_else(|| core::cmp::min(max::PCLK1_MAX, hclk));
let (ppre1_bits, ppre1) = match (hclk + pclk1 - 1) / pclk1 {
0 => unreachable!(),
1 => (0b000, 1),
2 => (0b100, 2),
3..=5 => (0b101, 4),
6..=11 => (0b110, 8),
_ => (0b111, 16),
};
let timer_mul1 = if ppre1 == 1 { 1 } else { 2 };
// Calculate real APB1 clock
let pclk1 = hclk / ppre1;
assert!((max::PCLK1_MIN..=max::PCLK1_MAX).contains(&pclk1));
let pclk2 = self
.config
.pclk2
.map(|p| p.0)
.unwrap_or_else(|| core::cmp::min(max::PCLK2_MAX, hclk));
let (ppre2_bits, ppre2) = match (hclk + pclk2 - 1) / pclk2 {
0 => unreachable!(),
1 => (0b000, 1),
2 => (0b100, 2),
3..=5 => (0b101, 4),
6..=11 => (0b110, 8),
_ => (0b111, 16),
};
let timer_mul2 = if ppre2 == 1 { 1 } else { 2 };
// Calculate real APB2 clock
let pclk2 = hclk / ppre2;
assert!((max::PCLK2_MIN..=max::PCLK2_MAX).contains(&pclk2));
Self::flash_setup(sysclk);
if self.config.hse.is_some() {
// NOTE(unsafe) We own the peripheral block
unsafe {
RCC.cr().modify(|w| {
w.set_hsebyp(Hsebyp(self.config.bypass_hse as u8));
w.set_hseon(true);
});
while !RCC.cr().read().hserdy() {}
}
}
if plls.use_pll {
unsafe {
RCC.cr().modify(|w| w.set_pllon(false));
// enable PWR and setup VOSScale
RCC.apb1enr().modify(|w| w.set_pwren(true));
let vos_scale = if sysclk <= 144_000_000 {
3
} else if sysclk <= 168_000_000 {
2
} else {
1
};
PWR.cr1().modify(|w| {
w.set_vos(match vos_scale {
3 => Vos::SCALE3,
2 => Vos::SCALE2,
1 => Vos::SCALE1,
_ => panic!("Invalid VOS Scale."),
})
});
RCC.cr().modify(|w| w.set_pllon(true));
if hclk > max::HCLK_OVERDRIVE_FREQUENCY {
PWR.cr1().modify(|w| w.set_oden(true));
while !PWR.csr1().read().odrdy() {}
PWR.cr1().modify(|w| w.set_odswen(true));
while !PWR.csr1().read().odswrdy() {}
}
while !RCC.cr().read().pllrdy() {}
}
}
unsafe {
RCC.cfgr().modify(|w| {
w.set_ppre2(Ppre(ppre2_bits));
w.set_ppre1(Ppre(ppre1_bits));
w.set_hpre(hpre_bits);
});
// Wait for the new prescalers to kick in
// "The clocks are divided with the new prescaler factor from 1 to 16 AHB cycles after write"
cortex_m::asm::delay(16);
RCC.cfgr().modify(|w| {
w.set_sw(if sysclk_on_pll {
Sw::PLL
} else if self.config.hse.is_some() {
Sw::HSE
} else {
Sw::HSI
})
});
}
Clocks {
sys: Hertz(sysclk),
apb1: Hertz(pclk1),
apb2: Hertz(pclk2),
apb1_tim: Hertz(pclk1 * timer_mul1),
apb2_tim: Hertz(pclk2 * timer_mul2),
ahb1: Hertz(hclk),
ahb2: Hertz(hclk),
ahb3: Hertz(hclk),
pll48: plls.pll48clk.map(Hertz),
}
}
// Safety: RCC init must have been called
pub fn clocks(&self) -> &'static Clocks {
unsafe { get_freqs() }
}
fn setup_pll(
&mut self,
pllsrcclk: u32, pllsrcclk: u32,
use_hse: bool, use_hse: bool,
pllsysclk: Option<u32>, pllsysclk: Option<u32>,
@ -238,11 +31,8 @@ impl<'d> Rcc<'d> {
let sysclk = pllsysclk.unwrap_or(pllsrcclk); let sysclk = pllsysclk.unwrap_or(pllsrcclk);
if pllsysclk.is_none() && !pll48clk { if pllsysclk.is_none() && !pll48clk {
// NOTE(unsafe) We have a mutable borrow to the owner of the RegBlock
unsafe {
RCC.pllcfgr() RCC.pllcfgr()
.modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8))); .modify(|w| w.set_pllsrc(Pllsrc(use_hse as u8)));
}
return PllResults { return PllResults {
use_pll: false, use_pll: false,
@ -296,7 +86,6 @@ impl<'d> Rcc<'d> {
let pllq = (vco_in * plln + 47_999_999) / 48_000_000; let pllq = (vco_in * plln + 47_999_999) / 48_000_000;
let real_pll48clk = vco_in * plln / pllq; let real_pll48clk = vco_in * plln / pllq;
unsafe {
RCC.pllcfgr().modify(|w| { RCC.pllcfgr().modify(|w| {
w.set_pllm(pllm as u8); w.set_pllm(pllm as u8);
w.set_plln(plln as u16); w.set_plln(plln as u16);
@ -304,7 +93,6 @@ impl<'d> Rcc<'d> {
w.set_pllq(pllq as u8); w.set_pllq(pllq as u8);
w.set_pllsrc(Pllsrc(use_hse as u8)); w.set_pllsrc(Pllsrc(use_hse as u8));
}); });
}
let real_pllsysclk = vco_in * plln / sysclk_div; let real_pllsysclk = vco_in * plln / sysclk_div;
@ -315,24 +103,192 @@ impl<'d> Rcc<'d> {
} }
} }
fn flash_setup(sysclk: u32) { unsafe fn flash_setup(sysclk: u32) {
use crate::pac::flash::vals::Latency; use crate::pac::flash::vals::Latency;
// Be conservative with voltage ranges // Be conservative with voltage ranges
const FLASH_LATENCY_STEP: u32 = 30_000_000; const FLASH_LATENCY_STEP: u32 = 30_000_000;
critical_section::with(|_| unsafe { critical_section::with(|_| {
FLASH FLASH
.acr() .acr()
.modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8))); .modify(|w| w.set_latency(Latency(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
}); });
} }
}
pub(crate) unsafe fn init(config: Config) { pub(crate) unsafe fn init(config: Config) {
let r = <peripherals::RCC as embassy::util::Steal>::steal(); crate::peripherals::PWR::enable();
let clocks = Rcc::new(r, config).freeze();
set_freqs(clocks); 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 { struct PllResults {

View File

@ -1,11 +1,7 @@
use crate::pac; use crate::pac::{PWR, RCC};
use crate::peripherals::{self, RCC}; use crate::rcc::{set_freqs, Clocks};
use crate::rcc::{get_freqs, set_freqs, Clocks};
use crate::time::Hertz; use crate::time::Hertz;
use crate::time::U32Ext; use crate::time::U32Ext;
use core::marker::PhantomData;
use embassy::util::Unborrow;
use embassy_hal_common::unborrow;
/// HSI speed /// HSI speed
pub const HSI_FREQ: u32 = 16_000_000; pub const HSI_FREQ: u32 = 16_000_000;
@ -120,78 +116,41 @@ impl Default for Config {
} }
} }
/// RCC peripheral pub(crate) unsafe fn init(config: Config) {
pub struct Rcc<'d> { let (sys_clk, sw) = match config.mux {
_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) => { ClockSrc::HSI16(div) => {
// Enable HSI16 // Enable HSI16
let div: u8 = div.into(); let div: u8 = div.into();
unsafe { RCC.cr().write(|w| {
rcc.cr().write(|w| {
w.set_hsidiv(div); w.set_hsidiv(div);
w.set_hsion(true) w.set_hsion(true)
}); });
while !rcc.cr().read().hsirdy() {} while !RCC.cr().read().hsirdy() {}
}
(HSI_FREQ >> div, 0x00) (HSI_FREQ >> div, 0x00)
} }
ClockSrc::HSE(freq) => { ClockSrc::HSE(freq) => {
// Enable HSE // Enable HSE
unsafe { RCC.cr().write(|w| w.set_hseon(true));
rcc.cr().write(|w| w.set_hseon(true)); while !RCC.cr().read().hserdy() {}
while !rcc.cr().read().hserdy() {}
}
(freq.0, 0x01) (freq.0, 0x01)
} }
ClockSrc::LSI => { ClockSrc::LSI => {
// Enable LSI // Enable LSI
unsafe { RCC.csr().write(|w| w.set_lsion(true));
rcc.csr().write(|w| w.set_lsion(true)); while !RCC.csr().read().lsirdy() {}
while !rcc.csr().read().lsirdy() {}
}
(LSI_FREQ, 0x03) (LSI_FREQ, 0x03)
} }
}; };
unsafe { RCC.cfgr().modify(|w| {
rcc.cfgr().modify(|w| {
w.set_sw(sw.into()); w.set_sw(sw.into());
w.set_hpre(cfgr.ahb_pre.into()); w.set_hpre(config.ahb_pre.into());
w.set_ppre(cfgr.apb_pre.into()); w.set_ppre(config.apb_pre.into());
}); });
}
let ahb_freq: u32 = match cfgr.ahb_pre { let ahb_freq: u32 = match config.ahb_pre {
AHBPrescaler::NotDivided => sys_clk, AHBPrescaler::NotDivided => sys_clk,
pre => { pre => {
let pre: u8 = pre.into(); let pre: u8 = pre.into();
@ -200,7 +159,7 @@ impl RccExt for RCC {
} }
}; };
let (apb_freq, apb_tim_freq) = match cfgr.apb_pre { let (apb_freq, apb_tim_freq) = match config.apb_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq), APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => { pre => {
let pre: u8 = pre.into(); let pre: u8 = pre.into();
@ -210,25 +169,15 @@ impl RccExt for RCC {
} }
}; };
let pwr = pac::PWR; if config.low_power_run {
if cfgr.low_power_run {
assert!(sys_clk.hz() <= 2_000_000.hz()); assert!(sys_clk.hz() <= 2_000_000.hz());
unsafe { PWR.cr1().modify(|w| w.set_lpr(true));
pwr.cr1().modify(|w| w.set_lpr(true));
}
} }
Clocks { set_freqs(Clocks {
sys: sys_clk.hz(), sys: sys_clk.hz(),
ahb: ahb_freq.hz(), ahb: ahb_freq.hz(),
apb: apb_freq.hz(), apb: apb_freq.hz(),
apb_tim: apb_tim_freq.hz(), apb_tim: apb_tim_freq.hz(),
} });
}
}
pub(crate) unsafe fn init(config: Config) {
let r = <peripherals::RCC as embassy::util::Steal>::steal();
let clocks = r.freeze(config);
set_freqs(clocks);
} }

View File

@ -1,11 +1,7 @@
use crate::pac; use crate::pac::{PWR, RCC};
use crate::peripherals::{self, RCC}; use crate::rcc::{set_freqs, Clocks};
use crate::rcc::{get_freqs, set_freqs, Clocks};
use crate::time::Hertz; use crate::time::Hertz;
use crate::time::U32Ext; use crate::time::U32Ext;
use core::marker::PhantomData;
use embassy::util::Unborrow;
use embassy_hal_common::unborrow;
/// HSI speed /// HSI speed
pub const HSI_FREQ: u32 = 16_000_000; pub const HSI_FREQ: u32 = 16_000_000;
@ -94,67 +90,32 @@ impl Default for Config {
} }
} }
/// RCC peripheral pub(crate) unsafe fn init(config: Config) {
pub struct Rcc<'d> { let (sys_clk, sw) = match config.mux {
_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 => { ClockSrc::HSI16 => {
// Enable HSI16 // Enable HSI16
unsafe { RCC.cr().write(|w| w.set_hsion(true));
rcc.cr().write(|w| w.set_hsion(true)); while !RCC.cr().read().hsirdy() {}
while !rcc.cr().read().hsirdy() {}
}
(HSI_FREQ, 0x01) (HSI_FREQ, 0x01)
} }
ClockSrc::HSE(freq) => { ClockSrc::HSE(freq) => {
// Enable HSE // Enable HSE
unsafe { RCC.cr().write(|w| w.set_hseon(true));
rcc.cr().write(|w| w.set_hseon(true)); while !RCC.cr().read().hserdy() {}
while !rcc.cr().read().hserdy() {}
}
(freq.0, 0x02) (freq.0, 0x02)
} }
}; };
unsafe { RCC.cfgr().modify(|w| {
rcc.cfgr().modify(|w| {
w.set_sw(sw.into()); w.set_sw(sw.into());
w.set_hpre(cfgr.ahb_pre.into()); w.set_hpre(config.ahb_pre.into());
w.set_ppre1(cfgr.apb1_pre.into()); w.set_ppre1(config.apb1_pre.into());
w.set_ppre2(cfgr.apb2_pre.into()); w.set_ppre2(config.apb2_pre.into());
}); });
}
let ahb_freq: u32 = match cfgr.ahb_pre { let ahb_freq: u32 = match config.ahb_pre {
AHBPrescaler::NotDivided => sys_clk, AHBPrescaler::NotDivided => sys_clk,
pre => { pre => {
let pre: u8 = pre.into(); let pre: u8 = pre.into();
@ -163,7 +124,7 @@ impl RccExt for RCC {
} }
}; };
let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre { let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq), APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => { pre => {
let pre: u8 = pre.into(); let pre: u8 = pre.into();
@ -173,7 +134,7 @@ impl RccExt for RCC {
} }
}; };
let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre { let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq), APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => { pre => {
let pre: u8 = pre.into(); let pre: u8 = pre.into();
@ -183,15 +144,12 @@ impl RccExt for RCC {
} }
}; };
let pwr = pac::PWR; if config.low_power_run {
if cfgr.low_power_run {
assert!(sys_clk.hz() <= 2_000_000.hz()); assert!(sys_clk.hz() <= 2_000_000.hz());
unsafe { PWR.cr1().modify(|w| w.set_lpr(true));
pwr.cr1().modify(|w| w.set_lpr(true));
}
} }
Clocks { set_freqs(Clocks {
sys: sys_clk.hz(), sys: sys_clk.hz(),
ahb1: ahb_freq.hz(), ahb1: ahb_freq.hz(),
ahb2: ahb_freq.hz(), ahb2: ahb_freq.hz(),
@ -199,12 +157,5 @@ impl RccExt for RCC {
apb1_tim: apb1_tim_freq.hz(), apb1_tim: apb1_tim_freq.hz(),
apb2: apb2_freq.hz(), apb2: apb2_freq.hz(),
apb2_tim: apb2_tim_freq.hz(), apb2_tim: apb2_tim_freq.hz(),
} });
}
}
pub(crate) unsafe fn init(config: Config) {
let r = <peripherals::RCC as embassy::util::Steal>::steal();
let clocks = r.freeze(config);
set_freqs(clocks);
} }

View File

@ -7,9 +7,9 @@ use stm32_metapac::rcc::vals::{Mco1, Mco2};
use crate::gpio::sealed::Pin as __GpioPin; use crate::gpio::sealed::Pin as __GpioPin;
use crate::gpio::Pin; use crate::gpio::Pin;
use crate::pac::rcc::vals::Timpre; use crate::pac::rcc::vals::Timpre;
use crate::pac::{RCC, SYSCFG}; use crate::pac::rcc::vals::{Ckpersel, Dppre, Hpre, Hsebyp, Hsidiv, Pllsrc, Sw};
use crate::pac::{PWR, RCC, SYSCFG};
use crate::peripherals; use crate::peripherals;
use crate::pwr::{Power, VoltageScale};
use crate::rcc::{set_freqs, Clocks}; use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz; use crate::time::Hertz;
@ -20,6 +20,22 @@ const CSI: Hertz = Hertz(4_000_000);
const HSI48: Hertz = Hertz(48_000_000); const HSI48: Hertz = Hertz(48_000_000);
const LSI: Hertz = Hertz(32_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 /// Core clock frequencies
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct CoreClocks { pub struct CoreClocks {
@ -72,310 +88,22 @@ pub struct Config {
pub pll3: 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::pll_setup(srcclk.0, &self.config.pll1, 0) };
let (pll2_p_ck, pll2_q_ck, pll2_r_ck) =
unsafe { pll::pll_setup(srcclk.0, &self.config.pll2, 1) };
let (pll3_p_ck, pll3_q_ck, pll3_r_ck) =
unsafe { pll::pll_setup(srcclk.0, &self.config.pll3, 2) };
let sys_ck = if sys_use_pll1_p {
Hertz(unwrap!(pll1_p_ck)) // Must have been set by sys_ck_setup
} else {
sys_ck
};
// 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 /// Setup traceclk
/// Returns a pll1_r_ck /// Returns a pll1_r_ck
fn traceclk_setup(&mut self, sys_use_pll1_p: bool) { fn traceclk_setup(config: &mut Config, sys_use_pll1_p: bool) {
let pll1_r_ck = match (sys_use_pll1_p, self.config.pll1.r_ck) { 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 // pll1_p_ck selected as system clock but pll1_r_ck not
// set. The traceclk mux is synchronous with the system // set. The traceclk mux is synchronous with the system
// clock mux, but has pll1_r_ck as an input. In order to // clock mux, but has pll1_r_ck as an input. In order to
// keep traceclk running, we force a pll1_r_ck. // keep traceclk running, we force a pll1_r_ck.
(true, None) => Some(Hertz(unwrap!(self.config.pll1.p_ck).0 / 2)), (true, None) => Some(Hertz(unwrap!(config.pll1.p_ck).0 / 2)),
// Either pll1 not selected as system clock, free choice // Either pll1 not selected as system clock, free choice
// of pll1_r_ck. Or pll1 is selected, assume user has set // of pll1_r_ck. Or pll1 is selected, assume user has set
// a suitable pll1_r_ck frequency. // a suitable pll1_r_ck frequency.
_ => self.config.pll1.r_ck, _ => config.pll1.r_ck,
}; };
self.config.pll1.r_ck = pll1_r_ck; config.pll1.r_ck = pll1_r_ck;
} }
/// Divider calculator for pclk 1 - 4 /// Divider calculator for pclk 1 - 4
@ -416,9 +144,9 @@ impl<'d> Rcc<'d> {
/// Setup sys_ck /// Setup sys_ck
/// Returns sys_ck frequency, and a pll1_p_ck /// Returns sys_ck frequency, and a pll1_p_ck
fn sys_ck_setup(&mut self, srcclk: Hertz) -> (Hertz, bool) { fn sys_ck_setup(config: &mut Config, srcclk: Hertz) -> (Hertz, bool) {
// Compare available with wanted clocks // Compare available with wanted clocks
let sys_ck = self.config.sys_ck.unwrap_or(srcclk); let sys_ck = config.sys_ck.unwrap_or(srcclk);
if sys_ck != srcclk { if sys_ck != srcclk {
// The requested system clock is not the immediately available // The requested system clock is not the immediately available
@ -427,7 +155,7 @@ impl<'d> Rcc<'d> {
// ignore those for now. // ignore those for now.
// //
// Therefore we must use pll1_p_ck // Therefore we must use pll1_p_ck
let pll1_p_ck = match self.config.pll1.p_ck { let pll1_p_ck = match config.pll1.p_ck {
Some(p_ck) => { Some(p_ck) => {
assert!(p_ck == sys_ck, assert!(p_ck == sys_ck,
"Error: Cannot set pll1_p_ck independently as it must be used to generate sys_ck"); "Error: Cannot set pll1_p_ck independently as it must be used to generate sys_ck");
@ -435,7 +163,7 @@ impl<'d> Rcc<'d> {
} }
None => Some(sys_ck), None => Some(sys_ck),
}; };
self.config.pll1.p_ck = pll1_p_ck; config.pll1.p_ck = pll1_p_ck;
(sys_ck, true) (sys_ck, true)
} else { } else {
@ -504,7 +232,7 @@ impl<'d> Rcc<'d> {
while FLASH.acr().read().latency() != wait_states {} while FLASH.acr().read().latency() != wait_states {}
} }
} }
}
pub enum McoClock { pub enum McoClock {
Disabled, Disabled,
Bypassed, Bypassed,
@ -681,10 +409,309 @@ impl<'d, T: McoInstance> Mco<'d, T> {
} }
} }
pub(crate) unsafe fn init(config: Config) { pub(crate) unsafe fn init(mut config: Config) {
let mut power = Power::new(<peripherals::PWR as embassy::util::Steal>::steal(), false); // TODO make configurable?
let rcc = Rcc::new(<peripherals::RCC as embassy::util::Steal>::steal(), config); let enable_overdrive = false;
let core_clocks = rcc.freeze(&mut power);
// 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 { set_freqs(Clocks {
sys: core_clocks.c_ck, sys: core_clocks.c_ck,
ahb1: core_clocks.hclk, ahb1: core_clocks.hclk,

View File

@ -1,18 +1,11 @@
use crate::pac; use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw};
use crate::peripherals::{self, CRS, RCC, SYSCFG}; use crate::pac::{CRS, RCC, SYSCFG};
use crate::rcc::{get_freqs, set_freqs, Clocks}; use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz; use crate::time::Hertz;
use crate::time::U32Ext; 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, /// HSI16 speed
/// and with the addition of the init function to configure a system clock. pub const HSI16_FREQ: u32 = 16_000_000;
/// HSI speed
pub const HSI_FREQ: u32 = 16_000_000;
/// System clock mux source /// System clock mux source
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -104,9 +97,9 @@ pub enum PLLSource {
HSE(Hertz), HSE(Hertz),
} }
impl Into<Pllmul> for PLLMul { impl From<PLLMul> for Pllmul {
fn into(self) -> Pllmul { fn from(val: PLLMul) -> Pllmul {
match self { match val {
PLLMul::Mul3 => Pllmul::MUL3, PLLMul::Mul3 => Pllmul::MUL3,
PLLMul::Mul4 => Pllmul::MUL4, PLLMul::Mul4 => Pllmul::MUL4,
PLLMul::Mul6 => Pllmul::MUL6, PLLMul::Mul6 => Pllmul::MUL6,
@ -120,9 +113,9 @@ impl Into<Pllmul> for PLLMul {
} }
} }
impl Into<Plldiv> for PLLDiv { impl From<PLLDiv> for Plldiv {
fn into(self) -> Plldiv { fn from(val: PLLDiv) -> Plldiv {
match self { match val {
PLLDiv::Div2 => Plldiv::DIV2, PLLDiv::Div2 => Plldiv::DIV2,
PLLDiv::Div3 => Plldiv::DIV3, PLLDiv::Div3 => Plldiv::DIV3,
PLLDiv::Div4 => Plldiv::DIV4, PLLDiv::Div4 => Plldiv::DIV4,
@ -130,18 +123,18 @@ impl Into<Plldiv> for PLLDiv {
} }
} }
impl Into<Pllsrc> for PLLSource { impl From<PLLSource> for Pllsrc {
fn into(self) -> Pllsrc { fn from(val: PLLSource) -> Pllsrc {
match self { match val {
PLLSource::HSI16 => Pllsrc::HSI16, PLLSource::HSI16 => Pllsrc::HSI16,
PLLSource::HSE(_) => Pllsrc::HSE, PLLSource::HSE(_) => Pllsrc::HSE,
} }
} }
} }
impl Into<Ppre> for APBPrescaler { impl From<APBPrescaler> for Ppre {
fn into(self) -> Ppre { fn from(val: APBPrescaler) -> Ppre {
match self { match val {
APBPrescaler::NotDivided => Ppre::DIV1, APBPrescaler::NotDivided => Ppre::DIV1,
APBPrescaler::Div2 => Ppre::DIV2, APBPrescaler::Div2 => Ppre::DIV2,
APBPrescaler::Div4 => Ppre::DIV4, APBPrescaler::Div4 => Ppre::DIV4,
@ -151,9 +144,9 @@ impl Into<Ppre> for APBPrescaler {
} }
} }
impl Into<Hpre> for AHBPrescaler { impl From<AHBPrescaler> for Hpre {
fn into(self) -> Hpre { fn from(val: AHBPrescaler) -> Hpre {
match self { match val {
AHBPrescaler::NotDivided => Hpre::DIV1, AHBPrescaler::NotDivided => Hpre::DIV1,
AHBPrescaler::Div2 => Hpre::DIV2, AHBPrescaler::Div2 => Hpre::DIV2,
AHBPrescaler::Div4 => Hpre::DIV4, AHBPrescaler::Div4 => Hpre::DIV4,
@ -167,9 +160,9 @@ impl Into<Hpre> for AHBPrescaler {
} }
} }
impl Into<Msirange> for MSIRange { impl From<MSIRange> for Msirange {
fn into(self) -> Msirange { fn from(val: MSIRange) -> Msirange {
match self { match val {
MSIRange::Range0 => Msirange::RANGE0, MSIRange::Range0 => Msirange::RANGE0,
MSIRange::Range1 => Msirange::RANGE1, MSIRange::Range1 => Msirange::RANGE1,
MSIRange::Range2 => Msirange::RANGE2, MSIRange::Range2 => Msirange::RANGE2,
@ -187,6 +180,7 @@ pub struct Config {
pub ahb_pre: AHBPrescaler, pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler, pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler, pub apb2_pre: APBPrescaler,
pub enable_hsi48: bool,
} }
impl Default for Config { impl Default for Config {
@ -197,120 +191,35 @@ impl Default for Config {
ahb_pre: AHBPrescaler::NotDivided, ahb_pre: AHBPrescaler::NotDivided,
apb1_pre: APBPrescaler::NotDivided, apb1_pre: APBPrescaler::NotDivided,
apb2_pre: APBPrescaler::NotDivided, apb2_pre: APBPrescaler::NotDivided,
enable_hsi48: false,
} }
} }
} }
/// RCC peripheral pub(crate) unsafe fn init(config: Config) {
pub struct Rcc<'d> { let (sys_clk, sw) = match config.mux {
_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) => { ClockSrc::MSI(range) => {
// Set MSI range // Set MSI range
unsafe { RCC.icscr().write(|w| w.set_msirange(range.into()));
rcc.icscr().write(|w| w.set_msirange(range.into()));
}
// Enable MSI // Enable MSI
unsafe { RCC.cr().write(|w| w.set_msion(true));
rcc.cr().write(|w| w.set_msion(true)); while !RCC.cr().read().msirdy() {}
while !rcc.cr().read().msirdy() {}
}
let freq = 32_768 * (1 << (range as u8 + 1)); let freq = 32_768 * (1 << (range as u8 + 1));
(freq, Sw::MSI) (freq, Sw::MSI)
} }
ClockSrc::HSI16 => { ClockSrc::HSI16 => {
// Enable HSI16 // Enable HSI16
unsafe { RCC.cr().write(|w| w.set_hsi16on(true));
rcc.cr().write(|w| w.set_hsi16on(true)); while !RCC.cr().read().hsi16rdyf() {}
while !rcc.cr().read().hsi16rdyf() {}
}
(HSI_FREQ, Sw::HSI16) (HSI16_FREQ, Sw::HSI16)
} }
ClockSrc::HSE(freq) => { ClockSrc::HSE(freq) => {
// Enable HSE // Enable HSE
unsafe { RCC.cr().write(|w| w.set_hseon(true));
rcc.cr().write(|w| w.set_hseon(true)); while !RCC.cr().read().hserdy() {}
while !rcc.cr().read().hserdy() {}
}
(freq.0, Sw::HSE) (freq.0, Sw::HSE)
} }
@ -318,27 +227,21 @@ impl RccExt for RCC {
let freq = match src { let freq = match src {
PLLSource::HSE(freq) => { PLLSource::HSE(freq) => {
// Enable HSE // Enable HSE
unsafe { RCC.cr().write(|w| w.set_hseon(true));
rcc.cr().write(|w| w.set_hseon(true)); while !RCC.cr().read().hserdy() {}
while !rcc.cr().read().hserdy() {}
}
freq.0 freq.0
} }
PLLSource::HSI16 => { PLLSource::HSI16 => {
// Enable HSI // Enable HSI
unsafe { RCC.cr().write(|w| w.set_hsi16on(true));
rcc.cr().write(|w| w.set_hsi16on(true)); while !RCC.cr().read().hsi16rdyf() {}
while !rcc.cr().read().hsi16rdyf() {} HSI16_FREQ
}
HSI_FREQ
} }
}; };
// Disable PLL // Disable PLL
unsafe { RCC.cr().modify(|w| w.set_pllon(false));
rcc.cr().modify(|w| w.set_pllon(false)); while RCC.cr().read().pllrdy() {}
while rcc.cr().read().pllrdy() {}
}
let freq = match mul { let freq = match mul {
PLLMul::Mul3 => freq * 3, PLLMul::Mul3 => freq * 3,
@ -359,32 +262,28 @@ impl RccExt for RCC {
}; };
assert!(freq <= 32_u32.mhz().0); assert!(freq <= 32_u32.mhz().0);
unsafe { RCC.cfgr().write(move |w| {
rcc.cfgr().write(move |w| {
w.set_pllmul(mul.into()); w.set_pllmul(mul.into());
w.set_plldiv(div.into()); w.set_plldiv(div.into());
w.set_pllsrc(src.into()); w.set_pllsrc(src.into());
}); });
// Enable PLL // Enable PLL
rcc.cr().modify(|w| w.set_pllon(true)); RCC.cr().modify(|w| w.set_pllon(true));
while !rcc.cr().read().pllrdy() {} while !RCC.cr().read().pllrdy() {}
}
(freq, Sw::PLL) (freq, Sw::PLL)
} }
}; };
unsafe { RCC.cfgr().modify(|w| {
rcc.cfgr().modify(|w| { w.set_sw(sw);
w.set_sw(sw.into()); w.set_hpre(config.ahb_pre.into());
w.set_hpre(cfgr.ahb_pre.into()); w.set_ppre1(config.apb1_pre.into());
w.set_ppre1(cfgr.apb1_pre.into()); w.set_ppre2(config.apb2_pre.into());
w.set_ppre2(cfgr.apb2_pre.into());
}); });
}
let ahb_freq: u32 = match cfgr.ahb_pre { let ahb_freq: u32 = match config.ahb_pre {
AHBPrescaler::NotDivided => sys_clk, AHBPrescaler::NotDivided => sys_clk,
pre => { pre => {
let pre: Hpre = pre.into(); let pre: Hpre = pre.into();
@ -393,7 +292,7 @@ impl RccExt for RCC {
} }
}; };
let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre { let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq), APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => { pre => {
let pre: Ppre = pre.into(); let pre: Ppre = pre.into();
@ -403,7 +302,7 @@ impl RccExt for RCC {
} }
}; };
let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre { let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq), APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => { pre => {
let pre: Ppre = pre.into(); let pre: Ppre = pre.into();
@ -413,25 +312,51 @@ impl RccExt for RCC {
} }
}; };
Clocks { 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(), sys: sys_clk.hz(),
ahb: ahb_freq.hz(), ahb: ahb_freq.hz(),
apb1: apb1_freq.hz(), apb1: apb1_freq.hz(),
apb2: apb2_freq.hz(), apb2: apb2_freq.hz(),
apb1_tim: apb1_tim_freq.hz(), apb1_tim: apb1_tim_freq.hz(),
apb2_tim: apb2_tim_freq.hz(), apb2_tim: apb2_tim_freq.hz(),
} });
}
}
/// Token that exists only, if the HSI48 clock has been enabled
///
/// You can get an instance of this struct by calling [`Rcc::enable_hsi48`].
#[derive(Clone, Copy)]
pub struct HSI48(());
pub(crate) unsafe fn init(config: Config) {
let r = <peripherals::RCC as embassy::util::Steal>::steal();
let clocks = r.freeze(config);
set_freqs(clocks);
} }

View File

@ -1,13 +1,8 @@
use crate::pac; use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw};
use crate::peripherals::{self, RCC}; use crate::pac::RCC;
use crate::rcc::{get_freqs, set_freqs, Clocks}; use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz; use crate::time::Hertz;
use crate::time::U32Ext; 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 /// HSI speed
pub const HSI_FREQ: u32 = 16_000_000; pub const HSI_FREQ: u32 = 16_000_000;
@ -16,6 +11,7 @@ pub const HSI_FREQ: u32 = 16_000_000;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum ClockSrc { pub enum ClockSrc {
MSI(MSIRange), MSI(MSIRange),
PLL(PLLSource, PLLMul, PLLDiv),
HSE(Hertz), HSE(Hertz),
HSI, HSI,
} }
@ -48,6 +44,28 @@ impl Default for MSIRange {
} }
} }
/// PLL divider
#[derive(Clone, Copy)]
pub enum PLLDiv {
Div2,
Div3,
Div4,
}
/// PLL multiplier
#[derive(Clone, Copy)]
pub enum PLLMul {
Mul3,
Mul4,
Mul6,
Mul8,
Mul12,
Mul16,
Mul24,
Mul32,
Mul48,
}
/// AHB prescaler /// AHB prescaler
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, PartialEq)]
pub enum AHBPrescaler { pub enum AHBPrescaler {
@ -72,46 +90,86 @@ pub enum APBPrescaler {
Div16, Div16,
} }
type Ppre = u8; /// PLL clock input source
impl Into<Ppre> for APBPrescaler { #[derive(Clone, Copy)]
fn into(self) -> Ppre { pub enum PLLSource {
match self { HSI,
APBPrescaler::NotDivided => 0b000, HSE(Hertz),
APBPrescaler::Div2 => 0b100, }
APBPrescaler::Div4 => 0b101,
APBPrescaler::Div8 => 0b110, impl From<PLLMul> for Pllmul {
APBPrescaler::Div16 => 0b111, fn from(val: PLLMul) -> Pllmul {
match val {
PLLMul::Mul3 => Pllmul::MUL3,
PLLMul::Mul4 => Pllmul::MUL4,
PLLMul::Mul6 => Pllmul::MUL6,
PLLMul::Mul8 => Pllmul::MUL8,
PLLMul::Mul12 => Pllmul::MUL12,
PLLMul::Mul16 => Pllmul::MUL16,
PLLMul::Mul24 => Pllmul::MUL24,
PLLMul::Mul32 => Pllmul::MUL32,
PLLMul::Mul48 => Pllmul::MUL48,
} }
} }
} }
type Hpre = u8; impl From<PLLDiv> for Plldiv {
impl Into<Hpre> for AHBPrescaler { fn from(val: PLLDiv) -> Plldiv {
fn into(self) -> Hpre { match val {
match self { PLLDiv::Div2 => Plldiv::DIV2,
AHBPrescaler::NotDivided => 0b0000, PLLDiv::Div3 => Plldiv::DIV3,
AHBPrescaler::Div2 => 0b1000, PLLDiv::Div4 => Plldiv::DIV4,
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 { impl From<PLLSource> for Pllsrc {
fn into(self) -> u8 { fn from(val: PLLSource) -> Pllsrc {
match self { match val {
MSIRange::Range0 => 0b000, PLLSource::HSI => Pllsrc::HSI,
MSIRange::Range1 => 0b001, PLLSource::HSE(_) => Pllsrc::HSE,
MSIRange::Range2 => 0b010, }
MSIRange::Range3 => 0b011, }
MSIRange::Range4 => 0b100, }
MSIRange::Range5 => 0b101,
MSIRange::Range6 => 0b110, impl From<APBPrescaler> for Ppre {
fn from(val: APBPrescaler) -> Ppre {
match val {
APBPrescaler::NotDivided => Ppre::DIV1,
APBPrescaler::Div2 => Ppre::DIV2,
APBPrescaler::Div4 => Ppre::DIV4,
APBPrescaler::Div8 => Ppre::DIV8,
APBPrescaler::Div16 => Ppre::DIV16,
}
}
}
impl From<AHBPrescaler> for Hpre {
fn from(val: AHBPrescaler) -> Hpre {
match val {
AHBPrescaler::NotDivided => Hpre::DIV1,
AHBPrescaler::Div2 => Hpre::DIV2,
AHBPrescaler::Div4 => Hpre::DIV4,
AHBPrescaler::Div8 => Hpre::DIV8,
AHBPrescaler::Div16 => Hpre::DIV16,
AHBPrescaler::Div64 => Hpre::DIV64,
AHBPrescaler::Div128 => Hpre::DIV128,
AHBPrescaler::Div256 => Hpre::DIV256,
AHBPrescaler::Div512 => Hpre::DIV512,
}
}
}
impl From<MSIRange> for Msirange {
fn from(val: MSIRange) -> Msirange {
match val {
MSIRange::Range0 => Msirange::RANGE0,
MSIRange::Range1 => Msirange::RANGE1,
MSIRange::Range2 => Msirange::RANGE2,
MSIRange::Range3 => Msirange::RANGE3,
MSIRange::Range4 => Msirange::RANGE4,
MSIRange::Range5 => Msirange::RANGE5,
MSIRange::Range6 => Msirange::RANGE6,
} }
} }
} }
@ -136,126 +194,128 @@ impl Default for Config {
} }
} }
/// RCC peripheral pub(crate) unsafe fn init(config: Config) {
pub struct Rcc<'d> { let (sys_clk, sw) = match config.mux {
_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) => { ClockSrc::MSI(range) => {
// Set MSI range // Set MSI range
unsafe { RCC.icscr().write(|w| w.set_msirange(range.into()));
rcc.icscr().write(|w| w.set_msirange(range.into()));
}
// Enable MSI // Enable MSI
unsafe { RCC.cr().write(|w| w.set_msion(true));
rcc.cr().write(|w| w.set_msion(true)); while !RCC.cr().read().msirdy() {}
while !rcc.cr().read().msirdy() {}
}
let freq = 32_768 * (1 << (range as u8 + 1)); let freq = 32_768 * (1 << (range as u8 + 1));
(freq, 0b00) (freq, Sw::MSI)
} }
ClockSrc::HSI => { ClockSrc::HSI => {
// Enable HSI // Enable HSI
unsafe { RCC.cr().write(|w| w.set_hsion(true));
rcc.cr().write(|w| w.set_hsion(true)); while !RCC.cr().read().hsirdy() {}
while !rcc.cr().read().hsirdy() {}
}
(HSI_FREQ, 0b01) (HSI_FREQ, Sw::HSI)
} }
ClockSrc::HSE(freq) => { ClockSrc::HSE(freq) => {
// Enable HSE // Enable HSE
unsafe { RCC.cr().write(|w| w.set_hseon(true));
rcc.cr().write(|w| w.set_hseon(true)); while !RCC.cr().read().hserdy() {}
while !rcc.cr().read().hserdy() {}
}
(freq.0, 0b10) (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
} }
}; };
unsafe { // Disable PLL
rcc.cfgr().modify(|w| { RCC.cr().modify(|w| w.set_pllon(false));
w.set_sw(sw.into()); while RCC.cr().read().pllrdy() {}
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 { 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, AHBPrescaler::NotDivided => sys_clk,
pre => { pre => {
let pre: Hpre = pre.into(); let pre: Hpre = pre.into();
let pre = 1 << (pre as u32 - 7); let pre = 1 << (pre.0 as u32 - 7);
sys_clk / pre sys_clk / pre
} }
}; };
let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre { let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq), APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => { pre => {
let pre: Ppre = pre.into(); let pre: Ppre = pre.into();
let pre: u8 = 1 << (pre - 3); let pre: u8 = 1 << (pre.0 - 3);
let freq = ahb_freq / pre as u32; let freq = ahb_freq / pre as u32;
(freq, freq * 2) (freq, freq * 2)
} }
}; };
let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre { let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq), APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => { pre => {
let pre: Ppre = pre.into(); let pre: Ppre = pre.into();
let pre: u8 = 1 << (pre - 3); let pre: u8 = 1 << (pre.0 - 3);
let freq = ahb_freq / (1 << (pre as u8 - 3)); let freq = ahb_freq / (1 << (pre as u8 - 3));
(freq, freq * 2) (freq, freq * 2)
} }
}; };
Clocks { set_freqs(Clocks {
sys: sys_clk.hz(), sys: sys_clk.hz(),
ahb: ahb_freq.hz(), ahb: ahb_freq.hz(),
apb1: apb1_freq.hz(), apb1: apb1_freq.hz(),
apb2: apb2_freq.hz(), apb2: apb2_freq.hz(),
apb1_tim: apb1_tim_freq.hz(), apb1_tim: apb1_tim_freq.hz(),
apb2_tim: apb2_tim_freq.hz(), apb2_tim: apb2_tim_freq.hz(),
} });
}
}
pub(crate) unsafe fn init(config: Config) {
let r = <peripherals::RCC as embassy::util::Steal>::steal();
let clocks = r.freeze(config);
set_freqs(clocks);
} }

View File

@ -1,17 +1,8 @@
use crate::pac; use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw};
use crate::peripherals::{self, RCC}; use crate::pac::{FLASH, RCC};
use crate::rcc::{get_freqs, set_freqs, Clocks}; use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz; use crate::time::Hertz;
use crate::time::U32Ext; 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 /// HSI16 speed
pub const HSI16_FREQ: u32 = 16_000_000; pub const HSI16_FREQ: u32 = 16_000_000;
@ -19,8 +10,8 @@ pub const HSI16_FREQ: u32 = 16_000_000;
/// System clock mux source /// System clock mux source
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum ClockSrc { pub enum ClockSrc {
PLL(PLLSource, PLLClkDiv, PLLSrcDiv, PLLMul, Option<PLL48Div>),
MSI(MSIRange), MSI(MSIRange),
PLL(PLLSource, PLLClkDiv, PLLSrcDiv, PLLMul, Option<PLL48Div>),
HSE(Hertz), HSE(Hertz),
HSI16, HSI16,
} }
@ -57,25 +48,6 @@ pub enum MSIRange {
Range11, 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 { impl Default for MSIRange {
fn default() -> MSIRange { fn default() -> MSIRange {
MSIRange::Range6 MSIRange::Range6
@ -131,9 +103,9 @@ seq_macro::seq!(N in 8..=86 {
)* )*
} }
impl Into<u8> for PLLMul { impl From<PLLMul> for u8 {
fn into(self) -> u8 { fn from(val: PLLMul) -> u8 {
match self { match val {
#( #(
PLLMul::Mul#N => N, PLLMul::Mul#N => N,
)* )*
@ -163,13 +135,13 @@ pub enum PLLClkDiv {
impl PLLClkDiv { impl PLLClkDiv {
pub fn to_div(self) -> u32 { pub fn to_div(self) -> u32 {
let val: u8 = self.into(); let val: u8 = self.into();
val as u32 + 1 * 2 (val as u32 + 1) * 2
} }
} }
impl Into<u8> for PLLClkDiv { impl From<PLLClkDiv> for u8 {
fn into(self) -> u8 { fn from(val: PLLClkDiv) -> u8 {
match self { match val {
PLLClkDiv::Div2 => 0b00, PLLClkDiv::Div2 => 0b00,
PLLClkDiv::Div4 => 0b01, PLLClkDiv::Div4 => 0b01,
PLLClkDiv::Div6 => 0b10, PLLClkDiv::Div6 => 0b10,
@ -197,9 +169,9 @@ impl PLLSrcDiv {
} }
} }
impl Into<u8> for PLLSrcDiv { impl From<PLLSrcDiv> for u8 {
fn into(self) -> u8 { fn from(val: PLLSrcDiv) -> u8 {
match self { match val {
PLLSrcDiv::Div1 => 0b000, PLLSrcDiv::Div1 => 0b000,
PLLSrcDiv::Div2 => 0b001, PLLSrcDiv::Div2 => 0b001,
PLLSrcDiv::Div3 => 0b010, PLLSrcDiv::Div3 => 0b010,
@ -212,18 +184,46 @@ impl Into<u8> for PLLSrcDiv {
} }
} }
impl Into<u8> for PLLSource { impl From<PLLSource> for Pllsrc {
fn into(self) -> u8 { fn from(val: PLLSource) -> Pllsrc {
match self { match val {
PLLSource::HSI16 => 0b10, PLLSource::HSI16 => Pllsrc::HSI16,
PLLSource::HSE(_) => 0b11, PLLSource::HSE(_) => Pllsrc::HSE,
} }
} }
} }
impl Into<Msirange> for MSIRange { impl From<APBPrescaler> for Ppre {
fn into(self) -> Msirange { fn from(val: APBPrescaler) -> Ppre {
match self { 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::Range0 => Msirange::RANGE100K,
MSIRange::Range1 => Msirange::RANGE200K, MSIRange::Range1 => Msirange::RANGE200K,
MSIRange::Range2 => Msirange::RANGE400K, MSIRange::Range2 => Msirange::RANGE400K,
@ -239,30 +239,22 @@ impl Into<Msirange> for MSIRange {
} }
} }
} }
impl Into<u8> for APBPrescaler {
fn into(self) -> u8 {
match self {
APBPrescaler::NotDivided => 1,
APBPrescaler::Div2 => 0x04,
APBPrescaler::Div4 => 0x05,
APBPrescaler::Div8 => 0x06,
APBPrescaler::Div16 => 0x07,
}
}
}
impl Into<u8> for AHBPrescaler { impl From<MSIRange> for u32 {
fn into(self) -> u8 { fn from(val: MSIRange) -> u32 {
match self { match val {
AHBPrescaler::NotDivided => 1, MSIRange::Range0 => 100_000,
AHBPrescaler::Div2 => 0x08, MSIRange::Range1 => 200_000,
AHBPrescaler::Div4 => 0x09, MSIRange::Range2 => 400_000,
AHBPrescaler::Div8 => 0x0a, MSIRange::Range3 => 800_000,
AHBPrescaler::Div16 => 0x0b, MSIRange::Range4 => 1_000_000,
AHBPrescaler::Div64 => 0x0c, MSIRange::Range5 => 2_000_000,
AHBPrescaler::Div128 => 0x0d, MSIRange::Range6 => 4_000_000,
AHBPrescaler::Div256 => 0x0e, MSIRange::Range7 => 8_000_000,
AHBPrescaler::Div512 => 0x0f, MSIRange::Range8 => 16_000_000,
MSIRange::Range9 => 24_000_000,
MSIRange::Range10 => 32_000_000,
MSIRange::Range11 => 48_000_000,
} }
} }
} }
@ -287,108 +279,66 @@ impl Default for Config {
} }
} }
/// RCC peripheral pub(crate) unsafe fn init(config: Config) {
pub struct Rcc<'d> { let (sys_clk, sw) = match config.mux {
_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) => { ClockSrc::MSI(range) => {
// Enable MSI // Enable MSI
unsafe { RCC.cr().write(|w| {
rcc.cr().write(|w| {
let bits: Msirange = range.into(); let bits: Msirange = range.into();
w.set_msirange(bits); w.set_msirange(bits);
w.set_msipllen(false); w.set_msipllen(false);
w.set_msirgsel(true); w.set_msirgsel(true);
w.set_msion(true); w.set_msion(true);
}); });
while !rcc.cr().read().msirdy() {} while !RCC.cr().read().msirdy() {}
// Enable as clock source for USB, RNG if running at 48 MHz // Enable as clock source for USB, RNG if running at 48 MHz
if let MSIRange::Range11 = range { if let MSIRange::Range11 = range {
rcc.ccipr().modify(|w| { RCC.ccipr().modify(|w| {
w.set_clk48sel(0b11); w.set_clk48sel(0b11);
}); });
} }
(range.into(), Sw::MSI)
} }
(range.into(), 0b00) 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) => { ClockSrc::PLL(src, div, prediv, mul, pll48div) => {
let freq = match src { let freq = match src {
PLLSource::HSE(freq) => { PLLSource::HSE(freq) => {
// Enable HSE // Enable HSE
unsafe { RCC.cr().write(|w| w.set_hseon(true));
rcc.cr().write(|w| w.set_hseon(true)); while !RCC.cr().read().hserdy() {}
while !rcc.cr().read().hserdy() {}
}
freq.0 freq.0
} }
PLLSource::HSI16 => { PLLSource::HSI16 => {
// Enable HSI // Enable HSI
unsafe { RCC.cr().write(|w| w.set_hsion(true));
rcc.cr().write(|w| w.set_hsion(true)); while !RCC.cr().read().hsirdy() {}
while !rcc.cr().read().hsirdy() {}
}
HSI16_FREQ HSI16_FREQ
} }
}; };
// Disable PLL // Disable PLL
unsafe { RCC.cr().modify(|w| w.set_pllon(false));
rcc.cr().modify(|w| w.set_pllon(false)); while RCC.cr().read().pllrdy() {}
while rcc.cr().read().pllrdy() {}
}
let freq = (freq / prediv.to_div() * mul.to_mul()) / div.to_div(); let freq = (freq / prediv.to_div() * mul.to_mul()) / div.to_div();
assert!(freq <= 80_000_000); assert!(freq <= 80_000_000);
unsafe { RCC.pllcfgr().write(move |w| {
rcc.pllcfgr().write(move |w| {
w.set_plln(mul.into()); w.set_plln(mul.into());
w.set_pllm(prediv.into()); w.set_pllm(prediv.into());
w.set_pllr(div.into()); w.set_pllr(div.into());
@ -401,23 +351,22 @@ impl RccExt for RCC {
// Enable as clock source for USB, RNG if PLL48 divisor is provided // Enable as clock source for USB, RNG if PLL48 divisor is provided
if pll48div.is_some() { if pll48div.is_some() {
rcc.ccipr().modify(|w| { RCC.ccipr().modify(|w| {
w.set_clk48sel(0b10); w.set_clk48sel(0b10);
}); });
} }
// Enable PLL // Enable PLL
rcc.cr().modify(|w| w.set_pllon(true)); RCC.cr().modify(|w| w.set_pllon(true));
while !rcc.cr().read().pllrdy() {} while !RCC.cr().read().pllrdy() {}
rcc.pllcfgr().modify(|w| w.set_pllren(true)); RCC.pllcfgr().modify(|w| w.set_pllren(true));
}
(freq, 0b11) (freq, Sw::PLL)
} }
}; };
unsafe {
// Set flash wait states // Set flash wait states
pac::FLASH.acr().modify(|w| { FLASH.acr().modify(|w| {
w.set_latency(if sys_clk <= 16_000_000 { w.set_latency(if sys_clk <= 16_000_000 {
0b000 0b000
} else if sys_clk <= 32_000_000 { } else if sys_clk <= 32_000_000 {
@ -431,45 +380,43 @@ impl RccExt for RCC {
}); });
}); });
// Switch active clocks to new clock source RCC.cfgr().modify(|w| {
rcc.cfgr().modify(|w| { w.set_sw(sw);
w.set_sw(sw.into()); w.set_hpre(config.ahb_pre.into());
w.set_hpre(cfgr.ahb_pre.into()); w.set_ppre1(config.apb1_pre.into());
w.set_ppre1(cfgr.apb1_pre.into()); w.set_ppre2(config.apb2_pre.into());
w.set_ppre2(cfgr.apb2_pre.into());
}); });
}
let ahb_freq: u32 = match cfgr.ahb_pre { let ahb_freq: u32 = match config.ahb_pre {
AHBPrescaler::NotDivided => sys_clk, AHBPrescaler::NotDivided => sys_clk,
pre => { pre => {
let pre: u8 = pre.into(); let pre: Hpre = pre.into();
let pre = 1 << (pre as u32 - 7); let pre = 1 << (pre.0 as u32 - 7);
sys_clk / pre sys_clk / pre
} }
}; };
let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre { let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq), APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => { pre => {
let pre: u8 = pre.into(); let pre: Ppre = pre.into();
let pre: u8 = 1 << (pre - 3); let pre: u8 = 1 << (pre.0 - 3);
let freq = ahb_freq / pre as u32; let freq = ahb_freq / pre as u32;
(freq, freq * 2) (freq, freq * 2)
} }
}; };
let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre { let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq), APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => { pre => {
let pre: u8 = pre.into(); let pre: Ppre = pre.into();
let pre: u8 = 1 << (pre - 3); let pre: u8 = 1 << (pre.0 - 3);
let freq = ahb_freq / (1 << (pre as u8 - 3)); let freq = ahb_freq / (1 << (pre as u8 - 3));
(freq, freq * 2) (freq, freq * 2)
} }
}; };
Clocks { set_freqs(Clocks {
sys: sys_clk.hz(), sys: sys_clk.hz(),
ahb1: ahb_freq.hz(), ahb1: ahb_freq.hz(),
ahb2: ahb_freq.hz(), ahb2: ahb_freq.hz(),
@ -478,12 +425,5 @@ impl RccExt for RCC {
apb2: apb2_freq.hz(), apb2: apb2_freq.hz(),
apb1_tim: apb1_tim_freq.hz(), apb1_tim: apb1_tim_freq.hz(),
apb2_tim: apb2_tim_freq.hz(), apb2_tim: apb2_tim_freq.hz(),
} });
}
}
pub(crate) unsafe fn init(config: Config) {
let r = <peripherals::RCC as embassy::util::Steal>::steal();
let clocks = r.freeze(config);
set_freqs(clocks);
} }

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,47 +305,32 @@ 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;
}
impl RccExt for RCC {
#[inline]
fn freeze(self, cfgr: Config, power: &Power) -> Clocks {
let rcc = pac::RCC;
let sys_clk = match cfgr.mux {
ClockSrc::MSI(range) => { ClockSrc::MSI(range) => {
unsafe { RCC.icscr1().modify(|w| {
rcc.icscr1().modify(|w| {
let bits: Msirange = range.into(); let bits: Msirange = range.into();
w.set_msisrange(bits); w.set_msisrange(bits);
w.set_msirgsel(Msirgsel::RCC_ICSCR1); w.set_msirgsel(Msirgsel::RCC_ICSCR1);
}); });
rcc.cr().write(|w| { RCC.cr().write(|w| {
w.set_msipllen(false); w.set_msipllen(false);
w.set_msison(true); w.set_msison(true);
w.set_msison(true); w.set_msison(true);
}); });
while !rcc.cr().read().msisrdy() {} while !RCC.cr().read().msisrdy() {}
}
range.into() range.into()
} }
ClockSrc::HSE(freq) => { ClockSrc::HSE(freq) => {
unsafe { RCC.cr().write(|w| w.set_hseon(true));
rcc.cr().write(|w| w.set_hseon(true)); while !RCC.cr().read().hserdy() {}
while !rcc.cr().read().hserdy() {}
}
freq.0 freq.0
} }
ClockSrc::HSI16 => { ClockSrc::HSI16 => {
unsafe { RCC.cr().write(|w| w.set_hsion(true));
rcc.cr().write(|w| w.set_hsion(true)); while !RCC.cr().read().hsirdy() {}
while !rcc.cr().read().hsirdy() {}
}
HSI16_FREQ HSI16_FREQ
} }
@ -345,42 +342,39 @@ impl RccExt for RCC {
}; };
// disable // disable
unsafe { RCC.cr().modify(|w| w.set_pllon(0, false));
rcc.cr().modify(|w| w.set_pllon(0, false)); while RCC.cr().read().pllrdy(0) {}
while rcc.cr().read().pllrdy(0) {}
}
let vco = freq * n as u8 as u32; let vco = freq * n as u8 as u32;
let pll_ck = vco / (div as u8 as u32 + 1); let pll_ck = vco / (div as u8 as u32 + 1);
unsafe { RCC.pll1cfgr().write(|w| {
rcc.pll1cfgr().write(|w| {
w.set_pllm(m.into()); w.set_pllm(m.into());
w.set_pllsrc(src.into()); w.set_pllsrc(src.into());
}); });
rcc.pll1divr().modify(|w| { RCC.pll1divr().modify(|w| {
w.set_pllr(div.to_div()); w.set_pllr(div.to_div());
w.set_plln(n.to_mul()); w.set_plln(n.to_mul());
}); });
// Enable PLL // Enable PLL
rcc.cr().modify(|w| w.set_pllon(0, true)); RCC.cr().modify(|w| w.set_pllon(0, true));
while !rcc.cr().read().pllrdy(0) {} while !RCC.cr().read().pllrdy(0) {}
rcc.pll1cfgr().modify(|w| w.set_pllren(true)); RCC.pll1cfgr().modify(|w| w.set_pllren(true));
}
unsafe { RCC.cr().write(|w| w.set_pllon(0, true));
rcc.cr().write(|w| w.set_pllon(0, true)); while !RCC.cr().read().pllrdy(0) {}
while !rcc.cr().read().pllrdy(0) {}
}
pll_ck pll_ck
} }
}; };
// TODO make configurable
let power_vos = VoltageScale::Range4;
// states and programming delay // states and programming delay
let wait_states = match power.vos { let wait_states = match power_vos {
// VOS 0 range VCORE 1.26V - 1.40V // VOS 0 range VCORE 1.26V - 1.40V
VoltageScale::Range1 => { VoltageScale::Range1 => {
if sys_clk < 32_000_000 { if sys_clk < 32_000_000 {
@ -427,29 +421,25 @@ impl RccExt for RCC {
} }
}; };
unsafe { FLASH.acr().modify(|w| {
pac::FLASH.acr().modify(|w| {
w.set_latency(wait_states); w.set_latency(wait_states);
})
}
unsafe {
rcc.cfgr1().modify(|w| {
w.set_sw(cfgr.mux.into());
}); });
rcc.cfgr2().modify(|w| { RCC.cfgr1().modify(|w| {
w.set_hpre(cfgr.ahb_pre.into()); w.set_sw(config.mux.into());
w.set_ppre1(cfgr.apb1_pre.into());
w.set_ppre2(cfgr.apb2_pre.into());
}); });
rcc.cfgr3().modify(|w| { RCC.cfgr2().modify(|w| {
w.set_ppre3(cfgr.apb3_pre.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 cfgr.ahb_pre { RCC.cfgr3().modify(|w| {
w.set_ppre3(config.apb3_pre.into());
});
let ahb_freq: u32 = match config.ahb_pre {
AHBPrescaler::NotDivided => sys_clk, AHBPrescaler::NotDivided => sys_clk,
pre => { pre => {
let pre: u8 = pre.into(); let pre: u8 = pre.into();
@ -458,7 +448,7 @@ impl RccExt for RCC {
} }
}; };
let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre { let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq), APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => { pre => {
let pre: u8 = pre.into(); let pre: u8 = pre.into();
@ -468,7 +458,7 @@ impl RccExt for RCC {
} }
}; };
let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre { let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq), APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => { pre => {
let pre: u8 = pre.into(); let pre: u8 = pre.into();
@ -478,7 +468,7 @@ impl RccExt for RCC {
} }
}; };
let (apb3_freq, _apb3_tim_freq) = match cfgr.apb3_pre { let (apb3_freq, _apb3_tim_freq) = match config.apb3_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq), APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => { pre => {
let pre: u8 = pre.into(); let pre: u8 = pre.into();
@ -488,7 +478,7 @@ impl RccExt for RCC {
} }
}; };
Clocks { set_freqs(Clocks {
sys: sys_clk.hz(), sys: sys_clk.hz(),
ahb1: ahb_freq.hz(), ahb1: ahb_freq.hz(),
ahb2: ahb_freq.hz(), ahb2: ahb_freq.hz(),
@ -498,13 +488,5 @@ impl RccExt for RCC {
apb3: apb3_freq.hz(), apb3: apb3_freq.hz(),
apb1_tim: apb1_tim_freq.hz(), apb1_tim: apb1_tim_freq.hz(),
apb2_tim: apb2_tim_freq.hz(), apb2_tim: apb2_tim_freq.hz(),
} });
}
}
pub(crate) unsafe fn init(config: Config) {
let r = <peripherals::RCC as embassy::util::Steal>::steal();
let power = Power::new(<peripherals::PWR as embassy::util::Steal>::steal());
let clocks = r.freeze(config, &power);
set_freqs(clocks);
} }

View File

@ -1,11 +1,7 @@
use crate::pac; use crate::pac::RCC;
use crate::peripherals::{self, RCC}; use crate::rcc::{set_freqs, Clocks};
use crate::rcc::{get_freqs, set_freqs, Clocks};
use crate::time::Hertz; use crate::time::Hertz;
use crate::time::U32Ext; 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, /// 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. /// and with the addition of the init function to configure a system clock.
@ -104,67 +100,32 @@ impl Default for Config {
} }
} }
/// RCC peripheral pub(crate) unsafe fn init(config: Config) {
pub struct Rcc<'d> { let (sys_clk, sw) = match config.mux {
_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 => { ClockSrc::HSI16 => {
// Enable HSI16 // Enable HSI16
unsafe { RCC.cr().write(|w| w.set_hsion(true));
rcc.cr().write(|w| w.set_hsion(true)); while !RCC.cr().read().hsirdy() {}
while !rcc.cr().read().hsirdy() {}
}
(HSI_FREQ, 0x01) (HSI_FREQ, 0x01)
} }
ClockSrc::HSE(freq) => { ClockSrc::HSE(freq) => {
// Enable HSE // Enable HSE
unsafe { RCC.cr().write(|w| w.set_hseon(true));
rcc.cr().write(|w| w.set_hseon(true)); while !RCC.cr().read().hserdy() {}
while !rcc.cr().read().hserdy() {}
}
(freq.0, 0x02) (freq.0, 0x02)
} }
}; };
unsafe { RCC.cfgr().modify(|w| {
rcc.cfgr().modify(|w| {
w.set_sw(sw.into()); w.set_sw(sw.into());
w.set_hpre(cfgr.ahb_pre.into()); w.set_hpre(config.ahb_pre.into());
w.set_ppre1(cfgr.apb1_pre.into()); w.set_ppre1(config.apb1_pre.into());
w.set_ppre2(cfgr.apb2_pre.into()); w.set_ppre2(config.apb2_pre.into());
}); });
}
let ahb_freq: u32 = match cfgr.ahb_pre { let ahb_freq: u32 = match config.ahb_pre {
AHBPrescaler::NotDivided => sys_clk, AHBPrescaler::NotDivided => sys_clk,
pre => { pre => {
let pre: u8 = pre.into(); let pre: u8 = pre.into();
@ -173,7 +134,7 @@ impl RccExt for RCC {
} }
}; };
let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre { let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq), APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => { pre => {
let pre: u8 = pre.into(); let pre: u8 = pre.into();
@ -183,7 +144,7 @@ impl RccExt for RCC {
} }
}; };
let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre { let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq), APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => { pre => {
let pre: u8 = pre.into(); let pre: u8 = pre.into();
@ -193,7 +154,7 @@ impl RccExt for RCC {
} }
}; };
Clocks { set_freqs(Clocks {
sys: sys_clk.hz(), sys: sys_clk.hz(),
ahb1: ahb_freq.hz(), ahb1: ahb_freq.hz(),
ahb2: ahb_freq.hz(), ahb2: ahb_freq.hz(),
@ -202,12 +163,5 @@ impl RccExt for RCC {
apb2: apb2_freq.hz(), apb2: apb2_freq.hz(),
apb1_tim: apb1_tim_freq.hz(), apb1_tim: apb1_tim_freq.hz(),
apb2_tim: apb2_tim_freq.hz(), apb2_tim: apb2_tim_freq.hz(),
} });
}
}
pub(crate) unsafe fn init(config: Config) {
let r = <peripherals::RCC as embassy::util::Steal>::steal();
let clocks = r.freeze(config);
set_freqs(clocks);
} }

View File

@ -1,10 +1,6 @@
use crate::pac; use crate::pac::RCC;
use crate::peripherals::{self, RCC}; use crate::rcc::{set_freqs, Clocks};
use crate::rcc::{get_freqs, set_freqs, Clocks};
use crate::time::U32Ext; 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, /// 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. /// and with the addition of the init function to configure a system clock.
@ -91,6 +87,7 @@ pub struct Config {
pub ahb_pre: AHBPrescaler, pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler, pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler, pub apb2_pre: APBPrescaler,
pub enable_lsi: bool,
} }
impl Default for Config { impl Default for Config {
@ -101,89 +98,44 @@ impl Default for Config {
ahb_pre: AHBPrescaler::NotDivided, ahb_pre: AHBPrescaler::NotDivided,
apb1_pre: APBPrescaler::NotDivided, apb1_pre: APBPrescaler::NotDivided,
apb2_pre: APBPrescaler::NotDivided, apb2_pre: APBPrescaler::NotDivided,
enable_lsi: false,
} }
} }
} }
/// RCC peripheral pub(crate) unsafe fn init(config: Config) {
pub struct Rcc<'d> { let (sys_clk, sw) = match config.mux {
_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 => { ClockSrc::HSI16 => {
// Enable HSI16 // Enable HSI16
unsafe { RCC.cr().write(|w| w.set_hsion(true));
rcc.cr().write(|w| w.set_hsion(true)); while !RCC.cr().read().hsirdy() {}
while !rcc.cr().read().hsirdy() {}
}
(HSI_FREQ, 0x01) (HSI_FREQ, 0x01)
} }
ClockSrc::HSE32 => { ClockSrc::HSE32 => {
// Enable HSE32 // Enable HSE32
unsafe { RCC.cr().write(|w| {
rcc.cr().write(|w| {
w.set_hsebyppwr(true); w.set_hsebyppwr(true);
w.set_hseon(true); w.set_hseon(true);
}); });
while !rcc.cr().read().hserdy() {} while !RCC.cr().read().hserdy() {}
}
(HSE32_FREQ, 0x02) (HSE32_FREQ, 0x02)
} }
}; };
unsafe { RCC.cfgr().modify(|w| {
rcc.cfgr().modify(|w| {
w.set_sw(sw.into()); w.set_sw(sw.into());
if cfgr.ahb_pre == AHBPrescaler::NotDivided { if config.ahb_pre == AHBPrescaler::NotDivided {
w.set_hpre(0); w.set_hpre(0);
} else { } else {
w.set_hpre(cfgr.ahb_pre.into()); w.set_hpre(config.ahb_pre.into());
} }
w.set_ppre1(cfgr.apb1_pre.into()); w.set_ppre1(config.apb1_pre.into());
w.set_ppre2(cfgr.apb2_pre.into()); w.set_ppre2(config.apb2_pre.into());
}); });
}
let ahb_freq: u32 = match cfgr.ahb_pre { let ahb_freq: u32 = match config.ahb_pre {
AHBPrescaler::NotDivided => sys_clk, AHBPrescaler::NotDivided => sys_clk,
pre => { pre => {
let pre: u8 = pre.into(); let pre: u8 = pre.into();
@ -192,7 +144,7 @@ impl RccExt for RCC {
} }
}; };
let (apb1_freq, apb1_tim_freq) = match cfgr.apb1_pre { let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq), APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => { pre => {
let pre: u8 = pre.into(); let pre: u8 = pre.into();
@ -202,7 +154,7 @@ impl RccExt for RCC {
} }
}; };
let (apb2_freq, apb2_tim_freq) = match cfgr.apb2_pre { let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
APBPrescaler::NotDivided => (ahb_freq, ahb_freq), APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
pre => { pre => {
let pre: u8 = pre.into(); let pre: u8 = pre.into();
@ -215,7 +167,15 @@ impl RccExt for RCC {
// TODO: completely untested // TODO: completely untested
let apb3_freq = ahb_freq; let apb3_freq = ahb_freq;
Clocks { 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(), sys: sys_clk.hz(),
ahb1: ahb_freq.hz(), ahb1: ahb_freq.hz(),
ahb2: ahb_freq.hz(), ahb2: ahb_freq.hz(),
@ -225,12 +185,5 @@ impl RccExt for RCC {
apb3: apb3_freq.hz(), apb3: apb3_freq.hz(),
apb1_tim: apb1_tim_freq.hz(), apb1_tim: apb1_tim_freq.hz(),
apb2_tim: apb2_tim_freq.hz(), apb2_tim: apb2_tim_freq.hz(),
} });
}
}
pub(crate) unsafe fn init(config: Config) {
let r = <peripherals::RCC as embassy::util::Steal>::steal();
let clocks = r.freeze(config);
set_freqs(clocks);
} }

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 8530a19ffdcdcbc608a97b40895827d09e670eb7 Subproject commit 3fa97966f07d43a28c0031175591e1c2ff5d0831