You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
175 lines
4.0 KiB
Rust
175 lines
4.0 KiB
Rust
use core::cmp;
|
|
|
|
use cast::u32;
|
|
use crate::stm32::{FLASH, RCC};
|
|
|
|
use crate::time::Hertz;
|
|
|
|
/// Extension trait that constrains the `RCC` peripheral
|
|
pub trait RccExt {
|
|
/// Constrains the `RCC` peripheral so it plays nicely with the other abstractions
|
|
fn constrain(self) -> Rcc;
|
|
}
|
|
|
|
impl RccExt for RCC {
|
|
fn constrain(self) -> Rcc {
|
|
Rcc {
|
|
cfgr: CFGR {
|
|
hclk: None,
|
|
pclk: None,
|
|
sysclk: None,
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Constrained RCC peripheral
|
|
pub struct Rcc {
|
|
pub cfgr: CFGR,
|
|
}
|
|
|
|
const HSI: u32 = 8_000_000; // Hz
|
|
|
|
pub struct CFGR {
|
|
hclk: Option<u32>,
|
|
pclk: Option<u32>,
|
|
sysclk: Option<u32>,
|
|
}
|
|
|
|
impl CFGR {
|
|
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
|
|
}
|
|
|
|
pub fn freeze(self) -> Clocks {
|
|
let pllmul = (4 * self.sysclk.unwrap_or(HSI) + HSI) / HSI / 2;
|
|
let pllmul = cmp::min(cmp::max(pllmul, 2), 16);
|
|
let sysclk = pllmul * HSI / 2;
|
|
|
|
let pllmul_bits = if pllmul == 2 {
|
|
None
|
|
} else {
|
|
Some(pllmul as u8 - 2)
|
|
};
|
|
|
|
let hpre_bits = self
|
|
.hclk
|
|
.map(|hclk| match 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 / u32(ppre);
|
|
|
|
// adjust flash wait states
|
|
unsafe {
|
|
let flash = &*FLASH::ptr();
|
|
flash.acr.write(|w| {
|
|
w.latency().bits(if sysclk <= 24_000_000 {
|
|
0b000
|
|
} else if sysclk <= 48_000_000 {
|
|
0b001
|
|
} else {
|
|
0b010
|
|
})
|
|
})
|
|
}
|
|
|
|
let rcc = unsafe { &*RCC::ptr() };
|
|
if let Some(pllmul_bits) = pllmul_bits {
|
|
// use PLL as source
|
|
|
|
rcc.cfgr.write(|w| unsafe { w.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().bits(2)
|
|
});
|
|
} else {
|
|
// use HSI as source
|
|
rcc.cfgr
|
|
.write(|w| unsafe { w.ppre().bits(ppre_bits).hpre().bits(hpre_bits).sw().bits(0) });
|
|
}
|
|
|
|
Clocks {
|
|
hclk: Hertz(hclk),
|
|
pclk: Hertz(pclk),
|
|
sysclk: Hertz(sysclk),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 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
|
|
}
|
|
}
|