You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

439 lines
12 KiB

use crate::stm32::RCC;
use crate::time::Hertz;
/// Extension trait that sets up the `RCC` peripheral
pub trait RccExt {
/// Configure the clocks of the RCC peripheral
fn configure(self) -> CFGR;
}
impl RccExt for RCC {
fn configure(self) -> CFGR {
CFGR {
hclk: None,
pclk: None,
sysclk: None,
clock_src: SysClkSource::HSI,
/// CRS is only available on devices with USB and HSI48
#[cfg(any(
feature = "stm32f031", // TODO: May be an SVD bug
feature = "stm32f038", // TODO: May be an SVD bug
feature = "stm32f042",
feature = "stm32f048",
feature = "stm32f051", // TODO: May be an SVD bug
feature = "stm32f058", // TODO: May be an SVD bug
feature = "stm32f072",
feature = "stm32f078",
))]
crs: None,
rcc: self,
}
}
}
/// Constrained RCC peripheral
pub struct Rcc {
pub clocks: Clocks,
pub(crate) regs: RCC,
}
#[cfg(any(
feature = "stm32f030",
feature = "stm32f070",
))]
mod inner {
use crate::stm32::{rcc::cfgr::SWW, RCC};
pub(super) const HSI: u32 = 8_000_000; // Hz
pub(super) enum SysClkSource {
HSI,
HSE(u32),
}
pub(super) fn get_freq(c_src: &SysClkSource) -> u32 {
// Select clock source based on user input and capability
// Highest selected frequency source available takes precedent.
match c_src {
SysClkSource::HSE(freq) => *freq,
_ => HSI,
}
}
pub(super) fn enable_clock(rcc: &mut RCC, c_src: &SysClkSource) {
// Enable the requested clock
match c_src {
SysClkSource::HSE(_) => {
rcc.cr
.modify(|_, w| w.csson().on().hseon().on().hsebyp().not_bypassed());
while !rcc.cr.read().hserdy().bit_is_set() {}
}
SysClkSource::HSI => {
rcc.cr.write(|w| w.hsion().set_bit());
while rcc.cr.read().hsirdy().bit_is_clear() {}
}
}
}
pub(super) fn enable_pll(
rcc: &mut RCC,
c_src: &SysClkSource,
pllmul_bits: u8,
ppre_bits: u8,
hpre_bits: u8,
) {
let pllsrc_bit: bool = match c_src {
SysClkSource::HSI => false,
SysClkSource::HSE(_) => true,
};
// Set PLL source and multiplier
rcc.cfgr
.modify(|_, w| unsafe { w.pllsrc().bit(pllsrc_bit).pllmul().bits(pllmul_bits) });
rcc.cr.write(|w| w.pllon().set_bit());
while rcc.cr.read().pllrdy().bit_is_clear() {}
rcc.cfgr
.modify(|_, w| unsafe { w.ppre().bits(ppre_bits).hpre().bits(hpre_bits).sw().pll() });
}
pub(super) fn get_sww(c_src: &SysClkSource) -> SWW {
match c_src {
SysClkSource::HSI => SWW::HSI,
SysClkSource::HSE(_) => SWW::HSE,
}
}
}
#[cfg(any(
feature = "stm32f031", // TODO: May be an SVD bug
feature = "stm32f038", // TODO: May be an SVD bug
feature = "stm32f042",
feature = "stm32f048",
feature = "stm32f051", // TODO: May be an SVD bug
feature = "stm32f058", // TODO: May be an SVD bug
feature = "stm32f071",
feature = "stm32f072",
feature = "stm32f078",
feature = "stm32f091",
feature = "stm32f098",
))]
mod inner {
use crate::stm32::{rcc::cfgr::SWW, RCC};
pub(super) const HSI: u32 = 8_000_000; // Hz
pub(super) const HSI48: u32 = 48_000_000; // Hz
pub(super) enum SysClkSource {
HSI,
HSE(u32),
HSI48,
}
pub(super) fn get_freq(c_src: &SysClkSource) -> u32 {
// Select clock source based on user input and capability
// Highest selected frequency source available takes precedent.
match c_src {
SysClkSource::HSE(freq) => *freq,
SysClkSource::HSI48 => HSI48,
_ => HSI,
}
}
pub(super) fn enable_clock(rcc: &mut RCC, c_src: &SysClkSource) {
// Enable the requested clock
match c_src {
SysClkSource::HSE(_) => {
rcc.cr
.modify(|_, w| w.csson().on().hseon().on().hsebyp().not_bypassed());
while !rcc.cr.read().hserdy().bit_is_set() {}
}
SysClkSource::HSI48 => {
rcc.cr2.modify(|_, w| w.hsi48on().set_bit());
while rcc.cr2.read().hsi48rdy().bit_is_clear() {}
}
SysClkSource::HSI => {
rcc.cr.write(|w| w.hsion().set_bit());
while rcc.cr.read().hsirdy().bit_is_clear() {}
}
}
}
pub(super) fn enable_pll(
rcc: &mut RCC,
c_src: &SysClkSource,
pllmul_bits: u8,
ppre_bits: u8,
hpre_bits: u8,
) {
let pllsrc_bit: u8 = match c_src {
SysClkSource::HSI => 0b00,
SysClkSource::HSI48 => 0b11,
SysClkSource::HSE(_) => 0b01,
};
// Set PLL source and multiplier
rcc.cfgr
.modify(|_, w| unsafe { w.pllsrc().bits(pllsrc_bit).pllmul().bits(pllmul_bits) });
rcc.cr.write(|w| w.pllon().set_bit());
while rcc.cr.read().pllrdy().bit_is_clear() {}
rcc.cfgr
.modify(|_, w| unsafe { w.ppre().bits(ppre_bits).hpre().bits(hpre_bits).sw().pll() });
}
pub(super) fn get_sww(c_src: &SysClkSource) -> SWW {
match c_src {
SysClkSource::HSI => SWW::HSI,
SysClkSource::HSI48 => SWW::HSI48,
SysClkSource::HSE(_) => SWW::HSE,
}
}
}
use self::inner::SysClkSource;
pub struct CFGR {
hclk: Option<u32>,
pclk: Option<u32>,
sysclk: Option<u32>,
clock_src: SysClkSource,
/// CRS is only available on devices with USB and HSI48
#[cfg(any(
feature = "stm32f031", // TODO: May be an SVD bug
feature = "stm32f038", // TODO: May be an SVD bug
feature = "stm32f042",
feature = "stm32f048",
feature = "stm32f051", // TODO: May be an SVD bug
feature = "stm32f058", // TODO: May be an SVD bug
feature = "stm32f072",
feature = "stm32f078",
))]
crs: Option<crate::stm32::CRS>,
rcc: RCC,
}
impl CFGR {
pub fn hse<F>(mut self, freq: F) -> Self
where
F: Into<Hertz>,
{
self.clock_src = SysClkSource::HSE(freq.into().0);
self
}
#[cfg(any(
feature = "stm32f042",
feature = "stm32f048",
feature = "stm32f071",
feature = "stm32f072",
feature = "stm32f078",
feature = "stm32f091",
feature = "stm32f098",
))]
pub fn hsi48(mut self) -> Self {
self.clock_src = SysClkSource::HSI48;
self
}
pub fn hclk<F>(mut self, freq: F) -> Self
where
F: Into<Hertz>,
{
self.hclk = Some(freq.into().0);
self
}
pub fn pclk<F>(mut self, freq: F) -> Self
where
F: Into<Hertz>,
{
self.pclk = Some(freq.into().0);
self
}
pub fn sysclk<F>(mut self, freq: F) -> Self
where
F: Into<Hertz>,
{
self.sysclk = Some(freq.into().0);
self
}
#[cfg(any(
feature = "stm32f042",
feature = "stm32f048",
feature = "stm32f072",
feature = "stm32f078",
))]
pub fn enable_crs(mut self, crs: crate::stm32::CRS) -> Self {
self.crs = Some(crs);
self
}
pub fn freeze(mut self, flash: &mut crate::stm32::FLASH) -> Rcc {
// Default to lowest frequency clock on all systems.
let sysclk = self.sysclk.unwrap_or(self::inner::HSI);
let r_sysclk; // The "real" sysclock value, calculated below
let pllmul_bits;
// Select clock source based on user input and capability
// Highest selected frequency source available takes precedent.
// For F04x, F07x, F09x parts, use HSI48 if requested.
let src_clk_freq = self::inner::get_freq(&self.clock_src);
// Pll check
if sysclk == src_clk_freq {
// Bypass pll if src clk and requested sysclk are the same, to save power.
// The only reason to override this behaviour is if the sysclk source were HSI, and you
// were running the USB off the PLL...
pllmul_bits = None;
r_sysclk = src_clk_freq;
} else {
let pllmul =
(4 * self.sysclk.unwrap_or(src_clk_freq) + src_clk_freq) / src_clk_freq / 2;
let pllmul = core::cmp::min(core::cmp::max(pllmul, 2), 16);
r_sysclk = pllmul * src_clk_freq / 2;
pllmul_bits = if pllmul == 2 {
None
} else {
Some(pllmul as u8 - 2)
};
}
let hpre_bits = self
.hclk
.map(|hclk| match r_sysclk / hclk {
0 => unreachable!(),
1 => 0b0111,
2 => 0b1000,
3...5 => 0b1001,
6...11 => 0b1010,
12...39 => 0b1011,
40...95 => 0b1100,
96...191 => 0b1101,
192...383 => 0b1110,
_ => 0b1111,
})
.unwrap_or(0b0111);
let hclk = sysclk / (1 << (hpre_bits - 0b0111));
let ppre_bits = self
.pclk
.map(|pclk| match hclk / pclk {
0 => unreachable!(),
1 => 0b011,
2 => 0b100,
3...5 => 0b101,
6...11 => 0b110,
_ => 0b111,
})
.unwrap_or(0b011);
let ppre: u8 = 1 << (ppre_bits - 0b011);
let pclk = hclk / cast::u32(ppre);
// adjust flash wait states
unsafe {
flash.acr.write(|w| {
w.latency().bits(if sysclk <= 24_000_000 {
0b000
} else if sysclk <= 48_000_000 {
0b001
} else {
0b010
})
})
}
// Enable the requested clock
self::inner::enable_clock(&mut self.rcc, &self.clock_src);
// Set up rcc based on above calculated configuration.
// Enable PLL
if let Some(pllmul_bits) = pllmul_bits {
self::inner::enable_pll(
&mut self.rcc,
&self.clock_src,
pllmul_bits,
ppre_bits,
hpre_bits,
);
} else {
let sw_var = self::inner::get_sww(&self.clock_src);
// CRS is only available on devices with USB and HSI48
#[cfg(any(
feature = "stm32f042",
feature = "stm32f048",
feature = "stm32f072",
feature = "stm32f078",
))]
match self.crs {
Some(crs) => {
self.rcc.apb1enr.modify(|_, w| w.crsen().set_bit());
// Initialize clock recovery
// Set autotrim enabled.
crs.cr.modify(|_, w| w.autotrimen().set_bit());
// Enable CR
crs.cr.modify(|_, w| w.cen().set_bit());
}
_ => {}
}
// use HSI as source
self.rcc.cfgr.write(|w| unsafe {
w.ppre()
.bits(ppre_bits)
.hpre()
.bits(hpre_bits)
.sw()
.variant(sw_var)
});
}
Rcc {
clocks: Clocks {
hclk: Hertz(hclk),
pclk: Hertz(pclk),
sysclk: Hertz(sysclk),
},
regs: self.rcc,
}
}
}
/// Frozen clock frequencies
///
/// The existence of this value indicates that the clock configuration can no longer be changed
#[derive(Clone, Copy)]
pub struct Clocks {
hclk: Hertz,
pclk: Hertz,
sysclk: Hertz,
}
impl Clocks {
/// Returns the frequency of the AHB
pub fn hclk(&self) -> Hertz {
self.hclk
}
/// Returns the frequency of the APB
pub fn pclk(&self) -> Hertz {
self.pclk
}
/// Returns the system (core) frequency
pub fn sysclk(&self) -> Hertz {
self.sysclk
}
}