use crate::{
clocks::{ClocksValid, SpeedError},
pac::{FLASH, RCC},
};
#[derive(Clone, Copy, PartialEq)]
pub enum PllSrc {
None,
Csi,
Hsi(HsiDiv),
Hse(u32),
}
impl PllSrc {
pub fn bits(&self) -> u8 {
match self {
Self::Hse(_) => 0b00,
Self::Csi => 0b01,
Self::Hsi(_) => 0b10,
Self::None => 0b11,
}
}
}
#[derive(Clone, Copy, PartialEq)]
pub enum InputSrc {
Hsi(HsiDiv),
Csi,
Hse(u32), Pll1(PllSrc),
}
impl InputSrc {
pub fn bits(&self) -> u8 {
match self {
Self::Hsi(_) => 0b000,
Self::Csi => 0b001,
Self::Hse(_) => 0b010,
Self::Pll1(_) => 0b011,
}
}
}
#[derive(Clone, Copy)]
#[repr(u8)]
pub enum Prediv {
Div1 = 0b0000,
Div2 = 0b0001,
Div3 = 0b0010,
Div4 = 0b0011,
Div5 = 0b0100,
Div6 = 0b0101,
Div7 = 0b0110,
Div8 = 0b0111,
}
impl Prediv {
pub fn value(&self) -> u8 {
match self {
Self::Div1 => 1,
Self::Div2 => 2,
Self::Div3 => 3,
Self::Div4 => 4,
Self::Div5 => 5,
Self::Div6 => 6,
Self::Div7 => 7,
Self::Div8 => 8,
}
}
}
#[derive(Clone, Copy)]
#[repr(u8)]
pub enum HclkPrescaler {
Div1 = 0b0000,
Div2 = 0b1000,
Div4 = 0b1001,
Div8 = 0b1010,
Div16 = 0b1011,
Div64 = 0b1100,
Div128 = 0b1101,
Div256 = 0b1110,
Div512 = 0b1111,
}
impl HclkPrescaler {
pub fn value(&self) -> u16 {
match self {
Self::Div1 => 1,
Self::Div2 => 2,
Self::Div4 => 4,
Self::Div8 => 8,
Self::Div16 => 16,
Self::Div64 => 64,
Self::Div128 => 128,
Self::Div256 => 256,
Self::Div512 => 512,
}
}
}
#[derive(Clone, Copy)]
#[repr(u8)]
pub enum ApbPrescaler {
Div1 = 0b000,
Div2 = 0b100,
Div4 = 0b101,
Div8 = 0b110,
Div16 = 0b111,
}
impl ApbPrescaler {
pub fn value(&self) -> u8 {
match self {
Self::Div1 => 1,
Self::Div2 => 2,
Self::Div4 => 4,
Self::Div8 => 8,
Self::Div16 => 16,
}
}
}
#[derive(Clone, Copy, PartialEq)]
#[repr(u8)]
pub enum HsiDiv {
Div1 = 0b00,
Div2 = 0b01,
Div4 = 0b10,
Div8 = 0b11,
}
impl HsiDiv {
pub fn value(&self) -> u8 {
match self {
Self::Div1 => 1,
Self::Div2 => 2,
Self::Div4 => 4,
Self::Div8 => 8,
}
}
}
pub struct Clocks {
pub input_src: InputSrc,
pub divm1: u8,
pub divn1: u16,
pub divp1: u8,
pub d1_core_prescaler: HclkPrescaler,
pub d1_prescaler: ApbPrescaler,
pub hclk_prescaler: HclkPrescaler,
pub d2_prescaler1: ApbPrescaler,
pub d2_prescaler2: ApbPrescaler,
pub d3_prescaler: ApbPrescaler,
pub hse_bypass: bool,
pub security_system: bool,
pub hsi48_on: bool,
}
impl Clocks {
pub fn setup(&self, rcc: &mut RCC, flash: &mut FLASH) -> Result<(), SpeedError> {
if let ClocksValid::NotValid = self.validate_speeds() {
return Err(SpeedError {});
}
let (_, sysclk) = self.calc_sysclock();
let hclk = sysclk / self.hclk_prescaler.value() as u32;
flash.acr.modify(|_, w| unsafe {
if hclk <= 16_000_000 {
w.latency().bits(0b000)
} else if hclk <= 32_000_000 {
w.latency().bits(0b001)
} else if hclk <= 48_000_000 {
w.latency().bits(0b010)
} else if hclk <= 64_000_000 {
w.latency().bits(0b011)
} else {
w.latency().bits(0b100)
}
});
match self.input_src {
InputSrc::Csi => {
rcc.cr.modify(|_, w| w.csion().bit(true));
while rcc.cr.read().csirdy().bit_is_clear() {}
}
InputSrc::Hse(_) => {
rcc.cr.modify(|_, w| w.hseon().bit(true));
while rcc.cr.read().hserdy().bit_is_clear() {}
}
InputSrc::Hsi(div) => {
rcc.cr.modify(|_, w| {
w.hsidiv().bits(div as u8);
w.hsion().bit(true)
});
while rcc.cr.read().hsirdy().bit_is_clear() {}
}
InputSrc::Pll1(pll_src) => {
match pll_src {
PllSrc::Csi => {
rcc.cr.modify(|_, w| w.csion().bit(true));
while rcc.cr.read().csirdy().bit_is_clear() {}
}
PllSrc::Hse(_) => {
rcc.cr.modify(|_, w| w.hseon().bit(true));
while rcc.cr.read().hserdy().bit_is_clear() {}
}
PllSrc::Hsi(div) => {
rcc.cr.modify(|_, w| {
w.hsidiv().bits(div as u8);
w.hsion().bit(true)
});
while rcc.cr.read().hsirdy().bit_is_clear() {}
}
PllSrc::None => {}
}
}
}
rcc.cr.modify(|_, w| {
w.hsebyp().bit(self.hse_bypass)
});
if let InputSrc::Pll1(pll_src) = self.input_src {
rcc.cr.modify(|_, w| w.pll1on().clear_bit());
while rcc.cr.read().pll1rdy().bit_is_set() {}
rcc.pllckselr.modify(|_, w| {
w.pllsrc().bits(pll_src.bits());
w.divm1().bits(self.divm1)
});
rcc.d1cfgr
.modify(|_, w| unsafe { w.hpre().bits(self.hclk_prescaler as u8) });
rcc.pll1divr.modify(|_, w| unsafe {
w.divn1().bits(self.divn1);
w.divp1().bits(self.divp1)
});
rcc.cr.modify(|_, w| w.pll1on().set_bit());
while rcc.cr.read().pll1rdy().bit_is_clear() {}
}
rcc.cfgr
.modify(|_, w| unsafe { w.sw().bits(self.input_src.bits()) });
rcc.d1cfgr.modify(|_, w| unsafe {
w.d1cpre().bits(self.d1_core_prescaler as u8);
w.d1ppre().bits(self.d1_prescaler as u8);
w.hpre().bits(self.hclk_prescaler as u8)
});
rcc.d2cfgr.modify(|_, w| unsafe {
w.d2ppre1().bits(self.d2_prescaler1 as u8);
w.d2ppre2().bits(self.d2_prescaler2 as u8)
});
rcc.d3cfgr
.modify(|_, w| unsafe { w.d3ppre().bits(self.d3_prescaler as u8) });
rcc.cr.modify(|_, w| w.hsecsson().bit(self.security_system));
if self.hsi48_on {
rcc.cr.modify(|_, w| w.hsi48on().set_bit());
while rcc.cr.read().hsi48rdy().bit_is_clear() {}
}
rcc.apb4enr.modify(|_, w| w.syscfgen().set_bit());
rcc.apb4rstr.modify(|_, w| w.syscfgrst().set_bit());
rcc.apb4rstr.modify(|_, w| w.syscfgrst().clear_bit());
Ok(())
}
pub fn reselect_input(&self, rcc: &mut RCC) {
match self.input_src {
InputSrc::Hse(_) => {
rcc.cr.modify(|_, w| w.hseon().set_bit());
while rcc.cr.read().hserdy().bit_is_clear() {}
rcc.cfgr
.modify(|_, w| unsafe { w.sw().bits(self.input_src.bits()) });
}
InputSrc::Pll1(pll_src) => {
match pll_src {
PllSrc::Hse(_) => {
rcc.cr.modify(|_, w| w.hseon().set_bit());
while rcc.cr.read().hserdy().bit_is_clear() {}
}
PllSrc::Hsi(div) => {
rcc.cr.modify(|_, w| {
w.hsidiv().bits(div as u8); w.hsion().bit(true)
});
while rcc.cr.read().hsirdy().bit_is_clear() {}
}
PllSrc::Csi => (), PllSrc::None => (),
}
rcc.cr.modify(|_, w| w.pll1on().clear_bit());
while rcc.cr.read().pll1rdy().bit_is_set() {}
rcc.cfgr
.modify(|_, w| unsafe { w.sw().bits(self.input_src.bits()) });
rcc.cr.modify(|_, w| w.pll1on().set_bit());
while rcc.cr.read().pll1rdy().bit_is_clear() {}
}
InputSrc::Hsi(div) => {
{
rcc.cr.modify(|_, w| {
w.hsidiv().bits(div as u8); w.hsion().bit(true)
});
while rcc.cr.read().hsirdy().bit_is_clear() {}
}
}
InputSrc::Csi => (), }
}
fn calc_sysclock(&self) -> (u32, u32) {
let input_freq;
let sysclk = match self.input_src {
InputSrc::Pll1(pll_src) => {
input_freq = match pll_src {
PllSrc::Csi => 4_000_000,
PllSrc::Hsi(div) => 64_000_000 / (div.value() as u32),
PllSrc::Hse(freq) => freq,
PllSrc::None => 0, };
input_freq / self.divm1 as u32 * self.divn1 as u32 / self.divp1 as u32
}
InputSrc::Csi => {
input_freq = 4_000_000;
input_freq
}
InputSrc::Hsi(div) => {
input_freq = 64_000_000 / (div.value() as u32);
input_freq
}
InputSrc::Hse(freq) => {
input_freq = freq;
input_freq
}
};
(input_freq, sysclk)
}
pub fn pll_is_enabled(&self, rcc: &mut RCC) -> bool {
rcc.cr.read().pll1on().bit_is_set()
}
pub fn sysclk(&self) -> u32 {
let (_, sysclk) = self.calc_sysclock();
sysclk
}
pub fn hclk(&self) -> u32 {
self.sysclk() / self.d1_core_prescaler as u32 / self.hclk_prescaler as u32
}
pub fn systick(&self) -> u32 {
self.hclk()
}
pub fn usb(&self) -> u32 {
0 }
pub fn apb1(&self) -> u32 {
self.hclk() / self.d2_prescaler1.value() as u32
}
pub fn apb1_timer(&self) -> u32 {
if let ApbPrescaler::Div1 = self.d2_prescaler1 {
self.apb1()
} else {
self.apb1() * 2
}
}
pub fn apb2(&self) -> u32 {
self.hclk() / self.d2_prescaler2.value() as u32
}
pub fn apb2_timer(&self) -> u32 {
if let ApbPrescaler::Div1 = self.d2_prescaler2 {
self.apb2()
} else {
self.apb2() * 2
}
}
pub fn validate_speeds(&self) -> ClocksValid {
let mut result = ClocksValid::Valid;
let max_clock = 550_000_000;
if self.divm1 > 63 || self.divn1 > 512 || self.divp1 > 128 {
return ClocksValid::NotValid;
}
if self.sysclk() > max_clock {
result = ClocksValid::NotValid;
}
if self.hclk() > max_clock {
result = ClocksValid::NotValid;
}
if self.apb1() > max_clock {
result = ClocksValid::NotValid;
}
if self.apb2() > max_clock {
result = ClocksValid::NotValid;
}
result
}
}
impl Default for Clocks {
fn default() -> Self {
Self {
input_src: InputSrc::Pll1(PllSrc::Hsi(HsiDiv::Div1)),
divm1: 32,
divn1: 129,
divp1: 1,
d1_core_prescaler: HclkPrescaler::Div1,
d1_prescaler: ApbPrescaler::Div1,
hclk_prescaler: HclkPrescaler::Div1,
d2_prescaler1: ApbPrescaler::Div1,
d2_prescaler2: ApbPrescaler::Div1,
d3_prescaler: ApbPrescaler::Div1,
hse_bypass: false,
security_system: false,
hsi48_on: false,
}
}
}