Merge branch 'main' of github.com:embassy-rs/embassy into rcc-mux
This commit is contained in:
commit
4ee39f6dce
7
ci.sh
7
ci.sh
@ -1,9 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
set -eo pipefail
|
||||
|
||||
export RUSTFLAGS=-Dwarnings
|
||||
export DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
|
||||
if [[ -z "${CARGO_TARGET_DIR}" ]]; then
|
||||
export CARGO_TARGET_DIR=target_ci
|
||||
fi
|
||||
|
||||
TARGET=$(rustc -vV | sed -n 's|host: ||p')
|
||||
|
||||
@ -36,7 +39,7 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly,dhcpv4-hostname \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ieee802154 \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,medium-ieee802154 \
|
||||
|
@ -11,7 +11,7 @@ pub use asynch::Partition;
|
||||
pub use blocking::BlockingPartition;
|
||||
|
||||
/// Partition error
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error<T> {
|
||||
/// The requested flash area is outside the partition
|
||||
|
@ -33,6 +33,7 @@ udp = ["smoltcp/socket-udp"]
|
||||
tcp = ["smoltcp/socket-tcp"]
|
||||
dns = ["smoltcp/socket-dns", "smoltcp/proto-dns"]
|
||||
dhcpv4 = ["proto-ipv4", "medium-ethernet", "smoltcp/socket-dhcpv4"]
|
||||
dhcpv4-hostname = ["dhcpv4"]
|
||||
proto-ipv4 = ["smoltcp/proto-ipv4"]
|
||||
proto-ipv6 = ["smoltcp/proto-ipv6"]
|
||||
medium-ethernet = ["smoltcp/medium-ethernet"]
|
||||
|
@ -56,12 +56,22 @@ const LOCAL_PORT_MIN: u16 = 1025;
|
||||
const LOCAL_PORT_MAX: u16 = 65535;
|
||||
#[cfg(feature = "dns")]
|
||||
const MAX_QUERIES: usize = 4;
|
||||
#[cfg(feature = "dhcpv4-hostname")]
|
||||
const MAX_HOSTNAME_LEN: usize = 32;
|
||||
|
||||
/// Memory resources needed for a network stack.
|
||||
pub struct StackResources<const SOCK: usize> {
|
||||
sockets: [SocketStorage<'static>; SOCK],
|
||||
#[cfg(feature = "dns")]
|
||||
queries: [Option<dns::DnsQuery>; MAX_QUERIES],
|
||||
#[cfg(feature = "dhcpv4-hostname")]
|
||||
hostname: core::cell::UnsafeCell<HostnameResources>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "dhcpv4-hostname")]
|
||||
struct HostnameResources {
|
||||
option: smoltcp::wire::DhcpOption<'static>,
|
||||
data: [u8; MAX_HOSTNAME_LEN],
|
||||
}
|
||||
|
||||
impl<const SOCK: usize> StackResources<SOCK> {
|
||||
@ -73,6 +83,11 @@ impl<const SOCK: usize> StackResources<SOCK> {
|
||||
sockets: [SocketStorage::EMPTY; SOCK],
|
||||
#[cfg(feature = "dns")]
|
||||
queries: [INIT; MAX_QUERIES],
|
||||
#[cfg(feature = "dhcpv4-hostname")]
|
||||
hostname: core::cell::UnsafeCell::new(HostnameResources {
|
||||
option: smoltcp::wire::DhcpOption { kind: 0, data: &[] },
|
||||
data: [0; MAX_HOSTNAME_LEN],
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -104,6 +119,7 @@ pub struct StaticConfigV6 {
|
||||
/// DHCP configuration.
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub struct DhcpConfig {
|
||||
/// Maximum lease duration.
|
||||
///
|
||||
@ -120,6 +136,9 @@ pub struct DhcpConfig {
|
||||
pub server_port: u16,
|
||||
/// Client port. This is almost always 68. Do not change unless you know what you're doing.
|
||||
pub client_port: u16,
|
||||
/// Our hostname. This will be sent to the DHCP server as Option 12.
|
||||
#[cfg(feature = "dhcpv4-hostname")]
|
||||
pub hostname: Option<heapless::String<MAX_HOSTNAME_LEN>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "dhcpv4")]
|
||||
@ -131,6 +150,8 @@ impl Default for DhcpConfig {
|
||||
ignore_naks: Default::default(),
|
||||
server_port: smoltcp::wire::DHCP_SERVER_PORT,
|
||||
client_port: smoltcp::wire::DHCP_CLIENT_PORT,
|
||||
#[cfg(feature = "dhcpv4-hostname")]
|
||||
hostname: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -232,6 +253,8 @@ struct Inner<D: Driver> {
|
||||
dns_socket: SocketHandle,
|
||||
#[cfg(feature = "dns")]
|
||||
dns_waker: WakerRegistration,
|
||||
#[cfg(feature = "dhcpv4-hostname")]
|
||||
hostname: &'static mut core::cell::UnsafeCell<HostnameResources>,
|
||||
}
|
||||
|
||||
pub(crate) struct SocketStack {
|
||||
@ -307,6 +330,8 @@ impl<D: Driver> Stack<D> {
|
||||
)),
|
||||
#[cfg(feature = "dns")]
|
||||
dns_waker: WakerRegistration::new(),
|
||||
#[cfg(feature = "dhcpv4-hostname")]
|
||||
hostname: &mut resources.hostname,
|
||||
};
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
@ -673,6 +698,25 @@ impl<D: Driver> Inner<D> {
|
||||
socket.set_max_lease_duration(c.max_lease_duration.map(crate::time::duration_to_smoltcp));
|
||||
socket.set_ports(c.server_port, c.client_port);
|
||||
socket.set_retry_config(c.retry_config);
|
||||
|
||||
socket.set_outgoing_options(&[]);
|
||||
#[cfg(feature = "dhcpv4-hostname")]
|
||||
if let Some(h) = c.hostname {
|
||||
// safety: we just did set_outgoing_options([]) so we know the socket is no longer holding a reference.
|
||||
let hostname = unsafe { &mut *self.hostname.get() };
|
||||
|
||||
// create data
|
||||
// safety: we know the buffer lives forever, new borrows the StackResources for 'static.
|
||||
// also we won't modify it until next call to this function.
|
||||
hostname.data[..h.len()].copy_from_slice(h.as_bytes());
|
||||
let data: &[u8] = &hostname.data[..h.len()];
|
||||
let data: &'static [u8] = unsafe { core::mem::transmute(data) };
|
||||
|
||||
// set the option.
|
||||
hostname.option = smoltcp::wire::DhcpOption { data, kind: 12 };
|
||||
socket.set_outgoing_options(core::slice::from_ref(&hostname.option));
|
||||
}
|
||||
|
||||
socket.reset();
|
||||
}
|
||||
_ => {
|
||||
|
83
embassy-rp/src/bootsel.rs
Normal file
83
embassy-rp/src/bootsel.rs
Normal file
@ -0,0 +1,83 @@
|
||||
//! Boot Select button
|
||||
//!
|
||||
//! The RP2040 rom supports a BOOTSEL button that is used to enter the USB bootloader
|
||||
//! if held during reset. To avoid wasting GPIO pins, the button is multiplexed onto
|
||||
//! the CS pin of the QSPI flash, but that makes it somewhat expensive and complicated
|
||||
//! to utilize outside of the rom's bootloader.
|
||||
//!
|
||||
//! This module provides functionality to poll BOOTSEL from an embassy application.
|
||||
|
||||
use crate::flash::in_ram;
|
||||
|
||||
impl crate::peripherals::BOOTSEL {
|
||||
/// Polls the BOOTSEL button. Returns true if the button is pressed.
|
||||
///
|
||||
/// Polling isn't cheap, as this function waits for core 1 to finish it's current
|
||||
/// task and for any DMAs from flash to complete
|
||||
pub fn is_pressed(&mut self) -> bool {
|
||||
let mut cs_status = Default::default();
|
||||
|
||||
unsafe { in_ram(|| cs_status = ram_helpers::read_cs_status()) }.expect("Must be called from Core 0");
|
||||
|
||||
// bootsel is active low, so invert
|
||||
!cs_status.infrompad()
|
||||
}
|
||||
}
|
||||
|
||||
mod ram_helpers {
|
||||
use rp_pac::io::regs::GpioStatus;
|
||||
|
||||
/// Temporally reconfigures the CS gpio and returns the GpioStatus.
|
||||
|
||||
/// This function runs from RAM so it can disable flash XIP.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure flash is idle and will remain idle.
|
||||
/// This function must live in ram. It uses inline asm to avoid any
|
||||
/// potential calls to ABI functions that might be in flash.
|
||||
#[inline(never)]
|
||||
#[link_section = ".data.ram_func"]
|
||||
#[cfg(target_arch = "arm")]
|
||||
pub unsafe fn read_cs_status() -> GpioStatus {
|
||||
let result: u32;
|
||||
|
||||
// Magic value, used as both OEOVER::DISABLE and delay loop counter
|
||||
let magic = 0x2000;
|
||||
|
||||
core::arch::asm!(
|
||||
".equiv GPIO_STATUS, 0x0",
|
||||
".equiv GPIO_CTRL, 0x4",
|
||||
|
||||
"ldr {orig_ctrl}, [{cs_gpio}, $GPIO_CTRL]",
|
||||
|
||||
// The BOOTSEL pulls the flash's CS line low though a 1K resistor.
|
||||
// this is weak enough to avoid disrupting normal operation.
|
||||
// But, if we disable CS's output drive and allow it to float...
|
||||
"str {val}, [{cs_gpio}, $GPIO_CTRL]",
|
||||
|
||||
// ...then wait for the state to settle...
|
||||
"1:", // ~4000 cycle delay loop
|
||||
"subs {val}, #8",
|
||||
"bne 1b",
|
||||
|
||||
// ...we can read the current state of bootsel
|
||||
"ldr {val}, [{cs_gpio}, $GPIO_STATUS]",
|
||||
|
||||
// Finally, restore CS to normal operation so XIP can continue
|
||||
"str {orig_ctrl}, [{cs_gpio}, $GPIO_CTRL]",
|
||||
|
||||
cs_gpio = in(reg) rp_pac::IO_QSPI.gpio(1).as_ptr(),
|
||||
orig_ctrl = out(reg) _,
|
||||
val = inout(reg) magic => result,
|
||||
options(nostack),
|
||||
);
|
||||
|
||||
core::mem::transmute(result)
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "arm"))]
|
||||
pub unsafe fn read_cs_status() -> GpioStatus {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
@ -131,7 +131,7 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI
|
||||
|
||||
let len = to - from;
|
||||
|
||||
unsafe { self.in_ram(|| ram_helpers::flash_range_erase(from, len))? };
|
||||
unsafe { in_ram(|| ram_helpers::flash_range_erase(from, len))? };
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -156,7 +156,7 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI
|
||||
|
||||
let unaligned_offset = offset as usize - start;
|
||||
|
||||
unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf))? }
|
||||
unsafe { in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf))? }
|
||||
}
|
||||
|
||||
let remaining_len = bytes.len() - start_padding;
|
||||
@ -174,12 +174,12 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI
|
||||
if bytes.as_ptr() as usize >= 0x2000_0000 {
|
||||
let aligned_data = &bytes[start_padding..end_padding];
|
||||
|
||||
unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data))? }
|
||||
unsafe { in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, aligned_data))? }
|
||||
} else {
|
||||
for chunk in bytes[start_padding..end_padding].chunks_exact(PAGE_SIZE) {
|
||||
let mut ram_buf = [0xFF_u8; PAGE_SIZE];
|
||||
ram_buf.copy_from_slice(chunk);
|
||||
unsafe { self.in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf))? }
|
||||
unsafe { in_ram(|| ram_helpers::flash_range_program(aligned_offset as u32, &ram_buf))? }
|
||||
aligned_offset += PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
@ -194,47 +194,15 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI
|
||||
|
||||
let unaligned_offset = end_offset - (PAGE_SIZE - rem_offset);
|
||||
|
||||
unsafe { self.in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf))? }
|
||||
unsafe { in_ram(|| ram_helpers::flash_range_program(unaligned_offset as u32, &pad_buf))? }
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Make sure to uphold the contract points with rp2040-flash.
|
||||
/// - interrupts must be disabled
|
||||
/// - DMA must not access flash memory
|
||||
unsafe fn in_ram(&mut self, operation: impl FnOnce()) -> Result<(), Error> {
|
||||
// Make sure we're running on CORE0
|
||||
let core_id: u32 = pac::SIO.cpuid().read();
|
||||
if core_id != 0 {
|
||||
return Err(Error::InvalidCore);
|
||||
}
|
||||
|
||||
// Make sure CORE1 is paused during the entire duration of the RAM function
|
||||
crate::multicore::pause_core1();
|
||||
|
||||
critical_section::with(|_| {
|
||||
// Wait for all DMA channels in flash to finish before ram operation
|
||||
const SRAM_LOWER: u32 = 0x2000_0000;
|
||||
for n in 0..crate::dma::CHANNEL_COUNT {
|
||||
let ch = crate::pac::DMA.ch(n);
|
||||
while ch.read_addr().read() < SRAM_LOWER && ch.ctrl_trig().read().busy() {}
|
||||
}
|
||||
// Wait for completion of any background reads
|
||||
while pac::XIP_CTRL.stream_ctr().read().0 > 0 {}
|
||||
|
||||
// Run our flash operation in RAM
|
||||
operation();
|
||||
});
|
||||
|
||||
// Resume CORE1 execution
|
||||
crate::multicore::resume_core1();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read SPI flash unique ID
|
||||
pub fn blocking_unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> {
|
||||
unsafe { self.in_ram(|| ram_helpers::flash_unique_id(uid))? };
|
||||
unsafe { in_ram(|| ram_helpers::flash_unique_id(uid))? };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -242,7 +210,7 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI
|
||||
pub fn blocking_jedec_id(&mut self) -> Result<u32, Error> {
|
||||
let mut jedec = None;
|
||||
unsafe {
|
||||
self.in_ram(|| {
|
||||
in_ram(|| {
|
||||
jedec.replace(ram_helpers::flash_jedec_id());
|
||||
})?;
|
||||
};
|
||||
@ -871,6 +839,38 @@ mod ram_helpers {
|
||||
}
|
||||
}
|
||||
|
||||
/// Make sure to uphold the contract points with rp2040-flash.
|
||||
/// - interrupts must be disabled
|
||||
/// - DMA must not access flash memory
|
||||
pub(crate) unsafe fn in_ram(operation: impl FnOnce()) -> Result<(), Error> {
|
||||
// Make sure we're running on CORE0
|
||||
let core_id: u32 = pac::SIO.cpuid().read();
|
||||
if core_id != 0 {
|
||||
return Err(Error::InvalidCore);
|
||||
}
|
||||
|
||||
// Make sure CORE1 is paused during the entire duration of the RAM function
|
||||
crate::multicore::pause_core1();
|
||||
|
||||
critical_section::with(|_| {
|
||||
// Wait for all DMA channels in flash to finish before ram operation
|
||||
const SRAM_LOWER: u32 = 0x2000_0000;
|
||||
for n in 0..crate::dma::CHANNEL_COUNT {
|
||||
let ch = crate::pac::DMA.ch(n);
|
||||
while ch.read_addr().read() < SRAM_LOWER && ch.ctrl_trig().read().busy() {}
|
||||
}
|
||||
// Wait for completion of any background reads
|
||||
while pac::XIP_CTRL.stream_ctr().read().0 > 0 {}
|
||||
|
||||
// Run our flash operation in RAM
|
||||
operation();
|
||||
});
|
||||
|
||||
// Resume CORE1 execution
|
||||
crate::multicore::resume_core1();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait Instance {}
|
||||
pub trait Mode {}
|
||||
|
@ -12,7 +12,7 @@ use crate::interrupt::typelevel::{Binding, Interrupt};
|
||||
use crate::{interrupt, pac, peripherals, Peripheral};
|
||||
|
||||
/// I2C error abort reason
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum AbortReason {
|
||||
/// A bus operation was not acknowledged, e.g. due to the addressed device
|
||||
@ -27,7 +27,7 @@ pub enum AbortReason {
|
||||
}
|
||||
|
||||
/// I2C error
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
/// I2C abort with error
|
||||
|
@ -10,7 +10,7 @@ use crate::interrupt::typelevel::{Binding, Interrupt};
|
||||
use crate::{pac, Peripheral};
|
||||
|
||||
/// I2C error
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {
|
||||
|
@ -10,6 +10,7 @@ mod critical_section_impl;
|
||||
mod intrinsics;
|
||||
|
||||
pub mod adc;
|
||||
pub mod bootsel;
|
||||
pub mod clocks;
|
||||
pub mod dma;
|
||||
pub mod flash;
|
||||
@ -193,6 +194,7 @@ embassy_hal_internal::peripherals! {
|
||||
PIO1,
|
||||
|
||||
WATCHDOG,
|
||||
BOOTSEL,
|
||||
}
|
||||
|
||||
macro_rules! select_bootloader {
|
||||
|
@ -59,12 +59,11 @@ sdio-host = "0.5.0"
|
||||
embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
|
||||
critical-section = "1.1"
|
||||
atomic-polyfill = "1.0.1"
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-a7bf7f02d1e0bc720c24dbb8881677a298890365" }
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e89b8cfc30e480036aaf502f34c874ee42d68026" }
|
||||
vcell = "0.1.3"
|
||||
bxcan = "0.7.0"
|
||||
nb = "1.0.0"
|
||||
stm32-fmc = "0.3.0"
|
||||
seq-macro = "0.3.0"
|
||||
cfg-if = "1.0.0"
|
||||
embedded-io = { version = "0.6.0" }
|
||||
embedded-io-async = { version = "0.6.0", optional = true }
|
||||
@ -78,7 +77,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-a7bf7f02d1e0bc720c24dbb8881677a298890365", default-features = false, features = ["metadata"]}
|
||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e89b8cfc30e480036aaf502f34c874ee42d68026", default-features = false, features = ["metadata"]}
|
||||
|
||||
|
||||
[features]
|
||||
|
@ -970,6 +970,105 @@ 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 {
|
||||
fn is_rcc_name(e: &str) -> bool {
|
||||
match e {
|
||||
"Pllp" | "Pllq" | "Pllr" | "Pllm" | "Plln" => true,
|
||||
"Timpre" | "Pllrclkpre" => false,
|
||||
e if e.ends_with("pre") || e.ends_with("div") || e.ends_with("mul") => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct Frac {
|
||||
num: u32,
|
||||
denom: u32,
|
||||
}
|
||||
|
||||
impl Frac {
|
||||
fn simplify(self) -> Self {
|
||||
let d = gcd(self.num, self.denom);
|
||||
Self {
|
||||
num: self.num / d,
|
||||
denom: self.denom / d,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gcd(a: u32, b: u32) -> u32 {
|
||||
if b == 0 {
|
||||
return a;
|
||||
}
|
||||
gcd(b, a % b)
|
||||
}
|
||||
|
||||
fn parse_num(n: &str) -> Result<Frac, ()> {
|
||||
for prefix in ["DIV", "MUL"] {
|
||||
if let Some(n) = n.strip_prefix(prefix) {
|
||||
let exponent = n.find('_').map(|e| n.len() - 1 - e).unwrap_or(0) as u32;
|
||||
let mantissa = n.replace('_', "").parse().map_err(|_| ())?;
|
||||
let f = Frac {
|
||||
num: mantissa,
|
||||
denom: 10u32.pow(exponent),
|
||||
};
|
||||
return Ok(f.simplify());
|
||||
}
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
|
||||
if is_rcc_name(e.name) {
|
||||
let enum_name = format_ident!("{}", e.name);
|
||||
let mut muls = Vec::new();
|
||||
let mut divs = Vec::new();
|
||||
for v in e.variants {
|
||||
let Ok(val) = parse_num(v.name) else {
|
||||
panic!("could not parse mul/div. enum={} variant={}", e.name, v.name)
|
||||
};
|
||||
let variant_name = format_ident!("{}", v.name);
|
||||
let variant = quote!(crate::pac::rcc::vals::#enum_name::#variant_name);
|
||||
let num = val.num;
|
||||
let denom = val.denom;
|
||||
muls.push(quote!(#variant => self * #num / #denom,));
|
||||
divs.push(quote!(#variant => self * #denom / #num,));
|
||||
}
|
||||
|
||||
g.extend(quote! {
|
||||
impl core::ops::Div<crate::pac::rcc::vals::#enum_name> for crate::time::Hertz {
|
||||
type Output = crate::time::Hertz;
|
||||
fn div(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output {
|
||||
match rhs {
|
||||
#(#divs)*
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl core::ops::Mul<crate::pac::rcc::vals::#enum_name> for crate::time::Hertz {
|
||||
type Output = crate::time::Hertz;
|
||||
fn mul(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output {
|
||||
match rhs {
|
||||
#(#muls)*
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ========
|
||||
// Write foreach_foo! macrotables
|
||||
|
||||
|
@ -11,7 +11,7 @@ use crate::{peripherals, Peripheral};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Curstom Errors
|
||||
/// Custom Errors
|
||||
pub enum Error {
|
||||
UnconfiguredChannel,
|
||||
InvalidValue,
|
||||
|
@ -14,7 +14,7 @@ pub use timeout::*;
|
||||
|
||||
use crate::peripherals;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
Bus,
|
||||
|
@ -1,56 +0,0 @@
|
||||
use core::ops::Div;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::pac::rcc;
|
||||
pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Ppre as APBPrescaler};
|
||||
use crate::time::Hertz;
|
||||
|
||||
impl Div<AHBPrescaler> for Hertz {
|
||||
type Output = Hertz;
|
||||
|
||||
fn div(self, rhs: AHBPrescaler) -> Self::Output {
|
||||
let divisor = match rhs {
|
||||
AHBPrescaler::DIV1 => 1,
|
||||
AHBPrescaler::DIV2 => 2,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::DIV3 => 3,
|
||||
AHBPrescaler::DIV4 => 4,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::DIV5 => 5,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::DIV6 => 6,
|
||||
AHBPrescaler::DIV8 => 8,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::DIV10 => 10,
|
||||
AHBPrescaler::DIV16 => 16,
|
||||
#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))]
|
||||
AHBPrescaler::DIV32 => 32,
|
||||
#[cfg(not(rcc_wba))]
|
||||
AHBPrescaler::DIV64 => 64,
|
||||
#[cfg(not(rcc_wba))]
|
||||
AHBPrescaler::DIV128 => 128,
|
||||
#[cfg(not(rcc_wba))]
|
||||
AHBPrescaler::DIV256 => 256,
|
||||
#[cfg(not(rcc_wba))]
|
||||
AHBPrescaler::DIV512 => 512,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Hertz(self.0 / divisor)
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<APBPrescaler> for Hertz {
|
||||
type Output = Hertz;
|
||||
|
||||
fn div(self, rhs: APBPrescaler) -> Self::Output {
|
||||
let divisor = match rhs {
|
||||
APBPrescaler::DIV1 => 1,
|
||||
APBPrescaler::DIV2 => 2,
|
||||
APBPrescaler::DIV4 => 4,
|
||||
APBPrescaler::DIV8 => 8,
|
||||
APBPrescaler::DIV16 => 16,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Hertz(self.0 / divisor)
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::pac::flash::vals::Latency;
|
||||
pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Ppre as APBPrescaler};
|
||||
use crate::pac::rcc::vals::{Hsidiv, Ppre, Sw};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
|
@ -1,9 +1,9 @@
|
||||
use core::convert::TryFrom;
|
||||
use core::ops::{Div, Mul};
|
||||
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::pac::flash::vals::Latency;
|
||||
use crate::pac::rcc::vals::{Pllp, Pllsrc, Sw};
|
||||
use crate::pac::rcc::vals::Sw;
|
||||
pub use crate::pac::rcc::vals::{
|
||||
Hpre as AHBPrescaler, Pllm as PLLPreDiv, Plln as PLLMul, Pllp as PLLPDiv, Pllq as PLLQDiv, Pllsrc as PLLSrc,
|
||||
Ppre as APBPrescaler,
|
||||
};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::bd::BackupDomain;
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
@ -43,17 +43,17 @@ pub enum HSESrc {
|
||||
pub struct PLLConfig {
|
||||
pub pre_div: PLLPreDiv,
|
||||
pub mul: PLLMul,
|
||||
pub main_div: PLLMainDiv,
|
||||
pub pll48_div: PLL48Div,
|
||||
pub p_div: PLLPDiv,
|
||||
pub q_div: PLLQDiv,
|
||||
}
|
||||
|
||||
impl Default for PLLConfig {
|
||||
fn default() -> Self {
|
||||
PLLConfig {
|
||||
pre_div: PLLPreDiv(16),
|
||||
mul: PLLMul(192),
|
||||
main_div: PLLMainDiv::Div2,
|
||||
pll48_div: PLL48Div(4),
|
||||
pre_div: PLLPreDiv::DIV16,
|
||||
mul: PLLMul::MUL192,
|
||||
p_div: PLLPDiv::DIV2,
|
||||
q_div: PLLQDiv::DIV4,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -61,9 +61,9 @@ impl Default for PLLConfig {
|
||||
impl PLLConfig {
|
||||
pub fn clocks(&self, src_freq: Hertz) -> PLLClocks {
|
||||
let in_freq = src_freq / self.pre_div;
|
||||
let vco_freq = Hertz((src_freq.0 as u64 * self.mul.0 as u64 / self.pre_div.0 as u64) as u32);
|
||||
let main_freq = vco_freq / self.main_div;
|
||||
let pll48_freq = vco_freq / self.pll48_div;
|
||||
let vco_freq = src_freq / self.pre_div * self.mul;
|
||||
let main_freq = vco_freq / self.p_div;
|
||||
let pll48_freq = vco_freq / self.q_div;
|
||||
PLLClocks {
|
||||
in_freq,
|
||||
vco_freq,
|
||||
@ -72,129 +72,6 @@ impl PLLConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clock source for both main PLL and PLLI2S
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum PLLSrc {
|
||||
HSE,
|
||||
HSI,
|
||||
}
|
||||
|
||||
impl Into<Pllsrc> for PLLSrc {
|
||||
fn into(self) -> Pllsrc {
|
||||
match self {
|
||||
PLLSrc::HSE => Pllsrc::HSE,
|
||||
PLLSrc::HSI => Pllsrc::HSI,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Division factor for both main PLL and PLLI2S
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct PLLPreDiv(u8);
|
||||
|
||||
impl TryFrom<u8> for PLLPreDiv {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
2..=63 => Ok(PLLPreDiv(value)),
|
||||
_ => Err("PLLPreDiv must be within range 2..=63"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<PLLPreDiv> for Hertz {
|
||||
type Output = Hertz;
|
||||
|
||||
fn div(self, rhs: PLLPreDiv) -> Self::Output {
|
||||
Hertz(self.0 / u32::from(rhs.0))
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiplication factor for main PLL
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct PLLMul(u16);
|
||||
|
||||
impl Mul<PLLMul> for Hertz {
|
||||
type Output = Hertz;
|
||||
|
||||
fn mul(self, rhs: PLLMul) -> Self::Output {
|
||||
Hertz(self.0 * u32::from(rhs.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u16> for PLLMul {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
192..=432 => Ok(PLLMul(value)),
|
||||
_ => Err("PLLMul must be within range 192..=432"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PLL division factor for the main system clock
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum PLLMainDiv {
|
||||
Div2,
|
||||
Div4,
|
||||
Div6,
|
||||
Div8,
|
||||
}
|
||||
|
||||
impl Into<Pllp> for PLLMainDiv {
|
||||
fn into(self) -> Pllp {
|
||||
match self {
|
||||
PLLMainDiv::Div2 => Pllp::DIV2,
|
||||
PLLMainDiv::Div4 => Pllp::DIV4,
|
||||
PLLMainDiv::Div6 => Pllp::DIV6,
|
||||
PLLMainDiv::Div8 => Pllp::DIV8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<PLLMainDiv> for Hertz {
|
||||
type Output = Hertz;
|
||||
|
||||
fn div(self, rhs: PLLMainDiv) -> Self::Output {
|
||||
let divisor = match rhs {
|
||||
PLLMainDiv::Div2 => 2,
|
||||
PLLMainDiv::Div4 => 4,
|
||||
PLLMainDiv::Div6 => 6,
|
||||
PLLMainDiv::Div8 => 8,
|
||||
};
|
||||
Hertz(self.0 / divisor)
|
||||
}
|
||||
}
|
||||
|
||||
/// PLL division factor for USB OTG FS / SDIO / RNG
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct PLL48Div(u8);
|
||||
|
||||
impl Div<PLL48Div> for Hertz {
|
||||
type Output = Hertz;
|
||||
|
||||
fn div(self, rhs: PLL48Div) -> Self::Output {
|
||||
Hertz(self.0 / u32::from(rhs.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for PLL48Div {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
2..=15 => Ok(PLL48Div(value)),
|
||||
_ => Err("PLL48Div must be within range 2..=15"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct PLLClocks {
|
||||
pub in_freq: Hertz,
|
||||
@ -367,11 +244,11 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
assert!(pll_clocks.pll48_freq <= Hertz(48_000_000));
|
||||
|
||||
RCC.pllcfgr().write(|w| {
|
||||
w.set_pllsrc(config.pll_mux.into());
|
||||
w.set_pllm(config.pll.pre_div.0);
|
||||
w.set_plln(config.pll.mul.0);
|
||||
w.set_pllp(config.pll.main_div.into());
|
||||
w.set_pllq(config.pll.pll48_div.0);
|
||||
w.set_pllsrc(config.pll_mux);
|
||||
w.set_pllm(config.pll.pre_div);
|
||||
w.set_plln(config.pll.mul);
|
||||
w.set_pllp(config.pll.p_div);
|
||||
w.set_pllq(config.pll.q_div);
|
||||
});
|
||||
|
||||
let (sys_clk, sw) = match config.mux {
|
||||
|
@ -1,3 +1,5 @@
|
||||
use stm32_metapac::rcc::vals::{Pllm, Plln, Pllq, Pllr};
|
||||
|
||||
use crate::pac::rcc::vals::{Hpre, Ppre, Sw};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::rcc::bd::{BackupDomain, RtcClockSource};
|
||||
@ -170,12 +172,12 @@ fn setup_pll(
|
||||
let real_pll48clk = vco_in * plln / pllq;
|
||||
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllm(pllm as u8);
|
||||
w.set_plln(plln as u16);
|
||||
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 as u8);
|
||||
w.set_pllq(Pllq::from_bits(pllq as u8));
|
||||
w.set_pllsrc(Pllsrc::from_bits(use_hse as u8));
|
||||
w.set_pllr(0);
|
||||
w.set_pllr(Pllr::from_bits(0));
|
||||
});
|
||||
|
||||
let real_pllsysclk = vco_in * plln / sysclk_div;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::pac::pwr::vals::Vos;
|
||||
use crate::pac::rcc::vals::{Hpre, Ppre, Sw};
|
||||
use crate::pac::rcc::vals::{Hpre, Pllm, Plln, Pllp, Pllq, Pllsrc, Ppre, Sw};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::rcc::bd::{BackupDomain, RtcClockSource};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
@ -29,8 +29,6 @@ pub struct Config {
|
||||
}
|
||||
|
||||
fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: 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)));
|
||||
@ -84,10 +82,10 @@ fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bo
|
||||
let real_pll48clk = vco_in * plln / pllq;
|
||||
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllm(pllm as u8);
|
||||
w.set_plln(plln as u16);
|
||||
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 as u8);
|
||||
w.set_pllq(Pllq::from_bits(pllq as u8));
|
||||
w.set_pllsrc(Pllsrc::from_bits(use_hse as u8));
|
||||
});
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::pac::flash::vals::Latency;
|
||||
use crate::pac::rcc::vals::{self, Hsidiv, Ppre, Sw};
|
||||
use crate::pac::rcc::vals::{self, Hsidiv, Sw};
|
||||
pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Pllm, Plln, Pllp, Pllq, Pllr, Ppre as APBPrescaler};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
@ -60,15 +60,15 @@ pub struct PllConfig {
|
||||
/// The initial divisor of that clock signal
|
||||
pub m: Pllm,
|
||||
/// The PLL VCO multiplier, which must be in the range `8..=86`.
|
||||
pub n: u8,
|
||||
pub n: Plln,
|
||||
/// The final divisor for `PLLRCLK` output which drives the system clock
|
||||
pub r: Pllr,
|
||||
|
||||
/// The divisor for the `PLLQCLK` output, if desired
|
||||
pub q: Option<Pllr>,
|
||||
pub q: Option<Pllq>,
|
||||
|
||||
/// The divisor for the `PLLPCLK` output, if desired
|
||||
pub p: Option<Pllr>,
|
||||
pub p: Option<Pllp>,
|
||||
}
|
||||
|
||||
impl Default for PllConfig {
|
||||
@ -77,9 +77,9 @@ impl Default for PllConfig {
|
||||
// HSI16 / 1 * 8 / 2 = 64 MHz
|
||||
PllConfig {
|
||||
source: PllSrc::HSI16,
|
||||
m: Pllm::Div1,
|
||||
n: 8,
|
||||
r: Pllr::Div2,
|
||||
m: Pllm::DIV1,
|
||||
n: Plln::MUL8,
|
||||
r: Pllr::DIV2,
|
||||
q: None,
|
||||
p: None,
|
||||
}
|
||||
@ -92,87 +92,6 @@ pub enum PllSrc {
|
||||
HSE(Hertz),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Pllm {
|
||||
Div1,
|
||||
Div2,
|
||||
Div3,
|
||||
Div4,
|
||||
Div5,
|
||||
Div6,
|
||||
Div7,
|
||||
Div8,
|
||||
}
|
||||
|
||||
impl From<Pllm> for u8 {
|
||||
fn from(v: Pllm) -> Self {
|
||||
match v {
|
||||
Pllm::Div1 => 0b000,
|
||||
Pllm::Div2 => 0b001,
|
||||
Pllm::Div3 => 0b010,
|
||||
Pllm::Div4 => 0b011,
|
||||
Pllm::Div5 => 0b100,
|
||||
Pllm::Div6 => 0b101,
|
||||
Pllm::Div7 => 0b110,
|
||||
Pllm::Div8 => 0b111,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Pllm> for u32 {
|
||||
fn from(v: Pllm) -> Self {
|
||||
match v {
|
||||
Pllm::Div1 => 1,
|
||||
Pllm::Div2 => 2,
|
||||
Pllm::Div3 => 3,
|
||||
Pllm::Div4 => 4,
|
||||
Pllm::Div5 => 5,
|
||||
Pllm::Div6 => 6,
|
||||
Pllm::Div7 => 7,
|
||||
Pllm::Div8 => 8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Pllr {
|
||||
Div2,
|
||||
Div3,
|
||||
Div4,
|
||||
Div5,
|
||||
Div6,
|
||||
Div7,
|
||||
Div8,
|
||||
}
|
||||
|
||||
impl From<Pllr> for u8 {
|
||||
fn from(v: Pllr) -> Self {
|
||||
match v {
|
||||
Pllr::Div2 => 0b000,
|
||||
Pllr::Div3 => 0b001,
|
||||
Pllr::Div4 => 0b010,
|
||||
Pllr::Div5 => 0b011,
|
||||
Pllr::Div6 => 0b101,
|
||||
Pllr::Div7 => 0b110,
|
||||
Pllr::Div8 => 0b111,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Pllr> for u32 {
|
||||
fn from(v: Pllr) -> Self {
|
||||
match v {
|
||||
Pllr::Div2 => 2,
|
||||
Pllr::Div3 => 3,
|
||||
Pllr::Div4 => 4,
|
||||
Pllr::Div5 => 5,
|
||||
Pllr::Div6 => 6,
|
||||
Pllr::Div7 => 7,
|
||||
Pllr::Div8 => 8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clocks configutation
|
||||
pub struct Config {
|
||||
pub mux: ClockSrc,
|
||||
@ -194,29 +113,28 @@ impl Default for Config {
|
||||
}
|
||||
|
||||
impl PllConfig {
|
||||
pub(crate) fn init(self) -> u32 {
|
||||
assert!(self.n >= 8 && self.n <= 86);
|
||||
pub(crate) fn init(self) -> Hertz {
|
||||
let (src, input_freq) = match self.source {
|
||||
PllSrc::HSI16 => (vals::Pllsrc::HSI16, HSI_FREQ.0),
|
||||
PllSrc::HSE(freq) => (vals::Pllsrc::HSE, freq.0),
|
||||
PllSrc::HSI16 => (vals::Pllsrc::HSI16, HSI_FREQ),
|
||||
PllSrc::HSE(freq) => (vals::Pllsrc::HSE, freq),
|
||||
};
|
||||
|
||||
let m_freq = input_freq / u32::from(self.m);
|
||||
let m_freq = input_freq / self.m;
|
||||
// RM0454 § 5.4.4:
|
||||
// > Caution: The software must set these bits so that the PLL input frequency after the
|
||||
// > /M divider is between 2.66 and 16 MHz.
|
||||
debug_assert!(m_freq >= 2_660_000 && m_freq <= 16_000_000);
|
||||
debug_assert!(m_freq.0 >= 2_660_000 && m_freq.0 <= 16_000_000);
|
||||
|
||||
let n_freq = m_freq * self.n as u32;
|
||||
// RM0454 § 5.4.4:
|
||||
// > Caution: The software must set these bits so that the VCO output frequency is between
|
||||
// > 64 and 344 MHz.
|
||||
debug_assert!(n_freq >= 64_000_000 && n_freq <= 344_000_000);
|
||||
debug_assert!(n_freq.0 >= 64_000_000 && n_freq.0 <= 344_000_000);
|
||||
|
||||
let r_freq = n_freq / u32::from(self.r);
|
||||
let r_freq = n_freq / self.r;
|
||||
// RM0454 § 5.4.4:
|
||||
// > Caution: The software must set this bitfield so as not to exceed 64 MHz on this clock.
|
||||
debug_assert!(r_freq <= 64_000_000);
|
||||
debug_assert!(r_freq.0 <= 64_000_000);
|
||||
|
||||
// RM0454 § 5.2.3:
|
||||
// > To modify the PLL configuration, proceed as follows:
|
||||
@ -239,25 +157,16 @@ impl PllConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// Configure PLLSYSCFGR
|
||||
RCC.pllsyscfgr().modify(|w| {
|
||||
w.set_pllr(u8::from(self.r));
|
||||
// Configure PLLCFGR
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllr(self.r);
|
||||
w.set_pllren(false);
|
||||
|
||||
if let Some(q) = self.q {
|
||||
w.set_pllq(u8::from(q));
|
||||
}
|
||||
w.set_pllq(self.q.unwrap_or(Pllq::DIV2));
|
||||
w.set_pllqen(false);
|
||||
|
||||
if let Some(p) = self.p {
|
||||
w.set_pllp(u8::from(p));
|
||||
}
|
||||
w.set_pllp(self.p.unwrap_or(Pllp::DIV2));
|
||||
w.set_pllpen(false);
|
||||
|
||||
w.set_plln(self.n);
|
||||
|
||||
w.set_pllm(self.m as u8);
|
||||
|
||||
w.set_pllm(self.m);
|
||||
w.set_pllsrc(src)
|
||||
});
|
||||
|
||||
@ -269,7 +178,7 @@ impl PllConfig {
|
||||
|
||||
// > 5. Enable the desired PLL outputs by configuring PLLPEN, PLLQEN, and PLLREN in PLL
|
||||
// > configuration register (RCC_PLLCFGR).
|
||||
RCC.pllsyscfgr().modify(|w| {
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
// We'll use R for system clock, so enable that unconditionally
|
||||
w.set_pllren(true);
|
||||
|
||||
@ -293,14 +202,14 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
});
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
(HSI_FREQ.0 >> div.to_bits(), Sw::HSI)
|
||||
(HSI_FREQ / div, Sw::HSI)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
(freq.0, Sw::HSE)
|
||||
(freq, Sw::HSE)
|
||||
}
|
||||
ClockSrc::PLL(pll) => {
|
||||
let freq = pll.init();
|
||||
@ -310,15 +219,15 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
// Enable LSI
|
||||
RCC.csr().write(|w| w.set_lsion(true));
|
||||
while !RCC.csr().read().lsirdy() {}
|
||||
(LSI_FREQ.0, Sw::LSI)
|
||||
(LSI_FREQ, Sw::LSI)
|
||||
}
|
||||
};
|
||||
|
||||
// Determine the flash latency implied by the target clock speed
|
||||
// RM0454 § 3.3.4:
|
||||
let target_flash_latency = if sys_clk <= 24_000_000 {
|
||||
let target_flash_latency = if sys_clk.0 <= 24_000_000 {
|
||||
Latency::WS0
|
||||
} else if sys_clk <= 48_000_000 {
|
||||
} else if sys_clk.0 <= 48_000_000 {
|
||||
Latency::WS1
|
||||
} else {
|
||||
Latency::WS2
|
||||
@ -374,27 +283,25 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
FLASH.acr().modify(|w| w.set_latency(target_flash_latency));
|
||||
}
|
||||
|
||||
let ahb_freq = Hertz(sys_clk) / config.ahb_pre;
|
||||
let ahb_freq = sys_clk / config.ahb_pre;
|
||||
|
||||
let (apb_freq, apb_tim_freq) = match config.apb_pre {
|
||||
APBPrescaler::DIV1 => (ahb_freq.0, ahb_freq.0),
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
let freq = ahb_freq.0 / pre as u32;
|
||||
(freq, freq * 2)
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, freq * 2u32)
|
||||
}
|
||||
};
|
||||
|
||||
if config.low_power_run {
|
||||
assert!(sys_clk <= 2_000_000);
|
||||
assert!(sys_clk.0 <= 2_000_000);
|
||||
PWR.cr1().modify(|w| w.set_lpr(true));
|
||||
}
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sys_clk),
|
||||
sys: sys_clk,
|
||||
ahb1: ahb_freq,
|
||||
apb1: Hertz(apb_freq),
|
||||
apb1_tim: Hertz(apb_tim_freq),
|
||||
apb1: apb_freq,
|
||||
apb1_tim: apb_tim_freq,
|
||||
});
|
||||
}
|
||||
|
@ -2,7 +2,9 @@ use stm32_metapac::flash::vals::Latency;
|
||||
use stm32_metapac::rcc::vals::{Adcsel, Pllsrc, Sw};
|
||||
use stm32_metapac::FLASH;
|
||||
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
pub use crate::pac::rcc::vals::{
|
||||
Hpre as AHBPrescaler, Pllm as PllM, Plln as PllN, Pllp as PllP, Pllq as PllQ, Pllr as PllR, Ppre as APBPrescaler,
|
||||
};
|
||||
use crate::pac::{PWR, RCC};
|
||||
use crate::rcc::sealed::RccPeripheral;
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
@ -61,181 +63,6 @@ impl Into<Pllsrc> for PllSrc {
|
||||
}
|
||||
}
|
||||
|
||||
seq_macro::seq!(P in 2..=31 {
|
||||
/// Output divider for the PLL P output.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PllP {
|
||||
// Note: If PLL P is set to 0 the PLLP bit controls the output division. There does not seem to
|
||||
// a good reason to do this so the API does not support it.
|
||||
// Div1 is invalid
|
||||
#(
|
||||
Div~P,
|
||||
)*
|
||||
}
|
||||
|
||||
impl From<PllP> for u8 {
|
||||
/// Returns the register value for the P output divider.
|
||||
fn from(val: PllP) -> u8 {
|
||||
match val {
|
||||
#(
|
||||
PllP::Div~P => P,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
impl PllP {
|
||||
/// Returns the numeric value of the P output divider.
|
||||
pub fn to_div(self) -> u32 {
|
||||
let val: u8 = self.into();
|
||||
val as u32
|
||||
}
|
||||
}
|
||||
|
||||
/// Output divider for the PLL Q output.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PllQ {
|
||||
Div2,
|
||||
Div4,
|
||||
Div6,
|
||||
Div8,
|
||||
}
|
||||
|
||||
impl PllQ {
|
||||
/// Returns the numeric value of the Q output divider.
|
||||
pub fn to_div(self) -> u32 {
|
||||
let val: u8 = self.into();
|
||||
(val as u32 + 1) * 2
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PllQ> for u8 {
|
||||
/// Returns the register value for the Q output divider.
|
||||
fn from(val: PllQ) -> u8 {
|
||||
match val {
|
||||
PllQ::Div2 => 0b00,
|
||||
PllQ::Div4 => 0b01,
|
||||
PllQ::Div6 => 0b10,
|
||||
PllQ::Div8 => 0b11,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Output divider for the PLL R output.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PllR {
|
||||
Div2,
|
||||
Div4,
|
||||
Div6,
|
||||
Div8,
|
||||
}
|
||||
|
||||
impl PllR {
|
||||
/// Returns the numeric value of the R output divider.
|
||||
pub fn to_div(self) -> u32 {
|
||||
let val: u8 = self.into();
|
||||
(val as u32 + 1) * 2
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PllR> for u8 {
|
||||
/// Returns the register value for the R output divider.
|
||||
fn from(val: PllR) -> u8 {
|
||||
match val {
|
||||
PllR::Div2 => 0b00,
|
||||
PllR::Div4 => 0b01,
|
||||
PllR::Div6 => 0b10,
|
||||
PllR::Div8 => 0b11,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
seq_macro::seq!(N in 8..=127 {
|
||||
/// Multiplication factor for the PLL VCO input clock.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PllN {
|
||||
#(
|
||||
Mul~N,
|
||||
)*
|
||||
}
|
||||
|
||||
impl From<PllN> for u8 {
|
||||
/// Returns the register value for the N multiplication factor.
|
||||
fn from(val: PllN) -> u8 {
|
||||
match val {
|
||||
#(
|
||||
PllN::Mul~N => N,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PllN {
|
||||
/// Returns the numeric value of the N multiplication factor.
|
||||
pub fn to_mul(self) -> u32 {
|
||||
match self {
|
||||
#(
|
||||
PllN::Mul~N => N,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/// PLL Pre-division. This must be set such that the PLL input is between 2.66 MHz and 16 MHz.
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum PllM {
|
||||
Div1,
|
||||
Div2,
|
||||
Div3,
|
||||
Div4,
|
||||
Div5,
|
||||
Div6,
|
||||
Div7,
|
||||
Div8,
|
||||
Div9,
|
||||
Div10,
|
||||
Div11,
|
||||
Div12,
|
||||
Div13,
|
||||
Div14,
|
||||
Div15,
|
||||
Div16,
|
||||
}
|
||||
|
||||
impl PllM {
|
||||
/// Returns the numeric value of the M pre-division.
|
||||
pub fn to_div(self) -> u32 {
|
||||
let val: u8 = self.into();
|
||||
val as u32 + 1
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PllM> for u8 {
|
||||
/// Returns the register value for the M pre-division.
|
||||
fn from(val: PllM) -> u8 {
|
||||
match val {
|
||||
PllM::Div1 => 0b0000,
|
||||
PllM::Div2 => 0b0001,
|
||||
PllM::Div3 => 0b0010,
|
||||
PllM::Div4 => 0b0011,
|
||||
PllM::Div5 => 0b0100,
|
||||
PllM::Div6 => 0b0101,
|
||||
PllM::Div7 => 0b0110,
|
||||
PllM::Div8 => 0b0111,
|
||||
PllM::Div9 => 0b1000,
|
||||
PllM::Div10 => 0b1001,
|
||||
PllM::Div11 => 0b1010,
|
||||
PllM::Div12 => 0b1011,
|
||||
PllM::Div13 => 0b1100,
|
||||
PllM::Div14 => 0b1101,
|
||||
PllM::Div15 => 0b1110,
|
||||
PllM::Div16 => 0b1111,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PLL Configuration
|
||||
///
|
||||
/// Use this struct to configure the PLL source, input frequency, multiplication factor, and output
|
||||
@ -360,12 +187,12 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
HSI_FREQ.0
|
||||
HSI_FREQ
|
||||
}
|
||||
PllSrc::HSE(freq) => {
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
freq.0
|
||||
freq
|
||||
}
|
||||
};
|
||||
|
||||
@ -373,7 +200,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
RCC.cr().modify(|w| w.set_pllon(false));
|
||||
while RCC.cr().read().pllrdy() {}
|
||||
|
||||
let internal_freq = src_freq / pll_config.prediv_m.to_div() * pll_config.mul_n.to_mul();
|
||||
let internal_freq = src_freq / pll_config.prediv_m * pll_config.mul_n;
|
||||
|
||||
RCC.pllcfgr().write(|w| {
|
||||
w.set_plln(pll_config.mul_n.into());
|
||||
@ -383,26 +210,26 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
|
||||
let pll_p_freq = pll_config.div_p.map(|div_p| {
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllpdiv(div_p.into());
|
||||
w.set_pllp(div_p);
|
||||
w.set_pllpen(true);
|
||||
});
|
||||
Hertz(internal_freq / div_p.to_div())
|
||||
internal_freq / div_p
|
||||
});
|
||||
|
||||
let pll_q_freq = pll_config.div_q.map(|div_q| {
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllq(div_q.into());
|
||||
w.set_pllq(div_q);
|
||||
w.set_pllqen(true);
|
||||
});
|
||||
Hertz(internal_freq / div_q.to_div())
|
||||
internal_freq / div_q
|
||||
});
|
||||
|
||||
let pll_r_freq = pll_config.div_r.map(|div_r| {
|
||||
RCC.pllcfgr().modify(|w| {
|
||||
w.set_pllr(div_r.into());
|
||||
w.set_pllr(div_r);
|
||||
w.set_pllren(true);
|
||||
});
|
||||
Hertz(internal_freq / div_r.to_div())
|
||||
internal_freq / div_r
|
||||
});
|
||||
|
||||
// Enable the PLL
|
||||
|
@ -6,8 +6,8 @@ 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;
|
||||
pub use crate::pac::rcc::vals::Ckpersel as PerClockSource;
|
||||
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};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
#[cfg(stm32h7)]
|
||||
use crate::rcc::bd::{BackupDomain, LseCfg, RtcClockSource};
|
||||
@ -34,7 +34,7 @@ const VCO_WIDE_RANGE: RangeInclusive<u32> = 192_000_000..=836_000_000;
|
||||
#[cfg(any(pwr_h7rm0399, pwr_h7rm0433))]
|
||||
const VCO_WIDE_RANGE: RangeInclusive<u32> = 192_000_000..=960_000_000;
|
||||
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Ppre as APBPrescaler};
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum VoltageScale {
|
||||
@ -109,19 +109,19 @@ pub struct Pll {
|
||||
#[cfg(stm32h5)]
|
||||
pub source: PllSource,
|
||||
|
||||
/// PLL pre-divider (DIVM). Must be between 1 and 63.
|
||||
pub prediv: u8,
|
||||
/// PLL pre-divider (DIVM).
|
||||
pub prediv: PllPreDiv,
|
||||
|
||||
/// PLL multiplication factor. Must be between 4 and 512.
|
||||
pub mul: u16,
|
||||
/// PLL multiplication factor.
|
||||
pub mul: PllMul,
|
||||
|
||||
/// PLL P division factor. If None, PLL P output is disabled. Must be between 1 and 128.
|
||||
/// PLL P division factor. If None, PLL P output is disabled.
|
||||
/// On PLL1, it must be even (in particular, it cannot be 1.)
|
||||
pub divp: Option<u16>,
|
||||
/// PLL Q division factor. If None, PLL Q output is disabled. Must be between 1 and 128.
|
||||
pub divq: Option<u16>,
|
||||
/// PLL R division factor. If None, PLL R output is disabled. Must be between 1 and 128.
|
||||
pub divr: Option<u16>,
|
||||
pub divp: Option<PllDiv>,
|
||||
/// PLL Q division factor. If None, PLL Q output is disabled.
|
||||
pub divq: Option<PllDiv>,
|
||||
/// PLL R division factor. If None, PLL R output is disabled.
|
||||
pub divr: Option<PllDiv>,
|
||||
}
|
||||
|
||||
fn apb_div_tim(apb: &APBPrescaler, clk: Hertz, tim: TimerPrescaler) -> Hertz {
|
||||
@ -651,9 +651,9 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
||||
|
||||
// "To save power when PLL1 is not used, the value of PLL1M must be set to 0.""
|
||||
#[cfg(stm32h7)]
|
||||
RCC.pllckselr().write(|w| w.set_divm(num, 0));
|
||||
RCC.pllckselr().write(|w| w.set_divm(num, PllPreDiv::from_bits(0)));
|
||||
#[cfg(stm32h5)]
|
||||
RCC.pllcfgr(num).write(|w| w.set_divm(0));
|
||||
RCC.pllcfgr(num).write(|w| w.set_divm(PllPreDiv::from_bits(0)));
|
||||
|
||||
return PllOutput {
|
||||
p: None,
|
||||
@ -662,9 +662,6 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
||||
};
|
||||
};
|
||||
|
||||
assert!(1 <= config.prediv && config.prediv <= 63);
|
||||
assert!(4 <= config.mul && config.mul <= 512);
|
||||
|
||||
#[cfg(stm32h5)]
|
||||
let source = config.source;
|
||||
#[cfg(stm32h7)]
|
||||
@ -700,22 +697,16 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
||||
};
|
||||
|
||||
let p = config.divp.map(|div| {
|
||||
assert!(1 <= div && div <= 128);
|
||||
if num == 0 {
|
||||
// on PLL1, DIVP must be even.
|
||||
assert!(div % 2 == 0);
|
||||
// The enum value is 1 less than the divider, so check it's odd.
|
||||
assert!(div.to_bits() % 2 == 1);
|
||||
}
|
||||
|
||||
vco_clk / div
|
||||
});
|
||||
let q = config.divq.map(|div| {
|
||||
assert!(1 <= div && div <= 128);
|
||||
vco_clk / div
|
||||
});
|
||||
let r = config.divr.map(|div| {
|
||||
assert!(1 <= div && div <= 128);
|
||||
vco_clk / div
|
||||
});
|
||||
let q = config.divq.map(|div| vco_clk / div);
|
||||
let r = config.divr.map(|div| vco_clk / div);
|
||||
|
||||
#[cfg(stm32h5)]
|
||||
RCC.pllcfgr(num).write(|w| {
|
||||
@ -746,10 +737,10 @@ fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
|
||||
}
|
||||
|
||||
RCC.plldivr(num).write(|w| {
|
||||
w.set_plln(config.mul - 1);
|
||||
w.set_pllp((config.divp.unwrap_or(1) - 1) as u8);
|
||||
w.set_pllq((config.divq.unwrap_or(1) - 1) as u8);
|
||||
w.set_pllr((config.divr.unwrap_or(1) - 1) as u8);
|
||||
w.set_plln(config.mul);
|
||||
w.set_pllp(config.divp.unwrap_or(PllDiv::DIV2));
|
||||
w.set_pllq(config.divq.unwrap_or(PllDiv::DIV2));
|
||||
w.set_pllr(config.divr.unwrap_or(PllDiv::DIV2));
|
||||
});
|
||||
|
||||
RCC.cr().modify(|w| w.set_pllon(num, true));
|
||||
|
@ -1,8 +1,8 @@
|
||||
use super::bd::BackupDomain;
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use super::RtcClockSource;
|
||||
pub use crate::pac::pwr::vals::Vos as VoltageScale;
|
||||
use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw};
|
||||
pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Ppre as APBPrescaler};
|
||||
#[cfg(crs)]
|
||||
use crate::pac::{crs, CRS, SYSCFG};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
|
@ -1,5 +1,5 @@
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw};
|
||||
pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Ppre as APBPrescaler};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
|
@ -1,7 +1,9 @@
|
||||
use stm32_metapac::rcc::regs::Cfgr;
|
||||
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw};
|
||||
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::bd::{BackupDomain, RtcClockSource};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
@ -17,62 +19,11 @@ pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ClockSrc {
|
||||
MSI(MSIRange),
|
||||
PLL(PLLSource, PLLClkDiv, PLLSrcDiv, PLLMul, Option<PLL48Div>),
|
||||
PLL(PLLSource, PllRDiv, PllPreDiv, PllMul, Option<PllQDiv>),
|
||||
HSE(Hertz),
|
||||
HSI16,
|
||||
}
|
||||
|
||||
/// MSI Clock Range
|
||||
///
|
||||
/// These ranges control the frequency of the MSI. Internally, these ranges map
|
||||
/// to the `MSIRANGE` bits in the `RCC_ICSCR` register.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum MSIRange {
|
||||
/// Around 100 kHz
|
||||
Range0,
|
||||
/// Around 200 kHz
|
||||
Range1,
|
||||
/// Around 400 kHz
|
||||
Range2,
|
||||
/// Around 800 kHz
|
||||
Range3,
|
||||
/// Around 1 MHz
|
||||
Range4,
|
||||
/// Around 2 MHz
|
||||
Range5,
|
||||
/// Around 4 MHz (reset value)
|
||||
Range6,
|
||||
/// Around 8 MHz
|
||||
Range7,
|
||||
/// Around 16 MHz
|
||||
Range8,
|
||||
/// Around 24 MHz
|
||||
Range9,
|
||||
/// Around 32 MHz
|
||||
Range10,
|
||||
/// Around 48 MHz
|
||||
Range11,
|
||||
}
|
||||
|
||||
impl Default for MSIRange {
|
||||
fn default() -> MSIRange {
|
||||
MSIRange::Range6
|
||||
}
|
||||
}
|
||||
|
||||
pub type PLL48Div = PLLClkDiv;
|
||||
pub type PLLSAI1RDiv = PLLClkDiv;
|
||||
pub type PLLSAI1QDiv = PLLClkDiv;
|
||||
pub type PLLSAI1PDiv = PLLClkDiv;
|
||||
|
||||
/// PLL divider
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PLLDiv {
|
||||
Div2,
|
||||
Div3,
|
||||
Div4,
|
||||
}
|
||||
|
||||
/// PLL clock input source
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PLLSource {
|
||||
@ -81,95 +32,6 @@ pub enum PLLSource {
|
||||
MSI(MSIRange),
|
||||
}
|
||||
|
||||
seq_macro::seq!(N in 8..=86 {
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PLLMul {
|
||||
#(
|
||||
Mul~N,
|
||||
)*
|
||||
}
|
||||
|
||||
impl From<PLLMul> for u8 {
|
||||
fn from(val: PLLMul) -> u8 {
|
||||
match val {
|
||||
#(
|
||||
PLLMul::Mul~N => N,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PLLMul {
|
||||
pub fn to_mul(self) -> u32 {
|
||||
match self {
|
||||
#(
|
||||
PLLMul::Mul~N => N,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PLLClkDiv {
|
||||
Div2,
|
||||
Div4,
|
||||
Div6,
|
||||
Div8,
|
||||
}
|
||||
|
||||
impl PLLClkDiv {
|
||||
pub fn to_div(self) -> u32 {
|
||||
let val: u8 = self.into();
|
||||
(val as u32 + 1) * 2
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PLLClkDiv> for u8 {
|
||||
fn from(val: PLLClkDiv) -> u8 {
|
||||
match val {
|
||||
PLLClkDiv::Div2 => 0b00,
|
||||
PLLClkDiv::Div4 => 0b01,
|
||||
PLLClkDiv::Div6 => 0b10,
|
||||
PLLClkDiv::Div8 => 0b11,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PLLSrcDiv {
|
||||
Div1,
|
||||
Div2,
|
||||
Div3,
|
||||
Div4,
|
||||
Div5,
|
||||
Div6,
|
||||
Div7,
|
||||
Div8,
|
||||
}
|
||||
|
||||
impl PLLSrcDiv {
|
||||
pub fn to_div(self) -> u32 {
|
||||
let val: u8 = self.into();
|
||||
val as u32 + 1
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PLLSrcDiv> for u8 {
|
||||
fn from(val: PLLSrcDiv) -> u8 {
|
||||
match val {
|
||||
PLLSrcDiv::Div1 => 0b000,
|
||||
PLLSrcDiv::Div2 => 0b001,
|
||||
PLLSrcDiv::Div3 => 0b010,
|
||||
PLLSrcDiv::Div4 => 0b011,
|
||||
PLLSrcDiv::Div5 => 0b100,
|
||||
PLLSrcDiv::Div6 => 0b101,
|
||||
PLLSrcDiv::Div7 => 0b110,
|
||||
PLLSrcDiv::Div8 => 0b111,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PLLSource> for Pllsrc {
|
||||
fn from(val: PLLSource) -> Pllsrc {
|
||||
match val {
|
||||
@ -180,57 +42,13 @@ impl From<PLLSource> for Pllsrc {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MSIRange> for Msirange {
|
||||
fn from(val: MSIRange) -> Msirange {
|
||||
match val {
|
||||
MSIRange::Range0 => Msirange::RANGE100K,
|
||||
MSIRange::Range1 => Msirange::RANGE200K,
|
||||
MSIRange::Range2 => Msirange::RANGE400K,
|
||||
MSIRange::Range3 => Msirange::RANGE800K,
|
||||
MSIRange::Range4 => Msirange::RANGE1M,
|
||||
MSIRange::Range5 => Msirange::RANGE2M,
|
||||
MSIRange::Range6 => Msirange::RANGE4M,
|
||||
MSIRange::Range7 => Msirange::RANGE8M,
|
||||
MSIRange::Range8 => Msirange::RANGE16M,
|
||||
MSIRange::Range9 => Msirange::RANGE24M,
|
||||
MSIRange::Range10 => Msirange::RANGE32M,
|
||||
MSIRange::Range11 => Msirange::RANGE48M,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MSIRange> for u32 {
|
||||
fn from(val: MSIRange) -> u32 {
|
||||
match val {
|
||||
MSIRange::Range0 => 100_000,
|
||||
MSIRange::Range1 => 200_000,
|
||||
MSIRange::Range2 => 400_000,
|
||||
MSIRange::Range3 => 800_000,
|
||||
MSIRange::Range4 => 1_000_000,
|
||||
MSIRange::Range5 => 2_000_000,
|
||||
MSIRange::Range6 => 4_000_000,
|
||||
MSIRange::Range7 => 8_000_000,
|
||||
MSIRange::Range8 => 16_000_000,
|
||||
MSIRange::Range9 => 24_000_000,
|
||||
MSIRange::Range10 => 32_000_000,
|
||||
MSIRange::Range11 => 48_000_000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clocks configutation
|
||||
pub struct Config {
|
||||
pub mux: ClockSrc,
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb1_pre: APBPrescaler,
|
||||
pub apb2_pre: APBPrescaler,
|
||||
pub pllsai1: Option<(
|
||||
PLLMul,
|
||||
PLLSrcDiv,
|
||||
Option<PLLSAI1RDiv>,
|
||||
Option<PLLSAI1QDiv>,
|
||||
Option<PLLSAI1PDiv>,
|
||||
)>,
|
||||
pub pllsai1: Option<(PllMul, PllPreDiv, Option<PllRDiv>, Option<PllQDiv>, Option<PllPDiv>)>,
|
||||
#[cfg(not(any(stm32l471, stm32l475, stm32l476, stm32l486)))]
|
||||
pub hsi48: bool,
|
||||
pub rtc_mux: RtcClockSource,
|
||||
@ -242,7 +60,7 @@ impl Default for Config {
|
||||
#[inline]
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
mux: ClockSrc::MSI(MSIRange::Range6),
|
||||
mux: ClockSrc::MSI(MSIRange::RANGE4M),
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
@ -262,7 +80,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
// 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::default().into());
|
||||
w.set_msirange(MSIRange::RANGE4M);
|
||||
w.set_msipllen(false);
|
||||
w.set_msion(true)
|
||||
});
|
||||
@ -298,40 +116,40 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
while !RCC.cr().read().msirdy() {}
|
||||
|
||||
// Enable as clock source for USB, RNG if running at 48 MHz
|
||||
if let MSIRange::Range11 = range {
|
||||
if range == MSIRange::RANGE48M {
|
||||
RCC.ccipr().modify(|w| {
|
||||
w.set_clk48sel(0b11);
|
||||
});
|
||||
}
|
||||
(range.into(), Sw::MSI)
|
||||
(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.0, Sw::HSI16)
|
||||
(HSI_FREQ, Sw::HSI16)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
(freq.0, Sw::HSE)
|
||||
(freq, Sw::HSE)
|
||||
}
|
||||
ClockSrc::PLL(src, div, prediv, mul, pll48div) => {
|
||||
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.0
|
||||
freq
|
||||
}
|
||||
PLLSource::HSI16 => {
|
||||
// Enable HSI
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
HSI_FREQ.0
|
||||
HSI_FREQ
|
||||
}
|
||||
PLLSource::MSI(range) => {
|
||||
// Enable MSI
|
||||
@ -343,7 +161,8 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
w.set_msion(true);
|
||||
});
|
||||
while !RCC.cr().read().msirdy() {}
|
||||
range.into()
|
||||
|
||||
msirange_to_hertz(range)
|
||||
}
|
||||
};
|
||||
|
||||
@ -351,28 +170,28 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
RCC.cr().modify(|w| w.set_pllon(false));
|
||||
while RCC.cr().read().pllrdy() {}
|
||||
|
||||
let freq = (src_freq / prediv.to_div() * mul.to_mul()) / div.to_div();
|
||||
let freq = src_freq / prediv * mul / divr;
|
||||
|
||||
#[cfg(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx))]
|
||||
assert!(freq <= 120_000_000);
|
||||
assert!(freq.0 <= 120_000_000);
|
||||
#[cfg(not(any(stm32l4px, stm32l4qx, stm32l4rx, stm32l4sx)))]
|
||||
assert!(freq <= 80_000_000);
|
||||
assert!(freq.0 <= 80_000_000);
|
||||
|
||||
RCC.pllcfgr().write(move |w| {
|
||||
w.set_plln(mul.into());
|
||||
w.set_pllm(prediv.into());
|
||||
w.set_pllr(div.into());
|
||||
if let Some(pll48div) = pll48div {
|
||||
w.set_pllq(pll48div.into());
|
||||
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(pll48div) = pll48div {
|
||||
let freq = (src_freq / prediv.to_div() * mul.to_mul()) / pll48div.to_div();
|
||||
assert!(freq == 48_000_000);
|
||||
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);
|
||||
});
|
||||
@ -380,25 +199,25 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
|
||||
if let Some((mul, prediv, r_div, q_div, p_div)) = config.pllsai1 {
|
||||
RCC.pllsai1cfgr().write(move |w| {
|
||||
w.set_pllsai1n(mul.into());
|
||||
w.set_pllsai1m(prediv.into());
|
||||
w.set_plln(mul);
|
||||
w.set_pllm(prediv);
|
||||
if let Some(r_div) = r_div {
|
||||
w.set_pllsai1r(r_div.into());
|
||||
w.set_pllsai1ren(true);
|
||||
w.set_pllr(r_div);
|
||||
w.set_pllren(true);
|
||||
}
|
||||
if let Some(q_div) = q_div {
|
||||
w.set_pllsai1q(q_div.into());
|
||||
w.set_pllsai1qen(true);
|
||||
let freq = (src_freq / prediv.to_div() * mul.to_mul()) / q_div.to_div();
|
||||
if freq == 48_000_000 {
|
||||
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_pllsai1pdiv(p_div.into());
|
||||
w.set_pllsai1pen(true);
|
||||
w.set_pllp(p_div);
|
||||
w.set_pllpen(true);
|
||||
}
|
||||
});
|
||||
|
||||
@ -425,17 +244,13 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
|
||||
// Set flash wait states
|
||||
FLASH.acr().modify(|w| {
|
||||
w.set_latency(if sys_clk <= 16_000_000 {
|
||||
0b000
|
||||
} else if sys_clk <= 32_000_000 {
|
||||
0b001
|
||||
} else if sys_clk <= 48_000_000 {
|
||||
0b010
|
||||
} else if sys_clk <= 64_000_000 {
|
||||
0b011
|
||||
} else {
|
||||
0b100
|
||||
});
|
||||
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| {
|
||||
@ -445,43 +260,50 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
w.set_ppre2(config.apb2_pre.into());
|
||||
});
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::DIV1 => sys_clk,
|
||||
pre => {
|
||||
let pre: Hpre = pre.into();
|
||||
let pre = 1 << (pre.to_bits() as u32 - 7);
|
||||
sys_clk / 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 pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
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 pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, freq * 2u32)
|
||||
}
|
||||
};
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sys_clk),
|
||||
ahb1: Hertz(ahb_freq),
|
||||
ahb2: Hertz(ahb_freq),
|
||||
ahb3: Hertz(ahb_freq),
|
||||
apb1: Hertz(apb1_freq),
|
||||
apb2: Hertz(apb2_freq),
|
||||
apb1_tim: Hertz(apb1_tim_freq),
|
||||
apb2_tim: Hertz(apb2_tim_freq),
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
use stm32_metapac::PWR;
|
||||
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
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::bd::RtcClockSource;
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
|
||||
@ -16,62 +19,11 @@ pub const LSI_FREQ: Hertz = Hertz(32_000);
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ClockSrc {
|
||||
MSI(MSIRange),
|
||||
PLL(PLLSource, PLLClkDiv, PLLSrcDiv, PLLMul, Option<PLL48Div>),
|
||||
PLL(PLLSource, PllRDiv, PllPreDiv, PllMul, Option<PllQDiv>),
|
||||
HSE(Hertz),
|
||||
HSI16,
|
||||
}
|
||||
|
||||
/// MSI Clock Range
|
||||
///
|
||||
/// These ranges control the frequency of the MSI. Internally, these ranges map
|
||||
/// to the `MSIRANGE` bits in the `RCC_ICSCR` register.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum MSIRange {
|
||||
/// Around 100 kHz
|
||||
Range0,
|
||||
/// Around 200 kHz
|
||||
Range1,
|
||||
/// Around 400 kHz
|
||||
Range2,
|
||||
/// Around 800 kHz
|
||||
Range3,
|
||||
/// Around 1 MHz
|
||||
Range4,
|
||||
/// Around 2 MHz
|
||||
Range5,
|
||||
/// Around 4 MHz (reset value)
|
||||
Range6,
|
||||
/// Around 8 MHz
|
||||
Range7,
|
||||
/// Around 16 MHz
|
||||
Range8,
|
||||
/// Around 24 MHz
|
||||
Range9,
|
||||
/// Around 32 MHz
|
||||
Range10,
|
||||
/// Around 48 MHz
|
||||
Range11,
|
||||
}
|
||||
|
||||
impl Default for MSIRange {
|
||||
fn default() -> MSIRange {
|
||||
MSIRange::Range6
|
||||
}
|
||||
}
|
||||
|
||||
pub type PLL48Div = PLLClkDiv;
|
||||
pub type PLLSAI1RDiv = PLLClkDiv;
|
||||
pub type PLLSAI1QDiv = PLLClkDiv;
|
||||
pub type PLLSAI1PDiv = PLLClkDiv;
|
||||
|
||||
/// PLL divider
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PLLDiv {
|
||||
Div2,
|
||||
Div3,
|
||||
Div4,
|
||||
}
|
||||
|
||||
/// PLL clock input source
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PLLSource {
|
||||
@ -80,95 +32,6 @@ pub enum PLLSource {
|
||||
MSI(MSIRange),
|
||||
}
|
||||
|
||||
seq_macro::seq!(N in 8..=86 {
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PLLMul {
|
||||
#(
|
||||
Mul~N,
|
||||
)*
|
||||
}
|
||||
|
||||
impl From<PLLMul> for u8 {
|
||||
fn from(val: PLLMul) -> u8 {
|
||||
match val {
|
||||
#(
|
||||
PLLMul::Mul~N => N,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PLLMul {
|
||||
pub fn to_mul(self) -> u32 {
|
||||
match self {
|
||||
#(
|
||||
PLLMul::Mul~N => N,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PLLClkDiv {
|
||||
Div2,
|
||||
Div4,
|
||||
Div6,
|
||||
Div8,
|
||||
}
|
||||
|
||||
impl PLLClkDiv {
|
||||
pub fn to_div(self) -> u32 {
|
||||
let val: u8 = self.into();
|
||||
(val as u32 + 1) * 2
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PLLClkDiv> for u8 {
|
||||
fn from(val: PLLClkDiv) -> u8 {
|
||||
match val {
|
||||
PLLClkDiv::Div2 => 0b00,
|
||||
PLLClkDiv::Div4 => 0b01,
|
||||
PLLClkDiv::Div6 => 0b10,
|
||||
PLLClkDiv::Div8 => 0b11,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PLLSrcDiv {
|
||||
Div1,
|
||||
Div2,
|
||||
Div3,
|
||||
Div4,
|
||||
Div5,
|
||||
Div6,
|
||||
Div7,
|
||||
Div8,
|
||||
}
|
||||
|
||||
impl PLLSrcDiv {
|
||||
pub fn to_div(self) -> u32 {
|
||||
let val: u8 = self.into();
|
||||
val as u32 + 1
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PLLSrcDiv> for u8 {
|
||||
fn from(val: PLLSrcDiv) -> u8 {
|
||||
match val {
|
||||
PLLSrcDiv::Div1 => 0b000,
|
||||
PLLSrcDiv::Div2 => 0b001,
|
||||
PLLSrcDiv::Div3 => 0b010,
|
||||
PLLSrcDiv::Div4 => 0b011,
|
||||
PLLSrcDiv::Div5 => 0b100,
|
||||
PLLSrcDiv::Div6 => 0b101,
|
||||
PLLSrcDiv::Div7 => 0b110,
|
||||
PLLSrcDiv::Div8 => 0b111,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PLLSource> for Pllsrc {
|
||||
fn from(val: PLLSource) -> Pllsrc {
|
||||
match val {
|
||||
@ -179,75 +42,59 @@ impl From<PLLSource> for Pllsrc {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MSIRange> for Msirange {
|
||||
fn from(val: MSIRange) -> Msirange {
|
||||
match val {
|
||||
MSIRange::Range0 => Msirange::RANGE100K,
|
||||
MSIRange::Range1 => Msirange::RANGE200K,
|
||||
MSIRange::Range2 => Msirange::RANGE400K,
|
||||
MSIRange::Range3 => Msirange::RANGE800K,
|
||||
MSIRange::Range4 => Msirange::RANGE1M,
|
||||
MSIRange::Range5 => Msirange::RANGE2M,
|
||||
MSIRange::Range6 => Msirange::RANGE4M,
|
||||
MSIRange::Range7 => Msirange::RANGE8M,
|
||||
MSIRange::Range8 => Msirange::RANGE16M,
|
||||
MSIRange::Range9 => Msirange::RANGE24M,
|
||||
MSIRange::Range10 => Msirange::RANGE32M,
|
||||
MSIRange::Range11 => Msirange::RANGE48M,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MSIRange> for u32 {
|
||||
fn from(val: MSIRange) -> u32 {
|
||||
match val {
|
||||
MSIRange::Range0 => 100_000,
|
||||
MSIRange::Range1 => 200_000,
|
||||
MSIRange::Range2 => 400_000,
|
||||
MSIRange::Range3 => 800_000,
|
||||
MSIRange::Range4 => 1_000_000,
|
||||
MSIRange::Range5 => 2_000_000,
|
||||
MSIRange::Range6 => 4_000_000,
|
||||
MSIRange::Range7 => 8_000_000,
|
||||
MSIRange::Range8 => 16_000_000,
|
||||
MSIRange::Range9 => 24_000_000,
|
||||
MSIRange::Range10 => 32_000_000,
|
||||
MSIRange::Range11 => 48_000_000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clocks configutation
|
||||
pub struct Config {
|
||||
pub mux: ClockSrc,
|
||||
pub ahb_pre: AHBPrescaler,
|
||||
pub apb1_pre: APBPrescaler,
|
||||
pub apb2_pre: APBPrescaler,
|
||||
pub pllsai1: Option<(
|
||||
PLLMul,
|
||||
PLLSrcDiv,
|
||||
Option<PLLSAI1RDiv>,
|
||||
Option<PLLSAI1QDiv>,
|
||||
Option<PLLSAI1PDiv>,
|
||||
)>,
|
||||
pub pllsai1: Option<(PllMul, PllPreDiv, Option<PllRDiv>, Option<PllQDiv>, Option<PllPDiv>)>,
|
||||
pub hsi48: bool,
|
||||
pub rtc_mux: RtcClockSource,
|
||||
pub lse: Option<Hertz>,
|
||||
pub lsi: bool,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
#[inline]
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
mux: ClockSrc::MSI(MSIRange::Range6),
|
||||
mux: ClockSrc::MSI(MSIRange::RANGE4M),
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
pllsai1: None,
|
||||
hsi48: false,
|
||||
rtc_mux: RtcClockSource::LSI,
|
||||
lsi: true,
|
||||
lse: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {}
|
||||
}
|
||||
|
||||
//BackupDomain::configure_ls(config.rtc_mux, config.lsi, config.lse.map(|_| Default::default()));
|
||||
|
||||
PWR.cr1().modify(|w| w.set_vos(stm32_metapac::pwr::vals::Vos::RANGE0));
|
||||
let (sys_clk, sw) = match config.mux {
|
||||
ClockSrc::MSI(range) => {
|
||||
@ -255,47 +102,53 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
RCC.cr().write(|w| {
|
||||
let bits: Msirange = range.into();
|
||||
w.set_msirange(bits);
|
||||
w.set_msipllen(false);
|
||||
w.set_msirgsel(true);
|
||||
w.set_msion(true);
|
||||
|
||||
if config.rtc_mux == RtcClockSource::LSE {
|
||||
// If LSE is enabled, enable calibration of MSI
|
||||
w.set_msipllen(true);
|
||||
} else {
|
||||
w.set_msipllen(false);
|
||||
}
|
||||
});
|
||||
while !RCC.cr().read().msirdy() {}
|
||||
|
||||
// Enable as clock source for USB, RNG if running at 48 MHz
|
||||
if let MSIRange::Range11 = range {
|
||||
if range == MSIRange::RANGE48M {
|
||||
RCC.ccipr1().modify(|w| {
|
||||
w.set_clk48msel(0b11);
|
||||
});
|
||||
}
|
||||
(range.into(), Sw::MSI)
|
||||
(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.0, Sw::HSI16)
|
||||
(HSI_FREQ, Sw::HSI16)
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
// Enable HSE
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
(freq.0, Sw::HSE)
|
||||
(freq, Sw::HSE)
|
||||
}
|
||||
ClockSrc::PLL(src, div, prediv, mul, pll48div) => {
|
||||
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.0
|
||||
freq
|
||||
}
|
||||
PLLSource::HSI16 => {
|
||||
// Enable HSI
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
HSI_FREQ.0
|
||||
HSI_FREQ
|
||||
}
|
||||
PLLSource::MSI(range) => {
|
||||
// Enable MSI
|
||||
@ -307,7 +160,8 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
w.set_msion(true);
|
||||
});
|
||||
while !RCC.cr().read().msirdy() {}
|
||||
range.into()
|
||||
|
||||
msirange_to_hertz(range)
|
||||
}
|
||||
};
|
||||
|
||||
@ -315,23 +169,23 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
RCC.cr().modify(|w| w.set_pllon(false));
|
||||
while RCC.cr().read().pllrdy() {}
|
||||
|
||||
let freq = (src_freq / prediv.to_div() * mul.to_mul()) / div.to_div();
|
||||
let freq = src_freq / prediv * mul / divr;
|
||||
|
||||
RCC.pllcfgr().write(move |w| {
|
||||
w.set_plln(mul.into());
|
||||
w.set_pllm(prediv.into());
|
||||
w.set_pllr(div.into());
|
||||
if let Some(pll48div) = pll48div {
|
||||
w.set_pllq(pll48div.into());
|
||||
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(pll48div) = pll48div {
|
||||
let freq = (src_freq / prediv.to_div() * mul.to_mul()) / pll48div.to_div();
|
||||
assert!(freq == 48_000_000);
|
||||
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);
|
||||
});
|
||||
@ -339,25 +193,25 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
|
||||
if let Some((mul, prediv, r_div, q_div, p_div)) = config.pllsai1 {
|
||||
RCC.pllsai1cfgr().write(move |w| {
|
||||
w.set_pllsai1n(mul.into());
|
||||
w.set_pllsai1m(prediv.into());
|
||||
w.set_plln(mul);
|
||||
w.set_pllm(prediv);
|
||||
if let Some(r_div) = r_div {
|
||||
w.set_pllsai1r(r_div.into());
|
||||
w.set_pllsai1ren(true);
|
||||
w.set_pllr(r_div);
|
||||
w.set_pllren(true);
|
||||
}
|
||||
if let Some(q_div) = q_div {
|
||||
w.set_pllsai1q(q_div.into());
|
||||
w.set_pllsai1qen(true);
|
||||
let freq = (src_freq / prediv.to_div() * mul.to_mul()) / q_div.to_div();
|
||||
if freq == 48_000_000 {
|
||||
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_pllsai1pdiv(p_div.into());
|
||||
w.set_pllsai1pen(true);
|
||||
w.set_pllp(p_div);
|
||||
w.set_pllpen(true);
|
||||
}
|
||||
});
|
||||
|
||||
@ -384,7 +238,7 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
// Set flash wait states
|
||||
// VCORE Range 0 (performance), others TODO
|
||||
FLASH.acr().modify(|w| {
|
||||
w.set_latency(match sys_clk {
|
||||
w.set_latency(match sys_clk.0 {
|
||||
0..=20_000_000 => 0,
|
||||
0..=40_000_000 => 1,
|
||||
0..=60_000_000 => 2,
|
||||
@ -401,43 +255,50 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
w.set_ppre2(config.apb2_pre.into());
|
||||
});
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::DIV1 => sys_clk,
|
||||
pre => {
|
||||
let pre: Hpre = pre.into();
|
||||
let pre = 1 << (pre.to_bits() as u32 - 7);
|
||||
sys_clk / 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 pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
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 pre: Ppre = pre.into();
|
||||
let pre: u8 = 1 << (pre.to_bits() - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, freq * 2u32)
|
||||
}
|
||||
};
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sys_clk),
|
||||
ahb1: Hertz(ahb_freq),
|
||||
ahb2: Hertz(ahb_freq),
|
||||
ahb3: Hertz(ahb_freq),
|
||||
apb1: Hertz(apb1_freq),
|
||||
apb2: Hertz(apb2_freq),
|
||||
apb1_tim: Hertz(apb1_tim_freq),
|
||||
apb2_tim: Hertz(apb2_tim_freq),
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ pub use crate::rcc::bd::RtcClockSource;
|
||||
use crate::time::Hertz;
|
||||
|
||||
pub(crate) mod bd;
|
||||
mod bus;
|
||||
mod mco;
|
||||
pub use mco::*;
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
use stm32_metapac::rcc::vals::{Msirange, Msirgsel, Pllm, Pllsrc, Sw};
|
||||
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Msirange, Plldiv, Pllm, Plln, Ppre as APBPrescaler};
|
||||
use crate::pac::rcc::vals::{Msirgsel, Pllmboost, Pllrge, Pllsrc, Sw};
|
||||
use crate::pac::{FLASH, PWR, RCC};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
use crate::time::Hertz;
|
||||
|
||||
@ -15,110 +14,92 @@ pub use crate::pac::pwr::vals::Vos as VoltageScale;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ClockSrc {
|
||||
MSI(MSIRange),
|
||||
/// Use an internal medium speed oscillator (MSIS) as the system clock.
|
||||
MSI(Msirange),
|
||||
/// Use the external high speed clock as the system clock.
|
||||
///
|
||||
/// HSE clocks faster than 25 MHz require at least `VoltageScale::RANGE3`, and HSE clocks must
|
||||
/// never exceed 50 MHz.
|
||||
HSE(Hertz),
|
||||
/// Use the 16 MHz internal high speed oscillator as the system clock.
|
||||
HSI16,
|
||||
PLL1R(PllSrc, PllM, PllN, PllClkDiv),
|
||||
/// Use PLL1 as the system clock.
|
||||
PLL1R(PllConfig),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
impl Default for ClockSrc {
|
||||
fn default() -> Self {
|
||||
// The default system clock source is MSIS @ 4 MHz, per RM0456 § 11.4.9
|
||||
ClockSrc::MSI(Msirange::RANGE_4MHZ)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct PllConfig {
|
||||
/// The clock source for the PLL.
|
||||
pub source: PllSrc,
|
||||
/// The PLL prescaler.
|
||||
///
|
||||
/// The clock speed of the `source` divided by `m` must be between 4 and 16 MHz.
|
||||
pub m: Pllm,
|
||||
/// The PLL multiplier.
|
||||
///
|
||||
/// The multiplied clock – `source` divided by `m` times `n` – must be between 128 and 544
|
||||
/// MHz. The upper limit may be lower depending on the `Config { voltage_range }`.
|
||||
pub n: Plln,
|
||||
/// The divider for the R output.
|
||||
///
|
||||
/// When used to drive the system clock, `source` divided by `m` times `n` divided by `r`
|
||||
/// must not exceed 160 MHz. System clocks above 55 MHz require a non-default
|
||||
/// `Config { voltage_range }`.
|
||||
pub r: Plldiv,
|
||||
}
|
||||
|
||||
impl PllConfig {
|
||||
/// A configuration for HSI16 / 1 * 10 / 1 = 160 MHz
|
||||
pub const fn hsi16_160mhz() -> Self {
|
||||
PllConfig {
|
||||
source: PllSrc::HSI16,
|
||||
m: Pllm::DIV1,
|
||||
n: Plln::MUL10,
|
||||
r: Plldiv::DIV1,
|
||||
}
|
||||
}
|
||||
|
||||
/// A configuration for MSIS @ 48 MHz / 3 * 10 / 1 = 160 MHz
|
||||
pub const fn msis_160mhz() -> Self {
|
||||
PllConfig {
|
||||
source: PllSrc::MSIS(Msirange::RANGE_48MHZ),
|
||||
m: Pllm::DIV3,
|
||||
n: Plln::MUL10,
|
||||
r: Plldiv::DIV1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PllSrc {
|
||||
MSI(MSIRange),
|
||||
/// Use an internal medium speed oscillator as the PLL source.
|
||||
MSIS(Msirange),
|
||||
/// Use the external high speed clock as the system PLL source.
|
||||
///
|
||||
/// HSE clocks faster than 25 MHz require at least `VoltageScale::RANGE3`, and HSE clocks must
|
||||
/// never exceed 50 MHz.
|
||||
HSE(Hertz),
|
||||
/// Use the 16 MHz internal high speed oscillator as the PLL source.
|
||||
HSI16,
|
||||
}
|
||||
|
||||
impl Into<Pllsrc> for PllSrc {
|
||||
fn into(self) -> Pllsrc {
|
||||
match self {
|
||||
PllSrc::MSI(..) => Pllsrc::MSIS,
|
||||
PllSrc::MSIS(..) => Pllsrc::MSIS,
|
||||
PllSrc::HSE(..) => Pllsrc::HSE,
|
||||
PllSrc::HSI16 => Pllsrc::HSI16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
seq_macro::seq!(N in 2..=128 {
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum PllClkDiv {
|
||||
NotDivided,
|
||||
#(
|
||||
Div~N = (N-1),
|
||||
)*
|
||||
}
|
||||
|
||||
impl PllClkDiv {
|
||||
fn to_div(&self) -> u8 {
|
||||
match self {
|
||||
PllClkDiv::NotDivided => 1,
|
||||
#(
|
||||
PllClkDiv::Div~N => N + 1,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
impl Into<u8> for PllClkDiv {
|
||||
fn into(self) -> u8 {
|
||||
(self as u8) + 1
|
||||
}
|
||||
}
|
||||
|
||||
seq_macro::seq!(N in 4..=512 {
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum PllN {
|
||||
NotMultiplied,
|
||||
#(
|
||||
Mul~N = N-1,
|
||||
)*
|
||||
}
|
||||
|
||||
impl PllN {
|
||||
fn to_mul(&self) -> u16 {
|
||||
match self {
|
||||
PllN::NotMultiplied => 1,
|
||||
#(
|
||||
PllN::Mul~N => N + 1,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
impl Into<u16> for PllN {
|
||||
fn into(self) -> u16 {
|
||||
(self as u16) + 1
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-division
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum PllM {
|
||||
NotDivided = 0b0000,
|
||||
Div2 = 0b0001,
|
||||
Div3 = 0b0010,
|
||||
Div4 = 0b0011,
|
||||
Div5 = 0b0100,
|
||||
Div6 = 0b0101,
|
||||
Div7 = 0b0110,
|
||||
Div8 = 0b0111,
|
||||
Div9 = 0b1000,
|
||||
Div10 = 0b1001,
|
||||
Div11 = 0b1010,
|
||||
Div12 = 0b1011,
|
||||
Div13 = 0b1100,
|
||||
Div14 = 0b1101,
|
||||
Div15 = 0b1110,
|
||||
Div16 = 0b1111,
|
||||
}
|
||||
|
||||
impl Into<Pllm> for PllM {
|
||||
fn into(self) -> Pllm {
|
||||
Pllm::from_bits(self as u8)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Sw> for ClockSrc {
|
||||
fn into(self) -> Sw {
|
||||
match self {
|
||||
@ -130,61 +111,6 @@ impl Into<Sw> for ClockSrc {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum MSIRange {
|
||||
Range48mhz = 48_000_000,
|
||||
Range24mhz = 24_000_000,
|
||||
Range16mhz = 16_000_000,
|
||||
Range12mhz = 12_000_000,
|
||||
Range4mhz = 4_000_000,
|
||||
Range2mhz = 2_000_000,
|
||||
Range1_33mhz = 1_330_000,
|
||||
Range1mhz = 1_000_000,
|
||||
Range3_072mhz = 3_072_000,
|
||||
Range1_536mhz = 1_536_000,
|
||||
Range1_024mhz = 1_024_000,
|
||||
Range768khz = 768_000,
|
||||
Range400khz = 400_000,
|
||||
Range200khz = 200_000,
|
||||
Range133khz = 133_000,
|
||||
Range100khz = 100_000,
|
||||
}
|
||||
|
||||
impl Into<u32> for MSIRange {
|
||||
fn into(self) -> u32 {
|
||||
self as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Msirange> for MSIRange {
|
||||
fn into(self) -> Msirange {
|
||||
match self {
|
||||
MSIRange::Range48mhz => Msirange::RANGE_48MHZ,
|
||||
MSIRange::Range24mhz => Msirange::RANGE_24MHZ,
|
||||
MSIRange::Range16mhz => Msirange::RANGE_16MHZ,
|
||||
MSIRange::Range12mhz => Msirange::RANGE_12MHZ,
|
||||
MSIRange::Range4mhz => Msirange::RANGE_4MHZ,
|
||||
MSIRange::Range2mhz => Msirange::RANGE_2MHZ,
|
||||
MSIRange::Range1_33mhz => Msirange::RANGE_1_33MHZ,
|
||||
MSIRange::Range1mhz => Msirange::RANGE_1MHZ,
|
||||
MSIRange::Range3_072mhz => Msirange::RANGE_3_072MHZ,
|
||||
MSIRange::Range1_536mhz => Msirange::RANGE_1_536MHZ,
|
||||
MSIRange::Range1_024mhz => Msirange::RANGE_1_024MHZ,
|
||||
MSIRange::Range768khz => Msirange::RANGE_768KHZ,
|
||||
MSIRange::Range400khz => Msirange::RANGE_400KHZ,
|
||||
MSIRange::Range200khz => Msirange::RANGE_200KHZ,
|
||||
MSIRange::Range133khz => Msirange::RANGE_133KHZ,
|
||||
MSIRange::Range100khz => Msirange::RANGE_100KHZ,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MSIRange {
|
||||
fn default() -> Self {
|
||||
MSIRange::Range4mhz
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Config {
|
||||
pub mux: ClockSrc,
|
||||
@ -193,27 +119,59 @@ pub struct Config {
|
||||
pub apb2_pre: APBPrescaler,
|
||||
pub apb3_pre: APBPrescaler,
|
||||
pub hsi48: bool,
|
||||
/// The voltage range influences the maximum clock frequencies for different parts of the
|
||||
/// device. In particular, system clocks exceeding 110 MHz require `RANGE1`, and system clocks
|
||||
/// exceeding 55 MHz require at least `RANGE2`.
|
||||
///
|
||||
/// See RM0456 § 10.5.4 for a general overview and § 11.4.10 for clock source frequency limits.
|
||||
pub voltage_range: VoltageScale,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
mux: ClockSrc::MSI(MSIRange::default()),
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
apb3_pre: APBPrescaler::DIV1,
|
||||
hsi48: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Config {
|
||||
unsafe fn init_hsi16(&self) -> Hertz {
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
|
||||
HSI_FREQ
|
||||
}
|
||||
|
||||
unsafe fn init_hse(&self, frequency: Hertz) -> Hertz {
|
||||
// Check frequency limits per RM456 § 11.4.10
|
||||
match self.voltage_range {
|
||||
VoltageScale::RANGE1 | VoltageScale::RANGE2 | VoltageScale::RANGE3 => {
|
||||
assert!(frequency.0 <= 50_000_000);
|
||||
}
|
||||
VoltageScale::RANGE4 => {
|
||||
assert!(frequency.0 <= 25_000_000);
|
||||
}
|
||||
}
|
||||
|
||||
// Enable HSE, and wait for it to stabilize
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
|
||||
frequency
|
||||
}
|
||||
|
||||
unsafe fn init_msis(&self, range: Msirange) -> Hertz {
|
||||
// Check MSI output per RM0456 § 11.4.10
|
||||
match self.voltage_range {
|
||||
VoltageScale::RANGE4 => {
|
||||
assert!(msirange_to_hertz(range).0 <= 24_000_000);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// RM0456 § 11.8.2: spin until MSIS is off or MSIS is ready before setting its range
|
||||
loop {
|
||||
let cr = RCC.cr().read();
|
||||
if cr.msison() == false || cr.msisrdy() == true {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
let sys_clk = match config.mux {
|
||||
ClockSrc::MSI(range) => {
|
||||
RCC.icscr1().modify(|w| {
|
||||
let bits: Msirange = range.into();
|
||||
w.set_msisrange(bits);
|
||||
w.set_msisrange(range);
|
||||
w.set_msirgsel(Msirgsel::RCC_ICSCR1);
|
||||
});
|
||||
RCC.cr().write(|w| {
|
||||
@ -221,62 +179,147 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
w.set_msison(true);
|
||||
});
|
||||
while !RCC.cr().read().msisrdy() {}
|
||||
msirange_to_hertz(range)
|
||||
}
|
||||
}
|
||||
|
||||
range.into()
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
mux: ClockSrc::default(),
|
||||
ahb_pre: AHBPrescaler::DIV1,
|
||||
apb1_pre: APBPrescaler::DIV1,
|
||||
apb2_pre: APBPrescaler::DIV1,
|
||||
apb3_pre: APBPrescaler::DIV1,
|
||||
hsi48: false,
|
||||
voltage_range: VoltageScale::RANGE3,
|
||||
}
|
||||
ClockSrc::HSE(freq) => {
|
||||
RCC.cr().write(|w| w.set_hseon(true));
|
||||
while !RCC.cr().read().hserdy() {}
|
||||
}
|
||||
}
|
||||
|
||||
freq.0
|
||||
}
|
||||
ClockSrc::HSI16 => {
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
pub(crate) unsafe fn init(config: Config) {
|
||||
// Ensure PWR peripheral clock is enabled
|
||||
RCC.ahb3enr().modify(|w| {
|
||||
w.set_pwren(true);
|
||||
});
|
||||
RCC.ahb3enr().read(); // synchronize
|
||||
|
||||
HSI_FREQ.0
|
||||
}
|
||||
ClockSrc::PLL1R(src, m, n, div) => {
|
||||
let freq = match src {
|
||||
PllSrc::MSI(_) => {
|
||||
// TODO: enable MSI
|
||||
MSIRange::default().into()
|
||||
}
|
||||
PllSrc::HSE(hertz) => {
|
||||
// TODO: enable HSE
|
||||
hertz.0
|
||||
}
|
||||
PllSrc::HSI16 => {
|
||||
RCC.cr().write(|w| w.set_hsion(true));
|
||||
while !RCC.cr().read().hsirdy() {}
|
||||
// Set the requested power mode
|
||||
PWR.vosr().modify(|w| {
|
||||
w.set_vos(config.voltage_range);
|
||||
});
|
||||
while !PWR.vosr().read().vosrdy() {}
|
||||
|
||||
HSI_FREQ.0
|
||||
}
|
||||
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) => {
|
||||
// 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(),
|
||||
};
|
||||
|
||||
// disable
|
||||
// Calculate the reference clock, which is the source divided by m
|
||||
let reference_clk = source_clk / pll.m;
|
||||
|
||||
// Check limits per RM0456 § 11.4.6
|
||||
assert!(Hertz::mhz(4) <= reference_clk && reference_clk <= Hertz::mhz(16));
|
||||
|
||||
// Calculate the PLL1 VCO clock and PLL1 R output clock
|
||||
let pll1_clk = reference_clk * pll.n;
|
||||
let pll1r_clk = pll1_clk / pll.r;
|
||||
|
||||
// Check system clock per RM0456 § 11.4.9
|
||||
assert!(pll1r_clk <= Hertz::mhz(160));
|
||||
|
||||
// Check PLL clocks per RM0456 § 11.4.10
|
||||
match config.voltage_range {
|
||||
VoltageScale::RANGE1 => {
|
||||
assert!(pll1_clk >= Hertz::mhz(128) && pll1_clk <= Hertz::mhz(544));
|
||||
assert!(pll1r_clk <= Hertz::mhz(208));
|
||||
}
|
||||
VoltageScale::RANGE2 => {
|
||||
assert!(pll1_clk >= Hertz::mhz(128) && pll1_clk <= Hertz::mhz(544));
|
||||
assert!(pll1r_clk <= Hertz::mhz(110));
|
||||
}
|
||||
VoltageScale::RANGE3 => {
|
||||
assert!(pll1_clk >= Hertz::mhz(128) && pll1_clk <= Hertz::mhz(330));
|
||||
assert!(pll1r_clk <= Hertz::mhz(55));
|
||||
}
|
||||
VoltageScale::RANGE4 => {
|
||||
panic!("PLL is unavailable in voltage range 4");
|
||||
}
|
||||
}
|
||||
|
||||
// § 10.5.4: if we're targeting >= 55 MHz, we must configure PLL1MBOOST to a prescaler
|
||||
// value that results in an output between 4 and 16 MHz for the PWR EPOD boost
|
||||
let mboost = if pll1r_clk >= Hertz::mhz(55) {
|
||||
// source_clk can be up to 50 MHz, so there's just a few cases:
|
||||
if source_clk > Hertz::mhz(32) {
|
||||
// Divide by 4, giving EPOD 8-12.5 MHz
|
||||
Pllmboost::DIV4
|
||||
} else if source_clk > Hertz::mhz(16) {
|
||||
// Divide by 2, giving EPOD 8-16 MHz
|
||||
Pllmboost::DIV2
|
||||
} else {
|
||||
// Bypass, giving EPOD 4-16 MHz
|
||||
Pllmboost::DIV1
|
||||
}
|
||||
} else {
|
||||
// Nothing to do
|
||||
Pllmboost::DIV1
|
||||
};
|
||||
|
||||
// Disable the PLL, and wait for it to disable
|
||||
RCC.cr().modify(|w| w.set_pllon(0, false));
|
||||
while RCC.cr().read().pllrdy(0) {}
|
||||
|
||||
let vco = freq * n as u8 as u32;
|
||||
let pll_ck = vco / (div as u8 as u32 + 1);
|
||||
|
||||
// Configure the PLL
|
||||
RCC.pll1cfgr().write(|w| {
|
||||
w.set_pllm(m.into());
|
||||
w.set_pllsrc(src.into());
|
||||
// Configure PLL1 source and prescaler
|
||||
w.set_pllsrc(pll.source.into());
|
||||
w.set_pllm(pll.m);
|
||||
|
||||
// Configure PLL1 input frequncy range
|
||||
let input_range = if reference_clk <= Hertz::mhz(8) {
|
||||
Pllrge::FREQ_4TO8MHZ
|
||||
} else {
|
||||
Pllrge::FREQ_8TO16MHZ
|
||||
};
|
||||
w.set_pllrge(input_range);
|
||||
|
||||
// Set the prescaler for PWR EPOD
|
||||
w.set_pllmboost(mboost);
|
||||
|
||||
// Enable PLL1R output
|
||||
w.set_pllren(true);
|
||||
});
|
||||
|
||||
// Configure the PLL divisors
|
||||
RCC.pll1divr().modify(|w| {
|
||||
w.set_pllr(div.to_div());
|
||||
w.set_plln(n.to_mul());
|
||||
// Set the VCO multiplier
|
||||
w.set_plln(pll.n);
|
||||
// Set the R output divisor
|
||||
w.set_pllr(pll.r);
|
||||
});
|
||||
|
||||
// Enable PLL
|
||||
// Do we need the EPOD booster to reach the target clock speed per § 10.5.4?
|
||||
if pll1r_clk >= Hertz::mhz(55) {
|
||||
// Enable the booster
|
||||
PWR.vosr().modify(|w| {
|
||||
w.set_boosten(true);
|
||||
});
|
||||
while !PWR.vosr().read().boostrdy() {}
|
||||
}
|
||||
|
||||
// Enable the PLL
|
||||
RCC.cr().modify(|w| w.set_pllon(0, true));
|
||||
while !RCC.cr().read().pllrdy(0) {}
|
||||
|
||||
pll_ck
|
||||
pll1r_clk
|
||||
}
|
||||
};
|
||||
|
||||
@ -285,20 +328,18 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
while !RCC.cr().read().hsi48rdy() {}
|
||||
}
|
||||
|
||||
// TODO make configurable
|
||||
let power_vos = VoltageScale::RANGE3;
|
||||
|
||||
// states and programming delay
|
||||
let wait_states = match power_vos {
|
||||
// The clock source is ready
|
||||
// Calculate and set the flash wait states
|
||||
let wait_states = match config.voltage_range {
|
||||
// VOS 1 range VCORE 1.26V - 1.40V
|
||||
VoltageScale::RANGE1 => {
|
||||
if sys_clk < 32_000_000 {
|
||||
if sys_clk.0 < 32_000_000 {
|
||||
0
|
||||
} else if sys_clk < 64_000_000 {
|
||||
} else if sys_clk.0 < 64_000_000 {
|
||||
1
|
||||
} else if sys_clk < 96_000_000 {
|
||||
} else if sys_clk.0 < 96_000_000 {
|
||||
2
|
||||
} else if sys_clk < 128_000_000 {
|
||||
} else if sys_clk.0 < 128_000_000 {
|
||||
3
|
||||
} else {
|
||||
4
|
||||
@ -306,11 +347,11 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
}
|
||||
// VOS 2 range VCORE 1.15V - 1.26V
|
||||
VoltageScale::RANGE2 => {
|
||||
if sys_clk < 30_000_000 {
|
||||
if sys_clk.0 < 30_000_000 {
|
||||
0
|
||||
} else if sys_clk < 60_000_000 {
|
||||
} else if sys_clk.0 < 60_000_000 {
|
||||
1
|
||||
} else if sys_clk < 90_000_000 {
|
||||
} else if sys_clk.0 < 90_000_000 {
|
||||
2
|
||||
} else {
|
||||
3
|
||||
@ -318,9 +359,9 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
}
|
||||
// VOS 3 range VCORE 1.05V - 1.15V
|
||||
VoltageScale::RANGE3 => {
|
||||
if sys_clk < 24_000_000 {
|
||||
if sys_clk.0 < 24_000_000 {
|
||||
0
|
||||
} else if sys_clk < 48_000_000 {
|
||||
} else if sys_clk.0 < 48_000_000 {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
@ -328,80 +369,101 @@ pub(crate) unsafe fn init(config: Config) {
|
||||
}
|
||||
// VOS 4 range VCORE 0.95V - 1.05V
|
||||
VoltageScale::RANGE4 => {
|
||||
if sys_clk < 12_000_000 {
|
||||
if sys_clk.0 < 12_000_000 {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
FLASH.acr().modify(|w| {
|
||||
w.set_latency(wait_states);
|
||||
});
|
||||
|
||||
// Switch the system clock source
|
||||
RCC.cfgr1().modify(|w| {
|
||||
w.set_sw(config.mux.into());
|
||||
});
|
||||
|
||||
// RM0456 § 11.4.9 specifies maximum bus frequencies per voltage range, but the maximum bus
|
||||
// frequency for each voltage range exactly matches the maximum permitted PLL output frequency.
|
||||
// Given that:
|
||||
//
|
||||
// 1. Any bus frequency can never exceed the system clock frequency;
|
||||
// 2. We checked the PLL output frequency if we're using it as a system clock;
|
||||
// 3. The maximum HSE frequencies at each voltage range are lower than the bus limits, and
|
||||
// we checked the HSE frequency if configured as a system clock; and
|
||||
// 4. The maximum frequencies from the other clock sources are lower than the lowest bus
|
||||
// frequency limit
|
||||
//
|
||||
// ...then we do not need to perform additional bus-related frequency checks.
|
||||
|
||||
// Configure the bus prescalers
|
||||
RCC.cfgr2().modify(|w| {
|
||||
w.set_hpre(config.ahb_pre.into());
|
||||
w.set_ppre1(config.apb1_pre.into());
|
||||
w.set_ppre2(config.apb2_pre.into());
|
||||
w.set_hpre(config.ahb_pre);
|
||||
w.set_ppre1(config.apb1_pre);
|
||||
w.set_ppre2(config.apb2_pre);
|
||||
});
|
||||
|
||||
RCC.cfgr3().modify(|w| {
|
||||
w.set_ppre3(config.apb3_pre.into());
|
||||
w.set_ppre3(config.apb3_pre);
|
||||
});
|
||||
|
||||
let ahb_freq: u32 = match config.ahb_pre {
|
||||
AHBPrescaler::DIV1 => sys_clk,
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre = 1 << (pre as u32 - 7);
|
||||
sys_clk / 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 pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
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 pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, freq * 2u32)
|
||||
}
|
||||
};
|
||||
|
||||
let (apb3_freq, _apb3_tim_freq) = match config.apb3_pre {
|
||||
APBPrescaler::DIV1 => (ahb_freq, ahb_freq),
|
||||
pre => {
|
||||
let pre: u8 = pre.into();
|
||||
let pre: u8 = 1 << (pre - 3);
|
||||
let freq = ahb_freq / pre as u32;
|
||||
(freq, freq * 2)
|
||||
let freq = ahb_freq / pre;
|
||||
(freq, freq * 2u32)
|
||||
}
|
||||
};
|
||||
|
||||
set_freqs(Clocks {
|
||||
sys: Hertz(sys_clk),
|
||||
ahb1: Hertz(ahb_freq),
|
||||
ahb2: Hertz(ahb_freq),
|
||||
ahb3: Hertz(ahb_freq),
|
||||
apb1: Hertz(apb1_freq),
|
||||
apb2: Hertz(apb2_freq),
|
||||
apb3: Hertz(apb3_freq),
|
||||
apb1_tim: Hertz(apb1_tim_freq),
|
||||
apb2_tim: Hertz(apb2_tim_freq),
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
fn msirange_to_hertz(range: Msirange) -> Hertz {
|
||||
match range {
|
||||
Msirange::RANGE_48MHZ => Hertz(48_000_000),
|
||||
Msirange::RANGE_24MHZ => Hertz(24_000_000),
|
||||
Msirange::RANGE_16MHZ => Hertz(16_000_000),
|
||||
Msirange::RANGE_12MHZ => Hertz(12_000_000),
|
||||
Msirange::RANGE_4MHZ => Hertz(4_000_000),
|
||||
Msirange::RANGE_2MHZ => Hertz(2_000_000),
|
||||
Msirange::RANGE_1_33MHZ => Hertz(1_330_000),
|
||||
Msirange::RANGE_1MHZ => Hertz(1_000_000),
|
||||
Msirange::RANGE_3_072MHZ => Hertz(3_072_000),
|
||||
Msirange::RANGE_1_536MHZ => Hertz(1_536_000),
|
||||
Msirange::RANGE_1_024MHZ => Hertz(1_024_000),
|
||||
Msirange::RANGE_768KHZ => Hertz(768_000),
|
||||
Msirange::RANGE_400KHZ => Hertz(400_000),
|
||||
Msirange::RANGE_200KHZ => Hertz(200_000),
|
||||
Msirange::RANGE_133KHZ => Hertz(133_000),
|
||||
Msirange::RANGE_100KHZ => Hertz(100_000),
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Ppre as APBPrescaler};
|
||||
use crate::rcc::bd::{BackupDomain, RtcClockSource};
|
||||
use crate::rcc::Clocks;
|
||||
use crate::time::{khz, mhz, Hertz};
|
||||
|
@ -1,6 +1,6 @@
|
||||
pub use super::bus::{AHBPrescaler, APBPrescaler};
|
||||
pub use crate::pac::pwr::vals::Vos as VoltageScale;
|
||||
use crate::pac::rcc::vals::Adcsel;
|
||||
pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Ppre as APBPrescaler};
|
||||
use crate::pac::{FLASH, RCC};
|
||||
use crate::rcc::bd::{BackupDomain, RtcClockSource};
|
||||
use crate::rcc::{set_freqs, Clocks};
|
||||
|
@ -13,6 +13,7 @@ use crate::{interrupt, pac, peripherals, Peripheral};
|
||||
|
||||
static RNG_WAKER: AtomicWaker = AtomicWaker::new();
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
SeedError,
|
||||
|
@ -11,7 +11,7 @@ use crate::pac::sai::{vals, Sai as Regs};
|
||||
use crate::rcc::RccPeripheral;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
NotATransmitter,
|
||||
|
@ -15,7 +15,7 @@ use crate::rcc::RccPeripheral;
|
||||
use crate::time::Hertz;
|
||||
use crate::{peripherals, Peripheral};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Error {
|
||||
Framing,
|
||||
|
@ -122,6 +122,46 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: ComplementaryCaptureCompare16bitInstance> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> {
|
||||
type Channel = Channel;
|
||||
type Time = Hertz;
|
||||
type Duty = u16;
|
||||
|
||||
fn disable(&mut self, channel: Self::Channel) {
|
||||
self.inner.enable_complementary_channel(channel, false);
|
||||
self.inner.enable_channel(channel, false);
|
||||
}
|
||||
|
||||
fn enable(&mut self, channel: Self::Channel) {
|
||||
self.inner.enable_channel(channel, true);
|
||||
self.inner.enable_complementary_channel(channel, true);
|
||||
}
|
||||
|
||||
fn get_period(&self) -> Self::Time {
|
||||
self.inner.get_frequency().into()
|
||||
}
|
||||
|
||||
fn get_duty(&self, channel: Self::Channel) -> Self::Duty {
|
||||
self.inner.get_compare_value(channel)
|
||||
}
|
||||
|
||||
fn get_max_duty(&self) -> Self::Duty {
|
||||
self.inner.get_max_compare_value() + 1
|
||||
}
|
||||
|
||||
fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {
|
||||
assert!(duty <= self.get_max_duty());
|
||||
self.inner.set_compare_value(channel, duty)
|
||||
}
|
||||
|
||||
fn set_period<P>(&mut self, period: P)
|
||||
where
|
||||
P: Into<Self::Time>,
|
||||
{
|
||||
self.inner.set_frequency(period.into());
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_dead_time_value(value: u16) -> (Ckd, u8) {
|
||||
/*
|
||||
Dead-time = T_clk * T_dts * T_dtg
|
||||
|
@ -70,6 +70,16 @@ pub(crate) mod sealed {
|
||||
fn set_autoreload_preload(&mut self, enable: vals::Arpe) {
|
||||
Self::regs().cr1().modify(|r| r.set_arpe(enable));
|
||||
}
|
||||
|
||||
fn get_frequency(&self) -> Hertz {
|
||||
let timer_f = Self::frequency();
|
||||
|
||||
let regs = Self::regs();
|
||||
let arr = regs.arr().read().arr();
|
||||
let psc = regs.psc().read().psc();
|
||||
|
||||
timer_f / arr / (psc + 1)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GeneralPurpose16bitInstance: Basic16bitInstance {
|
||||
@ -103,6 +113,16 @@ pub(crate) mod sealed {
|
||||
regs.egr().write(|r| r.set_ug(true));
|
||||
regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
|
||||
}
|
||||
|
||||
fn get_frequency(&self) -> Hertz {
|
||||
let timer_f = Self::frequency();
|
||||
|
||||
let regs = Self::regs_gp32();
|
||||
let arr = regs.arr().read().arr();
|
||||
let psc = regs.psc().read().psc();
|
||||
|
||||
timer_f / arr / (psc + 1)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AdvancedControlInstance: GeneralPurpose16bitInstance {
|
||||
@ -183,6 +203,10 @@ pub(crate) mod sealed {
|
||||
fn get_max_compare_value(&self) -> u16 {
|
||||
Self::regs_gp16().arr().read().arr()
|
||||
}
|
||||
|
||||
fn get_compare_value(&self, channel: Channel) -> u16 {
|
||||
Self::regs_gp16().ccr(channel.raw()).read().ccr()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance + AdvancedControlInstance {
|
||||
@ -219,6 +243,10 @@ pub(crate) mod sealed {
|
||||
fn get_max_compare_value(&self) -> u32 {
|
||||
Self::regs_gp32().arr().read().arr()
|
||||
}
|
||||
|
||||
fn get_compare_value(&self, channel: Channel) -> u32 {
|
||||
Self::regs_gp32().ccr(channel.raw()).read().ccr()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,3 +109,41 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
|
||||
self.inner.set_output_polarity(channel, polarity);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: CaptureCompare16bitInstance> embedded_hal_02::Pwm for SimplePwm<'d, T> {
|
||||
type Channel = Channel;
|
||||
type Time = Hertz;
|
||||
type Duty = u16;
|
||||
|
||||
fn disable(&mut self, channel: Self::Channel) {
|
||||
self.inner.enable_channel(channel, false);
|
||||
}
|
||||
|
||||
fn enable(&mut self, channel: Self::Channel) {
|
||||
self.inner.enable_channel(channel, true);
|
||||
}
|
||||
|
||||
fn get_period(&self) -> Self::Time {
|
||||
self.inner.get_frequency().into()
|
||||
}
|
||||
|
||||
fn get_duty(&self, channel: Self::Channel) -> Self::Duty {
|
||||
self.inner.get_compare_value(channel)
|
||||
}
|
||||
|
||||
fn get_max_duty(&self) -> Self::Duty {
|
||||
self.inner.get_max_compare_value() + 1
|
||||
}
|
||||
|
||||
fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {
|
||||
assert!(duty <= self.get_max_duty());
|
||||
self.inner.set_compare_value(channel, duty)
|
||||
}
|
||||
|
||||
fn set_period<P>(&mut self, period: P)
|
||||
where
|
||||
P: Into<Self::Time>,
|
||||
{
|
||||
self.inner.set_frequency(period.into());
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use core::convert::TryFrom;
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::rcc::{
|
||||
APBPrescaler, ClockSrc, HSEConfig, HSESrc, PLL48Div, PLLConfig, PLLMainDiv, PLLMul, PLLPreDiv, PLLSrc,
|
||||
APBPrescaler, ClockSrc, HSEConfig, HSESrc, PLLConfig, PLLMul, PLLPDiv, PLLPreDiv, PLLQDiv, PLLSrc,
|
||||
};
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::Config;
|
||||
@ -32,9 +32,9 @@ async fn main(_spawner: Spawner) {
|
||||
// 1 MHz PLL input * 240 = 240 MHz PLL VCO
|
||||
mul: unwrap!(PLLMul::try_from(240)),
|
||||
// 240 MHz PLL VCO / 2 = 120 MHz main PLL output
|
||||
main_div: PLLMainDiv::Div2,
|
||||
p_div: PLLPDiv::DIV2,
|
||||
// 240 MHz PLL VCO / 5 = 48 MHz PLL48 output
|
||||
pll48_div: unwrap!(PLL48Div::try_from(5)),
|
||||
q_div: PLLQDiv::DIV5,
|
||||
};
|
||||
// System clock comes from PLL (= the 120 MHz main PLL output)
|
||||
config.rcc.mux = ClockSrc::PLL;
|
||||
|
@ -16,12 +16,12 @@ async fn main(_spawner: Spawner) {
|
||||
|
||||
config.rcc.pll = Some(Pll {
|
||||
source: PllSrc::HSI16,
|
||||
prediv_m: PllM::Div4,
|
||||
mul_n: PllN::Mul85,
|
||||
prediv_m: PllM::DIV4,
|
||||
mul_n: PllN::MUL85,
|
||||
div_p: None,
|
||||
div_q: None,
|
||||
// Main system clock at 170 MHz
|
||||
div_r: Some(PllR::Div2),
|
||||
div_r: Some(PllR::DIV2),
|
||||
});
|
||||
|
||||
config.rcc.adc12_clock_source = AdcClockSource::SysClk;
|
||||
|
@ -15,12 +15,12 @@ async fn main(_spawner: Spawner) {
|
||||
|
||||
config.rcc.pll = Some(Pll {
|
||||
source: PllSrc::HSI16,
|
||||
prediv_m: PllM::Div4,
|
||||
mul_n: PllN::Mul85,
|
||||
prediv_m: PllM::DIV4,
|
||||
mul_n: PllN::MUL85,
|
||||
div_p: None,
|
||||
div_q: None,
|
||||
// Main system clock at 170 MHz
|
||||
div_r: Some(PllR::Div2),
|
||||
div_r: Some(PllR::DIV2),
|
||||
});
|
||||
|
||||
config.rcc.mux = ClockSrc::PLL;
|
||||
|
@ -25,16 +25,16 @@ async fn main(_spawner: Spawner) {
|
||||
// Change this to `false` to use the HSE clock source for the USB. This example assumes an 8MHz HSE.
|
||||
const USE_HSI48: bool = true;
|
||||
|
||||
let pllq_div = if USE_HSI48 { None } else { Some(PllQ::Div6) };
|
||||
let pllq_div = if USE_HSI48 { None } else { Some(PllQ::DIV6) };
|
||||
|
||||
config.rcc.pll = Some(Pll {
|
||||
source: PllSrc::HSE(Hertz(8_000_000)),
|
||||
prediv_m: PllM::Div2,
|
||||
mul_n: PllN::Mul72,
|
||||
prediv_m: PllM::DIV2,
|
||||
mul_n: PllN::MUL72,
|
||||
div_p: None,
|
||||
div_q: pllq_div,
|
||||
// Main system clock at 144 MHz
|
||||
div_r: Some(PllR::Div2),
|
||||
div_r: Some(PllR::DIV2),
|
||||
});
|
||||
|
||||
config.rcc.mux = ClockSrc::PLL;
|
||||
|
@ -9,7 +9,9 @@ use embassy_net::{Ipv4Address, Stack, StackResources};
|
||||
use embassy_stm32::eth::generic_smi::GenericSMI;
|
||||
use embassy_stm32::eth::{Ethernet, PacketQueue};
|
||||
use embassy_stm32::peripherals::ETH;
|
||||
use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllSource, Sysclk, VoltageScale};
|
||||
use embassy_stm32::rcc::{
|
||||
AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,
|
||||
};
|
||||
use embassy_stm32::rng::Rng;
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config};
|
||||
@ -42,10 +44,10 @@ async fn main(spawner: Spawner) -> ! {
|
||||
});
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
source: PllSource::Hse,
|
||||
prediv: 2,
|
||||
mul: 125,
|
||||
divp: Some(2),
|
||||
divq: Some(2),
|
||||
prediv: PllPreDiv::DIV2,
|
||||
mul: PllMul::MUL125,
|
||||
divp: Some(PllDiv::DIV2),
|
||||
divq: Some(PllDiv::DIV2),
|
||||
divr: None,
|
||||
});
|
||||
config.rcc.ahb_pre = AHBPrescaler::DIV1;
|
||||
|
@ -4,7 +4,9 @@
|
||||
|
||||
use defmt::{panic, *};
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllSource, Sysclk, VoltageScale};
|
||||
use embassy_stm32::rcc::{
|
||||
AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,
|
||||
};
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::usb::{Driver, Instance};
|
||||
use embassy_stm32::{bind_interrupts, pac, peripherals, usb, Config};
|
||||
@ -29,9 +31,9 @@ async fn main(_spawner: Spawner) {
|
||||
});
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
source: PllSource::Hse,
|
||||
prediv: 2,
|
||||
mul: 125,
|
||||
divp: Some(2), // 250mhz
|
||||
prediv: PllPreDiv::DIV2,
|
||||
mul: PllMul::MUL125,
|
||||
divp: Some(PllDiv::DIV2), // 250mhz
|
||||
divq: None,
|
||||
divr: None,
|
||||
});
|
||||
|
@ -18,16 +18,16 @@ async fn main(_spawner: Spawner) {
|
||||
config.rcc.csi = true;
|
||||
config.rcc.pll_src = PllSource::Hsi;
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
prediv: 4,
|
||||
mul: 50,
|
||||
divp: Some(2),
|
||||
divq: Some(8), // SPI1 cksel defaults to pll1_q
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL50,
|
||||
divp: Some(PllDiv::DIV2),
|
||||
divq: Some(PllDiv::DIV8), // SPI1 cksel defaults to pll1_q
|
||||
divr: None,
|
||||
});
|
||||
config.rcc.pll2 = Some(Pll {
|
||||
prediv: 4,
|
||||
mul: 50,
|
||||
divp: Some(8), // 100mhz
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL50,
|
||||
divp: Some(PllDiv::DIV8), // 100mhz
|
||||
divq: None,
|
||||
divr: None,
|
||||
});
|
||||
|
@ -32,10 +32,10 @@ async fn main(_spawner: Spawner) {
|
||||
config.rcc.csi = true;
|
||||
config.rcc.pll_src = PllSource::Hsi;
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
prediv: 4,
|
||||
mul: 50,
|
||||
divp: Some(2),
|
||||
divq: Some(8), // 100mhz
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL50,
|
||||
divp: Some(PllDiv::DIV2),
|
||||
divq: Some(PllDiv::DIV8), // 100mhz
|
||||
divr: None,
|
||||
});
|
||||
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
|
||||
@ -184,7 +184,7 @@ mod ov7725 {
|
||||
|
||||
const CAM_ADDR: u8 = 0x21;
|
||||
|
||||
#[derive(Format)]
|
||||
#[derive(Format, PartialEq, Eq)]
|
||||
pub enum Error<I2cError: Format> {
|
||||
I2c(I2cError),
|
||||
}
|
||||
|
@ -20,16 +20,16 @@ fn main() -> ! {
|
||||
config.rcc.csi = true;
|
||||
config.rcc.pll_src = PllSource::Hsi;
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
prediv: 4,
|
||||
mul: 50,
|
||||
divp: Some(2),
|
||||
divq: Some(8), // SPI1 cksel defaults to pll1_q
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL50,
|
||||
divp: Some(PllDiv::DIV2),
|
||||
divq: Some(PllDiv::DIV8), // 100mhz
|
||||
divr: None,
|
||||
});
|
||||
config.rcc.pll2 = Some(Pll {
|
||||
prediv: 4,
|
||||
mul: 50,
|
||||
divp: Some(8), // 100mhz
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL50,
|
||||
divp: Some(PllDiv::DIV8), // 100mhz
|
||||
divq: None,
|
||||
divr: None,
|
||||
});
|
||||
|
@ -28,16 +28,16 @@ async fn main(spawner: Spawner) {
|
||||
config.rcc.csi = true;
|
||||
config.rcc.pll_src = PllSource::Hsi;
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
prediv: 4,
|
||||
mul: 50,
|
||||
divp: Some(2),
|
||||
divq: Some(8), // SPI1 cksel defaults to pll1_q
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL50,
|
||||
divp: Some(PllDiv::DIV2),
|
||||
divq: Some(PllDiv::DIV8), // 100mhz
|
||||
divr: None,
|
||||
});
|
||||
config.rcc.pll2 = Some(Pll {
|
||||
prediv: 4,
|
||||
mul: 50,
|
||||
divp: Some(8), // 100mhz
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL50,
|
||||
divp: Some(PllDiv::DIV8), // 100mhz
|
||||
divq: None,
|
||||
divr: None,
|
||||
});
|
||||
|
@ -39,9 +39,9 @@ async fn main(spawner: Spawner) -> ! {
|
||||
config.rcc.hsi48 = true; // needed for RNG
|
||||
config.rcc.pll_src = PllSource::Hsi;
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
prediv: 4,
|
||||
mul: 50,
|
||||
divp: Some(2),
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL50,
|
||||
divp: Some(PllDiv::DIV2),
|
||||
divq: None,
|
||||
divr: None,
|
||||
});
|
||||
|
@ -40,9 +40,9 @@ async fn main(spawner: Spawner) -> ! {
|
||||
config.rcc.hsi48 = true; // needed for RNG
|
||||
config.rcc.pll_src = PllSource::Hsi;
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
prediv: 4,
|
||||
mul: 50,
|
||||
divp: Some(2),
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL50,
|
||||
divp: Some(PllDiv::DIV2),
|
||||
divq: None,
|
||||
divr: None,
|
||||
});
|
||||
|
@ -18,10 +18,10 @@ async fn main(_spawner: Spawner) {
|
||||
config.rcc.csi = true;
|
||||
config.rcc.pll_src = PllSource::Hsi;
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
prediv: 4,
|
||||
mul: 50,
|
||||
divp: Some(2),
|
||||
divq: Some(8), // 100mhz
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL50,
|
||||
divp: Some(PllDiv::DIV2),
|
||||
divq: Some(PllDiv::DIV8), // 100mhz
|
||||
divr: None,
|
||||
});
|
||||
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
|
||||
|
@ -22,10 +22,10 @@ async fn main(_spawner: Spawner) {
|
||||
config.rcc.hsi48 = true; // needed for RNG
|
||||
config.rcc.pll_src = PllSource::Hsi;
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
prediv: 4,
|
||||
mul: 50,
|
||||
divp: Some(2),
|
||||
divq: Some(8), // 100 Mhz
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL50,
|
||||
divp: Some(PllDiv::DIV2),
|
||||
divq: Some(PllDiv::DIV8), // 100mhz
|
||||
divr: None,
|
||||
});
|
||||
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
|
||||
|
@ -21,9 +21,9 @@ async fn main(_spawner: Spawner) {
|
||||
config.rcc.csi = true;
|
||||
config.rcc.pll_src = PllSource::Hsi;
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
prediv: 4,
|
||||
mul: 50,
|
||||
divp: Some(2),
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL50,
|
||||
divp: Some(PllDiv::DIV2),
|
||||
divq: None,
|
||||
divr: None,
|
||||
});
|
||||
|
@ -22,10 +22,10 @@ async fn main(_spawner: Spawner) -> ! {
|
||||
config.rcc.csi = true;
|
||||
config.rcc.pll_src = PllSource::Hsi;
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
prediv: 4,
|
||||
mul: 50,
|
||||
divp: Some(2),
|
||||
divq: Some(4), // default clock chosen by SDMMCSEL. 200 Mhz
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL50,
|
||||
divp: Some(PllDiv::DIV2),
|
||||
divq: Some(PllDiv::DIV4), // default clock chosen by SDMMCSEL. 200 Mhz
|
||||
divr: None,
|
||||
});
|
||||
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
|
||||
|
@ -44,10 +44,10 @@ fn main() -> ! {
|
||||
config.rcc.csi = true;
|
||||
config.rcc.pll_src = PllSource::Hsi;
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
prediv: 4,
|
||||
mul: 50,
|
||||
divp: Some(2),
|
||||
divq: Some(4), // used by SPI3. 100Mhz.
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL50,
|
||||
divp: Some(PllDiv::DIV2),
|
||||
divq: Some(PllDiv::DIV8), // used by SPI3. 100Mhz.
|
||||
divr: None,
|
||||
});
|
||||
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
|
||||
|
@ -40,10 +40,10 @@ fn main() -> ! {
|
||||
config.rcc.csi = true;
|
||||
config.rcc.pll_src = PllSource::Hsi;
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
prediv: 4,
|
||||
mul: 50,
|
||||
divp: Some(2),
|
||||
divq: Some(4), // used by SPI3. 100Mhz.
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL50,
|
||||
divp: Some(PllDiv::DIV2),
|
||||
divq: Some(PllDiv::DIV8), // used by SPI3. 100Mhz.
|
||||
divr: None,
|
||||
});
|
||||
config.rcc.sys = Sysclk::Pll1P; // 400 Mhz
|
||||
|
@ -28,9 +28,9 @@ async fn main(_spawner: Spawner) {
|
||||
config.rcc.hsi48 = true; // needed for USB
|
||||
config.rcc.pll_src = PllSource::Hsi;
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
prediv: 4,
|
||||
mul: 50,
|
||||
divp: Some(2),
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL50,
|
||||
divp: Some(PllDiv::DIV2),
|
||||
divq: None,
|
||||
divr: None,
|
||||
});
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv};
|
||||
use embassy_stm32::rcc::{ClockSrc, PLLSource, PllMul, PllPreDiv, PllQDiv, PllRDiv};
|
||||
use embassy_stm32::rng::Rng;
|
||||
use embassy_stm32::{bind_interrupts, peripherals, rng, Config};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
@ -19,10 +19,10 @@ async fn main(_spawner: Spawner) {
|
||||
// 72Mhz clock (16 / 1 * 18 / 4)
|
||||
config.rcc.mux = ClockSrc::PLL(
|
||||
PLLSource::HSI16,
|
||||
PLLClkDiv::Div4,
|
||||
PLLSrcDiv::Div1,
|
||||
PLLMul::Mul18,
|
||||
Some(PLLClkDiv::Div6), // 48Mhz (16 / 1 * 18 / 6)
|
||||
PllRDiv::DIV4,
|
||||
PllPreDiv::DIV1,
|
||||
PllMul::MUL18,
|
||||
Some(PllQDiv::DIV6), // 48Mhz (16 / 1 * 18 / 6)
|
||||
);
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
use chrono::{NaiveDate, NaiveDateTime};
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::rcc::{self, ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv};
|
||||
use embassy_stm32::rcc::{self, ClockSrc, PLLSource, PllMul, PllPreDiv, PllRDiv};
|
||||
use embassy_stm32::rtc::{Rtc, RtcConfig};
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::Config;
|
||||
@ -18,9 +18,9 @@ async fn main(_spawner: Spawner) {
|
||||
let mut config = Config::default();
|
||||
config.rcc.mux = ClockSrc::PLL(
|
||||
PLLSource::HSE(Hertz::mhz(8)),
|
||||
PLLClkDiv::Div2,
|
||||
PLLSrcDiv::Div1,
|
||||
PLLMul::Mul20,
|
||||
PllRDiv::DIV2,
|
||||
PllPreDiv::DIV1,
|
||||
PllMul::MUL20,
|
||||
None,
|
||||
);
|
||||
config.rcc.lse = Some(Hertz(32_768));
|
||||
|
@ -49,7 +49,7 @@ use embassy_net_adin1110::{self, Device, Runner, ADIN1110};
|
||||
use embedded_hal_bus::spi::ExclusiveDevice;
|
||||
use hal::gpio::Pull;
|
||||
use hal::i2c::Config as I2C_Config;
|
||||
use hal::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv};
|
||||
use hal::rcc::{ClockSrc, PLLSource, PllMul, PllPreDiv, PllRDiv};
|
||||
use hal::spi::{Config as SPI_Config, Spi};
|
||||
use hal::time::Hertz;
|
||||
|
||||
@ -80,9 +80,9 @@ async fn main(spawner: Spawner) {
|
||||
// 80MHz highest frequency for flash 0 wait.
|
||||
config.rcc.mux = ClockSrc::PLL(
|
||||
PLLSource::HSE(Hertz(8_000_000)),
|
||||
PLLClkDiv::Div2,
|
||||
PLLSrcDiv::Div1,
|
||||
PLLMul::Mul20,
|
||||
PllRDiv::DIV2,
|
||||
PllPreDiv::DIV1,
|
||||
PllMul::MUL20,
|
||||
None,
|
||||
);
|
||||
config.rcc.hsi48 = true; // needed for rng
|
||||
@ -366,7 +366,7 @@ pub struct ADT7422<'d, BUS: I2cBus> {
|
||||
bus: BUS,
|
||||
}
|
||||
|
||||
#[derive(Debug, Format)]
|
||||
#[derive(Debug, Format, PartialEq, Eq)]
|
||||
pub enum Error<I2cError: Format> {
|
||||
I2c(I2cError),
|
||||
Address,
|
||||
|
@ -23,7 +23,7 @@ async fn main(_spawner: Spawner) {
|
||||
info!("Hello World!");
|
||||
|
||||
let mut config = Config::default();
|
||||
config.rcc.mux = ClockSrc::PLL(PLLSource::HSI16, PLLClkDiv::Div2, PLLSrcDiv::Div1, PLLMul::Mul10, None);
|
||||
config.rcc.mux = ClockSrc::PLL(PLLSource::HSI16, PllRDiv::DIV2, PllPreDiv::DIV1, PllMul::MUL10, None);
|
||||
config.rcc.hsi48 = true;
|
||||
|
||||
let p = embassy_stm32::init(config);
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv};
|
||||
use embassy_stm32::rcc::{ClockSrc, PLLSource, PllMul, PllPreDiv, PllQDiv, PllRDiv};
|
||||
use embassy_stm32::rng::Rng;
|
||||
use embassy_stm32::{bind_interrupts, peripherals, rng, Config};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
@ -18,10 +18,10 @@ async fn main(_spawner: Spawner) {
|
||||
let mut config = Config::default();
|
||||
config.rcc.mux = ClockSrc::PLL(
|
||||
PLLSource::HSI16,
|
||||
PLLClkDiv::Div2,
|
||||
PLLSrcDiv::Div1,
|
||||
PLLMul::Mul8,
|
||||
Some(PLLClkDiv::Div2),
|
||||
PllRDiv::DIV2,
|
||||
PllPreDiv::DIV1,
|
||||
PllMul::MUL8,
|
||||
Some(PllQDiv::DIV2),
|
||||
);
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
|
@ -45,7 +45,7 @@ async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! {
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let mut config = Config::default();
|
||||
config.rcc.mux = ClockSrc::PLL(PLLSource::HSI16, PLLClkDiv::Div2, PLLSrcDiv::Div1, PLLMul::Mul10, None);
|
||||
config.rcc.mux = ClockSrc::PLL(PLLSource::HSI16, PllRDiv::DIV2, PllPreDiv::DIV1, PllMul::MUL10, None);
|
||||
config.rcc.hsi48 = true;
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
|
@ -22,7 +22,7 @@ bind_interrupts!(struct Irqs {
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut config = Config::default();
|
||||
config.rcc.mux = ClockSrc::PLL(PLLSource::HSI16, PLLClkDiv::Div2, PLLSrcDiv::Div1, PLLMul::Mul10, None);
|
||||
config.rcc.mux = ClockSrc::PLL(PLLSource::HSI16, PllRDiv::DIV2, PllPreDiv::DIV1, PllMul::MUL10, None);
|
||||
config.rcc.hsi48 = true;
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
|
@ -20,7 +20,7 @@ bind_interrupts!(struct Irqs {
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut config = Config::default();
|
||||
config.rcc.mux = ClockSrc::PLL(PLLSource::HSI16, PLLClkDiv::Div2, PLLSrcDiv::Div1, PLLMul::Mul10, None);
|
||||
config.rcc.mux = ClockSrc::PLL(PLLSource::HSI16, PllRDiv::DIV2, PllPreDiv::DIV1, PllMul::MUL10, None);
|
||||
config.rcc.hsi48 = true;
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
|
@ -23,7 +23,12 @@ async fn main(_spawner: Spawner) {
|
||||
info!("Hello World!");
|
||||
|
||||
let mut config = Config::default();
|
||||
config.rcc.mux = ClockSrc::PLL1R(PllSrc::HSI16, PllM::Div2, PllN::Mul10, PllClkDiv::NotDivided);
|
||||
config.rcc.mux = ClockSrc::PLL1R(PllConfig {
|
||||
source: PllSrc::HSI16,
|
||||
m: Pllm::DIV2,
|
||||
n: Plln::MUL10,
|
||||
r: Plldiv::DIV1,
|
||||
});
|
||||
//config.rcc.mux = ClockSrc::MSI(MSIRange::Range48mhz);
|
||||
config.rcc.hsi48 = true;
|
||||
|
||||
|
26
tests/rp/src/bin/bootsel.rs
Normal file
26
tests/rp/src/bin/bootsel.rs
Normal file
@ -0,0 +1,26 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
teleprobe_meta::target!(b"rpi-pico");
|
||||
|
||||
use defmt::{assert_eq, *};
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::{Duration, Timer};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let mut p = embassy_rp::init(Default::default());
|
||||
info!("Hello World!");
|
||||
|
||||
// add some delay to give an attached debug probe time to parse the
|
||||
// defmt RTT header. Reading that header might touch flash memory, which
|
||||
// interferes with flash write operations.
|
||||
// https://github.com/knurling-rs/defmt/pull/683
|
||||
Timer::after(Duration::from_millis(10)).await;
|
||||
|
||||
assert_eq!(p.BOOTSEL.is_pressed(), false);
|
||||
|
||||
info!("Test OK");
|
||||
cortex_m::asm::bkpt();
|
||||
}
|
@ -202,9 +202,9 @@ pub fn config() -> Config {
|
||||
// 1 MHz PLL input * 240 = 240 MHz PLL VCO
|
||||
mul: unwrap!(PLLMul::try_from(240)),
|
||||
// 240 MHz PLL VCO / 2 = 120 MHz main PLL output
|
||||
main_div: PLLMainDiv::Div2,
|
||||
p_div: PLLPDiv::DIV2,
|
||||
// 240 MHz PLL VCO / 5 = 48 MHz PLL48 output
|
||||
pll48_div: unwrap!(PLL48Div::try_from(5)),
|
||||
q_div: PLLQDiv::DIV5,
|
||||
};
|
||||
// System clock comes from PLL (= the 120 MHz main PLL output)
|
||||
config.rcc.mux = ClockSrc::PLL;
|
||||
@ -239,10 +239,10 @@ pub fn config() -> Config {
|
||||
});
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
source: PllSource::Hse,
|
||||
prediv: 2,
|
||||
mul: 125,
|
||||
divp: Some(2),
|
||||
divq: Some(2),
|
||||
prediv: PllPreDiv::DIV2,
|
||||
mul: PllMul::MUL125,
|
||||
divp: Some(PllDiv::DIV2),
|
||||
divq: Some(PllDiv::DIV2),
|
||||
divr: None,
|
||||
});
|
||||
config.rcc.ahb_pre = AHBPrescaler::DIV1;
|
||||
@ -261,16 +261,16 @@ pub fn config() -> Config {
|
||||
config.rcc.hsi48 = true; // needed for RNG
|
||||
config.rcc.pll_src = PllSource::Hsi;
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
prediv: 4,
|
||||
mul: 50,
|
||||
divp: Some(2),
|
||||
divq: Some(8), // SPI1 cksel defaults to pll1_q
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL50,
|
||||
divp: Some(PllDiv::DIV2),
|
||||
divq: Some(PllDiv::DIV8), // SPI1 cksel defaults to pll1_q
|
||||
divr: None,
|
||||
});
|
||||
config.rcc.pll2 = Some(Pll {
|
||||
prediv: 4,
|
||||
mul: 50,
|
||||
divp: Some(8), // 100mhz
|
||||
prediv: PllPreDiv::DIV4,
|
||||
mul: PllMul::MUL50,
|
||||
divp: Some(PllDiv::DIV8), // 100mhz
|
||||
divq: None,
|
||||
divr: None,
|
||||
});
|
||||
@ -290,10 +290,10 @@ pub fn config() -> Config {
|
||||
config.rcc.mux = ClockSrc::PLL(
|
||||
// 72Mhz clock (16 / 1 * 18 / 4)
|
||||
PLLSource::HSI16,
|
||||
PLLClkDiv::Div4,
|
||||
PLLSrcDiv::Div1,
|
||||
PLLMul::Mul18,
|
||||
Some(PLLClkDiv::Div6), // 48Mhz (16 / 1 * 18 / 6)
|
||||
PllRDiv::DIV4,
|
||||
PllPreDiv::DIV1,
|
||||
PllMul::MUL18,
|
||||
Some(PllQDiv::DIV6), // 48Mhz (16 / 1 * 18 / 6)
|
||||
);
|
||||
}
|
||||
|
||||
@ -303,9 +303,9 @@ pub fn config() -> Config {
|
||||
config.rcc.mux = ClockSrc::PLL(
|
||||
// 110Mhz clock (16 / 4 * 55 / 2)
|
||||
PLLSource::HSI16,
|
||||
PLLClkDiv::Div2,
|
||||
PLLSrcDiv::Div4,
|
||||
PLLMul::Mul55,
|
||||
PllRDiv::DIV2,
|
||||
PllPreDiv::DIV4,
|
||||
PllMul::MUL55,
|
||||
None,
|
||||
);
|
||||
}
|
||||
@ -313,7 +313,7 @@ pub fn config() -> Config {
|
||||
#[cfg(feature = "stm32u585ai")]
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
config.rcc.mux = ClockSrc::MSI(MSIRange::Range48mhz);
|
||||
config.rcc.mux = ClockSrc::MSI(Msirange::RANGE_48MHZ);
|
||||
}
|
||||
|
||||
#[cfg(feature = "stm32l073rz")]
|
||||
|
Loading…
Reference in New Issue
Block a user