Merge remote-tracking branch 'origin/master' into feature-i2c-slave

# Conflicts:
#	examples/stm32g0/Cargo.toml
This commit is contained in:
anton smeenk
2023-10-27 14:58:43 +02:00
350 changed files with 4132 additions and 3717 deletions

View File

@ -33,11 +33,11 @@ flavors = [
[dependencies]
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
embassy-time = { version = "0.1.4", path = "../embassy-time", optional = true }
embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] }
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true }
embassy-executor = { version = "0.3.0", path = "../embassy-executor", optional = true }
@ -58,7 +58,7 @@ rand_core = "0.6.3"
sdio-host = "0.5.0"
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
critical-section = "1.1"
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e4a769aa67aa82603448377daa579d67a789983a" }
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-bcc9b6bf9fa195e91625849efc4ba473d9ace4e9" }
vcell = "0.1.3"
bxcan = "0.7.0"
nb = "1.0.0"
@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] }
[build-dependencies]
proc-macro2 = "1.0.36"
quote = "1.0.15"
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e4a769aa67aa82603448377daa579d67a789983a", default-features = false, features = ["metadata"]}
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-bcc9b6bf9fa195e91625849efc4ba473d9ace4e9", default-features = false, features = ["metadata"]}
[features]

View File

@ -1,4 +1,4 @@
use std::collections::{HashMap, HashSet};
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::fmt::Write as _;
use std::path::PathBuf;
use std::{env, fs};
@ -352,7 +352,7 @@ fn main() {
// ========
// Generate DMA IRQs.
let mut dma_irqs: HashMap<&str, Vec<(&str, &str, &str)>> = HashMap::new();
let mut dma_irqs: BTreeMap<&str, Vec<(&str, &str, &str)>> = BTreeMap::new();
for p in METADATA.peripherals {
if let Some(r) = &p.registers {
@ -371,37 +371,43 @@ fn main() {
}
}
for (irq, channels) in dma_irqs {
let irq = format_ident!("{}", irq);
let dma_irqs: TokenStream = dma_irqs
.iter()
.map(|(irq, channels)| {
let irq = format_ident!("{}", irq);
let xdma = format_ident!("{}", channels[0].0);
let channels = channels.iter().map(|(_, dma, ch)| format_ident!("{}_{}", dma, ch));
let xdma = format_ident!("{}", channels[0].0);
let channels = channels.iter().map(|(_, dma, ch)| format_ident!("{}_{}", dma, ch));
g.extend(quote! {
#[cfg(feature = "rt")]
#[crate::interrupt]
unsafe fn #irq () {
#(
<crate::peripherals::#channels as crate::dma::#xdma::sealed::Channel>::on_irq();
)*
quote! {
#[cfg(feature = "rt")]
#[crate::interrupt]
unsafe fn #irq () {
#(
<crate::peripherals::#channels as crate::dma::#xdma::sealed::Channel>::on_irq();
)*
}
}
});
}
})
.collect();
g.extend(dma_irqs);
// ========
// Extract the rcc registers
let rcc_registers = METADATA
.peripherals
.iter()
.filter_map(|p| p.registers.as_ref())
.find(|r| r.kind == "rcc")
.unwrap();
// ========
// Generate rcc fieldset and enum maps
let rcc_enum_map: HashMap<&str, HashMap<&str, &Enum>> = {
let rcc_registers = METADATA
.peripherals
.iter()
.filter_map(|p| p.registers.as_ref())
.find(|r| r.kind == "rcc")
.unwrap()
.ir;
let rcc_blocks = rcc_registers.blocks.iter().find(|b| b.name == "Rcc").unwrap().items;
let rcc_fieldsets: HashMap<&str, &FieldSet> = rcc_registers.fieldsets.iter().map(|f| (f.name, f)).collect();
let rcc_enums: HashMap<&str, &Enum> = rcc_registers.enums.iter().map(|e| (e.name, e)).collect();
let rcc_blocks = rcc_registers.ir.blocks.iter().find(|b| b.name == "Rcc").unwrap().items;
let rcc_fieldsets: HashMap<&str, &FieldSet> = rcc_registers.ir.fieldsets.iter().map(|f| (f.name, f)).collect();
let rcc_enums: HashMap<&str, &Enum> = rcc_registers.ir.enums.iter().map(|e| (e.name, e)).collect();
rcc_blocks
.iter()
@ -432,7 +438,7 @@ fn main() {
// Generate RccPeripheral impls
let refcounted_peripherals = HashSet::from(["usart", "adc"]);
let mut refcount_statics = HashSet::new();
let mut refcount_statics = BTreeSet::new();
for p in METADATA.peripherals {
if !singletons.contains(&p.name.to_string()) {
@ -465,9 +471,9 @@ fn main() {
let ptype = if let Some(reg) = &p.registers { reg.kind } else { "" };
let pname = format_ident!("{}", p.name);
let clk = format_ident!("{}", rcc.clock.to_ascii_lowercase());
let en_reg = format_ident!("{}", en.register.to_ascii_lowercase());
let set_en_field = format_ident!("set_{}", en.field.to_ascii_lowercase());
let clk = format_ident!("{}", rcc.clock);
let en_reg = format_ident!("{}", en.register);
let set_en_field = format_ident!("set_{}", en.field);
let (before_enable, before_disable) = if refcounted_peripherals.contains(ptype) {
let refcount_static =
@ -493,9 +499,11 @@ fn main() {
(TokenStream::new(), TokenStream::new())
};
let mux_supported = HashSet::from(["c0", "h5", "h50", "h7", "h7ab", "h7rm0433", "g4", "l4"])
.contains(rcc_registers.version);
let mux_for = |mux: Option<&'static PeripheralRccRegister>| {
// temporary hack to restrict the scope of the implementation to h5
if !&chip_name.starts_with("stm32h5") {
// restrict mux implementation to supported versions
if !mux_supported {
return None;
}
@ -518,12 +526,16 @@ fn main() {
.filter(|v| v.name != "DISABLE")
.map(|v| {
let variant_name = format_ident!("{}", v.name);
let clock_name = format_ident!("{}", v.name.to_ascii_lowercase());
// temporary hack to restrict the scope of the implementation until clock names can be stabilized
let clock_name = format_ident!("mux_{}", v.name.to_ascii_lowercase());
quote! {
#enum_name::#variant_name => unsafe { crate::rcc::get_freqs().#clock_name.unwrap() },
if v.name.starts_with("HCLK") || v.name.starts_with("PCLK") || v.name == "SYS" {
quote! {
#enum_name::#variant_name => unsafe { crate::rcc::get_freqs().#clock_name },
}
} else {
quote! {
#enum_name::#variant_name => unsafe { crate::rcc::get_freqs().#clock_name.unwrap() },
}
}
})
.collect();
@ -552,7 +564,7 @@ fn main() {
fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) {
#before_enable
#[cfg(feature = "low-power")]
crate::rcc::clock_refcount_add(_cs);
unsafe { crate::rcc::REFCOUNT_STOP2 += 1 };
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true));
#after_enable
#rst
@ -561,7 +573,7 @@ fn main() {
#before_disable
crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false));
#[cfg(feature = "low-power")]
crate::rcc::clock_refcount_sub(_cs);
unsafe { crate::rcc::REFCOUNT_STOP2 -= 1 };
}
}
@ -1007,15 +1019,7 @@ fn main() {
// ========
// Generate Div/Mul impls for RCC prescalers/dividers/multipliers.
let rcc_registers = METADATA
.peripherals
.iter()
.filter_map(|p| p.registers.as_ref())
.find(|r| r.kind == "rcc")
.unwrap()
.ir;
for e in rcc_registers.enums {
for e in rcc_registers.ir.enums {
fn is_rcc_name(e: &str) -> bool {
match e {
"Pllp" | "Pllq" | "Pllr" | "Pllm" | "Plln" => true,

View File

@ -564,7 +564,7 @@ foreach_peripheral!(
#[cfg(any(rcc_h7, rcc_h7rm0433))]
impl crate::rcc::sealed::RccPeripheral for peripherals::$inst {
fn frequency() -> crate::time::Hertz {
critical_section::with(|_| unsafe { crate::rcc::get_freqs().apb1 })
critical_section::with(|_| unsafe { crate::rcc::get_freqs().pclk1 })
}
fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) {

View File

@ -191,7 +191,7 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
// TODO MTU size setting not found for v1 ethernet, check if correct
// NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called
let hclk = unsafe { crate::rcc::get_freqs() }.ahb1;
let hclk = unsafe { crate::rcc::get_freqs() }.hclk1;
let hclk_mhz = hclk.0 / 1_000_000;
// Set the MDC clock frequency in the range 1MHz - 2.5MHz

View File

@ -164,7 +164,7 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
});
// NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called
let hclk = unsafe { crate::rcc::get_freqs() }.ahb1;
let hclk = unsafe { crate::rcc::get_freqs() }.hclk1;
let hclk_mhz = hclk.0 / 1_000_000;
// Set the MDC clock frequency in the range 1MHz - 2.5MHz

View File

@ -465,7 +465,7 @@ pub(crate) fn assert_not_corrupted_read(end_address: u32) {
feature = "stm32f439vg",
feature = "stm32f439zg",
))]
if second_bank_read && unsafe { pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() } {
if second_bank_read && pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() {
panic!("Read corruption for stm32f42xxG and stm32f43xxG in dual bank mode when PA12 is in use for chips below revision 3, see errata 2.2.11");
}
}

View File

@ -763,6 +763,13 @@ pub(crate) unsafe fn init(_cs: CriticalSection) {
<crate::peripherals::AFIO as crate::rcc::sealed::RccPeripheral>::enable_and_reset_with_cs(_cs);
crate::_generated::init_gpio();
// Setting this bit is mandatory to use PG[15:2].
#[cfg(stm32u5)]
crate::pac::PWR.svmcr().modify(|w| {
w.set_io2sv(true);
w.set_io2vmen(true);
});
}
mod eh02 {

View File

@ -170,7 +170,7 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> {
let spi = Spi::new_internal(peri, txdma, rxdma, spi_cfg);
#[cfg(all(rcc_f4, not(stm32f410)))]
let pclk = unsafe { get_freqs() }.plli2s.unwrap();
let pclk = unsafe { get_freqs() }.plli2s1_q.unwrap();
#[cfg(stm32f410)]
let pclk = T::frequency();

View File

@ -5,6 +5,7 @@ use core::task::Poll;
use self::sealed::Instance;
use crate::interrupt;
use crate::interrupt::typelevel::Interrupt;
use crate::pac::rcc::vals::{Lptim1sel, Lptim2sel};
use crate::peripherals::IPCC;
use crate::rcc::sealed::RccPeripheral;
@ -273,7 +274,7 @@ fn _configure_pwr() {
// set LPTIM1 & LPTIM2 clock source
rcc.ccipr().modify(|w| {
w.set_lptim1sel(0b00); // PCLK
w.set_lptim2sel(0b00); // PCLK
w.set_lptim1sel(Lptim1sel::PCLK1);
w.set_lptim2sel(Lptim2sel::PCLK1);
});
}

View File

@ -226,9 +226,9 @@ pub fn init(config: Config) -> Peripherals {
time_driver::init(cs);
#[cfg(feature = "low-power")]
while !crate::rcc::low_power_ready() {
crate::rcc::clock_refcount_sub(cs);
}
{
crate::rcc::REFCOUNT_STOP2 = 0
};
}
p

View File

@ -6,7 +6,6 @@ use cortex_m::peripheral::SCB;
use embassy_executor::*;
use crate::interrupt;
use crate::rcc::low_power_ready;
use crate::time_driver::{get_driver, RtcDriver};
const THREAD_PENDER: usize = usize::MAX;
@ -33,6 +32,15 @@ pub fn stop_with_rtc(rtc: &'static Rtc) {
unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc)
}
pub fn stop_ready(stop_mode: StopMode) -> bool {
unsafe { EXECUTOR.as_mut().unwrap() }.stop_ready(stop_mode)
}
#[non_exhaustive]
pub enum StopMode {
Stop2,
}
/// Thread mode executor, using WFE/SEV.
///
/// This is the simplest and most common kind of executor. It runs on
@ -80,12 +88,18 @@ impl Executor {
trace!("low power: stop with rtc configured");
}
fn stop_ready(&self, stop_mode: StopMode) -> bool {
match stop_mode {
StopMode::Stop2 => unsafe { crate::rcc::REFCOUNT_STOP2 == 0 },
}
}
fn configure_pwr(&mut self) {
self.scb.clear_sleepdeep();
compiler_fence(Ordering::SeqCst);
if !low_power_ready() {
if !self.stop_ready(StopMode::Stop2) {
trace!("low power: not ready to stop");
} else if self.time_driver.pause_time().is_err() {
trace!("low power: failed to pause time");

View File

@ -101,18 +101,22 @@ pub trait InvertingPin<T: Instance>: sealed::InvertingPin<T> {}
#[cfg(opamp_f3)]
macro_rules! impl_opamp_output {
($inst:ident, $adc:ident, $ch:expr) => {
impl<'d, 'p, P: NonInvertingPin<crate::peripherals::$inst>> crate::adc::sealed::AdcPin<crate::peripherals::$adc>
for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P>
{
fn channel(&self) -> u8 {
$ch
}
}
foreach_adc!(
($adc, $common_inst:ident, $adc_clock:ident) => {
impl<'d, 'p, P: NonInvertingPin<crate::peripherals::$inst>> crate::adc::sealed::AdcPin<crate::peripherals::$adc>
for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P>
{
fn channel(&self) -> u8 {
$ch
}
}
impl<'d, 'p, P: NonInvertingPin<crate::peripherals::$inst>> crate::adc::AdcPin<crate::peripherals::$adc>
for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P>
{
}
impl<'d, 'p, P: NonInvertingPin<crate::peripherals::$inst>> crate::adc::AdcPin<crate::peripherals::$adc>
for OpAmpOutput<'d, 'p, crate::peripherals::$inst, P>
{
}
};
);
};
}

View File

@ -106,7 +106,7 @@ impl LsConfig {
pub const fn off() -> Self {
Self {
rtc: RtcClockSource::NOCLOCK,
rtc: RtcClockSource::DISABLE,
lsi: false,
lse: None,
}
@ -133,7 +133,7 @@ impl LsConfig {
Some(LSI_FREQ)
}
RtcClockSource::LSE => Some(self.lse.as_ref().unwrap().frequency),
RtcClockSource::NOCLOCK => None,
RtcClockSource::DISABLE => None,
_ => todo!(),
};
@ -180,7 +180,7 @@ impl LsConfig {
ok &= reg.rtcsel() == self.rtc;
#[cfg(not(rcc_wba))]
{
ok &= reg.rtcen() == (self.rtc != RtcClockSource::NOCLOCK);
ok &= reg.rtcen() == (self.rtc != RtcClockSource::DISABLE);
}
ok &= reg.lseon() == lse_en;
ok &= reg.lsebyp() == lse_byp;
@ -225,7 +225,7 @@ impl LsConfig {
while !bdcr().read().lserdy() {}
}
if self.rtc != RtcClockSource::NOCLOCK {
if self.rtc != RtcClockSource::DISABLE {
bdcr().modify(|w| {
#[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb, rtc_v3, rtc_v3u5))]
assert!(!w.lsecsson(), "RTC is not compatible with LSE CSS, yet.");

View File

@ -134,10 +134,12 @@ pub(crate) unsafe fn init(config: Config) {
};
set_freqs(Clocks {
hsi: None,
lse: None,
sys: sys_clk,
ahb1: ahb_freq,
apb1: apb_freq,
apb1_tim: apb_tim_freq,
hclk1: ahb_freq,
pclk1: apb_freq,
pclk1_tim: apb_tim_freq,
rtc,
});
}

View File

@ -127,7 +127,7 @@ pub(crate) unsafe fn init(config: Config) {
}
if config.usb_pll {
RCC.cfgr3().modify(|w| w.set_usbsw(Usbsw::PLLCLK));
RCC.cfgr3().modify(|w| w.set_usbsw(Usbsw::PLL1_P));
}
// TODO: Option to use CRS (Clock Recovery)
@ -140,7 +140,7 @@ pub(crate) unsafe fn init(config: Config) {
RCC.cfgr().modify(|w| {
w.set_ppre(Ppre::from_bits(ppre_bits));
w.set_hpre(Hpre::from_bits(hpre_bits));
w.set_sw(Sw::PLL)
w.set_sw(Sw::PLL1_P)
});
} else {
RCC.cfgr().modify(|w| {
@ -162,11 +162,11 @@ pub(crate) unsafe fn init(config: Config) {
set_freqs(Clocks {
sys: Hertz(real_sysclk),
apb1: Hertz(pclk),
apb2: Hertz(pclk),
apb1_tim: Hertz(pclk * timer_mul),
apb2_tim: Hertz(pclk * timer_mul),
ahb1: Hertz(hclk),
pclk1: Hertz(pclk),
pclk2: Hertz(pclk),
pclk1_tim: Hertz(pclk * timer_mul),
pclk2_tim: Hertz(pclk * timer_mul),
hclk1: Hertz(hclk),
rtc,
});
}

View File

@ -102,7 +102,6 @@ pub(crate) unsafe fn init(config: Config) {
assert!(pclk2 <= 72_000_000);
// Only needed for stm32f103?
FLASH.acr().write(|w| {
w.set_latency(if real_sysclk <= 24_000_000 {
Latency::WS0
@ -111,6 +110,8 @@ pub(crate) unsafe fn init(config: Config) {
} else {
Latency::WS2
});
// the prefetch buffer is enabled by default, let's keep it enabled
w.set_prftbe(true);
});
// the USB clock is only valid if an external crystal is used, the PLL is enabled, and the
@ -168,7 +169,7 @@ pub(crate) unsafe fn init(config: Config) {
#[cfg(not(rcc_f100))]
w.set_usbpre(Usbpre::from_bits(usbpre as u8));
w.set_sw(if pllmul_bits.is_some() {
Sw::PLL
Sw::PLL1_P
} else if config.hse.is_some() {
Sw::HSE
} else {
@ -180,11 +181,11 @@ pub(crate) unsafe fn init(config: Config) {
set_freqs(Clocks {
sys: Hertz(real_sysclk),
apb1: Hertz(pclk1),
apb2: Hertz(pclk2),
apb1_tim: Hertz(pclk1 * timer_mul1),
apb2_tim: Hertz(pclk2 * timer_mul2),
ahb1: Hertz(hclk),
pclk1: Hertz(pclk1),
pclk2: Hertz(pclk2),
pclk1_tim: Hertz(pclk1 * timer_mul1),
pclk2_tim: Hertz(pclk2 * timer_mul2),
hclk1: Hertz(hclk),
adc: Some(Hertz(adcclk)),
rtc,
});

View File

@ -256,7 +256,7 @@ pub(crate) unsafe fn init(config: Config) {
ClockSrc::PLL => {
RCC.cr().modify(|w| w.set_pllon(true));
while !RCC.cr().read().pllrdy() {}
(pll_clocks.main_freq, Sw::PLL)
(pll_clocks.main_freq, Sw::PLL1_P)
}
};
// RM0033 Figure 9. Clock tree suggests max SYSCLK/HCLK is 168 MHz, but datasheet specifies PLL
@ -307,14 +307,14 @@ pub(crate) unsafe fn init(config: Config) {
set_freqs(Clocks {
sys: sys_clk,
ahb1: ahb_freq,
ahb2: ahb_freq,
ahb3: ahb_freq,
apb1: apb1_freq,
apb1_tim: apb1_tim_freq,
apb2: apb2_freq,
apb2_tim: apb2_tim_freq,
pll48: Some(pll_clocks.pll48_freq),
hclk1: ahb_freq,
hclk2: ahb_freq,
hclk3: ahb_freq,
pclk1: apb1_freq,
pclk1_tim: apb1_tim_freq,
pclk2: apb2_freq,
pclk2_tim: apb2_tim_freq,
pll1_q: Some(pll_clocks.pll48_freq),
rtc,
});
}

View File

@ -214,7 +214,7 @@ pub(crate) unsafe fn init(config: Config) {
// CFGR has been written before (PLL, PLL48, clock divider) don't overwrite these settings
RCC.cfgr().modify(|w| {
w.set_sw(match (pll_config, config.hse) {
(Some(_), _) => Sw::PLL,
(Some(_), _) => Sw::PLL1_P,
(None, Some(_)) => Sw::HSE,
(None, None) => Sw::HSI,
})
@ -271,7 +271,7 @@ pub(crate) unsafe fn init(config: Config) {
pll_config.unwrap();
assert!((pclk2 == sysclk) || (pclk2 * 2u32 == sysclk));
RCC.cfgr3().modify(|w| w.set_hrtim1sw(Timsw::PLL));
RCC.cfgr3().modify(|w| w.set_hrtim1sw(Timsw::PLL1_P));
Some(sysclk * 2u32)
}
@ -281,11 +281,11 @@ pub(crate) unsafe fn init(config: Config) {
set_freqs(Clocks {
sys: sysclk,
apb1: pclk1,
apb2: pclk2,
apb1_tim: pclk1 * timer_mul1,
apb2_tim: pclk2 * timer_mul2,
ahb1: hclk,
pclk1: pclk1,
pclk2: pclk2,
pclk1_tim: pclk1 * timer_mul1,
pclk2_tim: pclk2 * timer_mul2,
hclk1: hclk,
#[cfg(rcc_f3)]
adc: adc,
#[cfg(all(rcc_f3, adc3_common))]

View File

@ -1,396 +0,0 @@
use crate::pac::rcc::vals::{Hpre, Pllm, Plln, Pllq, Pllr, Ppre, Sw};
use crate::pac::{FLASH, PWR, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
/// HSI speed
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
/// Clocks configuration
#[non_exhaustive]
#[derive(Default)]
pub struct Config {
pub hse: Option<Hertz>,
pub bypass_hse: bool,
pub hclk: Option<Hertz>,
pub sys_ck: Option<Hertz>,
pub pclk1: Option<Hertz>,
pub pclk2: Option<Hertz>,
#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
pub plli2s: Option<Hertz>,
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
pub pllsai: Option<Hertz>,
pub pll48: bool,
pub ls: super::LsConfig,
}
#[cfg(stm32f410)]
fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> {
None
}
// Not currently implemented, but will be in the future
#[cfg(any(stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))]
fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> {
None
}
#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423)))]
fn calculate_sai_i2s_pll_values(vco_in: u32, max_div: u32, target: Option<u32>) -> Option<(u32, u32, u32)> {
let min_div = 2;
let target = match target {
Some(target) => target,
None => return None,
};
// We loop through the possible divider values to find the best configuration. Looping
// through all possible "N" values would result in more iterations.
let (n, outdiv, output, _error) = (min_div..=max_div)
.filter_map(|outdiv| {
let target_vco_out = match target.checked_mul(outdiv) {
Some(x) => x,
None => return None,
};
let n = (target_vco_out + (vco_in >> 1)) / vco_in;
let vco_out = vco_in * n;
if !(100_000_000..=432_000_000).contains(&vco_out) {
return None;
}
let output = vco_out / outdiv;
let error = (output as i32 - target as i32).unsigned_abs();
Some((n, outdiv, output, error))
})
.min_by_key(|(_, _, _, error)| *error)?;
Some((n, outdiv, output))
}
#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
fn setup_i2s_pll(vco_in: u32, plli2s: Option<u32>) -> Option<u32> {
let (n, outdiv, output) = calculate_sai_i2s_pll_values(vco_in, 7, plli2s)?;
RCC.plli2scfgr().modify(|w| {
w.set_plli2sn(n as u16);
w.set_plli2sr(outdiv as u8);
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
w.set_plli2sq(outdiv as u8); //set sai divider same as i2s
});
Some(output)
}
#[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479)))]
fn setup_sai_pll(_vco_in: u32, _pllsai: Option<u32>) -> Option<u32> {
None
}
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
fn setup_sai_pll(vco_in: u32, pllsai: Option<u32>) -> Option<u32> {
let (n, outdiv, output) = calculate_sai_i2s_pll_values(vco_in, 15, pllsai)?;
RCC.pllsaicfgr().modify(|w| {
w.set_pllsain(n as u16);
w.set_pllsaiq(outdiv as u8);
});
Some(output)
}
fn setup_pll(
pllsrcclk: u32,
use_hse: bool,
pllsysclk: Option<u32>,
plli2s: Option<u32>,
pllsai: Option<u32>,
pll48clk: bool,
) -> PllResults {
use crate::pac::rcc::vals::{Pllp, Pllsrc};
let sysclk = pllsysclk.unwrap_or(pllsrcclk);
if pllsysclk.is_none() && !pll48clk {
RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc::from_bits(use_hse as u8)));
return PllResults {
use_pll: false,
pllsysclk: None,
pll48clk: None,
plli2sclk: None,
pllsaiclk: None,
};
}
// Input divisor from PLL source clock, must result to frequency in
// the range from 1 to 2 MHz
let pllm_min = (pllsrcclk + 1_999_999) / 2_000_000;
let pllm_max = pllsrcclk / 1_000_000;
// Sysclk output divisor must be one of 2, 4, 6 or 8
let sysclk_div = core::cmp::min(8, (432_000_000 / sysclk) & !1);
let target_freq = if pll48clk { 48_000_000 } else { sysclk * sysclk_div };
// Find the lowest pllm value that minimize the difference between
// target frequency and the real vco_out frequency.
let pllm = unwrap!((pllm_min..=pllm_max).min_by_key(|pllm| {
let vco_in = pllsrcclk / pllm;
let plln = target_freq / vco_in;
target_freq - vco_in * plln
}));
let vco_in = pllsrcclk / pllm;
assert!((1_000_000..=2_000_000).contains(&vco_in));
// Main scaler, must result in >= 100MHz (>= 192MHz for F401)
// and <= 432MHz, min 50, max 432
let plln = if pll48clk {
// try the different valid pllq according to the valid
// main scaller values, and take the best
let pllq = unwrap!((4..=9).min_by_key(|pllq| {
let plln = 48_000_000 * pllq / vco_in;
let pll48_diff = 48_000_000 - vco_in * plln / pllq;
let sysclk_diff = (sysclk as i32 - (vco_in * plln / sysclk_div) as i32).abs();
(pll48_diff, sysclk_diff)
}));
48_000_000 * pllq / vco_in
} else {
sysclk * sysclk_div / vco_in
};
let pllp = (sysclk_div / 2) - 1;
let pllq = (vco_in * plln + 47_999_999) / 48_000_000;
let real_pll48clk = vco_in * plln / pllq;
RCC.pllcfgr().modify(|w| {
w.set_pllm(Pllm::from_bits(pllm as u8));
w.set_plln(Plln::from_bits(plln as u16));
w.set_pllp(Pllp::from_bits(pllp as u8));
w.set_pllq(Pllq::from_bits(pllq as u8));
w.set_pllsrc(Pllsrc::from_bits(use_hse as u8));
w.set_pllr(Pllr::from_bits(0));
});
let real_pllsysclk = vco_in * plln / sysclk_div;
PllResults {
use_pll: true,
pllsysclk: Some(real_pllsysclk),
pll48clk: if pll48clk { Some(real_pll48clk) } else { None },
plli2sclk: setup_i2s_pll(vco_in, plli2s),
pllsaiclk: setup_sai_pll(vco_in, pllsai),
}
}
fn flash_setup(sysclk: u32) {
use crate::pac::flash::vals::Latency;
// Be conservative with voltage ranges
const FLASH_LATENCY_STEP: u32 = 30_000_000;
critical_section::with(|_| {
FLASH
.acr()
.modify(|w| w.set_latency(Latency::from_bits(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
});
}
pub(crate) unsafe fn init(config: Config) {
let pllsrcclk = config.hse.map(|hse| hse.0).unwrap_or(HSI_FREQ.0);
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 },
#[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
config.plli2s.map(|i2s| i2s.0),
#[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))]
None,
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
config.pllsai.map(|sai| sai.0),
#[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479)))]
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(config.bypass_hse);
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() {}
}
#[cfg(not(stm32f410))]
if plls.plli2sclk.is_some() {
RCC.cr().modify(|w| w.set_plli2son(true));
while !RCC.cr().read().plli2srdy() {}
}
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
if plls.pllsaiclk.is_some() {
RCC.cr().modify(|w| w.set_pllsaion(true));
while !RCC.cr().read().pllsairdy() {}
}
RCC.cfgr().modify(|w| {
w.set_ppre2(Ppre::from_bits(ppre2_bits));
w.set_ppre1(Ppre::from_bits(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
})
});
let rtc = config.ls.init();
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),
#[cfg(not(stm32f410))]
plli2s: plls.plli2sclk.map(Hertz),
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
pllsai: plls.pllsaiclk.map(Hertz),
rtc,
});
}
struct PllResults {
use_pll: bool,
pllsysclk: Option<u32>,
pll48clk: Option<u32>,
#[allow(dead_code)]
plli2sclk: Option<u32>,
#[allow(dead_code)]
pllsaiclk: Option<u32>,
}
mod max {
#[cfg(stm32f401)]
pub(crate) const SYSCLK_MAX: u32 = 84_000_000;
#[cfg(any(stm32f405, stm32f407, stm32f415, stm32f417,))]
pub(crate) const SYSCLK_MAX: u32 = 168_000_000;
#[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))]
pub(crate) const SYSCLK_MAX: u32 = 100_000_000;
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479,))]
pub(crate) const SYSCLK_MAX: u32 = 180_000_000;
pub(crate) const HCLK_OVERDRIVE_FREQUENCY: u32 = 168_000_000;
pub(crate) const PCLK1_MAX: u32 = PCLK2_MAX / 2;
#[cfg(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))]
pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX;
#[cfg(not(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,)))]
pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX / 2;
pub(crate) const PLL_48_CLK: u32 = 48_000_000;
pub(crate) const PLL_48_TOLERANCE: u32 = 120_000;
}

View File

@ -0,0 +1,387 @@
pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp, Pllq, Pllr, Pllsrc as PllSource,
Ppre as APBPrescaler, Sw as Sysclk,
};
use crate::pac::{FLASH, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
// TODO: on some F4s, PLLM is shared between all PLLs. Enforce that.
// TODO: on some F4s, add support for plli2s_src
//
// plli2s plli2s_m plli2s_src pllsai pllsai_m
// f401 y shared
// f410
// f411 y individual
// f412 y individual y
// f4[12]3 y individual y
// f446 y individual y individual
// f4[67]9 y shared y shared
// f4[23][79] y shared y shared
// f4[01][57] y shared
/// HSI speed
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum HseMode {
/// crystal/ceramic oscillator (HSEBYP=0)
Oscillator,
/// external analog clock (low swing) (HSEBYP=1)
Bypass,
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Hse {
/// HSE frequency.
pub freq: Hertz,
/// HSE mode.
pub mode: HseMode,
}
#[derive(Clone, Copy)]
pub struct Pll {
/// PLL pre-divider (DIVM).
pub prediv: PllPreDiv,
/// PLL multiplication factor.
pub mul: PllMul,
/// PLL P division factor. If None, PLL P output is disabled.
pub divp: Option<Pllp>,
/// PLL Q division factor. If None, PLL Q output is disabled.
pub divq: Option<Pllq>,
/// PLL R division factor. If None, PLL R output is disabled.
pub divr: Option<Pllr>,
}
/// Configuration of the core clocks
#[non_exhaustive]
pub struct Config {
pub hsi: bool,
pub hse: Option<Hse>,
pub sys: Sysclk,
pub pll_src: PllSource,
pub pll: Option<Pll>,
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
pub plli2s: Option<Pll>,
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
pub pllsai: Option<Pll>,
pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler,
pub ls: super::LsConfig,
}
impl Default for Config {
fn default() -> Self {
Self {
hsi: true,
hse: None,
sys: Sysclk::HSI,
pll_src: PllSource::HSI,
pll: None,
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
plli2s: None,
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
pllsai: None,
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
ls: Default::default(),
}
}
}
pub(crate) unsafe fn init(config: Config) {
// always enable overdrive for now. Make it configurable in the future.
#[cfg(not(any(
stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f405, stm32f407, stm32f415, stm32f417
)))]
{
use crate::pac::PWR;
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() {}
}
// Configure HSI
let hsi = match config.hsi {
false => {
RCC.cr().modify(|w| w.set_hsion(false));
None
}
true => {
RCC.cr().modify(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
Some(HSI_FREQ)
}
};
// Configure HSE
let hse = match config.hse {
None => {
RCC.cr().modify(|w| w.set_hseon(false));
None
}
Some(hse) => {
match hse.mode {
HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)),
HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)),
}
RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator));
RCC.cr().modify(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
Some(hse.freq)
}
};
// Configure PLLs.
let pll_input = PllInput {
hse,
hsi,
source: config.pll_src,
};
let pll = init_pll(PllInstance::Pll, config.pll, &pll_input);
#[cfg(any(all(stm32f4, not(any(stm32f410, stm32f429))), stm32f7))]
let _plli2s = init_pll(PllInstance::Plli2s, config.plli2s, &pll_input);
#[cfg(all(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7), not(stm32f429)))]
let _pllsai = init_pll(PllInstance::Pllsai, config.pllsai, &pll_input);
// Configure sysclk
let sys = match config.sys {
Sysclk::HSI => unwrap!(hsi),
Sysclk::HSE => unwrap!(hse),
Sysclk::PLL1_P => unwrap!(pll.p),
_ => unreachable!(),
};
let hclk = sys / config.ahb_pre;
let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre);
let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre);
assert!(max::SYSCLK.contains(&sys));
assert!(max::HCLK.contains(&hclk));
assert!(max::PCLK1.contains(&pclk1));
assert!(max::PCLK2.contains(&pclk2));
let rtc = config.ls.init();
flash_setup(hclk);
RCC.cfgr().modify(|w| {
w.set_sw(config.sys);
w.set_hpre(config.ahb_pre);
w.set_ppre1(config.apb1_pre);
w.set_ppre2(config.apb2_pre);
});
while RCC.cfgr().read().sws() != config.sys {}
set_freqs(Clocks {
sys,
hclk1: hclk,
hclk2: hclk,
hclk3: hclk,
pclk1,
pclk2,
pclk1_tim,
pclk2_tim,
rtc,
pll1_q: pll.q,
#[cfg(all(rcc_f4, not(any(stm32f410, stm32f429))))]
plli2s1_q: _plli2s.q,
#[cfg(all(rcc_f4, not(any(stm32f410, stm32f429))))]
plli2s1_r: _plli2s.r,
#[cfg(stm32f429)]
plli2s1_q: None,
#[cfg(stm32f429)]
plli2s1_r: None,
#[cfg(any(stm32f427, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
pllsai1_q: _pllsai.q,
#[cfg(any(stm32f427, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
pllsai1_r: _pllsai.r,
#[cfg(stm32f429)]
pllsai1_q: None,
#[cfg(stm32f429)]
pllsai1_r: None,
});
}
struct PllInput {
source: PllSource,
hsi: Option<Hertz>,
hse: Option<Hertz>,
}
#[derive(Default)]
#[allow(unused)]
struct PllOutput {
p: Option<Hertz>,
q: Option<Hertz>,
r: Option<Hertz>,
}
#[allow(dead_code)]
#[derive(PartialEq, Eq, Clone, Copy)]
enum PllInstance {
Pll,
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
Plli2s,
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
Pllsai,
}
fn pll_enable(instance: PllInstance, enabled: bool) {
match instance {
PllInstance::Pll => {
RCC.cr().modify(|w| w.set_pllon(enabled));
while RCC.cr().read().pllrdy() != enabled {}
}
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
PllInstance::Plli2s => {
RCC.cr().modify(|w| w.set_plli2son(enabled));
while RCC.cr().read().plli2srdy() != enabled {}
}
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
PllInstance::Pllsai => {
RCC.cr().modify(|w| w.set_pllsaion(enabled));
while RCC.cr().read().pllsairdy() != enabled {}
}
}
}
fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> PllOutput {
// Disable PLL
pll_enable(instance, false);
let Some(pll) = config else { return PllOutput::default() };
let pll_src = match input.source {
PllSource::HSE => input.hse,
PllSource::HSI => input.hsi,
};
let pll_src = pll_src.unwrap();
let in_freq = pll_src / pll.prediv;
assert!(max::PLL_IN.contains(&in_freq));
let vco_freq = in_freq * pll.mul;
assert!(max::PLL_VCO.contains(&vco_freq));
let p = pll.divp.map(|div| vco_freq / div);
let q = pll.divq.map(|div| vco_freq / div);
let r = pll.divr.map(|div| vco_freq / div);
macro_rules! write_fields {
($w:ident) => {
$w.set_plln(pll.mul);
if let Some(divp) = pll.divp {
$w.set_pllp(divp);
}
if let Some(divq) = pll.divq {
$w.set_pllq(divq);
}
if let Some(divr) = pll.divr {
$w.set_pllr(divr);
}
};
}
match instance {
PllInstance::Pll => RCC.pllcfgr().write(|w| {
w.set_pllm(pll.prediv);
w.set_pllsrc(input.source);
write_fields!(w);
}),
#[cfg(any(all(stm32f4, not(stm32f410)), stm32f7))]
PllInstance::Plli2s => RCC.plli2scfgr().write(|w| {
write_fields!(w);
}),
#[cfg(any(stm32f446, stm32f427, stm32f437, stm32f4x9, stm32f7))]
PllInstance::Pllsai => RCC.pllsaicfgr().write(|w| {
write_fields!(w);
}),
}
// Enable PLL
pll_enable(instance, true);
PllOutput { p, q, r }
}
fn flash_setup(clk: Hertz) {
use crate::pac::flash::vals::Latency;
// Be conservative with voltage ranges
const FLASH_LATENCY_STEP: u32 = 30_000_000;
let latency = (clk.0 - 1) / FLASH_LATENCY_STEP;
debug!("flash: latency={}", latency);
let latency = Latency::from_bits(latency as u8);
FLASH.acr().write(|w| {
w.set_latency(latency);
});
while FLASH.acr().read().latency() != latency {}
}
#[cfg(stm32f7)]
mod max {
use core::ops::RangeInclusive;
use crate::time::Hertz;
pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(26_000_000);
pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(50_000_000);
pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000);
pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000);
pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000 / 4);
pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(12_500_000)..=Hertz(216_000_000 / 2);
pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(2_100_000);
pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(100_000_000)..=Hertz(432_000_000);
}
#[cfg(stm32f4)]
mod max {
use core::ops::RangeInclusive;
use crate::time::Hertz;
pub(crate) const HSE_OSC: RangeInclusive<Hertz> = Hertz(4_000_000)..=Hertz(26_000_000);
pub(crate) const HSE_BYP: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(50_000_000);
#[cfg(stm32f401)]
pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(84_000_000);
#[cfg(any(stm32f405, stm32f407, stm32f415, stm32f417,))]
pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(168_000_000);
#[cfg(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))]
pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(100_000_000);
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479,))]
pub(crate) const SYSCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(180_000_000);
pub(crate) const HCLK: RangeInclusive<Hertz> = Hertz(0)..=Hertz(SYSCLK.end().0);
pub(crate) const PCLK1: RangeInclusive<Hertz> = Hertz(0)..=Hertz(PCLK2.end().0 / 2);
#[cfg(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,))]
pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(0)..=Hertz(HCLK.end().0);
#[cfg(not(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423,)))]
pub(crate) const PCLK2: RangeInclusive<Hertz> = Hertz(0)..=Hertz(HCLK.end().0 / 2);
pub(crate) const PLL_IN: RangeInclusive<Hertz> = Hertz(1_000_000)..=Hertz(2_100_000);
pub(crate) const PLL_VCO: RangeInclusive<Hertz> = Hertz(100_000_000)..=Hertz(432_000_000);
}

View File

@ -1,305 +0,0 @@
use crate::pac::pwr::vals::Vos;
use crate::pac::rcc::vals::{Hpre, Pllm, Plln, Pllp, Pllq, Pllsrc, Ppre, Sw};
use crate::pac::{FLASH, PWR, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
/// HSI speed
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
/// Clocks configuration
#[non_exhaustive]
#[derive(Default)]
pub struct Config {
pub hse: Option<Hertz>,
pub bypass_hse: bool,
pub hclk: Option<Hertz>,
pub sys_ck: Option<Hertz>,
pub pclk1: Option<Hertz>,
pub pclk2: Option<Hertz>,
pub pll48: bool,
pub ls: super::LsConfig,
}
fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bool) -> PllResults {
let sysclk = pllsysclk.unwrap_or(pllsrcclk);
if pllsysclk.is_none() && !pll48clk {
RCC.pllcfgr().modify(|w| w.set_pllsrc(Pllsrc::from_bits(use_hse as u8)));
return PllResults {
use_pll: false,
pllsysclk: None,
pll48clk: None,
};
}
// Input divisor from PLL source clock, must result to frequency in
// the range from 1 to 2 MHz
let pllm_min = (pllsrcclk + 1_999_999) / 2_000_000;
let pllm_max = pllsrcclk / 1_000_000;
// Sysclk output divisor must be one of 2, 4, 6 or 8
let sysclk_div = core::cmp::min(8, (432_000_000 / sysclk) & !1);
let target_freq = if pll48clk { 48_000_000 } else { sysclk * sysclk_div };
// Find the lowest pllm value that minimize the difference between
// target frequency and the real vco_out frequency.
let pllm = unwrap!((pllm_min..=pllm_max).min_by_key(|pllm| {
let vco_in = pllsrcclk / pllm;
let plln = target_freq / vco_in;
target_freq - vco_in * plln
}));
let vco_in = pllsrcclk / pllm;
assert!((1_000_000..=2_000_000).contains(&vco_in));
// Main scaler, must result in >= 100MHz (>= 192MHz for F401)
// and <= 432MHz, min 50, max 432
let plln = if pll48clk {
// try the different valid pllq according to the valid
// main scaller values, and take the best
let pllq = unwrap!((4..=9).min_by_key(|pllq| {
let plln = 48_000_000 * pllq / vco_in;
let pll48_diff = 48_000_000 - vco_in * plln / pllq;
let sysclk_diff = (sysclk as i32 - (vco_in * plln / sysclk_div) as i32).abs();
(pll48_diff, sysclk_diff)
}));
48_000_000 * pllq / vco_in
} else {
sysclk * sysclk_div / vco_in
};
let pllp = (sysclk_div / 2) - 1;
let pllq = (vco_in * plln + 47_999_999) / 48_000_000;
let real_pll48clk = vco_in * plln / pllq;
RCC.pllcfgr().modify(|w| {
w.set_pllm(Pllm::from_bits(pllm as u8));
w.set_plln(Plln::from_bits(plln as u16));
w.set_pllp(Pllp::from_bits(pllp as u8));
w.set_pllq(Pllq::from_bits(pllq as u8));
w.set_pllsrc(Pllsrc::from_bits(use_hse as u8));
});
let real_pllsysclk = vco_in * plln / sysclk_div;
PllResults {
use_pll: true,
pllsysclk: Some(real_pllsysclk),
pll48clk: if pll48clk { Some(real_pll48clk) } else { None },
}
}
fn flash_setup(sysclk: u32) {
use crate::pac::flash::vals::Latency;
// Be conservative with voltage ranges
const FLASH_LATENCY_STEP: u32 = 30_000_000;
critical_section::with(|_| {
FLASH
.acr()
.modify(|w| w.set_latency(Latency::from_bits(((sysclk - 1) / FLASH_LATENCY_STEP) as u8)));
});
}
pub(crate) unsafe fn init(config: Config) {
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_FREQ.0);
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(config.bypass_hse);
w.set_hseon(true);
});
while !RCC.cr().read().hserdy() {}
}
if plls.use_pll {
RCC.cr().modify(|w| w.set_pllon(false));
// setup VOSScale
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::from_bits(ppre2_bits));
w.set_ppre1(Ppre::from_bits(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
})
});
let rtc = config.ls.init();
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),
rtc,
});
}
struct PllResults {
use_pll: bool,
pllsysclk: Option<u32>,
pll48clk: Option<u32>,
}
mod max {
pub(crate) const HSE_OSC_MIN: u32 = 4_000_000;
pub(crate) const HSE_OSC_MAX: u32 = 26_000_000;
pub(crate) const HSE_BYPASS_MIN: u32 = 1_000_000;
pub(crate) const HSE_BYPASS_MAX: u32 = 50_000_000;
pub(crate) const HCLK_MAX: u32 = 216_000_000;
pub(crate) const HCLK_OVERDRIVE_FREQUENCY: u32 = 180_000_000;
pub(crate) const SYSCLK_MIN: u32 = 12_500_000;
pub(crate) const SYSCLK_MAX: u32 = 216_000_000;
pub(crate) const PCLK1_MIN: u32 = SYSCLK_MIN;
pub(crate) const PCLK1_MAX: u32 = SYSCLK_MAX / 4;
pub(crate) const PCLK2_MIN: u32 = SYSCLK_MIN;
pub(crate) const PCLK2_MAX: u32 = SYSCLK_MAX / 2;
// USB specification allows +-0.25%
pub(crate) const PLL_48_CLK: u32 = 48_000_000;
pub(crate) const PLL_48_TOLERANCE: u32 = 120_000;
}

View File

@ -1,7 +1,7 @@
use crate::pac::flash::vals::Latency;
use crate::pac::rcc::vals::{self, Sw};
pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Hsidiv as HSI16Prescaler, Pllm, Plln, Pllp, Pllq, Pllr, Ppre as APBPrescaler,
Hpre as AHBPrescaler, Hsidiv as HSIPrescaler, Pllm, Plln, Pllp, Pllq, Pllr, Ppre as APBPrescaler,
};
use crate::pac::{FLASH, PWR, RCC};
use crate::rcc::{set_freqs, Clocks};
@ -14,7 +14,7 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000);
#[derive(Clone, Copy)]
pub enum ClockSrc {
HSE(Hertz),
HSI16(HSI16Prescaler),
HSI(HSIPrescaler),
PLL(PllConfig),
LSI,
}
@ -46,9 +46,9 @@ pub struct PllConfig {
impl Default for PllConfig {
#[inline]
fn default() -> PllConfig {
// HSI16 / 1 * 8 / 2 = 64 MHz
// HSI / 1 * 8 / 2 = 64 MHz
PllConfig {
source: PllSrc::HSI16,
source: PllSrc::HSI,
m: Pllm::DIV1,
n: Plln::MUL8,
r: Pllr::DIV2,
@ -60,7 +60,7 @@ impl Default for PllConfig {
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum PllSrc {
HSI16,
HSI,
HSE(Hertz),
}
@ -77,7 +77,7 @@ impl Default for Config {
#[inline]
fn default() -> Config {
Config {
mux: ClockSrc::HSI16(HSI16Prescaler::DIV1),
mux: ClockSrc::HSI(HSIPrescaler::DIV1),
ahb_pre: AHBPrescaler::DIV1,
apb_pre: APBPrescaler::DIV1,
low_power_run: false,
@ -89,7 +89,7 @@ impl Default for Config {
impl PllConfig {
pub(crate) fn init(self) -> Hertz {
let (src, input_freq) = match self.source {
PllSrc::HSI16 => (vals::Pllsrc::HSI16, HSI_FREQ),
PllSrc::HSI => (vals::Pllsrc::HSI, HSI_FREQ),
PllSrc::HSE(freq) => (vals::Pllsrc::HSE, freq),
};
@ -121,7 +121,7 @@ impl PllConfig {
// > 3. Change the desired parameter.
// Enable whichever clock source we're using, and wait for it to become ready
match self.source {
PllSrc::HSI16 => {
PllSrc::HSI => {
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
}
@ -167,8 +167,8 @@ impl PllConfig {
pub(crate) unsafe fn init(config: Config) {
let (sys_clk, sw) = match config.mux {
ClockSrc::HSI16(div) => {
// Enable HSI16
ClockSrc::HSI(div) => {
// Enable HSI
RCC.cr().write(|w| {
w.set_hsidiv(div);
w.set_hsion(true)
@ -186,7 +186,7 @@ pub(crate) unsafe fn init(config: Config) {
}
ClockSrc::PLL(pll) => {
let freq = pll.init();
(freq, Sw::PLLRCLK)
(freq, Sw::PLL1_R)
}
ClockSrc::LSI => {
// Enable LSI
@ -275,9 +275,9 @@ pub(crate) unsafe fn init(config: Config) {
set_freqs(Clocks {
sys: sys_clk,
ahb1: ahb_freq,
apb1: apb_freq,
apb1_tim: apb_tim_freq,
hclk1: ahb_freq,
pclk1: apb_freq,
pclk1_tim: apb_tim_freq,
rtc,
});
}

View File

@ -18,14 +18,14 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000);
#[derive(Clone, Copy)]
pub enum ClockSrc {
HSE(Hertz),
HSI16,
HSI,
PLL,
}
/// PLL clock input source
#[derive(Clone, Copy, Debug)]
pub enum PllSrc {
HSI16,
HSI,
HSE(Hertz),
}
@ -33,7 +33,7 @@ impl Into<Pllsrc> for PllSrc {
fn into(self) -> Pllsrc {
match self {
PllSrc::HSE(..) => Pllsrc::HSE,
PllSrc::HSI16 => Pllsrc::HSI16,
PllSrc::HSI => Pllsrc::HSI,
}
}
}
@ -112,15 +112,15 @@ impl Default for Config {
#[inline]
fn default() -> Config {
Config {
mux: ClockSrc::HSI16,
mux: ClockSrc::HSI,
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
low_power_run: false,
pll: None,
clock_48mhz_src: None,
adc12_clock_source: Adcsel::NOCLK,
adc345_clock_source: Adcsel::NOCLK,
clock_48mhz_src: Some(Clock48MhzSrc::Hsi48(None)),
adc12_clock_source: Adcsel::DISABLE,
adc345_clock_source: Adcsel::DISABLE,
ls: Default::default(),
}
}
@ -135,7 +135,7 @@ pub struct PllFreq {
pub(crate) unsafe fn init(config: Config) {
let pll_freq = config.pll.map(|pll_config| {
let src_freq = match pll_config.source {
PllSrc::HSI16 => {
PllSrc::HSI => {
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
@ -196,12 +196,12 @@ pub(crate) unsafe fn init(config: Config) {
});
let (sys_clk, sw) = match config.mux {
ClockSrc::HSI16 => {
// Enable HSI16
ClockSrc::HSI => {
// Enable HSI
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
(HSI_FREQ, Sw::HSI16)
(HSI_FREQ, Sw::HSI)
}
ClockSrc::HSE(freq) => {
// Enable HSE
@ -249,7 +249,7 @@ pub(crate) unsafe fn init(config: Config) {
}
}
(Hertz(freq), Sw::PLLRCLK)
(Hertz(freq), Sw::PLL1_R)
}
};
@ -286,7 +286,7 @@ pub(crate) unsafe fn init(config: Config) {
let pllq_freq = pll_freq.as_ref().and_then(|f| f.pll_q);
assert!(pllq_freq.is_some() && pllq_freq.unwrap().0 == 48_000_000);
crate::pac::rcc::vals::Clk48sel::PLLQCLK
crate::pac::rcc::vals::Clk48sel::PLL1_Q
}
Clock48MhzSrc::Hsi48(crs_config) => {
// Enable HSI48
@ -326,16 +326,16 @@ pub(crate) unsafe fn init(config: Config) {
RCC.ccipr().modify(|w| w.set_adc345sel(config.adc345_clock_source));
let adc12_ck = match config.adc12_clock_source {
AdcClockSource::NOCLK => None,
AdcClockSource::PLLP => pll_freq.as_ref().unwrap().pll_p,
AdcClockSource::SYSCLK => Some(sys_clk),
AdcClockSource::DISABLE => None,
AdcClockSource::PLL1_P => pll_freq.as_ref().unwrap().pll_p,
AdcClockSource::SYS => Some(sys_clk),
_ => unreachable!(),
};
let adc345_ck = match config.adc345_clock_source {
AdcClockSource::NOCLK => None,
AdcClockSource::PLLP => pll_freq.as_ref().unwrap().pll_p,
AdcClockSource::SYSCLK => Some(sys_clk),
AdcClockSource::DISABLE => None,
AdcClockSource::PLL1_P => pll_freq.as_ref().unwrap().pll_p,
AdcClockSource::SYS => Some(sys_clk),
_ => unreachable!(),
};
@ -348,14 +348,15 @@ pub(crate) unsafe fn init(config: Config) {
set_freqs(Clocks {
sys: sys_clk,
ahb1: ahb_freq,
ahb2: ahb_freq,
apb1: apb1_freq,
apb1_tim: apb1_tim_freq,
apb2: apb2_freq,
apb2_tim: apb2_tim_freq,
hclk1: ahb_freq,
hclk2: ahb_freq,
pclk1: apb1_freq,
pclk1_tim: apb1_tim_freq,
pclk2: apb2_freq,
pclk2_tim: apb2_tim_freq,
adc: adc12_ck,
adc34: adc345_ck,
pll1_p: None,
rtc,
});
}

View File

@ -6,8 +6,11 @@ use crate::pac::pwr::vals::Vos;
pub use crate::pac::rcc::vals::Adcdacsel as AdcClockSource;
#[cfg(stm32h7)]
pub use crate::pac::rcc::vals::Adcsel as AdcClockSource;
use crate::pac::rcc::vals::{Ckpersel, Hsidiv, Pllrge, Pllsrc, Pllvcosel, Sw, Timpre};
pub use crate::pac::rcc::vals::{Ckpersel as PerClockSource, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul};
pub use crate::pac::rcc::vals::{
Ckpersel as PerClockSource, Hsidiv as HSIPrescaler, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul,
Pllsrc as PllSource, Sw as Sysclk,
};
use crate::pac::rcc::vals::{Ckpersel, Pllrge, Pllvcosel, Timpre};
use crate::pac::{FLASH, PWR, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
@ -58,50 +61,9 @@ pub struct Hse {
pub mode: HseMode,
}
#[cfg(stm32h7)]
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Lse {
/// 32.768 kHz crystal/ceramic oscillator (LSEBYP=0)
Oscillator,
/// external clock input up to 1MHz (LSEBYP=1)
Bypass(Hertz),
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Hsi {
/// 64Mhz
Mhz64,
/// 32Mhz (divided by 2)
Mhz32,
/// 16Mhz (divided by 4)
Mhz16,
/// 8Mhz (divided by 8)
Mhz8,
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Sysclk {
/// HSI selected as sysclk
HSI,
/// HSE selected as sysclk
HSE,
/// CSI selected as sysclk
CSI,
/// PLL1_P selected as sysclk
Pll1P,
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum PllSource {
Hsi,
Csi,
Hse,
}
#[derive(Clone, Copy)]
pub struct Pll {
/// Source clock selection.
#[cfg(stm32h5)]
pub source: PllSource,
/// PLL pre-divider (DIVM).
@ -161,19 +123,12 @@ impl From<TimerPrescaler> for Timpre {
/// Configuration of the core clocks
#[non_exhaustive]
pub struct Config {
pub hsi: Option<Hsi>,
pub hsi: Option<HSIPrescaler>,
pub hse: Option<Hse>,
#[cfg(stm32h7)]
pub lse: Option<Lse>,
#[cfg(stm32h7)]
pub lsi: bool,
pub csi: bool,
pub hsi48: bool,
pub sys: Sysclk,
#[cfg(stm32h7)]
pub pll_src: PllSource,
pub pll1: Option<Pll>,
pub pll2: Option<Pll>,
#[cfg(any(rcc_h5, stm32h7))]
@ -197,17 +152,11 @@ pub struct Config {
impl Default for Config {
fn default() -> Self {
Self {
hsi: Some(Hsi::Mhz64),
hsi: Some(HSIPrescaler::DIV1),
hse: None,
#[cfg(stm32h7)]
lse: None,
#[cfg(stm32h7)]
lsi: false,
csi: false,
hsi48: false,
sys: Sysclk::HSI,
#[cfg(stm32h7)]
pll_src: PllSource::Hsi,
pll1: None,
pll2: None,
#[cfg(any(rcc_h5, stm32h7))]
@ -320,19 +269,13 @@ pub(crate) unsafe fn init(config: Config) {
RCC.cr().modify(|w| w.set_hsion(false));
None
}
Some(hsi) => {
let (freq, hsidiv) = match hsi {
Hsi::Mhz64 => (HSI_FREQ / 1u32, Hsidiv::DIV1),
Hsi::Mhz32 => (HSI_FREQ / 2u32, Hsidiv::DIV2),
Hsi::Mhz16 => (HSI_FREQ / 4u32, Hsidiv::DIV4),
Hsi::Mhz8 => (HSI_FREQ / 8u32, Hsidiv::DIV8),
};
Some(hsidiv) => {
RCC.cr().modify(|w| {
w.set_hsidiv(hsidiv);
w.set_hsion(true);
});
while !RCC.cr().read().hsirdy() {}
Some(freq)
Some(HSI_FREQ / hsidiv)
}
};
@ -377,25 +320,29 @@ pub(crate) unsafe fn init(config: Config) {
}
};
// H7 has shared PLLSRC, check it's equal in all PLLs.
#[cfg(stm32h7)]
{
let plls = [&config.pll1, &config.pll2, &config.pll3];
if !super::util::all_equal(plls.into_iter().flatten().map(|p| p.source)) {
panic!("Source must be equal across all enabled PLLs.")
};
}
// Configure PLLs.
let pll_input = PllInput {
csi,
hse,
hsi,
#[cfg(stm32h7)]
source: config.pll_src,
};
let pll_input = PllInput { csi, hse, hsi };
let pll1 = init_pll(0, config.pll1, &pll_input);
let pll2 = init_pll(1, config.pll2, &pll_input);
#[cfg(any(rcc_h5, stm32h7))]
let pll3 = init_pll(2, config.pll3, &pll_input);
// Configure sysclk
let (sys, sw) = match config.sys {
Sysclk::HSI => (unwrap!(hsi), Sw::HSI),
Sysclk::HSE => (unwrap!(hse), Sw::HSE),
Sysclk::CSI => (unwrap!(csi), Sw::CSI),
Sysclk::Pll1P => (unwrap!(pll1.p), Sw::PLL1),
let sys = match config.sys {
Sysclk::HSI => unwrap!(hsi),
Sysclk::HSE => unwrap!(hse),
Sysclk::CSI => unwrap!(csi),
Sysclk::PLL1_P => unwrap!(pll1.p),
_ => unreachable!(),
};
// Check limits.
@ -406,7 +353,14 @@ pub(crate) unsafe fn init(config: Config) {
VoltageScale::Scale2 => (Hertz(150_000_000), Hertz(150_000_000)),
VoltageScale::Scale3 => (Hertz(100_000_000), Hertz(100_000_000)),
};
#[cfg(stm32h7)]
#[cfg(pwr_h7rm0455)]
let (d1cpre_clk_max, hclk_max, pclk_max) = match config.voltage_scale {
VoltageScale::Scale0 => (Hertz(280_000_000), Hertz(280_000_000), Hertz(140_000_000)),
VoltageScale::Scale1 => (Hertz(225_000_000), Hertz(225_000_000), Hertz(112_500_000)),
VoltageScale::Scale2 => (Hertz(160_000_000), Hertz(160_000_000), Hertz(80_000_000)),
VoltageScale::Scale3 => (Hertz(88_000_000), Hertz(88_000_000), Hertz(44_000_000)),
};
#[cfg(all(stm32h7, not(pwr_h7rm0455)))]
let (d1cpre_clk_max, hclk_max, pclk_max) = match config.voltage_scale {
VoltageScale::Scale0 => (Hertz(480_000_000), Hertz(240_000_000), Hertz(120_000_000)),
VoltageScale::Scale1 => (Hertz(400_000_000), Hertz(200_000_000), Hertz(100_000_000)),
@ -453,8 +407,8 @@ pub(crate) unsafe fn init(config: Config) {
};
#[cfg(stm32h5)]
let adc = match config.adc_clock_source {
AdcClockSource::HCLK => Some(hclk),
AdcClockSource::SYSCLK => Some(sys),
AdcClockSource::HCLK1 => Some(hclk),
AdcClockSource::SYS => Some(sys),
AdcClockSource::PLL2_R => pll2.r,
AdcClockSource::HSE => hse,
AdcClockSource::HSI => hsi,
@ -512,8 +466,8 @@ pub(crate) unsafe fn init(config: Config) {
RCC.cfgr().modify(|w| w.set_timpre(config.timer_prescaler.into()));
RCC.cfgr().modify(|w| w.set_sw(sw));
while RCC.cfgr().read().sws() != sw {}
RCC.cfgr().modify(|w| w.set_sw(config.sys));
while RCC.cfgr().read().sws() != config.sys {}
// IO compensation cell - Requires CSI clock and SYSCFG
#[cfg(stm32h7)] // TODO h5
@ -532,70 +486,65 @@ pub(crate) unsafe fn init(config: Config) {
set_freqs(Clocks {
sys,
ahb1: hclk,
ahb2: hclk,
ahb3: hclk,
ahb4: hclk,
apb1,
apb2,
apb3,
hclk1: hclk,
hclk2: hclk,
hclk3: hclk,
hclk4: hclk,
pclk1: apb1,
pclk2: apb2,
pclk3: apb3,
#[cfg(stm32h7)]
apb4,
apb1_tim,
apb2_tim,
pclk4: apb4,
#[cfg(stm32h5)]
pclk4: Hertz(1),
pclk1_tim: apb1_tim,
pclk2_tim: apb2_tim,
adc,
rtc,
#[cfg(any(stm32h5, stm32h7))]
hsi: None,
#[cfg(stm32h5)]
mux_apb1: Some(apb1),
hsi48: None,
#[cfg(stm32h5)]
mux_apb2: Some(apb2),
#[cfg(stm32h5)]
mux_apb3: Some(apb3),
#[cfg(stm32h5)]
mux_apb4: None,
lsi: None,
#[cfg(any(stm32h5, stm32h7))]
csi: None,
#[cfg(any(stm32h5, stm32h7))]
lse: None,
#[cfg(any(stm32h5, stm32h7))]
hse: None,
#[cfg(any(stm32h5, stm32h7))]
pll1_q: pll1.q,
#[cfg(any(stm32h5, stm32h7))]
pll2_p: pll2.p,
#[cfg(any(stm32h5, stm32h7))]
pll2_q: pll2.q,
#[cfg(any(stm32h5, stm32h7))]
pll2_r: pll2.r,
#[cfg(any(rcc_h5, stm32h7))]
pll3_p: pll3.p,
#[cfg(any(rcc_h5, stm32h7))]
pll3_q: pll3.q,
#[cfg(any(rcc_h5, stm32h7))]
pll3_r: pll3.r,
#[cfg(rcc_h50)]
pll3_p: None,
#[cfg(rcc_h50)]
pll3_q: None,
#[cfg(rcc_h50)]
pll3_r: None,
#[cfg(stm32h5)]
mux_rcc_hclk4: None,
audioclk: None,
#[cfg(any(stm32h5, stm32h7))]
per: None,
#[cfg(stm32h5)]
mux_pll2_q: None,
#[cfg(stm32h5)]
mux_pll3_q: None,
#[cfg(stm32h5)]
mux_hsi: None,
#[cfg(stm32h5)]
mux_csi: None,
#[cfg(stm32h5)]
mux_lse: None,
#[cfg(stm32h5)]
mux_pll1_q: pll1.q,
#[cfg(stm32h5)]
mux_pll2_p: pll2.p,
#[cfg(rcc_h5)]
mux_pll3_p: pll3.p,
#[cfg(stm32h5)]
mux_audioclk: None,
#[cfg(stm32h5)]
mux_per: None,
#[cfg(rcc_h5)]
mux_pll3_r: pll3.r,
#[cfg(all(not(rcc_h5), stm32h5))]
mux_pll3_r: None,
#[cfg(stm32h5)]
mux_pll3_1: None,
#[cfg(stm32h5)]
mux_hsi48_ker: None,
#[cfg(stm32h5)]
mux_lsi: None,
#[cfg(stm32h5)]
mux_pll2_r: pll2.r,
#[cfg(stm32h5)]
mux_hse: hse,
#[cfg(stm32h5)]
mux_hsi48: None,
#[cfg(stm32h7)]
rcc_pclk_d3: None,
});
}
@ -603,8 +552,6 @@ struct PllInput {
hsi: Option<Hertz>,
hse: Option<Hertz>,
csi: Option<Hertz>,
#[cfg(stm32h7)]
source: PllSource,
}
struct PllOutput {
@ -634,15 +581,11 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
};
};
#[cfg(stm32h5)]
let source = config.source;
#[cfg(stm32h7)]
let source = input.source;
let (in_clk, src) = match source {
PllSource::Hsi => (unwrap!(input.hsi), Pllsrc::HSI),
PllSource::Hse => (unwrap!(input.hse), Pllsrc::HSE),
PllSource::Csi => (unwrap!(input.csi), Pllsrc::CSI),
let in_clk = match config.source {
PllSource::DISABLE => panic!("must not set PllSource::Disable"),
PllSource::HSI => unwrap!(input.hsi),
PllSource::HSE => unwrap!(input.hse),
PllSource::CSI => unwrap!(input.csi),
};
let ref_clk = in_clk / config.prediv as u32;
@ -682,7 +625,7 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
#[cfg(stm32h5)]
RCC.pllcfgr(num).write(|w| {
w.set_pllsrc(src);
w.set_pllsrc(config.source);
w.set_divm(config.prediv);
w.set_pllvcosel(vco_range);
w.set_pllrge(ref_range);
@ -696,7 +639,7 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
{
RCC.pllckselr().modify(|w| {
w.set_divm(num, config.prediv);
w.set_pllsrc(src);
w.set_pllsrc(config.source);
});
RCC.pllcfgr().modify(|w| {
w.set_pllvcosel(num, vco_range);

View File

@ -18,20 +18,20 @@ pub enum ClockSrc {
MSI(MSIRange),
PLL(PLLSource, PLLMul, PLLDiv),
HSE(Hertz),
HSI16,
HSI,
}
/// PLL clock input source
#[derive(Clone, Copy)]
pub enum PLLSource {
HSI16,
HSI,
HSE(Hertz),
}
impl From<PLLSource> for Pllsrc {
fn from(val: PLLSource) -> Pllsrc {
match val {
PLLSource::HSI16 => Pllsrc::HSI16,
PLLSource::HSI => Pllsrc::HSI,
PLLSource::HSE(_) => Pllsrc::HSE,
}
}
@ -83,12 +83,12 @@ pub(crate) unsafe fn init(config: Config) {
let freq = 32_768 * (1 << (range as u8 + 1));
(Hertz(freq), Sw::MSI)
}
ClockSrc::HSI16 => {
// Enable HSI16
RCC.cr().write(|w| w.set_hsi16on(true));
while !RCC.cr().read().hsi16rdy() {}
ClockSrc::HSI => {
// Enable HSI
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
(HSI_FREQ, Sw::HSI16)
(HSI_FREQ, Sw::HSI)
}
ClockSrc::HSE(freq) => {
// Enable HSE
@ -105,10 +105,10 @@ pub(crate) unsafe fn init(config: Config) {
while !RCC.cr().read().hserdy() {}
freq
}
PLLSource::HSI16 => {
PLLSource::HSI => {
// Enable HSI
RCC.cr().write(|w| w.set_hsi16on(true));
while !RCC.cr().read().hsi16rdy() {}
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
HSI_FREQ
}
};
@ -131,7 +131,7 @@ pub(crate) unsafe fn init(config: Config) {
RCC.cr().modify(|w| w.set_pllon(true));
while !RCC.cr().read().pllrdy() {}
(freq, Sw::PLL)
(freq, Sw::PLL1_P)
}
};
@ -156,23 +156,9 @@ pub(crate) unsafe fn init(config: Config) {
w.set_ppre2(config.apb2_pre);
});
let ahb_freq = sys_clk / config.ahb_pre;
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
pre => {
let freq = ahb_freq / pre;
(freq, freq * 2u32)
}
};
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
pre => {
let freq = ahb_freq / pre;
(freq, freq * 2u32)
}
};
let hclk1 = sys_clk / config.ahb_pre;
let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre);
let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre);
#[cfg(crs)]
if config.enable_hsi48 {
@ -209,11 +195,11 @@ pub(crate) unsafe fn init(config: Config) {
set_freqs(Clocks {
sys: sys_clk,
ahb1: ahb_freq,
apb1: apb1_freq,
apb2: apb2_freq,
apb1_tim: apb1_tim_freq,
apb2_tim: apb2_tim_freq,
hclk1,
pclk1,
pclk2,
pclk1_tim,
pclk2_tim,
rtc,
});
}

View File

@ -1,296 +0,0 @@
use crate::pac::rcc::regs::Cfgr;
pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Msirange as MSIRange, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv,
Pllr as PllRDiv, Ppre as APBPrescaler,
};
use crate::pac::rcc::vals::{Msirange, Pllsrc, Sw};
use crate::pac::{FLASH, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
/// HSI speed
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
/// System clock mux source
#[derive(Clone, Copy)]
pub enum ClockSrc {
MSI(MSIRange),
PLL(PLLSource, PllRDiv, PllPreDiv, PllMul, Option<PllQDiv>),
HSE(Hertz),
HSI16,
}
/// PLL clock input source
#[derive(Clone, Copy)]
pub enum PLLSource {
HSI16,
HSE(Hertz),
MSI(MSIRange),
}
impl From<PLLSource> for Pllsrc {
fn from(val: PLLSource) -> Pllsrc {
match val {
PLLSource::HSI16 => Pllsrc::HSI16,
PLLSource::HSE(_) => Pllsrc::HSE,
PLLSource::MSI(_) => Pllsrc::MSI,
}
}
}
/// Clocks configutation
pub struct Config {
pub mux: ClockSrc,
pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler,
pub pllsai1: Option<(PllMul, PllPreDiv, Option<PllRDiv>, Option<PllQDiv>, Option<PllPDiv>)>,
#[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
pub hsi48: bool,
pub ls: super::LsConfig,
}
impl Default for Config {
#[inline]
fn default() -> Config {
Config {
mux: ClockSrc::MSI(MSIRange::RANGE4M),
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
pllsai1: None,
#[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
hsi48: false,
ls: Default::default(),
}
}
}
pub(crate) unsafe fn init(config: Config) {
// Switch to MSI to prevent problems with PLL configuration.
if !RCC.cr().read().msion() {
// Turn on MSI and configure it to 4MHz.
RCC.cr().modify(|w| {
w.set_msirgsel(true); // MSI Range is provided by MSIRANGE[3:0].
w.set_msirange(MSIRange::RANGE4M);
w.set_msipllen(false);
w.set_msion(true)
});
// Wait until MSI is running
while !RCC.cr().read().msirdy() {}
}
if RCC.cfgr().read().sws() != Sw::MSI {
// Set MSI as a clock source, reset prescalers.
RCC.cfgr().write_value(Cfgr::default());
// Wait for clock switch status bits to change.
while RCC.cfgr().read().sws() != Sw::MSI {}
}
let rtc = config.ls.init();
let (sys_clk, sw) = match config.mux {
ClockSrc::MSI(range) => {
// Enable MSI
RCC.cr().write(|w| {
w.set_msirange(range);
w.set_msirgsel(true);
w.set_msion(true);
// If LSE is enabled, enable calibration of MSI
w.set_msipllen(config.ls.lse.is_some());
});
while !RCC.cr().read().msirdy() {}
// Enable as clock source for USB, RNG if running at 48 MHz
if range == MSIRange::RANGE48M {
RCC.ccipr().modify(|w| {
w.set_clk48sel(0b11);
});
}
(msirange_to_hertz(range), Sw::MSI)
}
ClockSrc::HSI16 => {
// Enable HSI16
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
(HSI_FREQ, Sw::HSI16)
}
ClockSrc::HSE(freq) => {
// Enable HSE
RCC.cr().write(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
(freq, Sw::HSE)
}
ClockSrc::PLL(src, divr, prediv, mul, divq) => {
let src_freq = match src {
PLLSource::HSE(freq) => {
// Enable HSE
RCC.cr().write(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
freq
}
PLLSource::HSI16 => {
// Enable HSI
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
HSI_FREQ
}
PLLSource::MSI(range) => {
// Enable MSI
RCC.cr().write(|w| {
w.set_msirange(range);
w.set_msipllen(false); // should be turned on if LSE is started
w.set_msirgsel(true);
w.set_msion(true);
});
while !RCC.cr().read().msirdy() {}
msirange_to_hertz(range)
}
};
// Disable PLL
RCC.cr().modify(|w| w.set_pllon(false));
while RCC.cr().read().pllrdy() {}
let freq = src_freq / prediv * mul / divr;
#[cfg(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx))]
assert!(freq.0 <= 120_000_000);
#[cfg(not(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx)))]
assert!(freq.0 <= 80_000_000);
RCC.pllcfgr().write(move |w| {
w.set_plln(mul);
w.set_pllm(prediv);
w.set_pllr(divr);
if let Some(divq) = divq {
w.set_pllq(divq);
w.set_pllqen(true);
}
w.set_pllsrc(src.into());
});
// Enable as clock source for USB, RNG if PLL48 divisor is provided
if let Some(divq) = divq {
let freq = src_freq / prediv * mul / divq;
assert!(freq.0 == 48_000_000);
RCC.ccipr().modify(|w| {
w.set_clk48sel(0b10);
});
}
if let Some((mul, prediv, r_div, q_div, p_div)) = config.pllsai1 {
RCC.pllsai1cfgr().write(move |w| {
w.set_plln(mul);
w.set_pllm(prediv);
if let Some(r_div) = r_div {
w.set_pllr(r_div);
w.set_pllren(true);
}
if let Some(q_div) = q_div {
w.set_pllq(q_div);
w.set_pllqen(true);
let freq = src_freq / prediv * mul / q_div;
if freq.0 == 48_000_000 {
RCC.ccipr().modify(|w| {
w.set_clk48sel(0b1);
});
}
}
if let Some(p_div) = p_div {
w.set_pllp(p_div);
w.set_pllpen(true);
}
});
RCC.cr().modify(|w| w.set_pllsai1on(true));
}
// Enable PLL
RCC.cr().modify(|w| w.set_pllon(true));
while !RCC.cr().read().pllrdy() {}
RCC.pllcfgr().modify(|w| w.set_pllren(true));
(freq, Sw::PLL)
}
};
#[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
if config.hsi48 {
RCC.crrcr().modify(|w| w.set_hsi48on(true));
while !RCC.crrcr().read().hsi48rdy() {}
// Enable as clock source for USB, RNG and SDMMC
RCC.ccipr().modify(|w| w.set_clk48sel(0));
}
// Set flash wait states
FLASH.acr().modify(|w| {
w.set_latency(match sys_clk.0 {
0..=16_000_000 => 0,
0..=32_000_000 => 1,
0..=48_000_000 => 2,
0..=64_000_000 => 3,
_ => 4,
})
});
RCC.cfgr().modify(|w| {
w.set_sw(sw);
w.set_hpre(config.ahb_pre);
w.set_ppre1(config.apb1_pre);
w.set_ppre2(config.apb2_pre);
});
let ahb_freq = sys_clk / config.ahb_pre;
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
pre => {
let freq = ahb_freq / pre;
(freq, freq * 2u32)
}
};
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
pre => {
let freq = ahb_freq / pre;
(freq, freq * 2u32)
}
};
set_freqs(Clocks {
sys: sys_clk,
ahb1: ahb_freq,
ahb2: ahb_freq,
ahb3: ahb_freq,
apb1: apb1_freq,
apb2: apb2_freq,
apb1_tim: apb1_tim_freq,
apb2_tim: apb2_tim_freq,
rtc,
});
}
fn msirange_to_hertz(range: Msirange) -> Hertz {
match range {
MSIRange::RANGE100K => Hertz(100_000),
MSIRange::RANGE200K => Hertz(200_000),
MSIRange::RANGE400K => Hertz(400_000),
MSIRange::RANGE800K => Hertz(800_000),
MSIRange::RANGE1M => Hertz(1_000_000),
MSIRange::RANGE2M => Hertz(2_000_000),
MSIRange::RANGE4M => Hertz(4_000_000),
MSIRange::RANGE8M => Hertz(8_000_000),
MSIRange::RANGE16M => Hertz(16_000_000),
MSIRange::RANGE24M => Hertz(24_000_000),
MSIRange::RANGE32M => Hertz(32_000_000),
MSIRange::RANGE48M => Hertz(48_000_000),
_ => unreachable!(),
}
}

View File

@ -0,0 +1,523 @@
use crate::pac::rcc::regs::Cfgr;
#[cfg(any(stm32l4, stm32l5, stm32wb))]
pub use crate::pac::rcc::vals::Clk48sel as Clk48Src;
#[cfg(any(stm32wb, stm32wl))]
pub use crate::pac::rcc::vals::Hsepre as HsePrescaler;
pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Msirange as MSIRange, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv,
Pllr as PllRDiv, Pllsrc as PLLSource, Ppre as APBPrescaler, Sw as ClockSrc,
};
use crate::pac::{FLASH, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
/// HSI speed
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum HseMode {
/// crystal/ceramic oscillator (HSEBYP=0)
Oscillator,
/// external analog clock (low swing) (HSEBYP=1)
Bypass,
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Hse {
/// HSE frequency.
pub freq: Hertz,
/// HSE mode.
pub mode: HseMode,
/// HSE prescaler
#[cfg(any(stm32wb, stm32wl))]
pub prescaler: HsePrescaler,
}
#[derive(Clone, Copy)]
pub struct Pll {
/// PLL source
pub source: PLLSource,
/// PLL pre-divider (DIVM).
pub prediv: PllPreDiv,
/// PLL multiplication factor.
pub mul: PllMul,
/// PLL P division factor. If None, PLL P output is disabled.
pub divp: Option<PllPDiv>,
/// PLL Q division factor. If None, PLL Q output is disabled.
pub divq: Option<PllQDiv>,
/// PLL R division factor. If None, PLL R output is disabled.
pub divr: Option<PllRDiv>,
}
/// Clocks configutation
pub struct Config {
// base clock sources
pub msi: Option<MSIRange>,
pub hsi: bool,
pub hse: Option<Hse>,
#[cfg(any(all(stm32l4, not(any(stm32l47x, stm32l48x))), stm32l5, stm32wb))]
pub hsi48: bool,
// pll
pub pll: Option<Pll>,
#[cfg(any(stm32l4, stm32l5, stm32wb))]
pub pllsai1: Option<Pll>,
#[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]
pub pllsai2: Option<Pll>,
// sysclk, buses.
pub mux: ClockSrc,
pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler,
#[cfg(any(stm32wl5x, stm32wb))]
pub core2_ahb_pre: AHBPrescaler,
#[cfg(any(stm32wl, stm32wb))]
pub shared_ahb_pre: AHBPrescaler,
// muxes
#[cfg(any(stm32l4, stm32l5, stm32wb))]
pub clk48_src: Clk48Src,
// low speed LSI/LSE/RTC
pub ls: super::LsConfig,
}
impl Default for Config {
#[inline]
fn default() -> Config {
Config {
hse: None,
hsi: false,
msi: Some(MSIRange::RANGE4M),
mux: ClockSrc::MSI,
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
#[cfg(any(stm32wl5x, stm32wb))]
core2_ahb_pre: AHBPrescaler::DIV1,
#[cfg(any(stm32wl, stm32wb))]
shared_ahb_pre: AHBPrescaler::DIV1,
pll: None,
#[cfg(any(stm32l4, stm32l5, stm32wb))]
pllsai1: None,
#[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]
pllsai2: None,
#[cfg(any(all(stm32l4, not(any(stm32l47x, stm32l48x))), stm32l5, stm32wb))]
hsi48: true,
#[cfg(any(stm32l4, stm32l5, stm32wb))]
clk48_src: Clk48Src::HSI48,
ls: Default::default(),
}
}
}
#[cfg(stm32wb)]
pub const WPAN_DEFAULT: Config = Config {
hse: Some(Hse {
freq: Hertz(32_000_000),
mode: HseMode::Oscillator,
prescaler: HsePrescaler::DIV1,
}),
mux: ClockSrc::PLL1_R,
hsi48: true,
msi: None,
hsi: false,
clk48_src: Clk48Src::PLL1_Q,
ls: super::LsConfig::default_lse(),
pll: Some(Pll {
source: PLLSource::HSE,
prediv: PllPreDiv::DIV2,
mul: PllMul::MUL12,
divp: Some(PllPDiv::DIV3), // 32 / 2 * 12 / 3 = 64Mhz
divq: Some(PllQDiv::DIV4), // 32 / 2 * 12 / 4 = 48Mhz
divr: Some(PllRDiv::DIV3), // 32 / 2 * 12 / 3 = 64Mhz
}),
pllsai1: None,
ahb_pre: AHBPrescaler::DIV1,
core2_ahb_pre: AHBPrescaler::DIV2,
shared_ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
};
pub(crate) unsafe fn init(config: Config) {
// Switch to MSI to prevent problems with PLL configuration.
if !RCC.cr().read().msion() {
// Turn on MSI and configure it to 4MHz.
RCC.cr().modify(|w| {
#[cfg(not(stm32wb))]
w.set_msirgsel(crate::pac::rcc::vals::Msirgsel::CR);
w.set_msirange(MSIRange::RANGE4M);
w.set_msipllen(false);
w.set_msion(true)
});
// Wait until MSI is running
while !RCC.cr().read().msirdy() {}
}
if RCC.cfgr().read().sws() != ClockSrc::MSI {
// Set MSI as a clock source, reset prescalers.
RCC.cfgr().write_value(Cfgr::default());
// Wait for clock switch status bits to change.
while RCC.cfgr().read().sws() != ClockSrc::MSI {}
}
#[cfg(stm32l5)]
crate::pac::PWR.cr1().modify(|w| {
w.set_vos(crate::pac::pwr::vals::Vos::RANGE0);
});
let rtc = config.ls.init();
let msi = config.msi.map(|range| {
// Enable MSI
RCC.cr().modify(|w| {
#[cfg(not(stm32wb))]
w.set_msirgsel(crate::pac::rcc::vals::Msirgsel::CR);
w.set_msirange(range);
w.set_msion(true);
// If LSE is enabled, enable calibration of MSI
w.set_msipllen(config.ls.lse.is_some());
});
while !RCC.cr().read().msirdy() {}
// Enable as clock source for USB, RNG if running at 48 MHz
if range == MSIRange::RANGE48M {}
msirange_to_hertz(range)
});
let hsi = config.hsi.then(|| {
RCC.cr().modify(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
HSI_FREQ
});
let hse = config.hse.map(|hse| {
RCC.cr().modify(|w| {
#[cfg(stm32wl)]
w.set_hsebyppwr(hse.mode == HseMode::Bypass);
#[cfg(not(stm32wl))]
w.set_hsebyp(hse.mode == HseMode::Bypass);
w.set_hseon(true);
});
while !RCC.cr().read().hserdy() {}
hse.freq
});
#[cfg(any(all(stm32l4, not(any(stm32l47x, stm32l48x))), stm32l5, stm32wb))]
let hsi48 = config.hsi48.then(|| {
RCC.crrcr().modify(|w| w.set_hsi48on(true));
while !RCC.crrcr().read().hsi48rdy() {}
Hertz(48_000_000)
});
#[cfg(any(stm32l47x, stm32l48x))]
let hsi48 = None;
let _plls = [
&config.pll,
#[cfg(any(stm32l4, stm32l5, stm32wb))]
&config.pllsai1,
#[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]
&config.pllsai2,
];
// L4 has shared PLLSRC, PLLM, check it's equal in all PLLs.
#[cfg(all(stm32l4, not(rcc_l4plus)))]
match super::util::get_equal(_plls.into_iter().flatten().map(|p| (p.source, p.prediv))) {
Err(()) => panic!("Source must be equal across all enabled PLLs."),
Ok(None) => {}
Ok(Some((source, prediv))) => RCC.pllcfgr().write(|w| {
w.set_pllm(prediv);
w.set_pllsrc(source);
}),
};
// L4+, WL has shared PLLSRC, check it's equal in all PLLs.
#[cfg(any(rcc_l4plus, stm32wl))]
match super::util::get_equal(_plls.into_iter().flatten().map(|p| p.source)) {
Err(()) => panic!("Source must be equal across all enabled PLLs."),
Ok(None) => {}
Ok(Some(source)) => RCC.pllcfgr().write(|w| {
w.set_pllsrc(source);
}),
};
let pll_input = PllInput { hse, hsi, msi };
let pll = init_pll(PllInstance::Pll, config.pll, &pll_input);
#[cfg(any(stm32l4, stm32l5, stm32wb))]
let pllsai1 = init_pll(PllInstance::Pllsai1, config.pllsai1, &pll_input);
#[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]
let _pllsai2 = init_pll(PllInstance::Pllsai2, config.pllsai2, &pll_input);
let sys_clk = match config.mux {
ClockSrc::HSE => hse.unwrap(),
ClockSrc::HSI => hsi.unwrap(),
ClockSrc::MSI => msi.unwrap(),
ClockSrc::PLL1_R => pll.r.unwrap(),
};
#[cfg(stm32l4)]
RCC.ccipr().modify(|w| w.set_clk48sel(config.clk48_src));
#[cfg(stm32l5)]
RCC.ccipr1().modify(|w| w.set_clk48sel(config.clk48_src));
#[cfg(any(stm32l4, stm32l5, stm32wb))]
let _clk48 = match config.clk48_src {
Clk48Src::HSI48 => hsi48,
Clk48Src::MSI => msi,
Clk48Src::PLLSAI1_Q => pllsai1.q,
Clk48Src::PLL1_Q => pll.q,
};
#[cfg(rcc_l4plus)]
assert!(sys_clk.0 <= 120_000_000);
#[cfg(all(stm32l4, not(rcc_l4plus)))]
assert!(sys_clk.0 <= 80_000_000);
let hclk1 = sys_clk / config.ahb_pre;
let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre);
let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre);
#[cfg(not(any(stm32wl5x, stm32wb)))]
let hclk2 = hclk1;
#[cfg(any(stm32wl5x, stm32wb))]
let hclk2 = sys_clk / config.core2_ahb_pre;
#[cfg(not(any(stm32wl, stm32wb)))]
let hclk3 = hclk1;
#[cfg(any(stm32wl, stm32wb))]
let hclk3 = sys_clk / config.shared_ahb_pre;
// Set flash wait states
#[cfg(stm32l4)]
let latency = match hclk1.0 {
0..=16_000_000 => 0,
0..=32_000_000 => 1,
0..=48_000_000 => 2,
0..=64_000_000 => 3,
_ => 4,
};
#[cfg(stm32l5)]
let latency = match hclk1.0 {
// VCORE Range 0 (performance), others TODO
0..=20_000_000 => 0,
0..=40_000_000 => 1,
0..=60_000_000 => 2,
0..=80_000_000 => 3,
0..=100_000_000 => 4,
_ => 5,
};
#[cfg(stm32wl)]
let latency = match hclk3.0 {
// VOS RANGE1, others TODO.
..=18_000_000 => 0,
..=36_000_000 => 1,
_ => 2,
};
#[cfg(stm32wb)]
let latency = match hclk3.0 {
// VOS RANGE1, others TODO.
..=18_000_000 => 0,
..=36_000_000 => 1,
..=54_000_000 => 2,
..=64_000_000 => 3,
_ => 4,
};
FLASH.acr().modify(|w| w.set_latency(latency));
while FLASH.acr().read().latency() != latency {}
RCC.cfgr().modify(|w| {
w.set_sw(config.mux);
w.set_hpre(config.ahb_pre);
w.set_ppre1(config.apb1_pre);
w.set_ppre2(config.apb2_pre);
});
while RCC.cfgr().read().sws() != config.mux {}
#[cfg(any(stm32wl, stm32wb))]
{
RCC.extcfgr().modify(|w| {
w.set_shdhpre(config.shared_ahb_pre);
#[cfg(any(stm32wl5x, stm32wb))]
w.set_c2hpre(config.core2_ahb_pre);
});
while !RCC.extcfgr().read().shdhpref() {}
#[cfg(any(stm32wl5x, stm32wb))]
while !RCC.extcfgr().read().c2hpref() {}
}
set_freqs(Clocks {
sys: sys_clk,
hclk1,
hclk2,
hclk3,
pclk1,
pclk2,
pclk1_tim,
pclk2_tim,
#[cfg(stm32wl)]
pclk3: hclk3,
#[cfg(rcc_l4)]
hsi: None,
#[cfg(rcc_l4)]
lse: None,
#[cfg(rcc_l4)]
pllsai1_p: None,
#[cfg(rcc_l4)]
pllsai2_p: None,
#[cfg(rcc_l4)]
pll1_p: None,
#[cfg(rcc_l4)]
pll1_q: None,
#[cfg(rcc_l4)]
sai1_extclk: None,
#[cfg(rcc_l4)]
sai2_extclk: None,
rtc,
});
}
fn msirange_to_hertz(range: MSIRange) -> Hertz {
match range {
MSIRange::RANGE100K => Hertz(100_000),
MSIRange::RANGE200K => Hertz(200_000),
MSIRange::RANGE400K => Hertz(400_000),
MSIRange::RANGE800K => Hertz(800_000),
MSIRange::RANGE1M => Hertz(1_000_000),
MSIRange::RANGE2M => Hertz(2_000_000),
MSIRange::RANGE4M => Hertz(4_000_000),
MSIRange::RANGE8M => Hertz(8_000_000),
MSIRange::RANGE16M => Hertz(16_000_000),
MSIRange::RANGE24M => Hertz(24_000_000),
MSIRange::RANGE32M => Hertz(32_000_000),
MSIRange::RANGE48M => Hertz(48_000_000),
_ => unreachable!(),
}
}
struct PllInput {
hsi: Option<Hertz>,
hse: Option<Hertz>,
msi: Option<Hertz>,
}
#[allow(unused)]
#[derive(Default)]
struct PllOutput {
p: Option<Hertz>,
q: Option<Hertz>,
r: Option<Hertz>,
}
#[derive(PartialEq, Eq, Clone, Copy)]
enum PllInstance {
Pll,
#[cfg(any(stm32l4, stm32l5, stm32wb))]
Pllsai1,
#[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]
Pllsai2,
}
fn pll_enable(instance: PllInstance, enabled: bool) {
match instance {
PllInstance::Pll => {
RCC.cr().modify(|w| w.set_pllon(enabled));
while RCC.cr().read().pllrdy() != enabled {}
}
#[cfg(any(stm32l4, stm32l5, stm32wb))]
PllInstance::Pllsai1 => {
RCC.cr().modify(|w| w.set_pllsai1on(enabled));
while RCC.cr().read().pllsai1rdy() != enabled {}
}
#[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]
PllInstance::Pllsai2 => {
RCC.cr().modify(|w| w.set_pllsai2on(enabled));
while RCC.cr().read().pllsai2rdy() != enabled {}
}
}
}
fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput) -> PllOutput {
// Disable PLL
pll_enable(instance, false);
let Some(pll) = config else { return PllOutput::default() };
let pll_src = match pll.source {
PLLSource::DISABLE => panic!("must not select PLL source as DISABLE"),
PLLSource::HSE => input.hse,
PLLSource::HSI => input.hsi,
PLLSource::MSI => input.msi,
};
let pll_src = pll_src.unwrap();
let vco_freq = pll_src / pll.prediv * pll.mul;
let p = pll.divp.map(|div| vco_freq / div);
let q = pll.divq.map(|div| vco_freq / div);
let r = pll.divr.map(|div| vco_freq / div);
#[cfg(stm32l5)]
if instance == PllInstance::Pllsai2 {
assert!(q.is_none(), "PLLSAI2_Q is not available on L5");
assert!(r.is_none(), "PLLSAI2_R is not available on L5");
}
macro_rules! write_fields {
($w:ident) => {
$w.set_plln(pll.mul);
if let Some(divp) = pll.divp {
$w.set_pllp(divp);
$w.set_pllpen(true);
}
if let Some(divq) = pll.divq {
$w.set_pllq(divq);
$w.set_pllqen(true);
}
if let Some(divr) = pll.divr {
$w.set_pllr(divr);
$w.set_pllren(true);
}
};
}
match instance {
PllInstance::Pll => RCC.pllcfgr().write(|w| {
w.set_pllm(pll.prediv);
w.set_pllsrc(pll.source);
write_fields!(w);
}),
#[cfg(any(stm32l4, stm32l5, stm32wb))]
PllInstance::Pllsai1 => RCC.pllsai1cfgr().write(|w| {
#[cfg(any(rcc_l4plus, stm32l5))]
w.set_pllm(pll.prediv);
#[cfg(stm32l5)]
w.set_pllsrc(pll.source);
write_fields!(w);
}),
#[cfg(any(stm32l47x, stm32l48x, stm32l49x, stm32l4ax, rcc_l4plus, stm32l5))]
PllInstance::Pllsai2 => RCC.pllsai2cfgr().write(|w| {
#[cfg(any(rcc_l4plus, stm32l5))]
w.set_pllm(pll.prediv);
#[cfg(stm32l5)]
w.set_pllsrc(pll.source);
write_fields!(w);
}),
}
// Enable PLL
pll_enable(instance, true);
PllOutput { p, q, r }
}

View File

@ -1,291 +0,0 @@
use crate::pac::rcc::regs::Cfgr;
pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Msirange as MSIRange, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv,
Pllr as PllRDiv, Ppre as APBPrescaler,
};
use crate::pac::rcc::vals::{Msirange, Pllsrc, Sw};
use crate::pac::{FLASH, PWR, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
/// HSI speed
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
/// System clock mux source
#[derive(Clone, Copy)]
pub enum ClockSrc {
MSI(MSIRange),
PLL(PLLSource, PllRDiv, PllPreDiv, PllMul, Option<PllQDiv>),
HSE(Hertz),
HSI16,
}
/// PLL clock input source
#[derive(Clone, Copy)]
pub enum PLLSource {
HSI16,
HSE(Hertz),
MSI(MSIRange),
}
impl From<PLLSource> for Pllsrc {
fn from(val: PLLSource) -> Pllsrc {
match val {
PLLSource::HSI16 => Pllsrc::HSI16,
PLLSource::HSE(_) => Pllsrc::HSE,
PLLSource::MSI(_) => Pllsrc::MSI,
}
}
}
/// Clocks configutation
pub struct Config {
pub mux: ClockSrc,
pub ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler,
pub pllsai1: Option<(PllMul, PllPreDiv, Option<PllRDiv>, Option<PllQDiv>, Option<PllPDiv>)>,
pub hsi48: bool,
pub ls: super::LsConfig,
}
impl Default for Config {
#[inline]
fn default() -> Config {
Config {
mux: ClockSrc::MSI(MSIRange::RANGE4M),
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
pllsai1: None,
hsi48: false,
ls: Default::default(),
}
}
}
pub(crate) unsafe fn init(config: Config) {
// Switch to MSI to prevent problems with PLL configuration.
if !RCC.cr().read().msion() {
// Turn on MSI and configure it to 4MHz.
RCC.cr().modify(|w| {
w.set_msirgsel(true); // MSI Range is provided by MSIRANGE[3:0].
w.set_msirange(MSIRange::RANGE4M);
w.set_msipllen(false);
w.set_msion(true)
});
// Wait until MSI is running
while !RCC.cr().read().msirdy() {}
}
if RCC.cfgr().read().sws() != Sw::MSI {
// Set MSI as a clock source, reset prescalers.
RCC.cfgr().write_value(Cfgr::default());
// Wait for clock switch status bits to change.
while RCC.cfgr().read().sws() != Sw::MSI {}
}
let rtc = config.ls.init();
PWR.cr1().modify(|w| w.set_vos(stm32_metapac::pwr::vals::Vos::RANGE0));
let (sys_clk, sw) = match config.mux {
ClockSrc::MSI(range) => {
// Enable MSI
RCC.cr().write(|w| {
w.set_msirange(range);
w.set_msirgsel(true);
w.set_msion(true);
// If LSE is enabled, enable calibration of MSI
w.set_msipllen(config.ls.lse.is_some());
});
while !RCC.cr().read().msirdy() {}
// Enable as clock source for USB, RNG if running at 48 MHz
if range == MSIRange::RANGE48M {
RCC.ccipr1().modify(|w| {
w.set_clk48msel(0b11);
});
}
(msirange_to_hertz(range), Sw::MSI)
}
ClockSrc::HSI16 => {
// Enable HSI16
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
(HSI_FREQ, Sw::HSI16)
}
ClockSrc::HSE(freq) => {
// Enable HSE
RCC.cr().write(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
(freq, Sw::HSE)
}
ClockSrc::PLL(src, divr, prediv, mul, divq) => {
let src_freq = match src {
PLLSource::HSE(freq) => {
// Enable HSE
RCC.cr().write(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
freq
}
PLLSource::HSI16 => {
// Enable HSI
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
HSI_FREQ
}
PLLSource::MSI(range) => {
// Enable MSI
RCC.cr().write(|w| {
w.set_msirange(range);
w.set_msipllen(false); // should be turned on if LSE is started
w.set_msirgsel(true);
w.set_msion(true);
});
while !RCC.cr().read().msirdy() {}
msirange_to_hertz(range)
}
};
// Disable PLL
RCC.cr().modify(|w| w.set_pllon(false));
while RCC.cr().read().pllrdy() {}
let freq = src_freq / prediv * mul / divr;
RCC.pllcfgr().write(move |w| {
w.set_plln(mul);
w.set_pllm(prediv);
w.set_pllr(divr);
if let Some(divq) = divq {
w.set_pllq(divq);
w.set_pllqen(true);
}
w.set_pllsrc(src.into());
});
// Enable as clock source for USB, RNG if PLL48 divisor is provided
if let Some(divq) = divq {
let freq = src_freq / prediv * mul / divq;
assert!(freq.0 == 48_000_000);
RCC.ccipr1().modify(|w| {
w.set_clk48msel(0b10);
});
}
if let Some((mul, prediv, r_div, q_div, p_div)) = config.pllsai1 {
RCC.pllsai1cfgr().write(move |w| {
w.set_plln(mul);
w.set_pllm(prediv);
if let Some(r_div) = r_div {
w.set_pllr(r_div);
w.set_pllren(true);
}
if let Some(q_div) = q_div {
w.set_pllq(q_div);
w.set_pllqen(true);
let freq = src_freq / prediv * mul / q_div;
if freq.0 == 48_000_000 {
RCC.ccipr1().modify(|w| {
w.set_clk48msel(0b1);
});
}
}
if let Some(p_div) = p_div {
w.set_pllp(p_div);
w.set_pllpen(true);
}
});
RCC.cr().modify(|w| w.set_pllsai1on(true));
}
// Enable PLL
RCC.cr().modify(|w| w.set_pllon(true));
while !RCC.cr().read().pllrdy() {}
RCC.pllcfgr().modify(|w| w.set_pllren(true));
(freq, Sw::PLL)
}
};
if config.hsi48 {
RCC.crrcr().modify(|w| w.set_hsi48on(true));
while !RCC.crrcr().read().hsi48rdy() {}
// Enable as clock source for USB, RNG and SDMMC
RCC.ccipr1().modify(|w| w.set_clk48msel(0));
}
// Set flash wait states
// VCORE Range 0 (performance), others TODO
FLASH.acr().modify(|w| {
w.set_latency(match sys_clk.0 {
0..=20_000_000 => 0,
0..=40_000_000 => 1,
0..=60_000_000 => 2,
0..=80_000_000 => 3,
0..=100_000_000 => 4,
_ => 5,
})
});
RCC.cfgr().modify(|w| {
w.set_sw(sw);
w.set_hpre(config.ahb_pre);
w.set_ppre1(config.apb1_pre);
w.set_ppre2(config.apb2_pre);
});
let ahb_freq = sys_clk / config.ahb_pre;
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
pre => {
let freq = ahb_freq / pre;
(freq, freq * 2u32)
}
};
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
pre => {
let freq = ahb_freq / pre;
(freq, freq * 2u32)
}
};
set_freqs(Clocks {
sys: sys_clk,
ahb1: ahb_freq,
ahb2: ahb_freq,
ahb3: ahb_freq,
apb1: apb1_freq,
apb2: apb2_freq,
apb1_tim: apb1_tim_freq,
apb2_tim: apb2_tim_freq,
rtc,
});
}
fn msirange_to_hertz(range: Msirange) -> Hertz {
match range {
MSIRange::RANGE100K => Hertz(100_000),
MSIRange::RANGE200K => Hertz(200_000),
MSIRange::RANGE400K => Hertz(400_000),
MSIRange::RANGE800K => Hertz(800_000),
MSIRange::RANGE1M => Hertz(1_000_000),
MSIRange::RANGE2M => Hertz(2_000_000),
MSIRange::RANGE4M => Hertz(4_000_000),
MSIRange::RANGE8M => Hertz(8_000_000),
MSIRange::RANGE16M => Hertz(16_000_000),
MSIRange::RANGE24M => Hertz(24_000_000),
MSIRange::RANGE32M => Hertz(32_000_000),
MSIRange::RANGE48M => Hertz(48_000_000),
_ => unreachable!(),
}
}

View File

@ -13,22 +13,16 @@ pub use mco::*;
#[cfg_attr(any(rcc_f1, rcc_f100, rcc_f1cl), path = "f1.rs")]
#[cfg_attr(rcc_f2, path = "f2.rs")]
#[cfg_attr(any(rcc_f3, rcc_f3_v2), path = "f3.rs")]
#[cfg_attr(any(rcc_f4, rcc_f410), path = "f4.rs")]
#[cfg_attr(rcc_f7, path = "f7.rs")]
#[cfg_attr(any(rcc_f4, rcc_f410, rcc_f7), path = "f4f7.rs")]
#[cfg_attr(rcc_c0, path = "c0.rs")]
#[cfg_attr(rcc_g0, path = "g0.rs")]
#[cfg_attr(rcc_g4, path = "g4.rs")]
#[cfg_attr(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7rm0433, rcc_h7ab), path = "h.rs")]
#[cfg_attr(any(rcc_l0, rcc_l0_v2, rcc_l1), path = "l0l1.rs")]
#[cfg_attr(rcc_l4, path = "l4.rs")]
#[cfg_attr(rcc_l5, path = "l5.rs")]
#[cfg_attr(any(rcc_l4, rcc_l4plus, rcc_l5, rcc_wl5, rcc_wle, rcc_wb), path = "l4l5.rs")]
#[cfg_attr(rcc_u5, path = "u5.rs")]
#[cfg_attr(rcc_wb, path = "wb.rs")]
#[cfg_attr(rcc_wba, path = "wba.rs")]
#[cfg_attr(any(rcc_wl5, rcc_wle), path = "wl.rs")]
mod _version;
#[cfg(feature = "low-power")]
use core::sync::atomic::{AtomicU32, Ordering};
pub use _version::*;
@ -48,23 +42,24 @@ pub struct Clocks {
pub sys: Hertz,
// APB
pub apb1: Hertz,
pub apb1_tim: Hertz,
pub pclk1: Hertz,
pub pclk1_tim: Hertz,
#[cfg(not(any(rcc_c0, rcc_g0)))]
pub apb2: Hertz,
pub pclk2: Hertz,
#[cfg(not(any(rcc_c0, rcc_g0)))]
pub apb2_tim: Hertz,
pub pclk2_tim: Hertz,
#[cfg(any(rcc_wl5, rcc_wle, rcc_h5, rcc_h50, rcc_h7, rcc_h7rm0433, rcc_h7ab, rcc_u5))]
pub apb3: Hertz,
#[cfg(any(rcc_h7, rcc_h7rm0433, rcc_h7ab))]
pub apb4: Hertz,
pub pclk3: Hertz,
#[cfg(any(rcc_h7, rcc_h7rm0433, rcc_h7ab, stm32h5))]
pub pclk4: Hertz,
#[cfg(any(rcc_wba))]
pub apb7: Hertz,
pub pclk7: Hertz,
// AHB
pub ahb1: Hertz,
pub hclk1: Hertz,
#[cfg(any(
rcc_l4,
rcc_l4plus,
rcc_l5,
rcc_f2,
rcc_f4,
@ -82,9 +77,10 @@ pub struct Clocks {
rcc_wl5,
rcc_wle
))]
pub ahb2: Hertz,
pub hclk2: Hertz,
#[cfg(any(
rcc_l4,
rcc_l4plus,
rcc_l5,
rcc_f2,
rcc_f4,
@ -100,18 +96,40 @@ pub struct Clocks {
rcc_wl5,
rcc_wle
))]
pub ahb3: Hertz,
pub hclk3: Hertz,
#[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7rm0433, rcc_h7ab, rcc_wba))]
pub ahb4: Hertz,
#[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))]
pub pll48: Option<Hertz>,
pub hclk4: Hertz,
#[cfg(all(rcc_f4, not(stm32f410)))]
pub plli2s: Option<Hertz>,
pub plli2s1_q: Option<Hertz>,
#[cfg(all(rcc_f4, not(stm32f410)))]
pub plli2s1_r: Option<Hertz>,
#[cfg(rcc_l4)]
pub pllsai1_p: Option<Hertz>,
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
pub pllsai: Option<Hertz>,
pub pllsai1_q: Option<Hertz>,
#[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
pub pllsai1_r: Option<Hertz>,
#[cfg(rcc_l4)]
pub pllsai2_p: Option<Hertz>,
#[cfg(any(stm32g4, rcc_l4))]
pub pll1_p: Option<Hertz>,
#[cfg(any(stm32h5, stm32h7, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_l4))]
pub pll1_q: Option<Hertz>,
#[cfg(any(stm32h5, stm32h7))]
pub pll2_p: Option<Hertz>,
#[cfg(any(stm32h5, stm32h7))]
pub pll2_q: Option<Hertz>,
#[cfg(any(stm32h5, stm32h7))]
pub pll2_r: Option<Hertz>,
#[cfg(any(stm32h5, stm32h7))]
pub pll3_p: Option<Hertz>,
#[cfg(any(stm32h5, stm32h7))]
pub pll3_q: Option<Hertz>,
#[cfg(any(stm32h5, stm32h7))]
pub pll3_r: Option<Hertz>,
#[cfg(any(
rcc_f1,
@ -135,79 +153,35 @@ pub struct Clocks {
pub rtc: Option<Hertz>,
#[cfg(any(stm32h5, stm32h7, rcc_l4, rcc_c0))]
pub hsi: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_apb1: Option<Hertz>,
pub hsi48: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_apb2: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_apb3: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_apb4: Option<Hertz>,
pub lsi: Option<Hertz>,
#[cfg(any(stm32h5, stm32h7))]
pub csi: Option<Hertz>,
#[cfg(any(stm32h5, stm32h7, rcc_l4, rcc_c0))]
pub lse: Option<Hertz>,
#[cfg(any(stm32h5, stm32h7))]
pub hse: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_rcc_hclk4: Option<Hertz>,
pub audioclk: Option<Hertz>,
#[cfg(any(stm32h5, stm32h7))]
pub per: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_pll2_q: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_pll3_q: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_hsi: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_csi: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_lse: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_pll1_q: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_pll2_p: Option<Hertz>,
#[cfg(rcc_h5)]
pub mux_pll3_p: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_audioclk: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_per: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_pll3_r: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_pll3_1: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_hsi48_ker: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_lsi: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_pll2_r: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_hse: Option<Hertz>,
#[cfg(stm32h5)]
pub mux_hsi48: Option<Hertz>,
#[cfg(stm32h7)]
pub rcc_pclk_d3: Option<Hertz>,
#[cfg(rcc_l4)]
pub sai1_extclk: Option<Hertz>,
#[cfg(rcc_l4)]
pub sai2_extclk: Option<Hertz>,
}
#[cfg(feature = "low-power")]
static CLOCK_REFCOUNT: AtomicU32 = AtomicU32::new(0);
#[cfg(feature = "low-power")]
pub fn low_power_ready() -> bool {
// trace!("clock refcount: {}", CLOCK_REFCOUNT.load(Ordering::SeqCst));
CLOCK_REFCOUNT.load(Ordering::SeqCst) == 0
}
#[cfg(feature = "low-power")]
pub(crate) fn clock_refcount_add(_cs: critical_section::CriticalSection) {
// We don't check for overflow because constructing more than u32 peripherals is unlikely
let n = CLOCK_REFCOUNT.load(Ordering::Relaxed);
CLOCK_REFCOUNT.store(n + 1, Ordering::Relaxed);
}
#[cfg(feature = "low-power")]
pub(crate) fn clock_refcount_sub(_cs: critical_section::CriticalSection) {
let n = CLOCK_REFCOUNT.load(Ordering::Relaxed);
assert!(n != 0);
CLOCK_REFCOUNT.store(n - 1, Ordering::Relaxed);
}
pub(crate) static mut REFCOUNT_STOP2: u32 = 0;
/// Frozen clock frequencies
///
@ -250,3 +224,33 @@ pub(crate) mod sealed {
}
pub trait RccPeripheral: sealed::RccPeripheral + 'static {}
#[allow(unused)]
mod util {
use crate::time::Hertz;
pub fn calc_pclk<D>(hclk: Hertz, ppre: D) -> (Hertz, Hertz)
where
Hertz: core::ops::Div<D, Output = Hertz>,
{
let pclk = hclk / ppre;
let pclk_tim = if hclk == pclk { pclk } else { pclk * 2u32 };
(pclk, pclk_tim)
}
pub fn all_equal<T: Eq>(mut iter: impl Iterator<Item = T>) -> bool {
let Some(x) = iter.next() else { return true };
if !iter.all(|y| y == x) {
return false;
}
true
}
pub fn get_equal<T: Eq>(mut iter: impl Iterator<Item = T>) -> Result<Option<T>, ()> {
let Some(x) = iter.next() else { return Ok(None) };
if !iter.all(|y| y == x) {
return Err(());
}
Ok(Some(x))
}
}

View File

@ -10,6 +10,7 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000);
pub use crate::pac::pwr::vals::Vos as VoltageScale;
#[derive(Copy, Clone)]
#[allow(non_camel_case_types)]
pub enum ClockSrc {
/// Use an internal medium speed oscillator (MSIS) as the system clock.
MSI(Msirange),
@ -19,9 +20,9 @@ pub enum ClockSrc {
/// never exceed 50 MHz.
HSE(Hertz),
/// Use the 16 MHz internal high speed oscillator as the system clock.
HSI16,
HSI,
/// Use PLL1 as the system clock.
PLL1R(PllConfig),
PLL1_R(PllConfig),
}
impl Default for ClockSrc {
@ -53,10 +54,10 @@ pub struct PllConfig {
}
impl PllConfig {
/// A configuration for HSI16 / 1 * 10 / 1 = 160 MHz
pub const fn hsi16_160mhz() -> Self {
/// A configuration for HSI / 1 * 10 / 1 = 160 MHz
pub const fn hsi_160mhz() -> Self {
PllConfig {
source: PllSrc::HSI16,
source: PllSrc::HSI,
m: Pllm::DIV1,
n: Plln::MUL10,
r: Plldiv::DIV1,
@ -84,7 +85,7 @@ pub enum PllSrc {
/// never exceed 50 MHz.
HSE(Hertz),
/// Use the 16 MHz internal high speed oscillator as the PLL source.
HSI16,
HSI,
}
impl Into<Pllsrc> for PllSrc {
@ -92,7 +93,7 @@ impl Into<Pllsrc> for PllSrc {
match self {
PllSrc::MSIS(..) => Pllsrc::MSIS,
PllSrc::HSE(..) => Pllsrc::HSE,
PllSrc::HSI16 => Pllsrc::HSI16,
PllSrc::HSI => Pllsrc::HSI,
}
}
}
@ -102,8 +103,8 @@ impl Into<Sw> for ClockSrc {
match self {
ClockSrc::MSI(..) => Sw::MSIS,
ClockSrc::HSE(..) => Sw::HSE,
ClockSrc::HSI16 => Sw::HSI16,
ClockSrc::PLL1R(..) => Sw::PLL1_R,
ClockSrc::HSI => Sw::HSI,
ClockSrc::PLL1_R(..) => Sw::PLL1_R,
}
}
}
@ -125,7 +126,7 @@ pub struct Config {
}
impl Config {
unsafe fn init_hsi16(&self) -> Hertz {
unsafe fn init_hsi(&self) -> Hertz {
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
@ -169,7 +170,7 @@ impl Config {
RCC.icscr1().modify(|w| {
w.set_msisrange(range);
w.set_msirgsel(Msirgsel::RCC_ICSCR1);
w.set_msirgsel(Msirgsel::ICSCR1);
});
RCC.cr().write(|w| {
w.set_msipllen(false);
@ -188,7 +189,7 @@ impl Default for Config {
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
apb3_pre: APBPrescaler::DIV1,
hsi48: false,
hsi48: true,
voltage_range: VoltageScale::RANGE3,
ls: Default::default(),
}
@ -211,13 +212,13 @@ pub(crate) unsafe fn init(config: Config) {
let sys_clk = match config.mux {
ClockSrc::MSI(range) => config.init_msis(range),
ClockSrc::HSE(freq) => config.init_hse(freq),
ClockSrc::HSI16 => config.init_hsi16(),
ClockSrc::PLL1R(pll) => {
ClockSrc::HSI => config.init_hsi(),
ClockSrc::PLL1_R(pll) => {
// Configure the PLL source
let source_clk = match pll.source {
PllSrc::MSIS(range) => config.init_msis(range),
PllSrc::HSE(hertz) => config.init_hse(hertz),
PllSrc::HSI16 => config.init_hsi16(),
PllSrc::HSI => config.init_hsi(),
};
// Calculate the reference clock, which is the source divided by m
@ -292,7 +293,7 @@ pub(crate) unsafe fn init(config: Config) {
// Set the prescaler for PWR EPOD
w.set_pllmboost(mboost);
// Enable PLL1R output
// Enable PLL1_R output
w.set_pllren(true);
});
@ -436,14 +437,14 @@ pub(crate) unsafe fn init(config: Config) {
set_freqs(Clocks {
sys: sys_clk,
ahb1: ahb_freq,
ahb2: ahb_freq,
ahb3: ahb_freq,
apb1: apb1_freq,
apb2: apb2_freq,
apb3: apb3_freq,
apb1_tim: apb1_tim_freq,
apb2_tim: apb2_tim_freq,
hclk1: ahb_freq,
hclk2: ahb_freq,
hclk3: ahb_freq,
pclk1: apb1_freq,
pclk2: apb2_freq,
pclk3: apb3_freq,
pclk1_tim: apb1_tim_freq,
pclk2_tim: apb2_tim_freq,
rtc,
});
}

View File

@ -1,248 +0,0 @@
pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Hsepre as HsePrescaler, Pllm, Plln, Pllp, Pllq, Pllr, Pllsrc as PllSource,
Ppre as APBPrescaler, Sw as Sysclk,
};
use crate::rcc::{set_freqs, Clocks};
use crate::time::{mhz, Hertz};
/// HSI speed
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
pub struct Hse {
pub prediv: HsePrescaler,
pub frequency: Hertz,
}
pub struct PllMux {
/// Source clock selection.
pub source: PllSource,
/// PLL pre-divider (DIVM). Must be between 1 and 63.
pub prediv: Pllm,
}
pub struct Pll {
/// PLL multiplication factor. Must be between 4 and 512.
pub mul: Plln,
/// PLL P division factor. If None, PLL P output is disabled. Must be between 1 and 128.
/// On PLL1, it must be even (in particular, it cannot be 1.)
pub divp: Option<Pllp>,
/// PLL Q division factor. If None, PLL Q output is disabled. Must be between 1 and 128.
pub divq: Option<Pllq>,
/// PLL R division factor. If None, PLL R output is disabled. Must be between 1 and 128.
pub divr: Option<Pllr>,
}
/// Clocks configutation
pub struct Config {
pub hse: Option<Hse>,
pub sys: Sysclk,
pub mux: Option<PllMux>,
pub pll: Option<Pll>,
pub pllsai: Option<Pll>,
pub ahb1_pre: AHBPrescaler,
pub ahb2_pre: AHBPrescaler,
pub ahb3_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler,
pub ls: super::LsConfig,
}
pub const WPAN_DEFAULT: Config = Config {
hse: Some(Hse {
frequency: mhz(32),
prediv: HsePrescaler::DIV1,
}),
sys: Sysclk::PLL,
mux: Some(PllMux {
source: PllSource::HSE,
prediv: Pllm::DIV2,
}),
ls: super::LsConfig::default_lse(),
pll: Some(Pll {
mul: Plln::MUL12,
divp: Some(Pllp::DIV3),
divq: Some(Pllq::DIV4),
divr: Some(Pllr::DIV3),
}),
pllsai: None,
ahb1_pre: AHBPrescaler::DIV1,
ahb2_pre: AHBPrescaler::DIV2,
ahb3_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
};
impl Default for Config {
#[inline]
fn default() -> Config {
Config {
hse: None,
sys: Sysclk::HSI16,
mux: None,
pll: None,
pllsai: None,
ls: Default::default(),
ahb1_pre: AHBPrescaler::DIV1,
ahb2_pre: AHBPrescaler::DIV1,
ahb3_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
}
}
}
#[cfg(stm32wb)]
/// RCC initialization function
pub(crate) unsafe fn init(config: Config) {
let hse_clk = config.hse.as_ref().map(|hse| hse.frequency / hse.prediv);
let mux_clk = config.mux.as_ref().map(|pll_mux| {
(match pll_mux.source {
PllSource::HSE => hse_clk.unwrap(),
PllSource::HSI16 => HSI_FREQ,
_ => unreachable!(),
} / pll_mux.prediv)
});
let (pll_r, _pll_q, _pll_p) = match &config.pll {
Some(pll) => {
let pll_vco = mux_clk.unwrap() * pll.mul as u32;
(
pll.divr.map(|divr| pll_vco / divr),
pll.divq.map(|divq| pll_vco / divq),
pll.divp.map(|divp| pll_vco / divp),
)
}
None => (None, None, None),
};
let sys_clk = match config.sys {
Sysclk::HSE => hse_clk.unwrap(),
Sysclk::HSI16 => HSI_FREQ,
Sysclk::PLL => pll_r.unwrap(),
_ => unreachable!(),
};
let ahb1_clk = sys_clk / config.ahb1_pre;
let ahb2_clk = sys_clk / config.ahb2_pre;
let ahb3_clk = sys_clk / config.ahb3_pre;
let (apb1_clk, apb1_tim_clk) = match config.apb1_pre {
APBPrescaler::DIV1 => (ahb1_clk, ahb1_clk),
pre => {
let freq = ahb1_clk / pre;
(freq, freq * 2u32)
}
};
let (apb2_clk, apb2_tim_clk) = match config.apb2_pre {
APBPrescaler::DIV1 => (ahb1_clk, ahb1_clk),
pre => {
let freq = ahb1_clk / pre;
(freq, freq * 2u32)
}
};
let rcc = crate::pac::RCC;
let needs_hsi = if let Some(pll_mux) = &config.mux {
pll_mux.source == PllSource::HSI16
} else {
false
};
if needs_hsi || config.sys == Sysclk::HSI16 {
rcc.cr().modify(|w| {
w.set_hsion(true);
});
while !rcc.cr().read().hsirdy() {}
}
rcc.cfgr().modify(|w| w.set_stopwuck(true));
let rtc = config.ls.init();
match &config.hse {
Some(hse) => {
rcc.cr().modify(|w| {
w.set_hsepre(hse.prediv);
w.set_hseon(true);
});
while !rcc.cr().read().hserdy() {}
}
_ => {}
}
match &config.mux {
Some(pll_mux) => {
rcc.pllcfgr().modify(|w| {
w.set_pllm(pll_mux.prediv);
w.set_pllsrc(pll_mux.source.into());
});
}
_ => {}
};
match &config.pll {
Some(pll) => {
rcc.pllcfgr().modify(|w| {
w.set_plln(pll.mul);
pll.divp.map(|divp| {
w.set_pllpen(true);
w.set_pllp(divp)
});
pll.divq.map(|divq| {
w.set_pllqen(true);
w.set_pllq(divq)
});
pll.divr.map(|divr| {
w.set_pllren(true);
w.set_pllr(divr);
});
});
rcc.cr().modify(|w| w.set_pllon(true));
while !rcc.cr().read().pllrdy() {}
}
_ => {}
}
rcc.cfgr().modify(|w| {
w.set_sw(config.sys.into());
w.set_hpre(config.ahb1_pre);
w.set_ppre1(config.apb1_pre);
w.set_ppre2(config.apb2_pre);
});
rcc.extcfgr().modify(|w| {
w.set_c2hpre(config.ahb2_pre);
w.set_shdhpre(config.ahb3_pre);
});
set_freqs(Clocks {
sys: sys_clk,
ahb1: ahb1_clk,
ahb2: ahb2_clk,
ahb3: ahb3_clk,
apb1: apb1_clk,
apb2: apb2_clk,
apb1_tim: apb1_tim_clk,
apb2_tim: apb2_tim_clk,
rtc,
})
}

View File

@ -13,20 +13,20 @@ pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Ppre as APBPrescaler};
#[derive(Copy, Clone)]
pub enum ClockSrc {
HSE(Hertz),
HSI16,
HSI,
}
#[derive(Clone, Copy, Debug)]
pub enum PllSrc {
HSE(Hertz),
HSI16,
HSI,
}
impl Into<Pllsrc> for PllSrc {
fn into(self) -> Pllsrc {
match self {
PllSrc::HSE(..) => Pllsrc::HSE,
PllSrc::HSI16 => Pllsrc::HSI16,
PllSrc::HSI => Pllsrc::HSI,
}
}
}
@ -35,7 +35,7 @@ impl Into<Sw> for ClockSrc {
fn into(self) -> Sw {
match self {
ClockSrc::HSE(..) => Sw::HSE,
ClockSrc::HSI16 => Sw::HSI16,
ClockSrc::HSI => Sw::HSI,
}
}
}
@ -52,7 +52,7 @@ pub struct Config {
impl Default for Config {
fn default() -> Self {
Self {
mux: ClockSrc::HSI16,
mux: ClockSrc::HSI,
ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
@ -70,7 +70,7 @@ pub(crate) unsafe fn init(config: Config) {
freq
}
ClockSrc::HSI16 => {
ClockSrc::HSI => {
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
@ -142,14 +142,14 @@ pub(crate) unsafe fn init(config: Config) {
set_freqs(Clocks {
sys: sys_clk,
ahb1: ahb_freq,
ahb2: ahb_freq,
ahb4: ahb_freq,
apb1: apb1_freq,
apb2: apb2_freq,
apb7: apb7_freq,
apb1_tim: apb1_tim_freq,
apb2_tim: apb2_tim_freq,
hclk1: ahb_freq,
hclk2: ahb_freq,
hclk4: ahb_freq,
pclk1: apb1_freq,
pclk2: apb2_freq,
pclk7: apb7_freq,
pclk1_tim: apb1_tim_freq,
pclk2_tim: apb2_tim_freq,
rtc,
});
}

View File

@ -1,184 +0,0 @@
pub use crate::pac::pwr::vals::Vos as VoltageScale;
use crate::pac::rcc::vals::Sw;
pub use crate::pac::rcc::vals::{
Adcsel as AdcClockSource, Hpre as AHBPrescaler, Msirange as MSIRange, Pllm, Plln, Pllp, Pllq, Pllr,
Pllsrc as PllSource, Ppre as APBPrescaler,
};
use crate::pac::{FLASH, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
/// HSI speed
pub const HSI_FREQ: Hertz = Hertz(16_000_000);
/// HSE speed
pub const HSE_FREQ: Hertz = Hertz(32_000_000);
/// System clock mux source
#[derive(Clone, Copy)]
pub enum ClockSrc {
MSI(MSIRange),
HSE,
HSI16,
}
/// Clocks configutation
pub struct Config {
pub mux: ClockSrc,
pub ahb_pre: AHBPrescaler,
pub shd_ahb_pre: AHBPrescaler,
pub apb1_pre: APBPrescaler,
pub apb2_pre: APBPrescaler,
pub adc_clock_source: AdcClockSource,
pub ls: super::LsConfig,
}
impl Default for Config {
#[inline]
fn default() -> Config {
Config {
mux: ClockSrc::MSI(MSIRange::RANGE4M),
ahb_pre: AHBPrescaler::DIV1,
shd_ahb_pre: AHBPrescaler::DIV1,
apb1_pre: APBPrescaler::DIV1,
apb2_pre: APBPrescaler::DIV1,
adc_clock_source: AdcClockSource::HSI16,
ls: Default::default(),
}
}
}
pub(crate) unsafe fn init(config: Config) {
let (sys_clk, sw, vos) = match config.mux {
ClockSrc::HSI16 => (HSI_FREQ, Sw::HSI16, VoltageScale::RANGE2),
ClockSrc::HSE => (HSE_FREQ, Sw::HSE, VoltageScale::RANGE1),
ClockSrc::MSI(range) => (msirange_to_hertz(range), Sw::MSI, msirange_to_vos(range)),
};
let ahb_freq = sys_clk / config.ahb_pre;
let shd_ahb_freq = sys_clk / config.shd_ahb_pre;
let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
pre => {
let freq = ahb_freq / pre;
(freq, freq * 2u32)
}
};
let (apb2_freq, apb2_tim_freq) = match config.apb2_pre {
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
pre => {
let freq = ahb_freq / pre;
(freq, freq * 2u32)
}
};
// Adjust flash latency
let flash_clk_src_freq = shd_ahb_freq;
let ws = match vos {
VoltageScale::RANGE1 => match flash_clk_src_freq.0 {
0..=18_000_000 => 0b000,
18_000_001..=36_000_000 => 0b001,
_ => 0b010,
},
VoltageScale::RANGE2 => match flash_clk_src_freq.0 {
0..=6_000_000 => 0b000,
6_000_001..=12_000_000 => 0b001,
_ => 0b010,
},
_ => unreachable!(),
};
FLASH.acr().modify(|w| {
w.set_latency(ws);
});
while FLASH.acr().read().latency() != ws {}
match config.mux {
ClockSrc::HSI16 => {
// Enable HSI16
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
}
ClockSrc::HSE => {
// Enable HSE
RCC.cr().write(|w| {
w.set_hsebyppwr(true);
w.set_hseon(true);
});
while !RCC.cr().read().hserdy() {}
}
ClockSrc::MSI(range) => {
let cr = RCC.cr().read();
assert!(!cr.msion() || cr.msirdy());
RCC.cr().write(|w| {
w.set_msirgsel(true);
w.set_msirange(range);
w.set_msion(true);
// If LSE is enabled, enable calibration of MSI
w.set_msipllen(config.ls.lse.is_some());
});
while !RCC.cr().read().msirdy() {}
}
}
RCC.extcfgr().modify(|w| {
w.set_shdhpre(config.shd_ahb_pre);
});
RCC.cfgr().modify(|w| {
w.set_sw(sw.into());
w.set_hpre(config.ahb_pre);
w.set_ppre1(config.apb1_pre);
w.set_ppre2(config.apb2_pre);
});
// ADC clock MUX
RCC.ccipr().modify(|w| w.set_adcsel(config.adc_clock_source));
// TODO: switch voltage range
let rtc = config.ls.init();
set_freqs(Clocks {
sys: sys_clk,
ahb1: ahb_freq,
ahb2: ahb_freq,
ahb3: shd_ahb_freq,
apb1: apb1_freq,
apb2: apb2_freq,
apb3: shd_ahb_freq,
apb1_tim: apb1_tim_freq,
apb2_tim: apb2_tim_freq,
rtc,
});
}
fn msirange_to_hertz(range: MSIRange) -> Hertz {
match range {
MSIRange::RANGE100K => Hertz(100_000),
MSIRange::RANGE200K => Hertz(200_000),
MSIRange::RANGE400K => Hertz(400_000),
MSIRange::RANGE800K => Hertz(800_000),
MSIRange::RANGE1M => Hertz(1_000_000),
MSIRange::RANGE2M => Hertz(2_000_000),
MSIRange::RANGE4M => Hertz(4_000_000),
MSIRange::RANGE8M => Hertz(8_000_000),
MSIRange::RANGE16M => Hertz(16_000_000),
MSIRange::RANGE24M => Hertz(24_000_000),
MSIRange::RANGE32M => Hertz(32_000_000),
MSIRange::RANGE48M => Hertz(48_000_000),
_ => unreachable!(),
}
}
fn msirange_to_vos(range: MSIRange) -> VoltageScale {
if range.to_bits() > MSIRange::RANGE16M.to_bits() {
VoltageScale::RANGE1
} else {
VoltageScale::RANGE2
}
}

View File

@ -85,7 +85,7 @@ impl<'d, T: Instance> Rng<'d, T> {
reg.set_ie(false);
reg.set_rngen(true);
});
T::regs().cr().write(|reg| {
T::regs().cr().modify(|reg| {
reg.set_ced(false);
});
// wait for CONDRST to be set

View File

@ -4,8 +4,60 @@ use core::convert::From;
#[cfg(feature = "chrono")]
use chrono::{self, Datelike, NaiveDate, Timelike, Weekday};
use super::byte_to_bcd2;
use crate::pac::rtc::Rtc;
#[cfg(any(feature = "defmt", feature = "time"))]
use crate::peripherals::RTC;
#[cfg(any(feature = "defmt", feature = "time"))]
use crate::rtc::sealed::Instance;
/// Represents an instant in time that can be substracted to compute a duration
pub struct RtcInstant {
/// 0..59
pub second: u8,
/// 0..256
pub subsecond: u16,
}
impl RtcInstant {
#[allow(dead_code)]
pub(super) fn from(second: u8, subsecond: u16) -> Result<Self, super::RtcError> {
Ok(Self { second, subsecond })
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for RtcInstant {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(
fmt,
"{}:{}",
self.second,
RTC::regs().prer().read().prediv_s() - self.subsecond,
)
}
}
#[cfg(feature = "time")]
impl core::ops::Sub for RtcInstant {
type Output = embassy_time::Duration;
fn sub(self, rhs: Self) -> Self::Output {
use embassy_time::{Duration, TICK_HZ};
let second = if self.second < rhs.second {
self.second + 60
} else {
self.second
};
let psc = RTC::regs().prer().read().prediv_s() as u32;
let self_ticks = second as u32 * (psc + 1) + (psc - self.subsecond as u32);
let other_ticks = rhs.second as u32 * (psc + 1) + (psc - rhs.subsecond as u32);
let rtc_ticks = self_ticks - other_ticks;
Duration::from_ticks(((rtc_ticks * TICK_HZ as u32) / (psc + 1)) as u64)
}
}
/// Errors regarding the [`DateTime`] struct.
#[derive(Clone, Debug, PartialEq, Eq)]
@ -32,19 +84,85 @@ pub enum Error {
/// Structure containing date and time information
pub struct DateTime {
/// 0..4095
pub year: u16,
year: u16,
/// 1..12, 1 is January
pub month: u8,
month: u8,
/// 1..28,29,30,31 depending on month
pub day: u8,
day: u8,
///
pub day_of_week: DayOfWeek,
day_of_week: DayOfWeek,
/// 0..23
pub hour: u8,
hour: u8,
/// 0..59
pub minute: u8,
minute: u8,
/// 0..59
pub second: u8,
second: u8,
}
impl DateTime {
pub const fn year(&self) -> u16 {
self.year
}
pub const fn month(&self) -> u8 {
self.month
}
pub const fn day(&self) -> u8 {
self.day
}
pub const fn day_of_week(&self) -> DayOfWeek {
self.day_of_week
}
pub const fn hour(&self) -> u8 {
self.hour
}
pub const fn minute(&self) -> u8 {
self.minute
}
pub const fn second(&self) -> u8 {
self.second
}
pub fn from(
year: u16,
month: u8,
day: u8,
day_of_week: u8,
hour: u8,
minute: u8,
second: u8,
) -> Result<Self, Error> {
let day_of_week = day_of_week_from_u8(day_of_week)?;
if year > 4095 {
Err(Error::InvalidYear)
} else if month < 1 || month > 12 {
Err(Error::InvalidMonth)
} else if day < 1 || day > 31 {
Err(Error::InvalidDay)
} else if hour > 23 {
Err(Error::InvalidHour)
} else if minute > 59 {
Err(Error::InvalidMinute)
} else if second > 59 {
Err(Error::InvalidSecond)
} else {
Ok(Self {
year,
month,
day,
day_of_week,
hour,
minute,
second,
})
}
}
}
#[cfg(feature = "chrono")]
@ -142,58 +260,3 @@ pub(super) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
Ok(())
}
}
pub(super) fn write_date_time(rtc: &Rtc, t: DateTime) {
let (ht, hu) = byte_to_bcd2(t.hour as u8);
let (mnt, mnu) = byte_to_bcd2(t.minute as u8);
let (st, su) = byte_to_bcd2(t.second as u8);
let (dt, du) = byte_to_bcd2(t.day as u8);
let (mt, mu) = byte_to_bcd2(t.month as u8);
let yr = t.year as u16;
let yr_offset = (yr - 1970_u16) as u8;
let (yt, yu) = byte_to_bcd2(yr_offset);
use crate::pac::rtc::vals::Ampm;
rtc.tr().write(|w| {
w.set_ht(ht);
w.set_hu(hu);
w.set_mnt(mnt);
w.set_mnu(mnu);
w.set_st(st);
w.set_su(su);
w.set_pm(Ampm::AM);
});
rtc.dr().write(|w| {
w.set_dt(dt);
w.set_du(du);
w.set_mt(mt > 0);
w.set_mu(mu);
w.set_yt(yt);
w.set_yu(yu);
w.set_wdu(day_of_week_to_u8(t.day_of_week));
});
}
pub(super) fn datetime(
year: u16,
month: u8,
day: u8,
day_of_week: u8,
hour: u8,
minute: u8,
second: u8,
) -> Result<DateTime, Error> {
let day_of_week = day_of_week_from_u8(day_of_week)?;
Ok(DateTime {
year,
month,
day,
day_of_week,
hour,
minute,
second,
})
}

View File

@ -9,7 +9,8 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
#[cfg(feature = "low-power")]
use embassy_sync::blocking_mutex::Mutex;
pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError, RtcInstant};
use crate::rtc::datetime::day_of_week_to_u8;
use crate::time::Hertz;
/// refer to AN4759 to compare features of RTC2 and RTC3
@ -39,48 +40,6 @@ pub enum RtcError {
NotRunning,
}
#[cfg(feature = "low-power")]
/// Represents an instant in time that can be substracted to compute a duration
struct RtcInstant {
second: u8,
subsecond: u16,
}
#[cfg(all(feature = "low-power", feature = "defmt"))]
impl defmt::Format for RtcInstant {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(
fmt,
"{}:{}",
self.second,
RTC::regs().prer().read().prediv_s() - self.subsecond,
)
}
}
#[cfg(feature = "low-power")]
impl core::ops::Sub for RtcInstant {
type Output = embassy_time::Duration;
fn sub(self, rhs: Self) -> Self::Output {
use embassy_time::{Duration, TICK_HZ};
let second = if self.second < rhs.second {
self.second + 60
} else {
self.second
};
let psc = RTC::regs().prer().read().prediv_s() as u32;
let self_ticks = second as u32 * (psc + 1) + (psc - self.subsecond as u32);
let other_ticks = rhs.second as u32 * (psc + 1) + (psc - rhs.subsecond as u32);
let rtc_ticks = self_ticks - other_ticks;
Duration::from_ticks(((rtc_ticks * TICK_HZ as u32) / (psc + 1)) as u64)
}
}
pub struct RtcTimeProvider {
_private: (),
}
@ -113,7 +72,7 @@ impl RtcTimeProvider {
let month = bcd2_to_byte((dr.mt() as u8, dr.mu()));
let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16;
return self::datetime::datetime(year, month, day, weekday, hour, minute, second)
return DateTime::from(year, month, day, weekday, hour, minute, second)
.map_err(RtcError::InvalidDateTime);
}
}
@ -134,7 +93,7 @@ impl RtcTimeProvider {
let month = bcd2_to_byte((dr.mt() as u8, dr.mu()));
let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16;
self::datetime::datetime(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime)
DateTime::from(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime)
}
}
}
@ -184,7 +143,13 @@ impl Default for RtcCalibrationCyclePeriod {
impl Rtc {
pub fn new(_rtc: impl Peripheral<P = RTC>, rtc_config: RtcConfig) -> Self {
#[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))]
<RTC as crate::rcc::sealed::RccPeripheral>::enable_and_reset();
critical_section::with(|cs| {
<RTC as crate::rcc::sealed::RccPeripheral>::enable_and_reset_with_cs(cs);
#[cfg(feature = "low-power")]
unsafe {
crate::rcc::REFCOUNT_STOP2 -= 1
};
});
let mut this = Self {
#[cfg(feature = "low-power")]
@ -219,14 +184,46 @@ impl Rtc {
/// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> {
self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?;
self.write(true, |rtc| self::datetime::write_date_time(rtc, t));
self.write(true, |rtc| {
let (ht, hu) = byte_to_bcd2(t.hour() as u8);
let (mnt, mnu) = byte_to_bcd2(t.minute() as u8);
let (st, su) = byte_to_bcd2(t.second() as u8);
let (dt, du) = byte_to_bcd2(t.day() as u8);
let (mt, mu) = byte_to_bcd2(t.month() as u8);
let yr = t.year() as u16;
let yr_offset = (yr - 1970_u16) as u8;
let (yt, yu) = byte_to_bcd2(yr_offset);
use crate::pac::rtc::vals::Ampm;
rtc.tr().write(|w| {
w.set_ht(ht);
w.set_hu(hu);
w.set_mnt(mnt);
w.set_mnu(mnu);
w.set_st(st);
w.set_su(su);
w.set_pm(Ampm::AM);
});
rtc.dr().write(|w| {
w.set_dt(dt);
w.set_du(du);
w.set_mt(mt > 0);
w.set_mu(mu);
w.set_yt(yt);
w.set_yu(yu);
w.set_wdu(day_of_week_to_u8(t.day_of_week()));
});
});
Ok(())
}
#[cfg(feature = "low-power")]
#[cfg(not(rtc_v2f2))]
/// Return the current instant.
fn instant(&self) -> RtcInstant {
pub fn instant(&self) -> Result<RtcInstant, RtcError> {
let r = RTC::regs();
let tr = r.tr().read();
let subsecond = r.ssr().read().ss();
@ -235,7 +232,7 @@ impl Rtc {
// Unlock the registers
r.dr().read();
RtcInstant { second, subsecond }
RtcInstant::from(second, subsecond.try_into().unwrap())
}
/// Return the current datetime.

View File

@ -95,15 +95,16 @@ impl super::Rtc {
regs.cr().modify(|w| w.set_wutie(true));
});
let instant = self.instant().unwrap();
trace!(
"rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}",
Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(),
prescaler as u32,
rtc_ticks,
self.instant(),
instant,
);
assert!(self.stop_time.borrow(cs).replace(Some(self.instant())).is_none())
assert!(self.stop_time.borrow(cs).replace(Some(instant)).is_none())
}
#[cfg(feature = "low-power")]
@ -112,8 +113,9 @@ impl super::Rtc {
pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> {
use crate::interrupt::typelevel::Interrupt;
let instant = self.instant().unwrap();
if RTC::regs().cr().read().wute() {
trace!("rtc: stop wakeup alarm at {}", self.instant());
trace!("rtc: stop wakeup alarm at {}", instant);
self.write(false, |regs| {
regs.cr().modify(|w| w.set_wutie(false));
@ -128,10 +130,7 @@ impl super::Rtc {
});
}
self.stop_time
.borrow(cs)
.take()
.map(|stop_time| self.instant() - stop_time)
self.stop_time.borrow(cs).take().map(|stop_time| instant - stop_time)
}
#[cfg(feature = "low-power")]

View File

@ -1457,7 +1457,7 @@ cfg_if::cfg_if! {
macro_rules! kernel_clk {
($inst:ident) => {
critical_section::with(|_| unsafe {
crate::rcc::get_freqs().pll48
crate::rcc::get_freqs().pll1_q
}).expect("PLL48 is required for SDIO")
}
}
@ -1466,20 +1466,20 @@ cfg_if::cfg_if! {
(SDMMC1) => {
critical_section::with(|_| unsafe {
let sdmmcsel = crate::pac::RCC.dckcfgr2().read().sdmmc1sel();
if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYSCLK {
if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYS {
crate::rcc::get_freqs().sys
} else {
crate::rcc::get_freqs().pll48.expect("PLL48 is required for SDMMC")
crate::rcc::get_freqs().pll1_q.expect("PLL48 is required for SDMMC")
}
})
};
(SDMMC2) => {
critical_section::with(|_| unsafe {
let sdmmcsel = crate::pac::RCC.dckcfgr2().read().sdmmc2sel();
if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYSCLK {
if sdmmcsel == crate::pac::rcc::vals::Sdmmcsel::SYS {
crate::rcc::get_freqs().sys
} else {
crate::rcc::get_freqs().pll48.expect("PLL48 is required for SDMMC")
crate::rcc::get_freqs().pll1_q.expect("PLL48 is required for SDMMC")
}
})
};

View File

@ -57,18 +57,20 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
_ch4: Option<PwmPin<'d, T, Ch4>>,
_ch4n: Option<ComplementaryPwmPin<'d, T, Ch4>>,
freq: Hertz,
counting_mode: CountingMode,
) -> Self {
Self::new_inner(tim, freq)
Self::new_inner(tim, freq, counting_mode)
}
fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz) -> Self {
fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self {
into_ref!(tim);
T::enable_and_reset();
let mut this = Self { inner: tim };
this.inner.set_frequency(freq);
this.inner.set_counting_mode(counting_mode);
this.set_freq(freq);
this.inner.start();
this.inner.enable_outputs();
@ -95,7 +97,12 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
}
pub fn set_freq(&mut self, freq: Hertz) {
self.inner.set_frequency(freq);
let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
2u8
} else {
1u8
};
self.inner.set_frequency(freq * multiplier);
}
pub fn get_max_duty(&self) -> u16 {

View File

@ -29,10 +29,17 @@ pub(crate) mod sealed {
Self::regs().cr1().modify(|r| r.set_cen(false));
}
/// Reset the counter value to 0
fn reset(&mut self) {
Self::regs().cnt().write(|r| r.set_cnt(0));
}
/// Set the frequency of how many times per second the timer counts up to the max value or down to 0.
///
/// This means that in the default edge-aligned mode,
/// the timer counter will wrap around at the same frequency as is being set.
/// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved
/// because it needs to count up and down.
fn set_frequency(&mut self, frequency: Hertz) {
let f = frequency.0;
let timer_f = Self::frequency().0;
@ -85,8 +92,21 @@ pub(crate) mod sealed {
pub trait GeneralPurpose16bitInstance: Basic16bitInstance {
fn regs_gp16() -> crate::pac::timer::TimGp16;
fn set_count_direction(&mut self, direction: vals::Dir) {
Self::regs_gp16().cr1().modify(|r| r.set_dir(direction));
fn set_counting_mode(&mut self, mode: CountingMode) {
let (cms, dir) = mode.into();
let timer_enabled = Self::regs().cr1().read().cen();
// Changing from edge aligned to center aligned (and vice versa) is not allowed while the timer is running.
// Changing direction is discouraged while the timer is running.
assert!(!timer_enabled);
Self::regs_gp16().cr1().modify(|r| r.set_dir(dir));
Self::regs_gp16().cr1().modify(|r| r.set_cms(cms))
}
fn get_counting_mode(&self) -> CountingMode {
let cr1 = Self::regs_gp16().cr1().read();
(cr1.cms(), cr1.dir()).into()
}
fn set_clock_division(&mut self, ckd: vals::Ckd) {
@ -293,6 +313,73 @@ impl From<InputTISelection> for stm32_metapac::timer::vals::CcmrInputCcs {
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum CountingMode {
#[default]
/// The timer counts up to the reload value and then resets back to 0.
EdgeAlignedUp,
/// The timer counts down to 0 and then resets back to the reload value.
EdgeAlignedDown,
/// The timer counts up to the reload value and then counts back to 0.
///
/// The output compare interrupt flags of channels configured in output are
/// set when the counter is counting down.
CenterAlignedDownInterrupts,
/// The timer counts up to the reload value and then counts back to 0.
///
/// The output compare interrupt flags of channels configured in output are
/// set when the counter is counting up.
CenterAlignedUpInterrupts,
/// The timer counts up to the reload value and then counts back to 0.
///
/// The output compare interrupt flags of channels configured in output are
/// set when the counter is counting both up or down.
CenterAlignedBothInterrupts,
}
impl CountingMode {
pub fn is_edge_aligned(&self) -> bool {
match self {
CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown => true,
_ => false,
}
}
pub fn is_center_aligned(&self) -> bool {
match self {
CountingMode::CenterAlignedDownInterrupts
| CountingMode::CenterAlignedUpInterrupts
| CountingMode::CenterAlignedBothInterrupts => true,
_ => false,
}
}
}
impl From<CountingMode> for (vals::Cms, vals::Dir) {
fn from(value: CountingMode) -> Self {
match value {
CountingMode::EdgeAlignedUp => (vals::Cms::EDGEALIGNED, vals::Dir::UP),
CountingMode::EdgeAlignedDown => (vals::Cms::EDGEALIGNED, vals::Dir::DOWN),
CountingMode::CenterAlignedDownInterrupts => (vals::Cms::CENTERALIGNED1, vals::Dir::UP),
CountingMode::CenterAlignedUpInterrupts => (vals::Cms::CENTERALIGNED2, vals::Dir::UP),
CountingMode::CenterAlignedBothInterrupts => (vals::Cms::CENTERALIGNED3, vals::Dir::UP),
}
}
}
impl From<(vals::Cms, vals::Dir)> for CountingMode {
fn from(value: (vals::Cms, vals::Dir)) -> Self {
match value {
(vals::Cms::EDGEALIGNED, vals::Dir::UP) => CountingMode::EdgeAlignedUp,
(vals::Cms::EDGEALIGNED, vals::Dir::DOWN) => CountingMode::EdgeAlignedDown,
(vals::Cms::CENTERALIGNED1, _) => CountingMode::CenterAlignedDownInterrupts,
(vals::Cms::CENTERALIGNED2, _) => CountingMode::CenterAlignedUpInterrupts,
(vals::Cms::CENTERALIGNED3, _) => CountingMode::CenterAlignedBothInterrupts,
}
}
}
#[derive(Clone, Copy)]
pub enum OutputCompareMode {
Frozen,
@ -471,9 +558,5 @@ foreach_interrupt! {
crate::pac::$inst
}
}
};
}

View File

@ -56,18 +56,20 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
_ch3: Option<PwmPin<'d, T, Ch3>>,
_ch4: Option<PwmPin<'d, T, Ch4>>,
freq: Hertz,
counting_mode: CountingMode,
) -> Self {
Self::new_inner(tim, freq)
Self::new_inner(tim, freq, counting_mode)
}
fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz) -> Self {
fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self {
into_ref!(tim);
T::enable_and_reset();
let mut this = Self { inner: tim };
this.inner.set_frequency(freq);
this.inner.set_counting_mode(counting_mode);
this.set_freq(freq);
this.inner.start();
this.inner.enable_outputs();
@ -92,7 +94,12 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
}
pub fn set_freq(&mut self, freq: Hertz) {
self.inner.set_frequency(freq);
let multiplier = if self.inner.get_counting_mode().is_center_aligned() {
2u8
} else {
1u8
};
self.inner.set_frequency(freq * multiplier);
}
pub fn get_max_duty(&self) -> u16 {

View File

@ -116,28 +116,28 @@ pub struct BufferedUartRx<'d, T: BasicInstance> {
impl<'d, T: BasicInstance> SetConfig for BufferedUart<'d, T> {
type Config = Config;
type ConfigError = ();
type ConfigError = ConfigError;
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
self.set_config(config).map_err(|_| ())
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
self.set_config(config)
}
}
impl<'d, T: BasicInstance> SetConfig for BufferedUartRx<'d, T> {
type Config = Config;
type ConfigError = ();
type ConfigError = ConfigError;
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
self.set_config(config).map_err(|_| ())
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
self.set_config(config)
}
}
impl<'d, T: BasicInstance> SetConfig for BufferedUartTx<'d, T> {
type Config = Config;
type ConfigError = ();
type ConfigError = ConfigError;
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
self.set_config(config).map_err(|_| ())
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
self.set_config(config)
}
}
@ -233,9 +233,6 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
configure(r, &config, T::frequency(), T::KIND, true, true)?;
r.cr1().modify(|w| {
#[cfg(lpuart_v2)]
w.set_fifoen(true);
w.set_rxneie(true);
w.set_idleie(true);
});
@ -254,7 +251,14 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
}
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
reconfigure::<T>(config)
reconfigure::<T>(config)?;
T::regs().cr1().modify(|w| {
w.set_rxneie(true);
w.set_idleie(true);
});
Ok(())
}
}
@ -334,7 +338,14 @@ impl<'d, T: BasicInstance> BufferedUartRx<'d, T> {
}
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
reconfigure::<T>(config)
reconfigure::<T>(config)?;
T::regs().cr1().modify(|w| {
w.set_rxneie(true);
w.set_idleie(true);
});
Ok(())
}
}
@ -408,7 +419,14 @@ impl<'d, T: BasicInstance> BufferedUartTx<'d, T> {
}
pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> {
reconfigure::<T>(config)
reconfigure::<T>(config)?;
T::regs().cr1().modify(|w| {
w.set_rxneie(true);
w.set_idleie(true);
});
Ok(())
}
}

View File

@ -108,6 +108,7 @@ pub enum StopBits {
pub enum ConfigError {
BaudrateTooLow,
BaudrateTooHigh,
RxOrTxNotEnabled,
}
#[non_exhaustive]
@ -181,11 +182,11 @@ pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> {
impl<'d, T: BasicInstance, TxDma, RxDma> SetConfig for Uart<'d, T, TxDma, RxDma> {
type Config = Config;
type ConfigError = ();
type ConfigError = ConfigError;
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
self.tx.set_config(config).map_err(|_| ())?;
self.rx.set_config(config).map_err(|_| ())
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
self.tx.set_config(config)?;
self.rx.set_config(config)
}
}
@ -196,10 +197,10 @@ pub struct UartTx<'d, T: BasicInstance, TxDma = NoDma> {
impl<'d, T: BasicInstance, TxDma> SetConfig for UartTx<'d, T, TxDma> {
type Config = Config;
type ConfigError = ();
type ConfigError = ConfigError;
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
self.set_config(config).map_err(|_| ())
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
self.set_config(config)
}
}
@ -213,10 +214,10 @@ pub struct UartRx<'d, T: BasicInstance, RxDma = NoDma> {
impl<'d, T: BasicInstance, RxDma> SetConfig for UartRx<'d, T, RxDma> {
type Config = Config;
type ConfigError = ();
type ConfigError = ConfigError;
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
self.set_config(config).map_err(|_| ())
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
self.set_config(config)
}
}
@ -866,7 +867,7 @@ fn configure(
enable_tx: bool,
) -> Result<(), ConfigError> {
if !enable_rx && !enable_tx {
panic!("USART: At least one of RX or TX should be enabled");
return Err(ConfigError::RxOrTxNotEnabled);
}
#[cfg(not(usart_v4))]
@ -909,6 +910,11 @@ fn configure(
brr + rounding
}
// UART must be disabled during configuration.
r.cr1().modify(|w| {
w.set_ue(false);
});
#[cfg(not(usart_v1))]
let mut over8 = false;
let mut found_brr = None;
@ -968,6 +974,12 @@ fn configure(
#[cfg(any(usart_v3, usart_v4))]
w.set_swap(config.swap_rx_tx);
});
#[cfg(not(usart_v1))]
r.cr3().modify(|w| {
w.set_onebit(config.assume_noise_free);
});
r.cr1().write(|w| {
// enable uart
w.set_ue(true);
@ -976,6 +988,7 @@ fn configure(
// enable receiver
w.set_re(enable_rx);
// configure word size
// if using odd or even parity it must be configured to 9bits
w.set_m0(if config.parity != Parity::ParityNone {
vals::M0::BIT9
} else {
@ -994,11 +1007,6 @@ fn configure(
w.set_fifoen(true);
});
#[cfg(not(usart_v1))]
r.cr3().modify(|w| {
w.set_onebit(config.assume_noise_free);
});
Ok(())
}

View File

@ -18,10 +18,10 @@ pub struct RingBufferedUartRx<'d, T: BasicInstance, RxDma: super::RxDma<T>> {
impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> SetConfig for RingBufferedUartRx<'d, T, RxDma> {
type Config = Config;
type ConfigError = ();
type ConfigError = ConfigError;
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
self.set_config(config).map_err(|_| ())
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
self.set_config(config)
}
}