use crate::fmc::Fmc;
use crate::pac;
use crate::time::Hertz;
const IRC8M: u32 = 8_000_000;
const IRC28M: u32 = 28_000_000;
const LXTAL: u32 = 32_768;
const PLL_SRC: u32 = IRC8M / 2;
pub(crate) const IRC40K: u32 = 40_000;
const IRC28MDIV_DIV1: bool = true;
const IRC28MDIV_DIV2: bool = false;
const ADCSEL_IRC28M: bool = false;
const ADCSEL_PRESCALED: bool = true;
const ADCPSC_MSB_APB2: bool = false;
const ADCPSC_MSB_AHB: bool = true;
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
pub enum PllFreq {
Mhz8 = 8_000_000,
Mhz12 = 12_000_000,
Mhz16 = 16_000_000,
Mhz20 = 20_000_000,
Mhz24 = 24_000_000,
Mhz28 = 28_000_000,
Mhz32 = 32_000_000,
Mhz36 = 36_000_000,
Mhz40 = 40_000_000,
Mhz44 = 44_000_000,
Mhz48 = 48_000_000,
Mhz52 = 52_000_000,
Mhz56 = 56_000_000,
Mhz60 = 60_000_000,
Mhz64 = 64_000_000,
Mhz68 = 68_000_000,
Mhz72 = 72_000_000,
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SysClk {
Irc8m,
Pll(PllFreq),
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
pub enum AhbPsc {
Div1 = 1,
Div2 = 2,
Div4 = 4,
Div8 = 8,
Div16 = 16,
Div64 = 64,
Div128 = 128,
Div256 = 256,
Div512 = 512,
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
pub enum ApbPsc {
Div1 = 1,
Div2 = 2,
Div4 = 4,
Div8 = 8,
Div16 = 16,
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
pub enum AdcPsc {
Apb2Div2,
Apb2Div4,
Apb2Div6,
Apb2Div8,
AhbDiv3,
AhbDiv5,
AhbDiv7,
AhbDiv9,
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
pub enum Irc28mDiv {
Div1,
Div2,
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum AdcSel {
Off,
Irc28m(Irc28mDiv),
Prescaled(AdcPsc),
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Usart0Sel {
Apb2,
Sysclk,
Lxtal,
Irc8m,
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Clocks {
hclk: Hertz,
pclk1: Hertz,
pclk2: Hertz,
sysclk: Hertz,
usart0: Hertz,
ck_adc: Hertz,
pclk1_tim: Hertz,
pclk2_tim: Hertz,
}
impl Clocks {
#[inline]
pub fn hclk(&self) -> Hertz {
self.hclk
}
#[inline]
pub fn pclk1(&self) -> Hertz {
self.pclk1
}
#[inline]
pub fn pclk2(&self) -> Hertz {
self.pclk2
}
#[inline]
pub fn sysclk(&self) -> Hertz {
self.sysclk
}
#[inline]
pub fn usart0(&self) -> Hertz {
self.usart0
}
#[inline]
pub fn ck_adc(&self) -> Hertz {
self.ck_adc
}
#[inline]
pub fn pclk1_tim(&self) -> Hertz {
self.pclk1_tim
}
#[inline]
pub fn pclk2_tim(&self) -> Hertz {
self.pclk2_tim
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[must_use]
pub struct ClockConfig {
hclk: AhbPsc,
pclk1: ApbPsc,
pclk2: ApbPsc,
sysclk: SysClk,
usart0_sel: Usart0Sel,
adc_sel: AdcSel,
}
impl Default for ClockConfig {
fn default() -> Self {
Self {
hclk: AhbPsc::Div1,
pclk1: ApbPsc::Div1,
pclk2: ApbPsc::Div1,
sysclk: SysClk::Irc8m,
usart0_sel: Usart0Sel::Apb2,
adc_sel: AdcSel::Off,
}
}
}
impl ClockConfig {
const fn pll_mul(desired: PllFreq) -> u32 {
(desired as u32) / PLL_SRC
}
pub const fn hclk(mut self, psc: AhbPsc) -> Self {
self.hclk = psc;
self
}
pub const fn pclk1(mut self, psc: ApbPsc) -> Self {
self.pclk1 = psc;
self
}
pub const fn pclk2(mut self, psc: ApbPsc) -> Self {
self.pclk2 = psc;
self
}
pub const fn sysclk(mut self, src: SysClk) -> Self {
self.sysclk = src;
self
}
pub const fn usart0_sel(mut self, src: Usart0Sel) -> Self {
self.usart0_sel = src;
self
}
pub const fn adc_sel(mut self, sel: AdcSel) -> Self {
self.adc_sel = sel;
self
}
}
pub struct UnfrozenRcu {
rcu: pac::Rcu,
}
impl UnfrozenRcu {
#[allow(clippy::too_many_lines)]
pub fn freeze(self, fmc: &mut Fmc, config: ClockConfig) -> Rcu {
let sysclk = match config.sysclk {
SysClk::Irc8m => IRC8M,
SysClk::Pll(desired) => {
let mul = ClockConfig::pll_mul(desired);
self.rcu.cfg0().modify(|_, w| {
let w = w.pllsel().irc8m_2();
match mul {
2 => w.pllmf().mul2().pllmf_msb().none(),
3 => w.pllmf().mul3().pllmf_msb().none(),
4 => w.pllmf().mul4().pllmf_msb().none(),
5 => w.pllmf().mul5().pllmf_msb().none(),
6 => w.pllmf().mul6().pllmf_msb().none(),
7 => w.pllmf().mul7().pllmf_msb().none(),
8 => w.pllmf().mul8().pllmf_msb().none(),
9 => w.pllmf().mul9().pllmf_msb().none(),
10 => w.pllmf().mul10().pllmf_msb().none(),
11 => w.pllmf().mul11().pllmf_msb().none(),
12 => w.pllmf().mul12().pllmf_msb().none(),
13 => w.pllmf().mul13().pllmf_msb().none(),
14 => w.pllmf().mul14().pllmf_msb().none(),
15 => w.pllmf().mul15().pllmf_msb().none(),
16 => w.pllmf().mul16().pllmf_msb().none(),
17 => w.pllmf().mul2().pllmf_msb().plus15(),
18 => w.pllmf().mul3().pllmf_msb().plus15(),
_ => unreachable!(),
}
});
self.rcu.ctl0().modify(|_, w| w.pllen().on());
while self.rcu.ctl0().read().pllstb().is_not_ready() {}
desired as u32
}
};
let ahb_psc = config.hclk;
let hclk = sysclk / (ahb_psc as u32);
let apb1_psc = config.pclk1;
let pclk1 = hclk / (apb1_psc as u32);
let apb2_psc = config.pclk2;
let pclk2 = hclk / (apb2_psc as u32);
let usart0_sel = config.usart0_sel;
let usart0 = match usart0_sel {
Usart0Sel::Apb2 => pclk2,
Usart0Sel::Sysclk => sysclk,
Usart0Sel::Lxtal => LXTAL,
Usart0Sel::Irc8m => IRC8M,
};
self.rcu.cfg2().modify(|_, w| match usart0_sel {
Usart0Sel::Apb2 => w.usart0sel().apb2(),
Usart0Sel::Sysclk => w.usart0sel().sys(),
Usart0Sel::Lxtal => w.usart0sel().lxtal(),
Usart0Sel::Irc8m => w.usart0sel().irc8m(),
});
let ck_adc = match config.adc_sel {
AdcSel::Off => 0,
AdcSel::Irc28m(div) => {
self.rcu.ctl1().modify(|_, w| w.irc28men().on());
while self.rcu.ctl1().read().irc28mstb().is_not_ready() {}
self.rcu.cfg2().modify(|_, w| {
let w = match div {
Irc28mDiv::Div1 => w.irc28mdiv().bit(IRC28MDIV_DIV1),
Irc28mDiv::Div2 => w.irc28mdiv().bit(IRC28MDIV_DIV2),
};
w.adcsel().bit(ADCSEL_IRC28M)
});
match div {
Irc28mDiv::Div1 => IRC28M,
Irc28mDiv::Div2 => IRC28M / 2,
}
}
AdcSel::Prescaled(psc) => {
self.rcu.cfg0().modify(|_, w| match psc {
AdcPsc::Apb2Div2 | AdcPsc::AhbDiv3 => w.adcpsc().div2(),
AdcPsc::Apb2Div4 | AdcPsc::AhbDiv5 => w.adcpsc().div4(),
AdcPsc::Apb2Div6 | AdcPsc::AhbDiv7 => w.adcpsc().div6(),
AdcPsc::Apb2Div8 | AdcPsc::AhbDiv9 => w.adcpsc().div8(),
});
self.rcu.cfg2().modify(|_, w| {
let w = match psc {
AdcPsc::Apb2Div2
| AdcPsc::Apb2Div4
| AdcPsc::Apb2Div6
| AdcPsc::Apb2Div8 => w.adcpsc().bit(ADCPSC_MSB_APB2),
AdcPsc::AhbDiv3 | AdcPsc::AhbDiv5 | AdcPsc::AhbDiv7 | AdcPsc::AhbDiv9 => {
w.adcpsc().bit(ADCPSC_MSB_AHB)
}
};
w.adcsel().bit(ADCSEL_PRESCALED)
});
match psc {
AdcPsc::Apb2Div2 => pclk2 / 2,
AdcPsc::Apb2Div4 => pclk2 / 4,
AdcPsc::Apb2Div6 => pclk2 / 6,
AdcPsc::Apb2Div8 => pclk2 / 8,
AdcPsc::AhbDiv3 => hclk / 3,
AdcPsc::AhbDiv5 => hclk / 5,
AdcPsc::AhbDiv7 => hclk / 7,
AdcPsc::AhbDiv9 => hclk / 9,
}
}
};
fmc.set_ws(hclk);
self.rcu.cfg0().modify(|_, w| {
let w = match ahb_psc {
AhbPsc::Div1 => w.ahbpsc().div1(),
AhbPsc::Div2 => w.ahbpsc().div2(),
AhbPsc::Div4 => w.ahbpsc().div4(),
AhbPsc::Div8 => w.ahbpsc().div8(),
AhbPsc::Div16 => w.ahbpsc().div16(),
AhbPsc::Div64 => w.ahbpsc().div64(),
AhbPsc::Div128 => w.ahbpsc().div128(),
AhbPsc::Div256 => w.ahbpsc().div256(),
AhbPsc::Div512 => w.ahbpsc().div512(),
};
let w = match apb1_psc {
ApbPsc::Div1 => w.apb1psc().div1(),
ApbPsc::Div2 => w.apb1psc().div2(),
ApbPsc::Div4 => w.apb1psc().div4(),
ApbPsc::Div8 => w.apb1psc().div8(),
ApbPsc::Div16 => w.apb1psc().div16(),
};
let w = match apb2_psc {
ApbPsc::Div1 => w.apb2psc().div1(),
ApbPsc::Div2 => w.apb2psc().div2(),
ApbPsc::Div4 => w.apb2psc().div4(),
ApbPsc::Div8 => w.apb2psc().div8(),
ApbPsc::Div16 => w.apb2psc().div16(),
};
match config.sysclk {
SysClk::Pll(_) => w.scs().pll(),
SysClk::Irc8m => w,
}
});
let clocks = Clocks {
hclk: Hertz::from_raw(hclk),
pclk1: Hertz::from_raw(pclk1),
pclk2: Hertz::from_raw(pclk2),
sysclk: Hertz::from_raw(sysclk),
usart0: Hertz::from_raw(usart0),
ck_adc: Hertz::from_raw(ck_adc),
pclk1_tim: Hertz::from_raw(match apb1_psc {
ApbPsc::Div1 => hclk, _ => hclk / (apb1_psc as u32 / 2), }),
pclk2_tim: Hertz::from_raw(match apb2_psc {
ApbPsc::Div1 => hclk, _ => hclk / (apb2_psc as u32 / 2), }),
};
Rcu {
rcu: self.rcu,
clocks,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
pub enum PllDiv {
Div1,
Div2,
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CkOutSrc {
None,
Irc14m,
Lsi40k,
Lxtal,
Sysclk,
Irc8m,
Hxtal,
Pll(PllDiv),
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
pub enum CkOutDiv {
Div1,
Div2,
Div4,
Div8,
Div16,
Div32,
Div64,
Div128,
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ResetFlag {
LowPower,
WindowWatchdog,
FreeWatchdog,
Software,
Power,
ExternalPin,
OptionByteLoader,
}
pub struct Rcu {
rcu: pac::Rcu,
clocks: Clocks,
}
impl Rcu {
pub fn clocks(&self) -> Clocks {
self.clocks
}
pub fn ck_out(&mut self, src: CkOutSrc, div: CkOutDiv) {
self.rcu.cfg0().modify(|_, w| {
let w = match div {
CkOutDiv::Div1 => w.ckoutdiv().div1(),
CkOutDiv::Div2 => w.ckoutdiv().div2(),
CkOutDiv::Div4 => w.ckoutdiv().div4(),
CkOutDiv::Div8 => w.ckoutdiv().div8(),
CkOutDiv::Div16 => w.ckoutdiv().div16(),
CkOutDiv::Div32 => w.ckoutdiv().div32(),
CkOutDiv::Div64 => w.ckoutdiv().div64(),
CkOutDiv::Div128 => w.ckoutdiv().div128(),
};
match src {
CkOutSrc::None => w.ckoutsel().none(),
CkOutSrc::Irc14m => w.ckoutsel().irc14m(),
CkOutSrc::Lsi40k => w.ckoutsel().lsi40k(),
CkOutSrc::Lxtal => w.ckoutsel().lxtal(),
CkOutSrc::Sysclk => w.ckoutsel().sysclk(),
CkOutSrc::Irc8m => w.ckoutsel().irc8m(),
CkOutSrc::Hxtal => w.ckoutsel().hxtal(),
CkOutSrc::Pll(d) => {
let w = w.ckoutsel().pll();
match d {
PllDiv::Div1 => w.plldv().div1(),
PllDiv::Div2 => w.plldv().div2(),
}
}
}
});
}
pub fn enable_irc40k(&mut self) {
self.rcu.rstsck().modify(|_, w| w.irc40ken().on());
while self.rcu.rstsck().read().irc40kstb().is_not_ready() {}
}
pub fn disable_irc40k(&mut self) {
self.rcu.rstsck().modify(|_, w| w.irc40ken().off());
}
pub fn enable_cfgcmp(&mut self) {
self.rcu.apb2en().modify(|_, w| w.cfgcmpen().enabled());
}
pub fn disable_cfgcmp(&mut self) {
self.rcu.apb2en().modify(|_, w| w.cfgcmpen().disabled());
}
pub fn reset_cfgcmp(&mut self) {
self.rcu.apb2rst().modify(|_, w| w.cfgcmprst().reset());
self.rcu.apb2rst().modify(|_, w| w.cfgcmprst().clear_bit());
}
pub fn reset_flag(&self, flag: ResetFlag) -> bool {
let rstsck = self.rcu.rstsck().read();
match flag {
ResetFlag::LowPower => rstsck.lprstf().is_reset(),
ResetFlag::WindowWatchdog => rstsck.wwdgtrstf().is_reset(),
ResetFlag::FreeWatchdog => rstsck.fwdgtrstf().is_reset(),
ResetFlag::Software => rstsck.swrstf().is_reset(),
ResetFlag::Power => rstsck.porrstf().is_reset(),
ResetFlag::ExternalPin => rstsck.eprstf().is_reset(),
ResetFlag::OptionByteLoader => rstsck.oblrstf().is_reset(),
}
}
pub fn clear_reset_flags(&mut self) {
self.rcu.rstsck().modify(|_, w| w.rstfc().clear());
}
}
pub trait RcuExt {
fn constrain(self) -> UnfrozenRcu;
}
impl RcuExt for pac::Rcu {
fn constrain(self) -> UnfrozenRcu {
UnfrozenRcu { rcu: self }
}
}
pub trait Enable {
fn enable(rcu: &mut Rcu);
fn disable(rcu: &mut Rcu);
}
pub trait Reset {
fn reset(rcu: &mut Rcu);
}
macro_rules! bus_en {
($($Periph:ty => $en_reg:ident, $en_bit:ident,)+) => {
$(
impl Enable for $Periph {
fn enable(rcu: &mut Rcu) {
rcu.rcu.$en_reg().modify(|_, w| w.$en_bit().enabled());
}
fn disable(rcu: &mut Rcu) {
rcu.rcu.$en_reg().modify(|_, w| w.$en_bit().disabled());
}
}
)+
};
}
macro_rules! bus_rst {
($($Periph:ty => $rst_reg:ident, $rst_bit:ident,)+) => {
$(
impl Reset for $Periph {
fn reset(rcu: &mut Rcu) {
rcu.rcu.$rst_reg().modify(|_, w| w.$rst_bit().reset());
rcu.rcu.$rst_reg().modify(|_, w| w.$rst_bit().clear_bit());
}
}
)+
};
}
bus_en! {
pac::Gpioa => ahben, paen,
pac::Gpiob => ahben, pben,
pac::Gpiof => ahben, pfen,
pac::Dma => ahben, dmaen,
pac::Crc => ahben, crcen,
pac::Usart1 => apb1en, usart1en,
pac::Spi1 => apb1en, spi1en,
pac::Timer2 => apb1en, timer2en,
pac::Timer5 => apb1en, timer5en,
pac::Timer13 => apb1en, timer13en,
pac::I2c0 => apb1en, i2c0en,
pac::I2c1 => apb1en, i2c1en,
pac::Wwdgt => apb1en, wwdgten,
pac::Usart0 => apb2en, usart0en,
pac::Adc => apb2en, adcen,
pac::Spi0 => apb2en, spi0en,
pac::Timer0 => apb2en, timer0en,
pac::Timer15 => apb2en, timer15en,
pac::Timer16 => apb2en, timer16en,
}
#[cfg(has_timer14)]
bus_en! {
pac::Timer14 => apb2en, timer14en,
}
#[cfg(pads_ge_48)]
bus_en! {
pac::Gpioc => ahben, pcen,
}
bus_rst! {
pac::Gpioa => ahbrst, parst,
pac::Gpiob => ahbrst, pbrst,
pac::Gpiof => ahbrst, pfrst,
pac::Usart1 => apb1rst, usart1rst,
pac::Spi1 => apb1rst, spi1rst,
pac::Timer2 => apb1rst, timer2rst,
pac::Timer5 => apb1rst, timer5rst,
pac::Timer13 => apb1rst, timer13rst,
pac::I2c0 => apb1rst, i2c0rst,
pac::I2c1 => apb1rst, i2c1rst,
pac::Wwdgt => apb1rst, wwdgtrst,
pac::Usart0 => apb2rst, usart0rst,
pac::Adc => apb2rst, adcrst,
pac::Spi0 => apb2rst, spi0rst,
pac::Timer0 => apb2rst, timer0rst,
pac::Timer15 => apb2rst, timer15rst,
pac::Timer16 => apb2rst, timer16rst,
}
#[cfg(has_timer14)]
bus_rst! {
pac::Timer14 => apb2rst, timer14rst,
}
#[cfg(pads_ge_48)]
bus_rst! {
pac::Gpioc => ahbrst, pcrst,
}