rcc and pwr needs PAC map fixes
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
/target
|
||||
stm32u5/
|
||||
|
||||
44
Cargo.lock
generated
44
Cargo.lock
generated
@@ -56,6 +56,15 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-dma"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446"
|
||||
dependencies = [
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal"
|
||||
version = "0.2.7"
|
||||
@@ -72,6 +81,21 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
|
||||
|
||||
[[package]]
|
||||
name = "fugit"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17186ad64927d5ac8f02c1e77ccefa08ccd9eaa314d5a4772278aa204a22f7e7"
|
||||
dependencies = [
|
||||
"gcd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gcd"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
|
||||
|
||||
[[package]]
|
||||
name = "nb"
|
||||
version = "0.1.3"
|
||||
@@ -129,13 +153,33 @@ version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "stm32u5"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d618b263f62f04405cb1a67976740dd89dfe82eb70f42811dc63ce24b09198f"
|
||||
dependencies = [
|
||||
"cortex-m",
|
||||
"cortex-m-rt",
|
||||
"vcell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stm32u575-hal"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cortex-m",
|
||||
"cortex-m-rt",
|
||||
"embedded-dma",
|
||||
"embedded-hal 1.0.0",
|
||||
"fugit",
|
||||
"stm32u5",
|
||||
"vcell",
|
||||
]
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@ description = "Hardware Abstraction Layer for STM32U575 microcontrollers"
|
||||
[dependencies]
|
||||
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7"
|
||||
embedded-dma = "0.2.0"
|
||||
embedded-hal = "1.0.0"
|
||||
fugit = "0.3.7"
|
||||
stm32u5 = { version = "0.16.0", default-features = false, features = ["stm32u575", "rt"] }
|
||||
vcell = "0.1.3"
|
||||
|
||||
|
||||
27
flake.lock
generated
Normal file
27
flake.lock
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1760524057,
|
||||
"narHash": "sha256-EVAqOteLBFmd7pKkb0+FIUyzTF61VKi7YmvP1tw4nEw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "544961dfcce86422ba200ed9a0b00dd4b1486ec5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -21,6 +21,8 @@
|
||||
curl
|
||||
wget
|
||||
unzip
|
||||
svd2rust
|
||||
form
|
||||
|
||||
# ARM embedded cross toolchain
|
||||
gcc-arm-embedded
|
||||
|
||||
955
src/adc/adc.rs
Normal file
955
src/adc/adc.rs
Normal file
@@ -0,0 +1,955 @@
|
||||
//! # Analog to Digital converter for STM32U575
|
||||
//!
|
||||
//! This implementation supports ADC1 and ADC2 only.
|
||||
//! ADC4 support is skipped for now.
|
||||
|
||||
use core::{
|
||||
convert::Infallible,
|
||||
marker::PhantomData,
|
||||
ops::DerefMut,
|
||||
sync::atomic::{self, Ordering},
|
||||
};
|
||||
use stable_deref_trait::StableDeref;
|
||||
|
||||
use crate::{
|
||||
dma::{dma1, Event as DMAEvent, RxDma, Transfer, TransferPayload, W},
|
||||
dmamux::{DmaInput, DmaMux},
|
||||
gpio::{self, Analog},
|
||||
hal::{
|
||||
adc::{Channel as EmbeddedHalChannel, OneShot},
|
||||
blocking::delay::DelayUs,
|
||||
},
|
||||
pac::{self, ADC1, ADC2},
|
||||
rcc::{Enable, Reset, AHB2},
|
||||
signature::{VrefCal, VtempCalHigh, VtempCalLow, VDDA_CALIB_MV},
|
||||
};
|
||||
|
||||
/// Internal voltage reference channel, used for calibration.
|
||||
pub struct Vref {
|
||||
_0: (),
|
||||
}
|
||||
|
||||
/// Internal battery monitoring channel.
|
||||
pub struct Vbat {
|
||||
_0: (),
|
||||
}
|
||||
|
||||
/// Internal temperature sensor channel.
|
||||
pub struct Temperature {
|
||||
_0: (),
|
||||
}
|
||||
|
||||
/// Wrapper for safely sharing [`ADC_COMMON`](pac::ADC_COMMON) between `Adc`s.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct AdcCommon {
|
||||
_0: PhantomData<pac::ADC_COMMON>,
|
||||
#[allow(unused)]
|
||||
csr: AdcCommonCsr,
|
||||
ccr: AdcCommonCcr,
|
||||
#[allow(unused)]
|
||||
cdr: AdcCommonCdr,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct AdcCommonCsr {
|
||||
_0: PhantomData<stm32u5::Reg<pac::adc_common::csr::CSR_SPEC>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct AdcCommonCcr {
|
||||
_0: PhantomData<stm32u5::Reg<pac::adc_common::ccr::CCR_SPEC>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct AdcCommonCdr {
|
||||
_0: PhantomData<stm32u5::Reg<pac::adc_common::cdr::CDR_SPEC>>,
|
||||
}
|
||||
|
||||
impl AdcCommonCcr {
|
||||
#[inline]
|
||||
fn read(&self) -> pac::adc_common::ccr::R {
|
||||
let adc_common = unsafe { &*pac::ADC_COMMON::ptr() };
|
||||
adc_common.ccr().read()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn modify<F>(&mut self, f: F)
|
||||
where
|
||||
for<'w> F: FnOnce(
|
||||
&pac::adc_common::ccr::R,
|
||||
&'w mut pac::adc_common::ccr::W,
|
||||
) -> &'w mut stm32u5::W<pac::adc_common::ccr::CCR_SPEC>,
|
||||
{
|
||||
cortex_m::interrupt::free(|_| {
|
||||
let adc_common = unsafe { &*pac::ADC_COMMON::ptr() };
|
||||
adc_common.ccr().modify(|r, w| f(r, w))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl AdcCommon {
|
||||
/// Enable and reset [`ADC_COMMON`](pac::ADC_COMMON) peripheral.
|
||||
pub fn new(adc_common: pac::ADC_COMMON, ahb: &mut AHB2) -> Self {
|
||||
<pac::ADC_COMMON>::enable(ahb);
|
||||
<pac::ADC_COMMON>::reset(ahb);
|
||||
drop(adc_common);
|
||||
Self {
|
||||
_0: PhantomData,
|
||||
csr: AdcCommonCsr { _0: PhantomData },
|
||||
ccr: AdcCommonCcr { _0: PhantomData },
|
||||
cdr: AdcCommonCdr { _0: PhantomData },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Analog to Digital converter interface
|
||||
pub struct Adc<ADC> {
|
||||
adc: ADC,
|
||||
adc_common: AdcCommon,
|
||||
resolution: Resolution,
|
||||
sample_time: SampleTime,
|
||||
calibrated_vdda: u32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum DmaMode {
|
||||
Disabled = 0,
|
||||
Oneshot = 1,
|
||||
// FIXME: Circular DMA mode implementation
|
||||
// Circular = 2,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, PartialOrd, Clone, Copy)]
|
||||
pub enum Sequence {
|
||||
One = 0,
|
||||
Two = 1,
|
||||
Three = 2,
|
||||
Four = 3,
|
||||
Five = 4,
|
||||
Six = 5,
|
||||
Seven = 6,
|
||||
Eight = 7,
|
||||
Nine = 8,
|
||||
Ten = 9,
|
||||
Eleven = 10,
|
||||
Twelve = 11,
|
||||
Thirteen = 12,
|
||||
Fourteen = 13,
|
||||
Fifteen = 14,
|
||||
Sixteen = 15,
|
||||
}
|
||||
|
||||
impl From<u8> for Sequence {
|
||||
fn from(bits: u8) -> Self {
|
||||
match bits {
|
||||
0 => Sequence::One,
|
||||
1 => Sequence::Two,
|
||||
2 => Sequence::Three,
|
||||
3 => Sequence::Four,
|
||||
4 => Sequence::Five,
|
||||
5 => Sequence::Six,
|
||||
6 => Sequence::Seven,
|
||||
7 => Sequence::Eight,
|
||||
8 => Sequence::Nine,
|
||||
9 => Sequence::Ten,
|
||||
10 => Sequence::Eleven,
|
||||
11 => Sequence::Twelve,
|
||||
12 => Sequence::Thirteen,
|
||||
13 => Sequence::Fourteen,
|
||||
14 => Sequence::Fifteen,
|
||||
15 => Sequence::Sixteen,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Sequence {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Sequence::One => 0,
|
||||
Sequence::Two => 1,
|
||||
Sequence::Three => 2,
|
||||
Sequence::Four => 3,
|
||||
Sequence::Five => 4,
|
||||
Sequence::Six => 5,
|
||||
Sequence::Seven => 6,
|
||||
Sequence::Eight => 7,
|
||||
Sequence::Nine => 8,
|
||||
Sequence::Ten => 9,
|
||||
Sequence::Eleven => 10,
|
||||
Sequence::Twelve => 11,
|
||||
Sequence::Thirteen => 12,
|
||||
Sequence::Fourteen => 13,
|
||||
Sequence::Fifteen => 14,
|
||||
Sequence::Sixteen => 15,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, PartialOrd, Clone, Copy)]
|
||||
pub enum Event {
|
||||
EndOfRegularSequence,
|
||||
EndOfRegularConversion,
|
||||
}
|
||||
|
||||
impl<ADC> Adc<ADC> {
|
||||
/// Set the ADC resolution
|
||||
pub fn set_resolution(&mut self, resolution: Resolution) {
|
||||
self.resolution = resolution;
|
||||
}
|
||||
|
||||
/// Set the sample time
|
||||
pub fn set_sample_time(&mut self, sample_time: SampleTime) {
|
||||
self.sample_time = sample_time;
|
||||
}
|
||||
|
||||
/// Get the max value for the current resolution
|
||||
pub fn get_max_value(&self) -> u16 {
|
||||
match self.resolution {
|
||||
Resolution::Bits14 => 16383,
|
||||
Resolution::Bits12 => 4095,
|
||||
Resolution::Bits10 => 1023,
|
||||
Resolution::Bits8 => 255,
|
||||
// SKIPPED: Bits6 (ADC4 only)
|
||||
}
|
||||
}
|
||||
|
||||
/// Release the ADC peripheral
|
||||
///
|
||||
/// Drops `Adc` and returns the `ADC` peripheral that it was wrapping,
|
||||
/// giving the user full access to the peripheral.
|
||||
pub fn release(self) -> ADC {
|
||||
self.adc
|
||||
}
|
||||
|
||||
/// Convert a measurement to millivolts
|
||||
pub fn to_millivolts(&self, sample: u16) -> u16 {
|
||||
((u32::from(sample) * self.calibrated_vdda) / self.resolution.to_max_count()) as u16
|
||||
}
|
||||
|
||||
/// Convert a raw sample from the `Temperature` to deg C
|
||||
pub fn to_degrees_centigrade(&self, sample: u16) -> f32 {
|
||||
let sample = (u32::from(sample) * self.calibrated_vdda) / VDDA_CALIB_MV;
|
||||
(VtempCalHigh::TEMP_DEGREES - VtempCalLow::TEMP_DEGREES) as f32
|
||||
/ (VtempCalHigh::get().read() as i32 - VtempCalLow::get().read() as i32) as f32
|
||||
* (sample as i32 - VtempCalLow::get().read() as i32) as f32
|
||||
+ 30.0
|
||||
}
|
||||
}
|
||||
|
||||
/// ADC resolution setting
|
||||
///
|
||||
/// The default setting is 12 bits.
|
||||
///
|
||||
/// NOTE: STM32U575 ADC1/ADC2 support 14-bit resolution (new for U5 series)
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum Resolution {
|
||||
/// 14-bit resolution (STM32U5 ADC1/ADC2 only, not available on L4)
|
||||
Bits14 = 0b000,
|
||||
/// 12-bit resolution
|
||||
Bits12 = 0b001,
|
||||
/// 10-bit resolution
|
||||
Bits10 = 0b010,
|
||||
/// 8-bit resolution
|
||||
Bits8 = 0b011,
|
||||
// SKIPPED: Bits6 = 0b100 (ADC4 only, we're only doing ADC1/ADC2)
|
||||
}
|
||||
|
||||
impl Default for Resolution {
|
||||
fn default() -> Self {
|
||||
Self::Bits12
|
||||
}
|
||||
}
|
||||
|
||||
impl Resolution {
|
||||
fn to_max_count(&self) -> u32 {
|
||||
match self {
|
||||
Resolution::Bits14 => (1 << 14) - 1,
|
||||
Resolution::Bits12 => (1 << 12) - 1,
|
||||
Resolution::Bits10 => (1 << 10) - 1,
|
||||
Resolution::Bits8 => (1 << 8) - 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ADC sample time
|
||||
///
|
||||
/// The default setting is 5 ADC clock cycles.
|
||||
///
|
||||
/// NOTE: STM32U5 has different sample time encoding than L4
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[repr(u8)]
|
||||
pub enum SampleTime {
|
||||
/// 5 ADC clock cycles
|
||||
Cycles5 = 0b000,
|
||||
/// 6 ADC clock cycles
|
||||
Cycles6 = 0b001,
|
||||
/// 12 ADC clock cycles
|
||||
Cycles12 = 0b010,
|
||||
/// 20 ADC clock cycles
|
||||
Cycles20 = 0b011,
|
||||
/// 36 ADC clock cycles
|
||||
Cycles36 = 0b100,
|
||||
/// 68 ADC clock cycles
|
||||
Cycles68 = 0b101,
|
||||
/// 391 ADC clock cycles
|
||||
Cycles391 = 0b110,
|
||||
/// 814 ADC clock cycles
|
||||
Cycles814 = 0b111,
|
||||
}
|
||||
|
||||
impl Default for SampleTime {
|
||||
fn default() -> Self {
|
||||
Self::Cycles5
|
||||
}
|
||||
}
|
||||
|
||||
/// Calibration mode for STM32U5
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum CalibrationMode {
|
||||
/// Offset calibration only (quick)
|
||||
Offset,
|
||||
/// Offset + Linearity calibration (slower, more accurate)
|
||||
/// This is a new feature in STM32U5
|
||||
OffsetLinearity,
|
||||
}
|
||||
|
||||
/// Implemented for all types that represent ADC channels
|
||||
pub trait Channel<T>: EmbeddedHalChannel<T, ID = u8> {
|
||||
fn set_sample_time(&mut self, adc: &mut T, sample_time: SampleTime);
|
||||
}
|
||||
|
||||
macro_rules! impl_embedded_hal_channel {
|
||||
($pin:ty => ($adc_type:ident, $chan:expr)) => {
|
||||
impl EmbeddedHalChannel<pac::$adc_type> for $pin {
|
||||
type ID = u8;
|
||||
fn channel() -> Self::ID {
|
||||
$chan
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_channel {
|
||||
($pin:ty => ($adc_type:ident, $smpr:ident, $smp:ident, $($min_sample_time:expr)?)) => {
|
||||
impl Channel<pac::$adc_type> for $pin {
|
||||
#[inline]
|
||||
fn set_sample_time(&mut self, adc: &mut pac::$adc_type, sample_time: SampleTime) {
|
||||
$(
|
||||
// Ensure minimum sample time.
|
||||
let sample_time = if sample_time < $min_sample_time {
|
||||
$min_sample_time
|
||||
} else {
|
||||
sample_time
|
||||
};
|
||||
)*
|
||||
adc.$smpr().modify(|_, w| {
|
||||
// This is sound, as all `SampleTime` values are valid for this field.
|
||||
unsafe { w.$smp().bits(sample_time as u8) }
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! adc_pins {
|
||||
($($pin:ty => ($adc_type:ident, $chan:expr, $smpr:ident, $smp:ident $(, $min_sample_time:expr)?),)+ $(,)?) => {
|
||||
$(
|
||||
impl_embedded_hal_channel!($pin => ($adc_type, $chan));
|
||||
impl_channel!($pin =>($adc_type, $smpr, $smp, $($min_sample_time)*));
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! adc {
|
||||
(@vref: $adc_type:ident) => {
|
||||
/// Calculates the system VDDA by sampling the internal VREF channel and comparing
|
||||
/// the result with the value stored at the factory. If the chip's VDDA is not stable, run
|
||||
/// this before each ADC conversion.
|
||||
///
|
||||
/// Returns the calibrated VDDA voltage in millivolts.
|
||||
#[inline]
|
||||
pub fn calibrate_vdda(&mut self, delay: &mut impl DelayUs<u32>) -> u16 {
|
||||
let vref = self.enable_vref(delay);
|
||||
let vref_cal = VrefCal::get().read();
|
||||
// This can't actually fail, it's just in a result to satisfy hal trait
|
||||
let vref_samp = self.read(&mut Vref { _0: () }).unwrap();
|
||||
// Safety: DIV by 0 is possible if vref_samp is 0
|
||||
self.calibrated_vdda = (VDDA_CALIB_MV * u32::from(vref_cal)) / u32::from(vref_samp);
|
||||
// Disable VREF again if it was disabled before.
|
||||
if let Some(vref) = vref {
|
||||
self.disable_vref(vref);
|
||||
}
|
||||
self.calibrated_vdda as u16
|
||||
}
|
||||
|
||||
/// Check if the internal voltage reference channel is enabled.
|
||||
#[inline]
|
||||
pub fn is_vref_enabled(&self) -> bool {
|
||||
self.adc_common.ccr.read().vrefen().bit_is_set()
|
||||
}
|
||||
|
||||
/// Enable the internal voltage reference channel.
|
||||
///
|
||||
/// Returns `Some(Vref)` if the channel was enabled or `None` if it was already enabled before.
|
||||
#[inline]
|
||||
pub fn enable_vref(&mut self, delay: &mut impl DelayUs<u32>) -> Option<Vref> {
|
||||
if self.is_vref_enabled() {
|
||||
return None;
|
||||
}
|
||||
self.adc_common.ccr.modify(|_, w| w.vrefen().set_bit());
|
||||
// "Embedded internal voltage reference" states that it takes a maximum of 12 us
|
||||
// to stabilize the internal voltage reference, we wait a little more.
|
||||
delay.delay_us(15);
|
||||
Some(Vref { _0: () })
|
||||
}
|
||||
|
||||
/// Disable the internal voltage reference channel.
|
||||
#[inline]
|
||||
pub fn disable_vref(&mut self, vref: Vref) {
|
||||
drop(vref);
|
||||
self.adc_common.ccr.modify(|_, w| w.vrefen().clear_bit());
|
||||
}
|
||||
};
|
||||
|
||||
(@vbat: $adc_type:ident) => {
|
||||
/// Check if the battery voltage monitoring channel is enabled.
|
||||
#[inline]
|
||||
pub fn is_vbat_enabled(&self) -> bool {
|
||||
self.adc_common.ccr.read().vbaten().bit_is_set()
|
||||
}
|
||||
|
||||
/// Enable the battery voltage monitoring channel.
|
||||
///
|
||||
/// Returns `Some(Vbat)` if the channel was enabled or `None` if it was already enabled before.
|
||||
#[inline]
|
||||
pub fn enable_vbat(&mut self) -> Option<Vbat> {
|
||||
if self.is_vbat_enabled() {
|
||||
return None;
|
||||
}
|
||||
self.adc_common.ccr.modify(|_, w| w.vbaten().set_bit());
|
||||
Some(Vbat { _0: () })
|
||||
}
|
||||
|
||||
/// Disable the battery voltage monitoring channel.
|
||||
#[inline]
|
||||
pub fn disable_vbat(&mut self, vbat: Vbat) {
|
||||
drop(vbat);
|
||||
self.adc_common.ccr.modify(|_, w| w.vbaten().clear_bit());
|
||||
}
|
||||
};
|
||||
|
||||
(@vts: $adc_type:ident) => {
|
||||
/// Check if the internal temperature sensor channel is enabled.
|
||||
pub fn is_temperature_enabled(&self) -> bool {
|
||||
self.adc_common.ccr.read().vsenseen().bit_is_set()
|
||||
}
|
||||
|
||||
/// Enable the internal temperature sensor channel.
|
||||
///
|
||||
/// Returns `Some(Temperature)` if the channel was enabled or `None` if it was already enabled before.
|
||||
pub fn enable_temperature(&mut self, delay: &mut impl DelayUs<u32>) -> Option<Temperature> {
|
||||
if self.is_temperature_enabled() {
|
||||
return None;
|
||||
}
|
||||
self.adc_common.ccr.modify(|_, w| w.vsenseen().set_bit());
|
||||
// Temperature sensor has startup time, wait ~120us
|
||||
delay.delay_us(150);
|
||||
Some(Temperature { _0: () })
|
||||
}
|
||||
|
||||
/// Disable the internal temperature sensor channel.
|
||||
pub fn disable_temperature(&mut self, temperature: Temperature) {
|
||||
drop(temperature);
|
||||
self.adc_common.ccr.modify(|_, w| w.vsenseen().clear_bit())
|
||||
}
|
||||
};
|
||||
|
||||
($($adc_type:ident => ($constructor_fn_name:ident)),+ $(,)?) => {
|
||||
$(
|
||||
impl Adc<pac::$adc_type> {
|
||||
/// Enable the ADC clock and runs calibration.
|
||||
///
|
||||
/// For STM32U5, this uses the async clock derived from the kernel clock
|
||||
pub fn $constructor_fn_name(
|
||||
adc: pac::$adc_type,
|
||||
adc_common: AdcCommon,
|
||||
rcc: &mut pac::RCC,
|
||||
delay: &mut impl DelayUs<u32>,
|
||||
) -> Self {
|
||||
// STM32U5: Configure ADC clock in RCC.CCIPR3
|
||||
// ADCSEL[1:0] = 0b11 for sys_ck (system clock)
|
||||
rcc.ccipr3().modify(|_, w| unsafe {
|
||||
w.adcsel().bits(0b11)
|
||||
});
|
||||
|
||||
// Initialize the ADC according to STM32U5 Reference Manual
|
||||
// Exit deep-power-down mode
|
||||
adc.cr().write(|w| w.deeppwd().clear_bit());
|
||||
|
||||
// Enable internal voltage regulator
|
||||
adc.cr().modify(|_, w| w.advregen().set_bit());
|
||||
|
||||
// Wait for voltage regulator startup (20 us typ, use 25 us)
|
||||
delay.delay_us(25);
|
||||
|
||||
// Perform basic calibration (offset only for now)
|
||||
// STM32U5 has extended calibration but we'll start with simple
|
||||
Self::perform_calibration(&adc, CalibrationMode::Offset, delay);
|
||||
|
||||
let mut s = Self {
|
||||
adc,
|
||||
adc_common,
|
||||
resolution: Resolution::default(),
|
||||
sample_time: SampleTime::default(),
|
||||
calibrated_vdda: VDDA_CALIB_MV,
|
||||
};
|
||||
|
||||
s.calibrate_vdda(delay);
|
||||
s
|
||||
}
|
||||
|
||||
/// Perform ADC calibration
|
||||
///
|
||||
/// STM32U5 supports both offset calibration (quick) and offset+linearity (accurate)
|
||||
fn perform_calibration(
|
||||
adc: &pac::$adc_type,
|
||||
mode: CalibrationMode,
|
||||
delay: &mut impl DelayUs<u32>,
|
||||
) {
|
||||
match mode {
|
||||
CalibrationMode::Offset => {
|
||||
// Simple offset calibration (compatible with L4)
|
||||
adc.cr().modify(|_, w| {
|
||||
w.adcal().set_bit(); // Start calibration
|
||||
w.adcaldif().clear_bit() // Single-ended mode
|
||||
});
|
||||
while adc.cr().read().adcal().bit_is_set() {}
|
||||
|
||||
// Wait 4 ADC clocks after calibration
|
||||
delay.delay_us(1);
|
||||
}
|
||||
CalibrationMode::OffsetLinearity => {
|
||||
// STM32U5 extended calibration (offset + linearity)
|
||||
// This is more complex and device-specific
|
||||
// Following RM0456 Rev 6 Section 25.4.8
|
||||
|
||||
// 1. Ensure ADC is disabled
|
||||
adc.cr().modify(|_, w| w.aden().clear_bit());
|
||||
|
||||
// 2. Exit deep power down and enable vreg (already done in init)
|
||||
|
||||
// 3. Enable ADC for calibration
|
||||
adc.isr().write(|w| w.adrdy().set_bit()); // Clear ADRDY
|
||||
adc.cr().modify(|_, w| w.aden().set_bit());
|
||||
while adc.isr().read().adrdy().bit_is_clear() {}
|
||||
|
||||
// 4. Write calibration factor initialization values
|
||||
adc.cr().modify(|_, w| unsafe {
|
||||
w.calindex().bits(0x9)
|
||||
});
|
||||
|
||||
// Memory barrier to ensure write order
|
||||
core::sync::atomic::compiler_fence(Ordering::SeqCst);
|
||||
|
||||
adc.calfact2().modify(|_, w| unsafe {
|
||||
w.lincalfact().bits(0x03021100)
|
||||
});
|
||||
|
||||
core::sync::atomic::compiler_fence(Ordering::SeqCst);
|
||||
|
||||
// 5. Latch calibration coefficients
|
||||
adc.calfact().modify(|_, w| w.latch_coef().set_bit());
|
||||
|
||||
// 6. Disable ADC
|
||||
adc.cr().modify(|_, w| w.addis().set_bit());
|
||||
while adc.cr().read().aden().bit_is_set() {}
|
||||
|
||||
// 7. Set linearity calibration
|
||||
adc.cr().modify(|_, w| w.adcallin().set_bit());
|
||||
|
||||
// 8. Start calibration
|
||||
adc.cr().modify(|_, w| w.adcal().set_bit());
|
||||
while adc.cr().read().adcal().bit_is_set() {}
|
||||
|
||||
delay.delay_us(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
adc!(@vref: $adc_type);
|
||||
adc!(@vbat: $adc_type);
|
||||
adc!(@vts: $adc_type);
|
||||
|
||||
/// Check if the ADC is enabled.
|
||||
#[inline]
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
self.adc.cr().read().aden().bit_is_set()
|
||||
}
|
||||
|
||||
/// Enable the ADC.
|
||||
#[inline]
|
||||
pub fn enable(&mut self) {
|
||||
if !self.is_enabled() {
|
||||
// Make sure bits are off
|
||||
while self.adc.cr().read().addis().bit_is_set() {}
|
||||
|
||||
// Clear ADRDY by setting it
|
||||
self.adc.isr().modify(|_, w| w.adrdy().set_bit());
|
||||
self.adc.cr().modify(|_, w| w.aden().set_bit());
|
||||
while self.adc.isr().read().adrdy().bit_is_clear() {}
|
||||
|
||||
// Configure ADC - NOTE: STM32U5 uses CFGR1 (not CFGR)
|
||||
self.adc.cfgr1().modify(|_, w| unsafe {
|
||||
w.res().bits(self.resolution as u8)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable the ADC.
|
||||
#[inline]
|
||||
pub fn disable(&mut self) {
|
||||
self.adc.cr().modify(|_, w| w.addis().set_bit());
|
||||
}
|
||||
|
||||
/// Returns the current sample stored in the ADC data register.
|
||||
#[inline]
|
||||
pub fn current_sample(&self) -> u16 {
|
||||
self.adc.dr().read().rdata().bits()
|
||||
}
|
||||
|
||||
/// Configure the channel for a specific step in the sequence.
|
||||
///
|
||||
/// Automatically sets the sequence length to the farthest sequence
|
||||
/// index that has been used so far. Use [`Adc::reset_sequence`] to
|
||||
/// reset the sequence length.
|
||||
pub fn configure_sequence<C>(
|
||||
&mut self,
|
||||
channel: &mut C,
|
||||
sequence: Sequence,
|
||||
sample_time: SampleTime,
|
||||
) where
|
||||
C: Channel<pac::$adc_type>,
|
||||
{
|
||||
let channel_bits = C::channel();
|
||||
channel.set_sample_time(&mut self.adc, sample_time);
|
||||
|
||||
// STM32U5: Enable channel in PCSEL (preselection register)
|
||||
self.adc.pcsel().modify(|r, w| unsafe {
|
||||
w.bits(r.bits() | (1 << channel_bits))
|
||||
});
|
||||
|
||||
unsafe {
|
||||
match sequence {
|
||||
Sequence::One => self.adc.sqr1().modify(|_, w| w.sq1().bits(channel_bits)),
|
||||
Sequence::Two => self.adc.sqr1().modify(|_, w| w.sq2().bits(channel_bits)),
|
||||
Sequence::Three => self.adc.sqr1().modify(|_, w| w.sq3().bits(channel_bits)),
|
||||
Sequence::Four => self.adc.sqr1().modify(|_, w| w.sq4().bits(channel_bits)),
|
||||
Sequence::Five => self.adc.sqr2().modify(|_, w| w.sq5().bits(channel_bits)),
|
||||
Sequence::Six => self.adc.sqr2().modify(|_, w| w.sq6().bits(channel_bits)),
|
||||
Sequence::Seven => self.adc.sqr2().modify(|_, w| w.sq7().bits(channel_bits)),
|
||||
Sequence::Eight => self.adc.sqr2().modify(|_, w| w.sq8().bits(channel_bits)),
|
||||
Sequence::Nine => self.adc.sqr2().modify(|_, w| w.sq9().bits(channel_bits)),
|
||||
Sequence::Ten => self.adc.sqr3().modify(|_, w| w.sq10().bits(channel_bits)),
|
||||
Sequence::Eleven => self.adc.sqr3().modify(|_, w| w.sq11().bits(channel_bits)),
|
||||
Sequence::Twelve => self.adc.sqr3().modify(|_, w| w.sq12().bits(channel_bits)),
|
||||
Sequence::Thirteen => self.adc.sqr3().modify(|_, w| w.sq13().bits(channel_bits)),
|
||||
Sequence::Fourteen => self.adc.sqr3().modify(|_, w| w.sq14().bits(channel_bits)),
|
||||
Sequence::Fifteen => self.adc.sqr4().modify(|_, w| w.sq15().bits(channel_bits)),
|
||||
Sequence::Sixteen => self.adc.sqr4().modify(|_, w| w.sq16().bits(channel_bits)),
|
||||
}
|
||||
}
|
||||
|
||||
// This will only ever extend the sequence, not shrink it.
|
||||
let current_seql = self.get_sequence_length();
|
||||
let next_seql: u8 = sequence.into();
|
||||
if next_seql >= current_seql {
|
||||
self.set_sequence_length(sequence.into());
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the configured sequence length (= `actual sequence length - 1`)
|
||||
#[inline]
|
||||
pub(crate) fn get_sequence_length(&self) -> u8 {
|
||||
self.adc.sqr1().read().l().bits()
|
||||
}
|
||||
|
||||
/// Private: length must be `actual sequence length - 1`
|
||||
#[inline]
|
||||
fn set_sequence_length(&mut self, length: u8) {
|
||||
self.adc.sqr1().modify(|_, w| unsafe { w.l().bits(length) });
|
||||
}
|
||||
|
||||
/// Reset the sequence length to 1
|
||||
///
|
||||
/// Does *not* erase previously configured sequence settings
|
||||
#[inline]
|
||||
pub fn reset_sequence(&mut self) {
|
||||
self.adc.sqr1().modify(|_, w| unsafe { w.l().bits(0b0000) })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_completed_conversion(&self) -> bool {
|
||||
self.adc.isr().read().eoc().bit_is_set()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_completed_sequence(&self) -> bool {
|
||||
self.adc.isr().read().eos().bit_is_set()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn clear_end_flags(&mut self) {
|
||||
// EOS and EOC are reset by setting them
|
||||
self.adc.isr().modify(|_, w| w.eos().set_bit().eoc().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn start_conversion(&mut self) {
|
||||
self.enable();
|
||||
self.clear_end_flags();
|
||||
self.adc.cr().modify(|_, w| w.adstart().set_bit());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_converting(&self) -> bool {
|
||||
self.adc.cr().read().adstart().bit_is_set()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn listen(&mut self, event: Event) {
|
||||
self.adc.ier().modify(|_, w| match event {
|
||||
Event::EndOfRegularSequence => w.eosie().set_bit(),
|
||||
Event::EndOfRegularConversion => w.eocie().set_bit(),
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn unlisten(&mut self, event: Event) {
|
||||
self.adc.ier().modify(|_, w| match event {
|
||||
Event::EndOfRegularSequence => w.eosie().clear_bit(),
|
||||
Event::EndOfRegularConversion => w.eocie().clear_bit(),
|
||||
});
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
// Instantiate ADC1 and ADC2
|
||||
adc!(
|
||||
ADC1 => (adc1),
|
||||
ADC2 => (adc2),
|
||||
);
|
||||
|
||||
impl<C> OneShot<ADC1, u16, C> for Adc<ADC1>
|
||||
where
|
||||
C: Channel<ADC1>,
|
||||
{
|
||||
type Error = Infallible;
|
||||
|
||||
fn read(&mut self, channel: &mut C) -> nb::Result<u16, Self::Error> {
|
||||
self.configure_sequence(channel, Sequence::One, self.sample_time);
|
||||
self.start_conversion();
|
||||
while !self.has_completed_sequence() {}
|
||||
|
||||
// Read ADC value first time and discard it, as per errata sheet.
|
||||
// The errata state that if we do conversions slower than 1 kHz, the
|
||||
// first read ADC value can be corrupted, so we discard it and measure again.
|
||||
let _ = self.current_sample();
|
||||
|
||||
self.start_conversion();
|
||||
while !self.has_completed_sequence() {}
|
||||
|
||||
// Read ADC value.
|
||||
let val = self.current_sample();
|
||||
|
||||
// Disable ADC.
|
||||
self.disable();
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> OneShot<ADC2, u16, C> for Adc<ADC2>
|
||||
where
|
||||
C: Channel<ADC2>,
|
||||
{
|
||||
type Error = Infallible;
|
||||
|
||||
fn read(&mut self, channel: &mut C) -> nb::Result<u16, Self::Error> {
|
||||
self.configure_sequence(channel, Sequence::One, self.sample_time);
|
||||
self.start_conversion();
|
||||
while !self.has_completed_sequence() {}
|
||||
|
||||
let _ = self.current_sample();
|
||||
|
||||
self.start_conversion();
|
||||
while !self.has_completed_sequence() {}
|
||||
|
||||
let val = self.current_sample();
|
||||
self.disable();
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
|
||||
// DMA support for ADC1
|
||||
impl TransferPayload for RxDma<Adc<ADC1>, dma1::C1> {
|
||||
fn start(&mut self) {
|
||||
self.channel.start();
|
||||
}
|
||||
|
||||
fn stop(&mut self) {
|
||||
self.channel.stop();
|
||||
}
|
||||
}
|
||||
|
||||
impl RxDma<Adc<ADC1>, dma1::C1> {
|
||||
pub fn split(mut self) -> (Adc<ADC1>, dma1::C1) {
|
||||
self.stop();
|
||||
(self.payload, self.channel)
|
||||
}
|
||||
}
|
||||
|
||||
impl<BUFFER, const N: usize> Transfer<W, BUFFER, RxDma<Adc<ADC1>, dma1::C1>>
|
||||
where
|
||||
BUFFER: Sized + StableDeref<Target = [u16; N]> + DerefMut + 'static,
|
||||
{
|
||||
pub fn from_adc_dma(
|
||||
dma: RxDma<Adc<ADC1>, dma1::C1>,
|
||||
buffer: BUFFER,
|
||||
dma_mode: DmaMode,
|
||||
transfer_complete_interrupt: bool,
|
||||
) -> Self {
|
||||
let (adc, channel) = dma.split();
|
||||
Transfer::from_adc(adc, channel, buffer, dma_mode, transfer_complete_interrupt)
|
||||
}
|
||||
|
||||
/// Initiate a new DMA transfer from an ADC.
|
||||
///
|
||||
/// `dma_mode` indicates the desired mode for DMA.
|
||||
///
|
||||
/// If `transfer_complete_interrupt` is true, the transfer
|
||||
/// complete interrupt will be enabled
|
||||
pub fn from_adc(
|
||||
mut adc: Adc<ADC1>,
|
||||
mut channel: dma1::C1,
|
||||
buffer: BUFFER,
|
||||
dma_mode: DmaMode,
|
||||
transfer_complete_interrupt: bool,
|
||||
) -> Self {
|
||||
assert!(dma_mode != DmaMode::Disabled);
|
||||
|
||||
let (enable, circular) = match dma_mode {
|
||||
DmaMode::Disabled => (false, false),
|
||||
DmaMode::Oneshot => (true, false),
|
||||
};
|
||||
|
||||
// STM32U5: Configure DMA in CFGR1 register (was CFGR on L4)
|
||||
// DMNGT field: 00=none, 01=DMA one shot, 10=DMA circular
|
||||
adc.adc.cfgr1().modify(|_, w| unsafe {
|
||||
if enable {
|
||||
if circular {
|
||||
w.dmngt().bits(0b10) // Circular (SKIPPED for now)
|
||||
} else {
|
||||
w.dmngt().bits(0b01) // One shot
|
||||
}
|
||||
} else {
|
||||
w.dmngt().bits(0b00) // Disabled
|
||||
}
|
||||
});
|
||||
|
||||
channel.set_peripheral_address(&adc.adc.dr() as *const _ as u32, false);
|
||||
channel.set_memory_address(buffer.as_ptr() as u32, true);
|
||||
channel.set_transfer_length(N as u16);
|
||||
channel.set_request_line(DmaInput::Adc1).unwrap();
|
||||
|
||||
channel.ccr().modify(|_, w| unsafe {
|
||||
w.mem2mem()
|
||||
.clear_bit()
|
||||
.pl()
|
||||
.bits(0b01) // Medium priority
|
||||
.msize()
|
||||
.bits(0b01) // 16-bits memory
|
||||
.psize()
|
||||
.bits(0b01) // 16-bits peripheral
|
||||
.dir()
|
||||
.clear_bit() // Peripheral to memory
|
||||
.circ()
|
||||
.bit(circular)
|
||||
});
|
||||
|
||||
if transfer_complete_interrupt {
|
||||
channel.listen(DMAEvent::TransferComplete);
|
||||
}
|
||||
|
||||
atomic::compiler_fence(Ordering::Release);
|
||||
channel.start();
|
||||
adc.start_conversion();
|
||||
|
||||
Transfer::w(
|
||||
buffer,
|
||||
RxDma {
|
||||
channel,
|
||||
payload: adc,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Pin mappings for STM32U575
|
||||
// Based on STM32U575 datasheet Table 22 "STM32U575xx pin definitions"
|
||||
// NOTE: Verify these mappings against your specific chip variant
|
||||
|
||||
adc_pins!(
|
||||
// Internal channels (ADC1 only)
|
||||
// VREF requires minimum 4us sample time (use Cycles391 = 4.89us @ 80MHz)
|
||||
Vref => (ADC1, 0, smpr1, smp0, SampleTime::Cycles391),
|
||||
Temperature => (ADC1, 17, smpr2, smp17, SampleTime::Cycles391),
|
||||
Vbat => (ADC1, 18, smpr2, smp18, SampleTime::Cycles391),
|
||||
|
||||
// ADC1 external channels (verify pin availability on your package)
|
||||
gpio::PC0<Analog> => (ADC1, 1, smpr1, smp1),
|
||||
gpio::PC1<Analog> => (ADC1, 2, smpr1, smp2),
|
||||
gpio::PC2<Analog> => (ADC1, 3, smpr1, smp3),
|
||||
gpio::PC3<Analog> => (ADC1, 4, smpr1, smp4),
|
||||
gpio::PA0<Analog> => (ADC1, 5, smpr1, smp5),
|
||||
gpio::PA1<Analog> => (ADC1, 6, smpr1, smp6),
|
||||
gpio::PA2<Analog> => (ADC1, 7, smpr1, smp7),
|
||||
gpio::PA3<Analog> => (ADC1, 8, smpr1, smp8),
|
||||
gpio::PA4<Analog> => (ADC1, 9, smpr1, smp9),
|
||||
gpio::PA5<Analog> => (ADC1, 10, smpr2, smp10),
|
||||
gpio::PA6<Analog> => (ADC1, 11, smpr2, smp11),
|
||||
gpio::PA7<Analog> => (ADC1, 12, smpr2, smp12),
|
||||
gpio::PC4<Analog> => (ADC1, 13, smpr2, smp13),
|
||||
gpio::PC5<Analog> => (ADC1, 14, smpr2, smp14),
|
||||
gpio::PB0<Analog> => (ADC1, 15, smpr2, smp15),
|
||||
gpio::PB1<Analog> => (ADC1, 16, smpr2, smp16),
|
||||
|
||||
// ADC2 external channels (same pins as ADC1)
|
||||
gpio::PC0<Analog> => (ADC2, 1, smpr1, smp1),
|
||||
gpio::PC1<Analog> => (ADC2, 2, smpr1, smp2),
|
||||
gpio::PC2<Analog> => (ADC2, 3, smpr1, smp3),
|
||||
gpio::PC3<Analog> => (ADC2, 4, smpr1, smp4),
|
||||
gpio::PA0<Analog> => (ADC2, 5, smpr1, smp5),
|
||||
gpio::PA1<Analog> => (ADC2, 6, smpr1, smp6),
|
||||
gpio::PA2<Analog> => (ADC2, 7, smpr1, smp7),
|
||||
gpio::PA3<Analog> => (ADC2, 8, smpr1, smp8),
|
||||
gpio::PA4<Analog> => (ADC2, 9, smpr1, smp9),
|
||||
gpio::PA5<Analog> => (ADC2, 10, smpr2, smp10),
|
||||
gpio::PA6<Analog> => (ADC2, 11, smpr2, smp11),
|
||||
gpio::PA7<Analog> => (ADC2, 12, smpr2, smp12),
|
||||
gpio::PC4<Analog> => (ADC2, 13, smpr2, smp13),
|
||||
gpio::PC5<Analog> => (ADC2, 14, smpr2, smp14),
|
||||
gpio::PB0<Analog> => (ADC2, 15, smpr2, smp15),
|
||||
gpio::PB1<Analog> => (ADC2, 16, smpr2, smp16),
|
||||
);
|
||||
|
||||
// SKIPPED: ADC3 (not present on STM32U575)
|
||||
// SKIPPED: ADC4 (different architecture, implement separately later)
|
||||
// SKIPPED: Circular DMA mode (needs further testing)
|
||||
// SKIPPED: Injected channels (implement later if needed)
|
||||
// SKIPPED: Differential mode (implement later if needed)
|
||||
// SKIPPED: Oversampling configuration (implement later if needed)
|
||||
// SKIPPED: Analog watchdog configuration (implement later if needed)
|
||||
0
src/adc/adc_ex.rs
Normal file
0
src/adc/adc_ex.rs
Normal file
9
src/adc/mod.rs
Normal file
9
src/adc/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
// src/adc/mod.rs
|
||||
|
||||
//! Analog-to-Digital Converter HAL for STM32U575
|
||||
//!
|
||||
//! This module provides ADC1 and ADC2 support.
|
||||
//! ADC4 and extended features will be added later.
|
||||
|
||||
pub mod adc;
|
||||
pub use adc::*;
|
||||
348
src/flash.rs
Normal file
348
src/flash.rs
Normal file
@@ -0,0 +1,348 @@
|
||||
// src/flash.rs
|
||||
//! Flash memory module (STM32U575, minimal non-secure subset)
|
||||
//!
|
||||
//! Included:
|
||||
//! - Constrain into parts (register proxies)
|
||||
//! - Unlock/lock (non-secure) via NSKEYR / NSCR
|
||||
//! - Page erase using NSCR.PER/PNB/BKER/START
|
||||
//! - 128-bit (quad-word) program using NSCR.PG and 4x32 writes
|
||||
//! - Simple wait (polling on BSY), minimal EOP clear
|
||||
//! - Byte-granular write built on top of 16-byte chunks
|
||||
//!
|
||||
//! Skipped / TODO:
|
||||
//! - Secure programming path (SECKEYR/SECCR/SECSR)
|
||||
//! - Burst program (8x quad-word)
|
||||
//! - Detailed error decoding (PROGERR/PGSERR/WRPERR...)
|
||||
//! - Option bytes, protections, ECC interactions
|
||||
//! - Exact geometry detection: we assume 8 KiB pages, 256 pages total
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use crate::pac as pac;
|
||||
use crate::pac::FLASH;
|
||||
use core::{mem, ptr};
|
||||
|
||||
/// Simple Flash error set for this minimal port
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Error {
|
||||
/// Flash controller is busy
|
||||
Busy,
|
||||
/// Operation failed (generic)
|
||||
Failure,
|
||||
/// Address alignment issue
|
||||
Unaligned,
|
||||
/// Page index out of range
|
||||
PageOutOfRange,
|
||||
}
|
||||
|
||||
/// A Flash page index wrapper (0-based)
|
||||
///
|
||||
/// For STM32U575 this example assumes 8 KiB pages, 256 total pages
|
||||
/// (128 per bank). Adjust if your device differs.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct FlashPage(pub u16);
|
||||
|
||||
impl FlashPage {
|
||||
/// Convert page index to absolute address
|
||||
///
|
||||
/// 0x0800_0000 + page_idx * PAGE_SIZE
|
||||
pub const fn to_address(&self) -> usize {
|
||||
FLASH_BASE as usize + (self.0 as usize) * PAGE_SIZE
|
||||
}
|
||||
}
|
||||
|
||||
/// Base of Flash memory
|
||||
pub const FLASH_BASE: u32 = 0x0800_0000;
|
||||
/// Page size in bytes (assumed)
|
||||
pub const PAGE_SIZE: usize = 8 * 1024; // 8 KiB
|
||||
/// Total pages assumed
|
||||
pub const TOTAL_PAGES: u16 = 256;
|
||||
/// Pages per bank assumed
|
||||
pub const PAGES_PER_BANK: u16 = TOTAL_PAGES / 2;
|
||||
|
||||
/// Extension trait to constrain the FLASH peripheral
|
||||
pub trait FlashExt {
|
||||
/// Constrain the FLASH peripheral to play nicely with the other abstractions
|
||||
fn constrain(self) -> Parts;
|
||||
}
|
||||
|
||||
impl FlashExt for FLASH {
|
||||
fn constrain(self) -> Parts {
|
||||
Parts {
|
||||
acr: ACR,
|
||||
nskeyr: NSKEYR,
|
||||
optkeyr: OPTKEYR,
|
||||
nssr: NSSR,
|
||||
nscr: NSCR,
|
||||
eccr: ECCR,
|
||||
pdkey1r: PDKEY1R,
|
||||
pdkey2r: PDKEY2R,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Constrained FLASH peripheral
|
||||
pub struct Parts {
|
||||
/// Opaque ACR register
|
||||
pub acr: ACR,
|
||||
/// Opaque NSKEYR register (non-secure key)
|
||||
pub nskeyr: NSKEYR,
|
||||
/// Opaque OPTKEYR register
|
||||
pub optkeyr: OPTKEYR,
|
||||
/// Opaque NSSR register (non-secure status)
|
||||
pub nssr: NSSR,
|
||||
/// Opaque NSCR register (non-secure control)
|
||||
pub nscr: NSCR,
|
||||
/// Opaque ECCR register
|
||||
pub eccr: ECCR,
|
||||
/// Opaque PDKEY1R register
|
||||
pub pdkey1r: PDKEY1R,
|
||||
/// Opaque PDKEY2R register
|
||||
pub pdkey2r: PDKEY2R,
|
||||
}
|
||||
|
||||
macro_rules! generate_register {
|
||||
($A:ident, $field:ident, $name:expr) => {
|
||||
#[doc = "Opaque "]
|
||||
#[doc = $name]
|
||||
#[doc = " register"]
|
||||
pub struct $A;
|
||||
|
||||
impl $A {
|
||||
#[allow(unused)]
|
||||
#[inline(always)]
|
||||
pub(crate) fn reg(&mut self) -> &pac::flash::$A {
|
||||
// NOTE(unsafe): this proxy grants exclusive access to this register
|
||||
unsafe { &(*FLASH::ptr()).$field() }
|
||||
}
|
||||
}
|
||||
};
|
||||
($A:ident, $field:ident) => {
|
||||
generate_register!($A, $field, stringify!($A));
|
||||
};
|
||||
}
|
||||
|
||||
// Register proxies (names match stm32u575 PAC)
|
||||
generate_register!(ACR, acr);
|
||||
generate_register!(NSKEYR, nskeyr);
|
||||
generate_register!(OPTKEYR, optkeyr);
|
||||
generate_register!(NSSR, nssr);
|
||||
generate_register!(NSCR, nscr);
|
||||
generate_register!(ECCR, eccr);
|
||||
generate_register!(PDKEY1R, pdkey1r);
|
||||
generate_register!(PDKEY2R, pdkey2r);
|
||||
|
||||
// Unlock keys (same values as STM32U5 HAL)
|
||||
const FLASH_KEY1: u32 = 0x4567_0123;
|
||||
const FLASH_KEY2: u32 = 0xCDEF_89AB;
|
||||
|
||||
/// Active programming session on FLASH (non-secure)
|
||||
///
|
||||
/// Drop locks the controller automatically.
|
||||
pub struct FlashProgramming<'a> {
|
||||
nssr: &'a mut NSSR,
|
||||
nscr: &'a mut NSCR,
|
||||
}
|
||||
|
||||
impl<'a> Drop for FlashProgramming<'a> {
|
||||
fn drop(&mut self) {
|
||||
// Lock on drop
|
||||
self.lock();
|
||||
}
|
||||
}
|
||||
|
||||
impl Parts {
|
||||
/// Unlock the flash non-secure control registers via NSKEYR for programming/erase
|
||||
pub fn unlock(&mut self) -> Result<FlashProgramming<'_>, Error> {
|
||||
// Write keys to NSKEYR
|
||||
unsafe {
|
||||
self.nskeyr.reg().write(|w| w.bits(FLASH_KEY1));
|
||||
self.nskeyr.reg().write(|w| w.bits(FLASH_KEY2));
|
||||
}
|
||||
|
||||
// Check NSCR.LOCK bit
|
||||
let locked = self.nscr.reg().read().lock().bit_is_set();
|
||||
if locked {
|
||||
Err(Error::Failure)
|
||||
} else {
|
||||
Ok(FlashProgramming {
|
||||
nssr: &mut self.nssr,
|
||||
nscr: &mut self.nscr,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FlashProgramming<'a> {
|
||||
/// Lock the flash memory controller (NSCR.LOCK=1)
|
||||
pub fn lock(&mut self) {
|
||||
self.nscr.reg().modify(|_, w| w.lock().set_bit());
|
||||
}
|
||||
|
||||
/// Poll for completion (busy clear).
|
||||
///
|
||||
/// Minimal: we only look at BSY. If BSY is clear, we return Ok.
|
||||
/// You can extend this to decode error flags and EOP as needed.
|
||||
fn wait(&mut self) -> Result<(), Error> {
|
||||
while self.nssr.reg().read().bsy().bit_is_set() {}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clear EOP if set (optional; helps keep SR clean between operations).
|
||||
fn clear_eop(&mut self) {
|
||||
let sr = self.nssr.reg().read();
|
||||
if sr.eop().bit_is_set() {
|
||||
// Write-one-to-clear EOP
|
||||
unsafe {
|
||||
self.nssr.reg().write(|w| w.eop().set_bit());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Erase a single page by index.
|
||||
///
|
||||
/// Assumes:
|
||||
/// - TOTAL_PAGES = 256
|
||||
/// - PAGES_PER_BANK = 128
|
||||
/// - BKER=0 => bank1, BKER=1 => bank2
|
||||
pub fn erase_page(&mut self, page: FlashPage) -> Result<(), Error> {
|
||||
let idx = page.0;
|
||||
if idx >= TOTAL_PAGES {
|
||||
return Err(Error::PageOutOfRange);
|
||||
}
|
||||
|
||||
let (bker_set, pnb) = if idx < PAGES_PER_BANK {
|
||||
(false, idx as u8)
|
||||
} else {
|
||||
(true, (idx - PAGES_PER_BANK) as u8)
|
||||
};
|
||||
|
||||
// PER=1, set PNB and BKER, then START=1
|
||||
unsafe {
|
||||
self.nscr.reg().modify(|_, w| {
|
||||
w.per().set_bit();
|
||||
if bker_set {
|
||||
w.bker().set_bit();
|
||||
} else {
|
||||
w.bker().clear_bit();
|
||||
}
|
||||
w.pnb().bits(pnb);
|
||||
w
|
||||
});
|
||||
self.nscr.reg().modify(|_, w| w.strt().set_bit());
|
||||
}
|
||||
|
||||
self.wait()?;
|
||||
self.clear_eop();
|
||||
|
||||
// Clear PER
|
||||
self.nscr.reg().modify(|_, w| w.per().clear_bit());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Program one 128-bit (quad-word) at the given address.
|
||||
///
|
||||
/// - Address must be 16-byte aligned.
|
||||
/// - Data are provided as 16 raw bytes; we program four u32 words.
|
||||
pub fn write_quadword(&mut self, address: usize, data16: &[u8; 16]) -> Result<(), Error> {
|
||||
if (address & 0xF) != 0 {
|
||||
return Err(Error::Unaligned);
|
||||
}
|
||||
|
||||
// Extract four 32-bit words (little-endian)
|
||||
let w0 = u32::from_le_bytes([data16[0], data16[1], data16[2], data16[3]]);
|
||||
let w1 = u32::from_le_bytes([data16[4], data16[5], data16[6], data16[7]]);
|
||||
let w2 = u32::from_le_bytes([data16[8], data16[9], data16[10], data16[11]]);
|
||||
let w3 = u32::from_le_bytes([data16[12], data16[13], data16[14], data16[15]]);
|
||||
|
||||
// PG=1
|
||||
self.nscr.reg().modify(|_, w| w.pg().set_bit());
|
||||
|
||||
// Perform 4x 32-bit writes
|
||||
let mut dst = address as *mut u32;
|
||||
unsafe {
|
||||
ptr::write_volatile(dst, w0);
|
||||
dst = dst.add(1);
|
||||
ptr::write_volatile(dst, w1);
|
||||
dst = dst.add(1);
|
||||
ptr::write_volatile(dst, w2);
|
||||
dst = dst.add(1);
|
||||
ptr::write_volatile(dst, w3);
|
||||
}
|
||||
|
||||
self.wait()?;
|
||||
self.clear_eop();
|
||||
|
||||
// PG=0
|
||||
self.nscr.reg().modify(|_, w| w.pg().clear_bit());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write arbitrary bytes, rounding to 16-byte chunks.
|
||||
///
|
||||
/// For leading/trailing partial chunks, we fill missing bytes with 0xFF,
|
||||
/// which matches erased Flash default.
|
||||
pub fn write(&mut self, address: usize, buf: &[u8]) -> Result<(), Error> {
|
||||
// Handle leading unaligned portion
|
||||
let align_off = address & 0xF;
|
||||
let mut cur_addr = address;
|
||||
|
||||
if align_off != 0 {
|
||||
let head = core::cmp::min(16 - align_off, buf.len());
|
||||
let mut q = [0xFFu8; 16];
|
||||
// Pull existing erased fill (0xFF), then overlay provided bytes
|
||||
q[align_off..align_off + head].copy_from_slice(&buf[..head]);
|
||||
self.write_quadword(address - align_off, &q)?;
|
||||
cur_addr += head;
|
||||
}
|
||||
|
||||
// Aligned middle
|
||||
let aligned_len = if cur_addr > address {
|
||||
buf.len() - (cur_addr - address)
|
||||
} else {
|
||||
buf.len()
|
||||
};
|
||||
let aligned_start = buf.len() - aligned_len + (cur_addr - address);
|
||||
let mut i = 0;
|
||||
while i + 16 <= aligned_len {
|
||||
let mut q = [0u8; 16];
|
||||
q.copy_from_slice(
|
||||
&buf[aligned_start + i..aligned_start + i + 16],
|
||||
);
|
||||
self.write_quadword(cur_addr, &q)?;
|
||||
cur_addr += 16;
|
||||
i += 16;
|
||||
}
|
||||
|
||||
// Trailing remainder
|
||||
let rem = aligned_len - i;
|
||||
if rem > 0 {
|
||||
let mut q = [0xFFu8; 16];
|
||||
q[..rem].copy_from_slice(
|
||||
&buf[aligned_start + i..aligned_start + i + rem],
|
||||
);
|
||||
self.write_quadword(cur_addr, &q)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Mass erase (both banks). Minimal example; adjust to your needs.
|
||||
pub fn erase_all(&mut self) -> Result<(), Error> {
|
||||
// Set MER1 and MER2, then START
|
||||
self.nscr.reg().modify(|_, w| w.mer1().set_bit());
|
||||
self.nscr.reg().modify(|_, w| w.mer2().set_bit());
|
||||
self.nscr.reg().modify(|_, w| w.strt().set_bit());
|
||||
|
||||
self.wait()?;
|
||||
self.clear_eop();
|
||||
|
||||
// Clear MER bits
|
||||
self.nscr.reg().modify(|_, w| w.mer1().clear_bit());
|
||||
self.nscr.reg().modify(|_, w| w.mer2().clear_bit());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
17
src/lib.rs
17
src/lib.rs
@@ -0,0 +1,17 @@
|
||||
#![no_std]
|
||||
|
||||
pub use stm32u5::stm32u575 as pac;
|
||||
|
||||
pub mod rcc;
|
||||
pub mod pwr;
|
||||
pub mod flash;
|
||||
pub mod time;
|
||||
// pub mod adc;
|
||||
// add others later:
|
||||
// pub mod gpio;
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
pub(crate) use sealed::Sealed;
|
||||
|
||||
288
src/pwr.rs
Normal file
288
src/pwr.rs
Normal file
@@ -0,0 +1,288 @@
|
||||
// src/pwr.rs
|
||||
//! Power management (STM32U575, minimal common subset with STM32U5 HAL C)
|
||||
//!
|
||||
//! Included:
|
||||
//! - Enable PWR peripheral clock
|
||||
//! - Backup-domain write access helpers (DBP)
|
||||
//! - Enter Standby / Shutdown low-power modes (CR1.LPMS + WFI)
|
||||
//! - Wakeup source configuration (basic WUPEN1..8 mask via WUCR1)
|
||||
//! - Wakeup reason reading (WUSR)
|
||||
//!
|
||||
//! Skipped (present in U5 HAL C or PWREx, not implemented here yet):
|
||||
//! - VOS/voltage scaling and EPOD boost
|
||||
//! - Low-power run mode helper
|
||||
//! - PVD configuration / EXTI integration
|
||||
//! - Wakeup pin polarity / selection details (WUCR2/WUCR3)
|
||||
//! - Security/privileged attributes (SECCFGR/PRIVCFGR)
|
||||
//! - Explicit STOP 0/1/2/3 helpers (can be added similarly)
|
||||
//!
|
||||
//! This module only depends on the PAC.
|
||||
|
||||
use crate::pac as pac;
|
||||
use crate::pac::{PWR, RCC};
|
||||
use cortex_m::peripheral::SCB;
|
||||
|
||||
/// Wake-up source mask for WUPEN1..WUPEN8 (PWR_WUCR1)
|
||||
///
|
||||
/// This is a minimal bitmask wrapper around the 8 WUPEN lines.
|
||||
/// For detailed pin source selection or polarity, configure WUCR2/WUCR3
|
||||
/// directly via the PAC (not covered here yet).
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct WakeUpSource {
|
||||
bits: u16,
|
||||
}
|
||||
|
||||
impl WakeUpSource {
|
||||
pub fn new() -> Self {
|
||||
Self { bits: 0 }
|
||||
}
|
||||
|
||||
pub fn enable_wkup1(mut self, en: bool) -> Self {
|
||||
self.set_bit(0, en);
|
||||
self
|
||||
}
|
||||
pub fn enable_wkup2(mut self, en: bool) -> Self {
|
||||
self.set_bit(1, en);
|
||||
self
|
||||
}
|
||||
pub fn enable_wkup3(mut self, en: bool) -> Self {
|
||||
self.set_bit(2, en);
|
||||
self
|
||||
}
|
||||
pub fn enable_wkup4(mut self, en: bool) -> Self {
|
||||
self.set_bit(3, en);
|
||||
self
|
||||
}
|
||||
pub fn enable_wkup5(mut self, en: bool) -> Self {
|
||||
self.set_bit(4, en);
|
||||
self
|
||||
}
|
||||
pub fn enable_wkup6(mut self, en: bool) -> Self {
|
||||
self.set_bit(5, en);
|
||||
self
|
||||
}
|
||||
pub fn enable_wkup7(mut self, en: bool) -> Self {
|
||||
self.set_bit(6, en);
|
||||
self
|
||||
}
|
||||
pub fn enable_wkup8(mut self, en: bool) -> Self {
|
||||
self.set_bit(7, en);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_bit(&mut self, pos: u8, en: bool) {
|
||||
if en {
|
||||
self.bits |= 1u16 << pos;
|
||||
} else {
|
||||
self.bits &= !(1u16 << pos);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mask8(&self) -> u32 {
|
||||
(self.bits as u32) & 0xFF
|
||||
}
|
||||
|
||||
/// Construct from raw WUSR flags (WUF1..WUF8).
|
||||
pub fn from_wusr_raw(raw: u32) -> Self {
|
||||
Self {
|
||||
bits: (raw as u16) & 0x00FF,
|
||||
}
|
||||
}
|
||||
|
||||
/// Raw WUPEN/WUF bit mask (lower 8 bits).
|
||||
pub fn bits(&self) -> u16 {
|
||||
self.bits
|
||||
}
|
||||
}
|
||||
|
||||
/// Minimal PWR handle
|
||||
pub struct Pwr {
|
||||
pub cr1: CR1,
|
||||
pub wucr1: WUCR1,
|
||||
pub wuscr: WUSCR,
|
||||
pub wusr: WUSR,
|
||||
pub dbpr: DBPR,
|
||||
}
|
||||
|
||||
impl Pwr {
|
||||
/// Enable write access to the backup domain (DBP = 1)
|
||||
pub fn enable_backup_write(&mut self) {
|
||||
unsafe {
|
||||
self.dbpr.reg().modify(|_, w| w.dbp().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable write access to the backup domain (DBP = 0)
|
||||
pub fn disable_backup_write(&mut self) {
|
||||
unsafe {
|
||||
self.dbpr.reg().modify(|_, w| w.dbp().clear_bit());
|
||||
}
|
||||
}
|
||||
|
||||
/// Enter Standby low-power mode.
|
||||
///
|
||||
/// Minimal flow:
|
||||
/// - Program WUCR1 enables from `wkup` mask
|
||||
/// - Clear WUF flags
|
||||
/// - Set LPMS to Standby, set SLEEPDEEP, WFI
|
||||
pub fn standby(&mut self, wkup: WakeUpSource, scb: &mut SCB) -> ! {
|
||||
// Enable selected wakeup lines (WUPEN1..8)
|
||||
unsafe {
|
||||
// WUCR1 contains WUPENx bits in the low 8 bits; write a mask.
|
||||
self.wucr1.reg().modify(|r, w| {
|
||||
let cur = r.bits();
|
||||
let new_en = (cur & !0xFF) | wkup.mask8();
|
||||
w.bits(new_en)
|
||||
});
|
||||
}
|
||||
|
||||
// Clear all wakeup flags (WUSCR CWUFx)
|
||||
// Writing 1s clears the corresponding WUF flag.
|
||||
unsafe {
|
||||
self.wuscr.reg().write(|w| w.bits(0xFF));
|
||||
}
|
||||
|
||||
// Configure Standby in CR1.LPMS
|
||||
// HAL C uses: MODIFY_REG(PWR->CR1, PWR_CR1_LPMS, PWR_CR1_LPMS_2);
|
||||
// We map to LPMS = 0b100 (Standby). Exact encoding may differ by die rev,
|
||||
// but LPMS field exists in U5.
|
||||
unsafe {
|
||||
self.cr1.reg().modify(|_, w| {
|
||||
// LPMS is a field; set to Standby (0b100)
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
w.lpms().bits(0b100)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
scb.set_sleepdeep();
|
||||
cortex_m::asm::dsb();
|
||||
cortex_m::asm::wfi();
|
||||
loop {}
|
||||
}
|
||||
|
||||
/// Enter Shutdown low-power mode.
|
||||
///
|
||||
/// Flow mirrors `standby()` but programs LPMS to Shutdown.
|
||||
/// This is provided for parity with your L4 API. While HAL PWR C sample
|
||||
/// shows Standby, U5 also supports Shutdown via CR1.LPMS.
|
||||
pub fn shutdown(&mut self, wkup: WakeUpSource, scb: &mut SCB) -> ! {
|
||||
// Enable selected wakeup lines (WUPEN1..8)
|
||||
unsafe {
|
||||
self.wucr1.reg().modify(|r, w| {
|
||||
let cur = r.bits();
|
||||
let new_en = (cur & !0xFF) | wkup.mask8();
|
||||
w.bits(new_en)
|
||||
});
|
||||
}
|
||||
|
||||
// Clear all wakeup flags
|
||||
unsafe {
|
||||
self.wuscr.reg().write(|w| w.bits(0xFF));
|
||||
}
|
||||
|
||||
// Configure Shutdown in CR1.LPMS
|
||||
// Typical encoding: 0b110 (consult RM for your exact part).
|
||||
unsafe {
|
||||
self.cr1.reg().modify(|_, w| {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
w.lpms().bits(0b110)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
scb.set_sleepdeep();
|
||||
cortex_m::asm::dsb();
|
||||
cortex_m::asm::wfi();
|
||||
loop {}
|
||||
}
|
||||
|
||||
/// Read current wakeup flags (WUSR WUF1..8) into a mask wrapper.
|
||||
pub fn read_wakeup_reason(&mut self) -> WakeUpSource {
|
||||
let raw = self.wusr.reg().read().bits();
|
||||
WakeUpSource::from_wusr_raw(raw)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait to constrain `PWR` and enable its bus clock.
|
||||
pub trait PwrExt {
|
||||
/// Constrain the `PWR` peripheral and enable the peripheral clock.
|
||||
fn constrain(self) -> Pwr;
|
||||
}
|
||||
|
||||
impl PwrExt for PWR {
|
||||
fn constrain(self) -> Pwr {
|
||||
// Enable PWR clock on AHB3 (per U5 HAL: __HAL_RCC_PWR_CLK_ENABLE())
|
||||
let rcc = unsafe { &*RCC::ptr() };
|
||||
rcc.ahb3enr.modify(|_, w| w.pwren().set_bit());
|
||||
// Small barrier after clock enabling (mirroring typical patterns)
|
||||
cortex_m::asm::dsb();
|
||||
|
||||
Pwr {
|
||||
cr1: CR1 { _0: () },
|
||||
wucr1: WUCR1 { _0: () },
|
||||
wuscr: WUSCR { _0: () },
|
||||
wusr: WUSR { _0: () },
|
||||
dbpr: DBPR { _0: () },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// CR1 proxy
|
||||
pub struct CR1 {
|
||||
_0: (),
|
||||
}
|
||||
impl CR1 {
|
||||
#[inline(always)]
|
||||
pub(crate) fn reg(&mut self) -> &pac::pwr::CR1 {
|
||||
unsafe { &(*PWR::ptr()).cr1 }
|
||||
}
|
||||
}
|
||||
|
||||
/// WUCR1 proxy
|
||||
pub struct WUCR1 {
|
||||
_0: (),
|
||||
}
|
||||
impl WUCR1 {
|
||||
#[inline(always)]
|
||||
pub(crate) fn reg(&mut self) -> &pac::pwr::WUCR1 {
|
||||
unsafe { &(*PWR::ptr()).wucr1 }
|
||||
}
|
||||
}
|
||||
|
||||
/// WUSCR proxy (wake-up flags clear)
|
||||
pub struct WUSCR {
|
||||
_0: (),
|
||||
}
|
||||
impl WUSCR {
|
||||
#[inline(always)]
|
||||
pub(crate) fn reg(&mut self) -> &pac::pwr::WUSCR {
|
||||
unsafe { &(*PWR::ptr()).wuscr }
|
||||
}
|
||||
}
|
||||
|
||||
/// WUSR proxy (wake-up flags status)
|
||||
pub struct WUSR {
|
||||
_0: (),
|
||||
}
|
||||
impl WUSR {
|
||||
#[inline(always)]
|
||||
pub(crate) fn reg(&mut self) -> &pac::pwr::WUSR {
|
||||
unsafe { &(*PWR::ptr()).wusr }
|
||||
}
|
||||
}
|
||||
|
||||
/// DBPR proxy (backup domain access)
|
||||
pub struct DBPR {
|
||||
_0: (),
|
||||
}
|
||||
impl DBPR {
|
||||
#[inline(always)]
|
||||
pub(crate) fn reg(&mut self) -> &pac::pwr::DBPR {
|
||||
unsafe { &(*PWR::ptr()).dbpr }
|
||||
}
|
||||
}
|
||||
280
src/rcc/enable.rs
Normal file
280
src/rcc/enable.rs
Normal file
@@ -0,0 +1,280 @@
|
||||
//! Enable, Sleep Mode Enable, and Reset traits for peripherals
|
||||
|
||||
/// Enable/disable peripheral
|
||||
pub trait Enable: crate::Sealed {
|
||||
/// Enables peripheral
|
||||
fn enable(bus: &mut Self::Bus);
|
||||
|
||||
/// Disables peripheral
|
||||
fn disable(bus: &mut Self::Bus);
|
||||
|
||||
/// Check if peripheral is enabled
|
||||
fn is_enabled() -> bool;
|
||||
|
||||
/// Check if peripheral is disabled
|
||||
fn is_disabled() -> bool;
|
||||
|
||||
/// Enables peripheral without taking bus ownership
|
||||
///
|
||||
/// # Safety
|
||||
/// This can break the bus abstraction if not used carefully
|
||||
unsafe fn enable_unchecked();
|
||||
|
||||
/// Disables peripheral without taking bus ownership
|
||||
///
|
||||
/// # Safety
|
||||
/// This can break the bus abstraction if not used carefully
|
||||
unsafe fn disable_unchecked();
|
||||
}
|
||||
|
||||
/// Enable/disable peripheral in sleep mode
|
||||
pub trait SMEnable: crate::Sealed {
|
||||
/// Enables peripheral clock in sleep mode
|
||||
fn enable_in_sleep_mode(bus: &mut Self::Bus);
|
||||
|
||||
/// Disables peripheral clock in sleep mode
|
||||
fn disable_in_sleep_mode(bus: &mut Self::Bus);
|
||||
|
||||
/// Check if peripheral is enabled in sleep mode
|
||||
fn is_enabled_in_sleep_mode() -> bool;
|
||||
|
||||
/// Check if peripheral is disabled in sleep mode
|
||||
fn is_disabled_in_sleep_mode() -> bool;
|
||||
|
||||
/// Enables peripheral in sleep mode without taking bus ownership
|
||||
///
|
||||
/// # Safety
|
||||
/// This can break the bus abstraction if not used carefully
|
||||
unsafe fn enable_in_sleep_mode_unchecked();
|
||||
|
||||
/// Disables peripheral in sleep mode without taking bus ownership
|
||||
///
|
||||
/// # Safety
|
||||
/// This can break the bus abstraction if not used carefully
|
||||
unsafe fn disable_in_sleep_mode_unchecked();
|
||||
}
|
||||
|
||||
/// Reset peripheral
|
||||
pub trait Reset: crate::Sealed {
|
||||
/// Resets peripheral
|
||||
fn reset(bus: &mut Self::Bus);
|
||||
|
||||
/// Resets peripheral without taking bus ownership
|
||||
///
|
||||
/// # Safety
|
||||
/// This can break the bus abstraction if not used carefully
|
||||
unsafe fn reset_unchecked();
|
||||
}
|
||||
|
||||
/// RCC bus trait
|
||||
pub trait RccBus: crate::Sealed {
|
||||
/// Bus type
|
||||
type Bus;
|
||||
}
|
||||
|
||||
macro_rules! bus_enable {
|
||||
($PER:ident => $en:ident) => {
|
||||
impl Enable for crate::pac::$PER {
|
||||
#[inline(always)]
|
||||
fn enable(bus: &mut Self::Bus) {
|
||||
bus.enr().modify(|_, w| w.$en().set_bit());
|
||||
// Stall pipeline to work around erratum
|
||||
cortex_m::asm::dsb();
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn disable(bus: &mut Self::Bus) {
|
||||
bus.enr().modify(|_, w| w.$en().clear_bit());
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn is_enabled() -> bool {
|
||||
Self::Bus::new().enr().read().$en().bit_is_set()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn is_disabled() -> bool {
|
||||
Self::Bus::new().enr().read().$en().bit_is_clear()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn enable_unchecked() {
|
||||
Self::enable(&mut Self::Bus::new());
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn disable_unchecked() {
|
||||
Self::disable(&mut Self::Bus::new());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! bus_smenable {
|
||||
($PER:ident => $smen:ident) => {
|
||||
impl SMEnable for crate::pac::$PER {
|
||||
#[inline(always)]
|
||||
fn enable_in_sleep_mode(bus: &mut Self::Bus) {
|
||||
bus.smenr().modify(|_, w| w.$smen().set_bit());
|
||||
cortex_m::asm::dsb();
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn disable_in_sleep_mode(bus: &mut Self::Bus) {
|
||||
bus.smenr().modify(|_, w| w.$smen().clear_bit());
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn is_enabled_in_sleep_mode() -> bool {
|
||||
Self::Bus::new().smenr().read().$smen().bit_is_set()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn is_disabled_in_sleep_mode() -> bool {
|
||||
Self::Bus::new().smenr().read().$smen().bit_is_clear()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn enable_in_sleep_mode_unchecked() {
|
||||
Self::enable_in_sleep_mode(&mut Self::Bus::new());
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn disable_in_sleep_mode_unchecked() {
|
||||
Self::disable_in_sleep_mode(&mut Self::Bus::new());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! bus_reset {
|
||||
($PER:ident => $rst:ident) => {
|
||||
impl Reset for crate::pac::$PER {
|
||||
#[inline(always)]
|
||||
fn reset(bus: &mut Self::Bus) {
|
||||
bus.rstr().modify(|_, w| w.$rst().set_bit());
|
||||
bus.rstr().modify(|_, w| w.$rst().clear_bit());
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn reset_unchecked() {
|
||||
Self::reset(&mut Self::Bus::new());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! bus {
|
||||
($($PER:ident => ($busX:ty, $($en:ident)?, $($smen:ident)?, $($rst:ident)?),)+) => {
|
||||
$(
|
||||
impl crate::Sealed for crate::pac::$PER {}
|
||||
|
||||
impl RccBus for crate::pac::$PER {
|
||||
type Bus = $busX;
|
||||
}
|
||||
|
||||
$(bus_enable!($PER => $en);)?
|
||||
$(bus_smenable!($PER => $smen);)?
|
||||
$(bus_reset!($PER => $rst);)?
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
// STM32U575 peripheral mappings (based on C HAL register definitions)
|
||||
bus! {
|
||||
// AHB1 peripherals
|
||||
GPDMA1 => (super::AHB1, gpdma1en, gpdma1smen, gpdma1rst),
|
||||
CORDIC => (super::AHB1, cordicen, cordicsmen, cordicrst),
|
||||
FMAC => (super::AHB1, fmacen, fmacsmen, fmacrst),
|
||||
MDF1 => (super::AHB1, mdf1en, mdf1smen, mdf1rst),
|
||||
FLASH => (super::AHB1, flashen, flashsmen,),
|
||||
CRC => (super::AHB1, crcen, crcsmen, crcrst),
|
||||
TSC => (super::AHB1, tscen, tscsmen, tscrst),
|
||||
RAMCFG => (super::AHB1, ramcfgen, ramcfgsmen, ramcfgrst),
|
||||
GTZC1 => (super::AHB1, gtzc1en, gtzc1smen,),
|
||||
ICACHE => (super::AHB1,, icachesmen,),
|
||||
DCACHE1 => (super::AHB1, dcache1en, dcache1smen,),
|
||||
SRAM1 => (super::AHB1, sram1en, sram1smen,),
|
||||
|
||||
// AHB2_1 peripherals (AHB2ENR1)
|
||||
GPIOA => (super::AHB2_1, gpioaen, gpioasmen, gpioarst),
|
||||
GPIOB => (super::AHB2_1, gpioben, gpiobsmen, gpiobrst),
|
||||
GPIOC => (super::AHB2_1, gpiocen, gpiocsmen, gpiocrst),
|
||||
GPIOD => (super::AHB2_1, gpioden, gpiodsmen, gpiodrst),
|
||||
GPIOE => (super::AHB2_1, gpioeen, gpioesmen, gpioerst),
|
||||
GPIOG => (super::AHB2_1, gpiogen, gpiogsmen, gpiogrst),
|
||||
GPIOH => (super::AHB2_1, gpiohen, gpiohsmen, gpiohrst),
|
||||
ADC12 => (super::AHB2_1, adc12en, adc12smen, adc12rst),
|
||||
DCMI => (super::AHB2_1, dcmi_pssien, dcmi_pssismen, dcmi_pssirst),
|
||||
// TODO: USB_OTG_FS => (super::AHB2_1, otgen, otgsmen, otgrst), // Check exact name
|
||||
AES => (super::AHB2_1, aesen, aessmen, aesrst),
|
||||
HASH => (super::AHB2_1, hashen, hashsmen, hashrst),
|
||||
RNG => (super::AHB2_1, rngen, rngsmen, rngrst),
|
||||
// TODO: PKA, SAES, OCTOSPIM, OTFDEC1, OTFDEC2 if present
|
||||
SDMMC1 => (super::AHB2_1, sdmmc1en, sdmmc1smen, sdmmc1rst),
|
||||
SRAM2 => (super::AHB2_1, sram2en, sram2smen,),
|
||||
|
||||
// AHB2_2 peripherals (AHB2ENR2)
|
||||
// TODO: FMC, OCTOSPI1, OCTOSPI2 based on actual device
|
||||
|
||||
// AHB3 peripherals
|
||||
LPGPIO1 => (super::AHB3, lpgpio1en, lpgpio1smen, lpgpio1rst),
|
||||
PWR => (super::AHB3, pwren, pwrsmen,),
|
||||
ADC4 => (super::AHB3, adc4en, adc4smen, adc4rst),
|
||||
DAC1 => (super::AHB3, dac1en, dac1smen, dac1rst),
|
||||
LPDMA1 => (super::AHB3, lpdma1en, lpdma1smen, lpdma1rst),
|
||||
ADF1 => (super::AHB3, adf1en, adf1smen, adf1rst),
|
||||
GTZC2 => (super::AHB3, gtzc2en, gtzc2smen,),
|
||||
SRAM4 => (super::AHB3, sram4en, sram4smen,),
|
||||
|
||||
// APB1R1 peripherals
|
||||
TIM2 => (super::APB1R1, tim2en, tim2smen, tim2rst),
|
||||
TIM3 => (super::APB1R1, tim3en, tim3smen, tim3rst),
|
||||
TIM4 => (super::APB1R1, tim4en, tim4smen, tim4rst),
|
||||
TIM5 => (super::APB1R1, tim5en, tim5smen, tim5rst),
|
||||
TIM6 => (super::APB1R1, tim6en, tim6smen, tim6rst),
|
||||
TIM7 => (super::APB1R1, tim7en, tim7smen, tim7rst),
|
||||
WWDG => (super::APB1R1, wwdgen, wwdgsmen,),
|
||||
SPI2 => (super::APB1R1, spi2en, spi2smen, spi2rst),
|
||||
USART3 => (super::APB1R1, usart3en, usart3smen, usart3rst),
|
||||
UART4 => (super::APB1R1, uart4en, uart4smen, uart4rst),
|
||||
UART5 => (super::APB1R1, uart5en, uart5smen, uart5rst),
|
||||
I2C1 => (super::APB1R1, i2c1en, i2c1smen, i2c1rst),
|
||||
I2C2 => (super::APB1R1, i2c2en, i2c2smen, i2c2rst),
|
||||
CRS => (super::APB1R1, crsen, crssmen, crsrst),
|
||||
|
||||
// APB1R2 peripherals
|
||||
I2C4 => (super::APB1R2, i2c4en, i2c4smen, i2c4rst),
|
||||
LPTIM2 => (super::APB1R2, lptim2en, lptim2smen, lptim2rst),
|
||||
FDCAN1 => (super::APB1R2, fdcan1en, fdcan1smen, fdcan1rst),
|
||||
|
||||
// APB2 peripherals
|
||||
TIM1 => (super::APB2, tim1en, tim1smen, tim1rst),
|
||||
SPI1 => (super::APB2, spi1en, spi1smen, spi1rst),
|
||||
TIM8 => (super::APB2, tim8en, tim8smen, tim8rst),
|
||||
USART1 => (super::APB2, usart1en, usart1smen, usart1rst),
|
||||
TIM15 => (super::APB2, tim15en, tim15smen, tim15rst),
|
||||
TIM16 => (super::APB2, tim16en, tim16smen, tim16rst),
|
||||
TIM17 => (super::APB2, tim17en, tim17smen, tim17rst),
|
||||
SAI1 => (super::APB2, sai1en, sai1smen, sai1rst),
|
||||
|
||||
// APB3 peripherals
|
||||
SYSCFG => (super::APB3, syscfgen, syscfgsmen, syscfgrst),
|
||||
SPI3 => (super::APB3, spi3en, spi3smen, spi3rst),
|
||||
LPUART1 => (super::APB3, lpuart1en, lpuart1smen, lpuart1rst),
|
||||
I2C3 => (super::APB3, i2c3en, i2c3smen, i2c3rst),
|
||||
LPTIM1 => (super::APB3, lptim1en, lptim1smen, lptim1rst),
|
||||
LPTIM3 => (super::APB3, lptim3en, lptim3smen, lptim3rst),
|
||||
LPTIM4 => (super::APB3, lptim4en, lptim4smen, lptim4rst),
|
||||
OPAMP => (super::APB3, opampen, opampsmen, opamprst),
|
||||
COMP => (super::APB3, compen, compsmen, comprst),
|
||||
VREFBUF => (super::APB3, vrefen, vrefsmen, vrefrst),
|
||||
}
|
||||
|
||||
// TODO: Add conditional compilation for optional peripherals:
|
||||
// - GPIOF, GPIOI, GPIOJ (not on all packages)
|
||||
// - SAI2, USART2, USART6, I2C5, I2C6
|
||||
// - USB_FS (USB_DRD_FS)
|
||||
// - FMC, OCTOSPI2, HSPI1
|
||||
// - DMA2D, JPEG, GPU2D, GFXMMU, LTDC, DSI
|
||||
// - UCPD1, SDMMC2
|
||||
6
src/rcc/mod.rs
Normal file
6
src/rcc/mod.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
// src/rcc/mod.rs
|
||||
|
||||
// pub mod enable;
|
||||
pub mod rcc;
|
||||
// pub use enable::*;
|
||||
pub use rcc::*;
|
||||
756
src/rcc/rcc.rs
Normal file
756
src/rcc/rcc.rs
Normal file
@@ -0,0 +1,756 @@
|
||||
// stm32u5xx-hal/src/rcc.rs
|
||||
//! Reset and Clock Control for STM32U575 (initial/minimal port)
|
||||
//!
|
||||
//! Scope: only features that clearly overlap between stm32l4xx-hal rcc.rs
|
||||
//! and the STM32U5 HAL C (stm32u5xx_hal_rcc.c/.h).
|
||||
//!
|
||||
//! Skipped (present in U5 HAL C, not here yet):
|
||||
//! - MSIK (kernel MSI), SHSI (secure HSI)
|
||||
//! - PLL2 / PLL3
|
||||
//! - PLL FRACN and MBOOST/EPOD handling
|
||||
//! - LSE glitch filter, LSE CSS, LSESYS
|
||||
//! - LSI divider (LSIPREDIV)
|
||||
//! - STOP wake-up clock selection (STOPWUCK / STOPKERWUCK)
|
||||
//! - Security/privileged attributes
|
||||
//! - APB3 (PCLK3) prescaler & clocks
|
||||
//! - MCO configuration
|
||||
//! - Per-peripheral Enable/Reset tables (normally in rcc/enable.rs)
|
||||
|
||||
use crate::pac::{rcc, RCC};
|
||||
use crate::flash::ACR;
|
||||
use crate::pwr::Pwr;
|
||||
use crate::time::Hertz;
|
||||
use fugit::RateExtU32;
|
||||
|
||||
// Uncomment when you add bus-peripheral enable/reset mapping like in L4
|
||||
// mod enable;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum MsiFreq {
|
||||
// Order and names reflect STM32U5 MSIRANGE mapping (RCC_MSIRANGE_x):
|
||||
// 0..15 encode the specific frequencies per stm32u5xx_hal_rcc.h
|
||||
// We provide exact frequencies in Hz.
|
||||
MSI48, // 48_000_000
|
||||
MSI24, // 24_000_000
|
||||
MSI16, // 16_000_000
|
||||
MSI12, // 12_000_000
|
||||
MSI4, // 4_000_000
|
||||
MSI2, // 2_000_000
|
||||
MSI1_33, // 1_330_000 (approx 1.33 MHz)
|
||||
MSI1, // 1_000_000
|
||||
MSI3_072, // 3_072_000
|
||||
MSI1_536, // 1_536_000
|
||||
MSI1_024, // 1_024_000
|
||||
MSI_768K, // 768_000
|
||||
MSI400K, // 400_000
|
||||
MSI200K, // 200_000
|
||||
MSI133K, // 133_000
|
||||
MSI100K, // 100_000
|
||||
}
|
||||
|
||||
impl MsiFreq {
|
||||
fn to_hertz(self) -> Hertz {
|
||||
let hz = match self {
|
||||
Self::MSI48 => 48_000_000,
|
||||
Self::MSI24 => 24_000_000,
|
||||
Self::MSI16 => 16_000_000,
|
||||
Self::MSI12 => 12_000_000,
|
||||
Self::MSI4 => 4_000_000,
|
||||
Self::MSI2 => 2_000_000,
|
||||
Self::MSI1_33 => 1_330_000,
|
||||
Self::MSI1 => 1_000_000,
|
||||
Self::MSI3_072 => 3_072_000,
|
||||
Self::MSI1_536 => 1_536_000,
|
||||
Self::MSI1_024 => 1_024_000,
|
||||
Self::MSI_768K => 768_000,
|
||||
Self::MSI400K => 400_000,
|
||||
Self::MSI200K => 200_000,
|
||||
Self::MSI133K => 133_000,
|
||||
Self::MSI100K => 100_000,
|
||||
};
|
||||
hz.Hz()
|
||||
}
|
||||
|
||||
// Map to ICSCR1.MSISRANGE field encoding (0..15)
|
||||
fn to_range_bits(self) -> u8 {
|
||||
match self {
|
||||
Self::MSI48 => 0,
|
||||
Self::MSI24 => 1,
|
||||
Self::MSI16 => 2,
|
||||
Self::MSI12 => 3,
|
||||
Self::MSI4 => 4,
|
||||
Self::MSI2 => 5,
|
||||
Self::MSI1_33 => 6,
|
||||
Self::MSI1 => 7,
|
||||
Self::MSI3_072 => 8,
|
||||
Self::MSI1_536 => 9,
|
||||
Self::MSI1_024 => 10,
|
||||
Self::MSI_768K => 11,
|
||||
Self::MSI400K => 12,
|
||||
Self::MSI200K => 13,
|
||||
Self::MSI133K => 14,
|
||||
Self::MSI100K => 15,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait that constrains the `RCC` peripheral
|
||||
pub trait RccExt {
|
||||
/// Constrains the `RCC` peripheral so it plays nicely with the other abstractions
|
||||
fn constrain(self) -> Rcc;
|
||||
}
|
||||
|
||||
impl RccExt for RCC {
|
||||
fn constrain(self) -> Rcc {
|
||||
Rcc {
|
||||
ahb1: AHB1::new(),
|
||||
ahb2_1: AHB2_1::new(),
|
||||
ahb2_2: AHB2_2::new(),
|
||||
ahb3: AHB3::new(),
|
||||
apb1r1: APB1R1::new(),
|
||||
apb1r2: APB1R2::new(),
|
||||
apb2: APB2::new(),
|
||||
apb3: APB3::new(),
|
||||
cfgr: CFGR {
|
||||
hse: None,
|
||||
lse: None,
|
||||
msi: None,
|
||||
hsi48: false,
|
||||
lsi: false,
|
||||
hclk: None,
|
||||
pclk1: None,
|
||||
pclk2: None,
|
||||
sysclk: None,
|
||||
pll_source: None,
|
||||
pll_config: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Constrained RCC peripheral
|
||||
pub struct Rcc {
|
||||
pub ahb1: AHB1,
|
||||
pub ahb2_1: AHB2_1,
|
||||
pub ahb2_2: AHB2_2,
|
||||
pub ahb3: AHB3,
|
||||
pub apb1r1: APB1R1,
|
||||
pub apb1r2: APB1R2,
|
||||
pub apb2: APB2,
|
||||
pub apb3: APB3,
|
||||
pub cfgr: CFGR,
|
||||
}
|
||||
|
||||
/// Backup domain control register proxy
|
||||
pub struct BDCR {
|
||||
_0: (),
|
||||
}
|
||||
|
||||
impl BDCR {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn bdcr(&mut self) -> &rcc::BDCR {
|
||||
unsafe { &(*RCC::ptr()).bdcr }
|
||||
}
|
||||
}
|
||||
|
||||
/// Control/Status Register proxy
|
||||
pub struct CSR {
|
||||
_0: (),
|
||||
}
|
||||
|
||||
impl CSR {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn csr(&mut self) -> &rcc::CSR {
|
||||
unsafe { &(*RCC::ptr()).csr }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! bus_struct_1 {
|
||||
($busX:ident => ($EN:ident, $en:ident, $SMEN:ident, $smen:ident, $RST:ident, $rst:ident, $doc:literal),) => {
|
||||
#[doc = $doc]
|
||||
pub struct $busX {
|
||||
_0: (),
|
||||
}
|
||||
impl $busX {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { _0: () }
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub(crate) fn enr(&self) -> &rcc::$EN {
|
||||
unsafe { &(*RCC::ptr()).$en }
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub(crate) fn smenr(&self) -> &rcc::$SMEN {
|
||||
unsafe { &(*RCC::ptr()).$smen }
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub(crate) fn rstr(&self) -> &rcc::$RST {
|
||||
unsafe { &(*RCC::ptr()).$rst }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// U5 splits AHB2/APB1 into *1/*2 register banks; we model both.
|
||||
bus_struct_1! {
|
||||
AHB1 => (AHB1ENR, ahb1enr, AHB1SMENR, ahb1smenr, AHB1RSTR, ahb1rstr, "AHB1 registers"),
|
||||
}
|
||||
bus_struct_1! {
|
||||
AHB2_1 => (AHB2ENR1, ahb2enr1, AHB2SMENR1, ahb2smenr1, AHB2RSTR1, ahb2rstr1, "AHB2 part 1 registers"),
|
||||
}
|
||||
bus_struct_1! {
|
||||
AHB2_2 => (AHB2ENR2, ahb2enr2, AHB2SMENR2, ahb2smenr2, AHB2RSTR2, ahb2rstr2, "AHB2 part 2 registers"),
|
||||
}
|
||||
bus_struct_1! {
|
||||
AHB3 => (AHB3ENR, ahb3enr, AHB3SMENR, ahb3smenr, AHB3RSTR, ahb3rstr, "AHB3 registers"),
|
||||
}
|
||||
bus_struct_1! {
|
||||
APB1R1 => (APB1ENR1, apb1enr1, APB1SMENR1, apb1smenr1, APB1RSTR1, apb1rstr1, "APB1 register set 1"),
|
||||
}
|
||||
bus_struct_1! {
|
||||
APB1R2 => (APB1ENR2, apb1enr2, APB1SMENR2, apb1smenr2, APB1RSTR2, apb1rstr2, "APB1 register set 2"),
|
||||
}
|
||||
bus_struct_1! {
|
||||
APB2 => (APB2ENR, apb2enr, APB2SMENR, apb2smenr, APB2RSTR, apb2rstr, "APB2 registers"),
|
||||
}
|
||||
bus_struct_1! {
|
||||
APB3 => (APB3ENR, apb3enr, APB3SMENR, apb3smenr, APB3RSTR, apb3rstr, "APB3 registers"),
|
||||
}
|
||||
|
||||
/// Bus associated to peripheral
|
||||
pub trait RccBus: crate::Sealed {
|
||||
type Bus;
|
||||
}
|
||||
|
||||
/// Enable/disable peripheral
|
||||
pub trait Enable: RccBus {
|
||||
fn enable(bus: &mut Self::Bus);
|
||||
fn disable(bus: &mut Self::Bus);
|
||||
fn is_enabled() -> bool;
|
||||
fn is_disabled() -> bool;
|
||||
unsafe fn enable_unchecked();
|
||||
unsafe fn disable_unchecked();
|
||||
}
|
||||
|
||||
/// Enable/disable peripheral in sleep mode
|
||||
pub trait SMEnable: RccBus {
|
||||
fn enable_in_sleep_mode(bus: &mut Self::Bus);
|
||||
fn disable_in_sleep_mode(bus: &mut Self::Bus);
|
||||
fn is_enabled_in_sleep_mode() -> bool;
|
||||
fn is_disabled_in_sleep_mode() -> bool;
|
||||
unsafe fn enable_in_sleep_mode_unchecked();
|
||||
unsafe fn disable_in_sleep_mode_unchecked();
|
||||
}
|
||||
|
||||
/// Reset peripheral
|
||||
pub trait Reset: RccBus {
|
||||
fn reset(bus: &mut Self::Bus);
|
||||
unsafe fn reset_unchecked();
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct HseConfig {
|
||||
speed: u32,
|
||||
bypass: CrystalBypass,
|
||||
css: ClockSecuritySystem,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct LseConfig {
|
||||
bypass: CrystalBypass,
|
||||
// Skipped: LSE CSS / glitch filter / LSESYS
|
||||
css: ClockSecuritySystem, // kept in struct for future, not applied
|
||||
}
|
||||
|
||||
/// Crystal bypass selector
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum CrystalBypass {
|
||||
Enable,
|
||||
Disable,
|
||||
}
|
||||
|
||||
/// Clock Security System
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum ClockSecuritySystem {
|
||||
Enable,
|
||||
Disable,
|
||||
}
|
||||
|
||||
const HSI: u32 = 16_000_000; // Hz (HSI16)
|
||||
|
||||
/// PLL Source (PLL1)
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum PllSource {
|
||||
MSI,
|
||||
HSI16,
|
||||
HSE,
|
||||
}
|
||||
|
||||
impl PllSource {
|
||||
// Map to RCC_PLL1CFGR PLL1SRC field bits (00: none, 01: MSI, 10: HSI, 11: HSE)
|
||||
fn to_pllsrc_bits(self) -> u8 {
|
||||
match self {
|
||||
Self::MSI => 0b01,
|
||||
Self::HSI16 => 0b10,
|
||||
Self::HSE => 0b11,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct PllConfig {
|
||||
// Main PLL division factor M (1..16) -> register stores (M-1)
|
||||
m: u8,
|
||||
// Main PLL multiplication factor N (4..512) -> register stores (N-1)
|
||||
n: u16,
|
||||
// Main PLL division factor for PLLR (system clock) (1..128) -> reg stores (R-1)
|
||||
r: u8,
|
||||
// Skipped: P, Q outputs, MBOOST, FRACN, RGE beyond minimal
|
||||
}
|
||||
|
||||
impl PllConfig {
|
||||
pub fn new(m: u8, n: u16, r: u8) -> Self {
|
||||
assert!(m >= 1 && m <= 16);
|
||||
assert!(n >= 4 && n <= 512);
|
||||
assert!(r >= 1 && r <= 128);
|
||||
Self { m, n, r }
|
||||
}
|
||||
}
|
||||
|
||||
/// Clock configuration builder
|
||||
pub struct CFGR {
|
||||
hse: Option<HseConfig>,
|
||||
lse: Option<LseConfig>,
|
||||
msi: Option<MsiFreq>,
|
||||
hsi48: bool,
|
||||
lsi: bool,
|
||||
hclk: Option<u32>,
|
||||
pclk1: Option<u32>,
|
||||
pclk2: Option<u32>,
|
||||
sysclk: Option<u32>,
|
||||
pll_source: Option<PllSource>,
|
||||
pll_config: Option<PllConfig>,
|
||||
}
|
||||
|
||||
impl CFGR {
|
||||
pub fn hse(mut self, freq: Hertz, bypass: CrystalBypass, css: ClockSecuritySystem) -> Self {
|
||||
self.hse = Some(HseConfig {
|
||||
speed: freq.raw(),
|
||||
bypass,
|
||||
css,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn lse(mut self, bypass: CrystalBypass, css: ClockSecuritySystem) -> Self {
|
||||
self.lse = Some(LseConfig { bypass, css });
|
||||
self
|
||||
}
|
||||
|
||||
pub fn msi(mut self, range: MsiFreq) -> Self {
|
||||
self.msi = Some(range);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn lsi(mut self, on: bool) -> Self {
|
||||
self.lsi = on;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn hsi48(mut self, on: bool) -> Self {
|
||||
self.hsi48 = on;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn hclk(mut self, freq: Hertz) -> Self {
|
||||
self.hclk = Some(freq.raw());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn pclk1(mut self, freq: Hertz) -> Self {
|
||||
self.pclk1 = Some(freq.raw());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn pclk2(mut self, freq: Hertz) -> Self {
|
||||
self.pclk2 = Some(freq.raw());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn sysclk(mut self, freq: Hertz) -> Self {
|
||||
self.sysclk = Some(freq.raw());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn sysclk_with_pll(mut self, freq: Hertz, cfg: PllConfig) -> Self {
|
||||
self.sysclk = Some(freq.raw());
|
||||
self.pll_config = Some(cfg);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn pll_source(mut self, source: PllSource) -> Self {
|
||||
self.pll_source = Some(source);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn freeze(&self, acr: &mut ACR, pwr: &mut Pwr) -> Clocks {
|
||||
let rcc = unsafe { &*RCC::ptr() };
|
||||
|
||||
// Ensure MSI is ON and selected first (safe base)
|
||||
if rcc.cr.read().msison().bit_is_clear() {
|
||||
rcc.cr.modify(|_, w| w.msison().set_bit());
|
||||
while rcc.cr.read().msisrdy().bit_is_clear() {}
|
||||
}
|
||||
|
||||
// Program MSI to 4 MHz as default base (MSIRGSEL=1, MSISRANGE=4MHz)
|
||||
unsafe {
|
||||
rcc.icscr1.modify(|_, w| {
|
||||
w.msirgsel().set_bit();
|
||||
w.msisrange().bits(MsiFreq::MSI4.to_range_bits());
|
||||
w
|
||||
});
|
||||
}
|
||||
|
||||
// If SYSCLK not MSI, force switch to MSI (SW=00) and wait
|
||||
if rcc.cfgr1.read().sws().bits() != 0 {
|
||||
// Reset CFGR1.SW to MSI
|
||||
rcc.cfgr1.modify(|_, w| unsafe { w.sw().bits(0b00) });
|
||||
while rcc.cfgr1.read().sws().bits() != 0b00 {}
|
||||
}
|
||||
|
||||
//
|
||||
// 1) Oscillators
|
||||
//
|
||||
|
||||
// LSI if requested (or if we needed by LSE CSS in future)
|
||||
let lsi_used = if self.lsi {
|
||||
rcc.bdcr.modify(|_, w| w.lsion().set_bit());
|
||||
while rcc.bdcr.read().lsirdy().bit_is_clear() {}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
// LSE if requested
|
||||
if let Some(lse_cfg) = &self.lse {
|
||||
// Enable backup domain write access
|
||||
// NOTE: depends on your Pwr wrapper; mirrors L4 HAL style
|
||||
pwr.cr1.reg().modify(|_, w| w.dbp().set_bit());
|
||||
// Enable LSE (bypass if set)
|
||||
if lse_cfg.bypass == CrystalBypass::Enable {
|
||||
rcc.bdcr.modify(|_, w| w.lsebyp().set_bit());
|
||||
}
|
||||
rcc.bdcr.modify(|_, w| w.lseon().set_bit());
|
||||
while rcc.bdcr.read().lserdy().bit_is_clear() {}
|
||||
// Skipped: LSESYS and LSE CSS/glitch filter
|
||||
}
|
||||
|
||||
// HSE if requested
|
||||
if let Some(hse_cfg) = &self.hse {
|
||||
rcc.cr.modify(|_, w| {
|
||||
if hse_cfg.bypass == CrystalBypass::Enable {
|
||||
w.hsebyp().set_bit();
|
||||
}
|
||||
w.hseon().set_bit()
|
||||
});
|
||||
while rcc.cr.read().hserdy().bit_is_clear() {}
|
||||
|
||||
if hse_cfg.css == ClockSecuritySystem::Enable {
|
||||
// Enable CSS on HSE (CR.CSSON)
|
||||
rcc.cr.modify(|_, w| w.csson().set_bit());
|
||||
}
|
||||
}
|
||||
|
||||
// MSI range if set explicitly
|
||||
if let Some(msi) = self.msi {
|
||||
unsafe {
|
||||
rcc.icscr1.modify(|_, w| {
|
||||
w.msirgsel().set_bit();
|
||||
w.msisrange().bits(msi.to_range_bits());
|
||||
w
|
||||
})
|
||||
};
|
||||
while rcc.cr.read().msisrdy().bit_is_clear() {}
|
||||
}
|
||||
|
||||
// HSI48
|
||||
if self.hsi48 {
|
||||
rcc.cr.modify(|_, w| w.hsi48on().set_bit());
|
||||
while rcc.cr.read().hsi48rdy().bit_is_clear() {}
|
||||
}
|
||||
|
||||
//
|
||||
// 2) PLL1 setup (optional)
|
||||
//
|
||||
// Decide PLL1 source
|
||||
let (src_freq, pll_src) = if let Some(src) = self.pll_source {
|
||||
match src {
|
||||
PllSource::HSE => {
|
||||
let hse = self
|
||||
.hse
|
||||
.as_ref()
|
||||
.expect("HSE selected as PLL source, but not enabled");
|
||||
(hse.speed, src)
|
||||
}
|
||||
PllSource::HSI16 => (HSI, src),
|
||||
PllSource::MSI => {
|
||||
let msi = self.msi.expect("MSI selected as PLL source, but not enabled");
|
||||
(msi.to_hertz().raw(), src)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Some(hse) = &self.hse {
|
||||
(hse.speed, PllSource::HSE)
|
||||
} else if let Some(msi) = self.msi {
|
||||
(msi.to_hertz().raw(), PllSource::MSI)
|
||||
} else {
|
||||
(HSI, PllSource::HSI16)
|
||||
}
|
||||
};
|
||||
|
||||
// If we need HSI for PLL1 and it's not on, turn it on
|
||||
if pll_src == PllSource::HSI16 {
|
||||
rcc.cr.modify(|_, w| w.hsion().set_bit());
|
||||
while rcc.cr.read().hsirdy().bit_is_clear() {}
|
||||
}
|
||||
|
||||
let want_sysclk = match (self.sysclk, self.msi) {
|
||||
(Some(sysclk), _) => sysclk,
|
||||
(None, Some(msi)) => msi.to_hertz().raw(),
|
||||
(None, None) => MsiFreq::MSI4.to_hertz().raw(),
|
||||
};
|
||||
|
||||
assert!(want_sysclk <= 80_000_000);
|
||||
|
||||
// Auto PLL config if not provided (simple best-effort: M=1, R=2)
|
||||
let pllconf = if self.pll_config.is_none() {
|
||||
if self.sysclk.is_some() {
|
||||
let m = 1u8;
|
||||
let r = 2u8;
|
||||
// want_sysclk = (src/m)*N / r => N = want_sysclk * r / (src/m)
|
||||
let n = ((want_sysclk as u64) * (r as u64) / (src_freq as u64)) as u16;
|
||||
Some(PllConfig::new(m, n, r))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
self.pll_config
|
||||
};
|
||||
|
||||
// Compute prescalers from requested hclk/pclk1/pclk2
|
||||
// HPRE (CFGR2.HPRE), PPRE1/PPRE2 (CFGR2.PPRE1/PPRE2)
|
||||
let (hpre_bits, hpre_div) = self
|
||||
.hclk
|
||||
.map(|hclk| match want_sysclk / hclk {
|
||||
0 => unreachable!(),
|
||||
1 => (0b0000, 1),
|
||||
2 => (0b1000, 2),
|
||||
3..=5 => (0b1001, 4),
|
||||
6..=11 => (0b1010, 8),
|
||||
12..=39 => (0b1011, 16),
|
||||
40..=95 => (0b1100, 64),
|
||||
96..=191 => (0b1101, 128),
|
||||
192..=383 => (0b1110, 256),
|
||||
_ => (0b1111, 512),
|
||||
})
|
||||
.unwrap_or((0b0000, 1));
|
||||
let hclk = want_sysclk / hpre_div;
|
||||
|
||||
let (ppre1_bits, ppre1_div) = self
|
||||
.pclk1
|
||||
.map(|pclk1| match hclk / pclk1 {
|
||||
0 => unreachable!(),
|
||||
1 => (0b000, 1),
|
||||
2 => (0b100, 2),
|
||||
3..=5 => (0b101, 4),
|
||||
6..=11 => (0b110, 8),
|
||||
_ => (0b111, 16),
|
||||
})
|
||||
.unwrap_or((0b000, 1));
|
||||
let pclk1 = hclk / (ppre1_div as u32);
|
||||
|
||||
let (ppre2_bits, ppre2_div) = self
|
||||
.pclk2
|
||||
.map(|pclk2| match hclk / pclk2 {
|
||||
0 => unreachable!(),
|
||||
1 => (0b000, 1),
|
||||
2 => (0b100, 2),
|
||||
3..=5 => (0b101, 4),
|
||||
6..=11 => (0b110, 8),
|
||||
_ => (0b111, 16),
|
||||
})
|
||||
.unwrap_or((0b000, 1));
|
||||
let pclk2 = hclk / (ppre2_div as u32);
|
||||
|
||||
// Timer clocks double if APB prescaler != 1
|
||||
let timclk1 = if ppre1_div == 1 { pclk1 } else { 2 * pclk1 };
|
||||
let timclk2 = if ppre2_div == 1 { pclk2 } else { 2 * pclk2 };
|
||||
|
||||
// Adjust Flash latency (simple L4-like thresholds for <=80 MHz)
|
||||
unsafe {
|
||||
acr.acr().write(|w| {
|
||||
w.latency().bits(if hclk <= 16_000_000 {
|
||||
0b000
|
||||
} else if hclk <= 32_000_000 {
|
||||
0b001
|
||||
} else if hclk <= 48_000_000 {
|
||||
0b010
|
||||
} else if hclk <= 64_000_000 {
|
||||
0b011
|
||||
} else {
|
||||
0b100
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Program prescalers first (CFGR2: HPRE/PPRE1/PPRE2)
|
||||
rcc.cfgr2.modify(|_, w| unsafe {
|
||||
w.hpre().bits(hpre_bits);
|
||||
w.ppre1().bits(ppre1_bits);
|
||||
w.ppre2().bits(ppre2_bits)
|
||||
});
|
||||
|
||||
let mut used_msi = self.msi;
|
||||
let sysclk_src_bits;
|
||||
|
||||
if let Some(pll) = pllconf {
|
||||
// Basic checks for VCO ranges (very simplified; FRACN/MBOOST ignored)
|
||||
let vco_in = (src_freq as u32) / (pll.m as u32);
|
||||
assert!(vco_in >= 1_000_000 && vco_in <= 16_000_000);
|
||||
let vco = (vco_in as u64) * (pll.n as u64);
|
||||
assert!(vco >= 128_000_000 && vco <= 544_000_000);
|
||||
let sysclk_calc = (vco / (pll.r as u64)) as u32;
|
||||
assert!(sysclk_calc <= 80_000_000);
|
||||
|
||||
// Disable PLL1 before reconfig
|
||||
rcc.cr.modify(|_, w| w.pll1on().clear_bit());
|
||||
while rcc.cr.read().pll1rdy().bit_is_set() {}
|
||||
|
||||
// Set PLL source
|
||||
let src_bits = pll_src.to_pllsrc_bits();
|
||||
rcc.pll1cfgr.modify(|_, w| unsafe {
|
||||
w.pll1src().bits(src_bits);
|
||||
// VCI range selection (very rough): 4..8 => range0, 8..16 => range1
|
||||
let rge = if vco_in < 8_000_000 { 0b00 } else { 0b11 };
|
||||
w.pll1rge().bits(rge);
|
||||
// M
|
||||
w.pll1m().bits(pll.m - 1);
|
||||
// Enable only R output
|
||||
w.pll1ren().clear_bit(); // clear first; will set after lock
|
||||
w
|
||||
});
|
||||
|
||||
// Program N, R in PLL1DIVR
|
||||
rcc.pll1divr.modify(|_, w| unsafe {
|
||||
w.pll1n().bits((pll.n - 1) as u16);
|
||||
w.pll1r().bits(pll.r - 1);
|
||||
// Leave P/Q untouched/disabled (skipped)
|
||||
w
|
||||
});
|
||||
|
||||
// Enable PLL1
|
||||
rcc.cr.modify(|_, w| w.pll1on().set_bit());
|
||||
while rcc.cr.read().pll1rdy().bit_is_clear() {}
|
||||
|
||||
// Enable PLL1 R output
|
||||
rcc.pll1cfgr.modify(|_, w| w.pll1ren().set_bit());
|
||||
|
||||
// Switch SYSCLK to PLL1 (CFGR1.SW = 11)
|
||||
sysclk_src_bits = 0b11;
|
||||
rcc.cfgr1.modify(|_, w| unsafe { w.sw().bits(sysclk_src_bits) });
|
||||
} else {
|
||||
// Keep MSI as SYSCLK (CFGR1.SW = 00)
|
||||
sysclk_src_bits = 0b00;
|
||||
if used_msi.is_none() {
|
||||
used_msi = Some(MsiFreq::MSI4);
|
||||
}
|
||||
rcc.cfgr1.modify(|_, w| unsafe { w.sw().bits(sysclk_src_bits) });
|
||||
}
|
||||
|
||||
// Wait for SWS
|
||||
while rcc.cfgr1.read().sws().bits() != sysclk_src_bits {}
|
||||
|
||||
// If we ended up on PLL, and MSI wasn't requested for other purposes, we can switch MSI off
|
||||
if used_msi.is_none() && sysclk_src_bits == 0b11 {
|
||||
rcc.cr.modify(|_, w| w.msison().clear_bit());
|
||||
}
|
||||
|
||||
Clocks {
|
||||
hclk: hclk.Hz(),
|
||||
lsi: lsi_used,
|
||||
lse: self.lse.is_some(),
|
||||
msi: used_msi,
|
||||
hsi48: self.hsi48,
|
||||
pclk1: pclk1.Hz(),
|
||||
pclk2: pclk2.Hz(),
|
||||
ppre1: ppre1_div as u8,
|
||||
ppre2: ppre2_div as u8,
|
||||
sysclk: want_sysclk.Hz(),
|
||||
timclk1: timclk1.Hz(),
|
||||
timclk2: timclk2.Hz(),
|
||||
pll_source: if pllconf.is_some() { Some(pll_src) } else { None },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Frozen clock frequencies (indicate RCC config is locked)
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Clocks {
|
||||
hclk: Hertz,
|
||||
hsi48: bool,
|
||||
msi: Option<MsiFreq>,
|
||||
lsi: bool,
|
||||
lse: bool,
|
||||
pclk1: Hertz,
|
||||
pclk2: Hertz,
|
||||
ppre1: u8,
|
||||
ppre2: u8,
|
||||
sysclk: Hertz,
|
||||
timclk1: Hertz,
|
||||
timclk2: Hertz,
|
||||
pll_source: Option<PllSource>,
|
||||
}
|
||||
|
||||
impl Clocks {
|
||||
pub fn hclk(&self) -> Hertz {
|
||||
self.hclk
|
||||
}
|
||||
pub fn hsi48(&self) -> bool {
|
||||
self.hsi48
|
||||
}
|
||||
pub fn msi(&self) -> Option<MsiFreq> {
|
||||
self.msi
|
||||
}
|
||||
pub fn lsi(&self) -> bool {
|
||||
self.lsi
|
||||
}
|
||||
pub fn lse(&self) -> bool {
|
||||
self.lse
|
||||
}
|
||||
pub fn pclk1(&self) -> Hertz {
|
||||
self.pclk1
|
||||
}
|
||||
pub fn pclk2(&self) -> Hertz {
|
||||
self.pclk2
|
||||
}
|
||||
pub fn pll_source(&self) -> Option<PllSource> {
|
||||
self.pll_source
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn ppre1(&self) -> u8 {
|
||||
self.ppre1
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn ppre2(&self) -> u8 {
|
||||
self.ppre2
|
||||
}
|
||||
pub fn sysclk(&self) -> Hertz {
|
||||
self.sysclk
|
||||
}
|
||||
pub fn timclk1(&self) -> Hertz {
|
||||
self.timclk1
|
||||
}
|
||||
pub fn timclk2(&self) -> Hertz {
|
||||
self.timclk2
|
||||
}
|
||||
}
|
||||
1
src/target/.rustc_info.json
Normal file
1
src/target/.rustc_info.json
Normal file
@@ -0,0 +1 @@
|
||||
{"rustc_fingerprint":7797895762148990127,"outputs":{"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.90.0 (1159e78c4 2025-09-14)\nbinary: rustc\ncommit-hash: 1159e78c4747b02ef996e55082b704c09b970588\ncommit-date: 2025-09-14\nhost: x86_64-unknown-linux-gnu\nrelease: 1.90.0\nLLVM version: 20.1.8\n","stderr":""},"2146565701525249126":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/filip/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""}},"successes":{}}
|
||||
3
src/target/CACHEDIR.TAG
Normal file
3
src/target/CACHEDIR.TAG
Normal file
@@ -0,0 +1,3 @@
|
||||
Signature: 8a477f597d28d172789f06886806bc55
|
||||
# This file is a cache directory tag created by cargo.
|
||||
# For information about cache directory tags see https://bford.info/cachedir/
|
||||
0
src/target/debug/.cargo-lock
Normal file
0
src/target/debug/.cargo-lock
Normal file
@@ -0,0 +1 @@
|
||||
c96e41e78fc4b1b5
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"[\"const-fn\"]","declared_features":"[\"const-fn\"]","target":12318548087768197662,"profile":2225463790103693989,"path":727814387382187207,"deps":[[6039000002955325809,"rustc_version",false,12743285913393872964]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/bare-metal-0441a201ec7ff19e/dep-build-script-build-script-build","checksum":false}}],"rustflags":["-Awarnings"],"config":2069994364910194474,"compile_kind":0}
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This file has an mtime of when this was started.
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This file has an mtime of when this was started.
|
||||
@@ -0,0 +1 @@
|
||||
12cc29099771db9d
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"[\"const-fn\"]","declared_features":"[\"const-fn\"]","target":798730107137846465,"profile":2241668132362809309,"path":1236641805781523185,"deps":[[15384096090752261737,"build_script_build",false,11614102898331340812]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/bare-metal-caca2e40baeb216d/dep-lib-bare_metal","checksum":false}}],"rustflags":["-Awarnings"],"config":2069994364910194474,"compile_kind":0}
|
||||
@@ -0,0 +1 @@
|
||||
0cd0df470d952da1
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"","declared_features":"","target":0,"profile":0,"path":0,"deps":[[15384096090752261737,"build_script_build",false,13092461714081935049]],"local":[{"Precalculated":"0.2.5"}],"rustflags":["-Awarnings"],"config":0,"compile_kind":0}
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This file has an mtime of when this was started.
|
||||
@@ -0,0 +1 @@
|
||||
9e693c4e92963c4d
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"[]","declared_features":"[]","target":3228570369864174577,"profile":2241668132362809309,"path":5962957290817863998,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/bitfield-85b54deaaa56d834/dep-lib-bitfield","checksum":false}}],"rustflags":["-Awarnings"],"config":2069994364910194474,"compile_kind":0}
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This file has an mtime of when this was started.
|
||||
@@ -0,0 +1 @@
|
||||
b6790a74456d93f5
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"[\"critical-section\", \"critical-section-single-core\"]","declared_features":"[\"cm7\", \"cm7-r0p1\", \"critical-section\", \"critical-section-single-core\", \"inline-asm\", \"linker-plugin-lto\", \"serde\", \"std\"]","target":16903219827764419198,"profile":2241668132362809309,"path":3737928084639157928,"deps":[[940283163401247653,"critical_section",false,6889657929426609084],[6064192862629450123,"embedded_hal",false,1098798841544667720],[6268991993315031017,"volatile_register",false,15191793622928741893],[9008560236759955788,"bitfield",false,5565488794645064094],[15384096090752261737,"bare_metal",false,11374810177362054162],[16907590962092906615,"build_script_build",false,17869660833099278682]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/cortex-m-24163fae679f67a4/dep-lib-cortex_m","checksum":false}}],"rustflags":["-Awarnings"],"config":2069994364910194474,"compile_kind":0}
|
||||
@@ -0,0 +1 @@
|
||||
5ae997a5d9c9fdf7
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"","declared_features":"","target":0,"profile":0,"path":0,"deps":[[16907590962092906615,"build_script_build",false,15003299260416408206]],"local":[{"Precalculated":"0.7.7"}],"rustflags":["-Awarnings"],"config":0,"compile_kind":0}
|
||||
@@ -0,0 +1 @@
|
||||
8e3a00bc2f6d36d0
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"[\"critical-section\", \"critical-section-single-core\"]","declared_features":"[\"cm7\", \"cm7-r0p1\", \"critical-section\", \"critical-section-single-core\", \"inline-asm\", \"linker-plugin-lto\", \"serde\", \"std\"]","target":17883862002600103897,"profile":2225463790103693989,"path":500019501507483128,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/cortex-m-b904d0b35ebbc296/dep-build-script-build-script-build","checksum":false}}],"rustflags":["-Awarnings"],"config":2069994364910194474,"compile_kind":0}
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This file has an mtime of when this was started.
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This file has an mtime of when this was started.
|
||||
@@ -0,0 +1 @@
|
||||
b1b796e7e9f50419
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"[\"device\"]","declared_features":"[\"device\", \"paint-stack\", \"set-sp\", \"set-vtor\", \"zero-init-ram\"]","target":7500287167573021594,"profile":2241668132362809309,"path":16602997734281123132,"deps":[[4185152142922722224,"build_script_build",false,6655054075734203440],[13693320939352097322,"cortex_m_rt_macros",false,4048601177095755467]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/cortex-m-rt-69be4e1286fecf2e/dep-lib-cortex_m_rt","checksum":false}}],"rustflags":["-Awarnings"],"config":2069994364910194474,"compile_kind":0}
|
||||
@@ -0,0 +1 @@
|
||||
5493402a8b8a7bbf
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"[\"device\"]","declared_features":"[\"device\", \"paint-stack\", \"set-sp\", \"set-vtor\", \"zero-init-ram\"]","target":5408242616063297496,"profile":2225463790103693989,"path":17590230721240710782,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/cortex-m-rt-8f06e70105324d5d/dep-build-script-build-script-build","checksum":false}}],"rustflags":["-Awarnings"],"config":2069994364910194474,"compile_kind":0}
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This file has an mtime of when this was started.
|
||||
@@ -0,0 +1 @@
|
||||
301c27016c805b5c
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"","declared_features":"","target":0,"profile":0,"path":0,"deps":[[4185152142922722224,"build_script_build",false,13797774213693608788]],"local":[{"RerunIfChanged":{"output":"debug/build/cortex-m-rt-b30a63aedc896301/output","paths":["build.rs","link.x.in"]}}],"rustflags":["-Awarnings"],"config":0,"compile_kind":0}
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This file has an mtime of when this was started.
|
||||
@@ -0,0 +1 @@
|
||||
cb5605f651852f38
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"[]","declared_features":"[]","target":15677508933736312558,"profile":2225463790103693989,"path":15007453374111989713,"deps":[[373107762698212489,"proc_macro2",false,16226973116626233901],[11004406779467019477,"syn",false,18020900704246757460],[11082282709338087849,"quote",false,13956431788072261233]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/cortex-m-rt-macros-d68a14a43d4a6ffd/dep-lib-cortex_m_rt_macros","checksum":false}}],"rustflags":["-Awarnings"],"config":2069994364910194474,"compile_kind":0}
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This file has an mtime of when this was started.
|
||||
@@ -0,0 +1 @@
|
||||
bc236d4362fb9c5f
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"[\"restore-state-bool\"]","declared_features":"[\"restore-state-bool\", \"restore-state-none\", \"restore-state-u16\", \"restore-state-u32\", \"restore-state-u64\", \"restore-state-u8\", \"restore-state-usize\", \"std\"]","target":6047854104591738533,"profile":2241668132362809309,"path":9211341459120640240,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/critical-section-be19d99f611807e9/dep-lib-critical_section","checksum":false}}],"rustflags":["-Awarnings"],"config":2069994364910194474,"compile_kind":0}
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This file has an mtime of when this was started.
|
||||
@@ -0,0 +1 @@
|
||||
c2b412224227c8c2
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"[]","declared_features":"[]","target":6675503679322096623,"profile":2241668132362809309,"path":2413519909266702954,"deps":[[12669569555400633618,"stable_deref_trait",false,334504914421932578]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/embedded-dma-9cc9ebd9c3bf1a78/dep-lib-embedded_dma","checksum":false}}],"rustflags":["-Awarnings"],"config":2069994364910194474,"compile_kind":0}
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This file has an mtime of when this was started.
|
||||
@@ -0,0 +1 @@
|
||||
28a5a7b19089120c
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"[]","declared_features":"[\"defmt-03\"]","target":10543535235496234955,"profile":2241668132362809309,"path":3104698083420708463,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/embedded-hal-28f9e2a89df6d646/dep-lib-embedded_hal","checksum":false}}],"rustflags":["-Awarnings"],"config":2069994364910194474,"compile_kind":0}
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This file has an mtime of when this was started.
|
||||
@@ -0,0 +1 @@
|
||||
4872e185b9b73f0f
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"[]","declared_features":"[\"unproven\"]","target":12477080980610433033,"profile":2241668132362809309,"path":11013167805085275336,"deps":[[15908183388125799874,"void",false,13113199807781901233],[16109205383622938406,"nb",false,8282130304605374876]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/embedded-hal-dc3a10ba02ec8390/dep-lib-embedded_hal","checksum":false}}],"rustflags":["-Awarnings"],"config":2069994364910194474,"compile_kind":0}
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This file has an mtime of when this was started.
|
||||
@@ -0,0 +1 @@
|
||||
df6404cb29880783
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"[]","declared_features":"[\"defmt\"]","target":14509429249698911482,"profile":2241668132362809309,"path":8354901679623613504,"deps":[[2610354610762496898,"gcd",false,4612767018939674926]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/fugit-b742ad295ea3ec5a/dep-lib-fugit","checksum":false}}],"rustflags":["-Awarnings"],"config":2069994364910194474,"compile_kind":0}
|
||||
BIN
src/target/debug/.fingerprint/gcd-da3a6161adc5a9f9/dep-lib-gcd
Normal file
BIN
src/target/debug/.fingerprint/gcd-da3a6161adc5a9f9/dep-lib-gcd
Normal file
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This file has an mtime of when this was started.
|
||||
@@ -0,0 +1 @@
|
||||
2e71890b2ad70340
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"[]","declared_features":"[]","target":250621938397769597,"profile":2241668132362809309,"path":734981053875278973,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/gcd-da3a6161adc5a9f9/dep-lib-gcd","checksum":false}}],"rustflags":["-Awarnings"],"config":2069994364910194474,"compile_kind":0}
|
||||
BIN
src/target/debug/.fingerprint/nb-11f99358cee37f58/dep-lib-nb
Normal file
BIN
src/target/debug/.fingerprint/nb-11f99358cee37f58/dep-lib-nb
Normal file
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This file has an mtime of when this was started.
|
||||
1
src/target/debug/.fingerprint/nb-11f99358cee37f58/lib-nb
Normal file
1
src/target/debug/.fingerprint/nb-11f99358cee37f58/lib-nb
Normal file
@@ -0,0 +1 @@
|
||||
bffd6c8919cbfac4
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"[]","declared_features":"[\"defmt-0-3\"]","target":4383844648039054697,"profile":2241668132362809309,"path":9556924083000438178,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/nb-11f99358cee37f58/dep-lib-nb","checksum":false}}],"rustflags":["-Awarnings"],"config":2069994364910194474,"compile_kind":0}
|
||||
BIN
src/target/debug/.fingerprint/nb-3dfd3b3f54c53a06/dep-lib-nb
Normal file
BIN
src/target/debug/.fingerprint/nb-3dfd3b3f54c53a06/dep-lib-nb
Normal file
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This file has an mtime of when this was started.
|
||||
1
src/target/debug/.fingerprint/nb-3dfd3b3f54c53a06/lib-nb
Normal file
1
src/target/debug/.fingerprint/nb-3dfd3b3f54c53a06/lib-nb
Normal file
@@ -0,0 +1 @@
|
||||
9c4980a5a109f072
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"[]","declared_features":"[\"unstable\"]","target":9278878797909942774,"profile":2241668132362809309,"path":17435201252822120298,"deps":[[9396512774562930307,"nb",false,14193880486151781823]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/nb-3dfd3b3f54c53a06/dep-lib-nb","checksum":false}}],"rustflags":["-Awarnings"],"config":2069994364910194474,"compile_kind":0}
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This file has an mtime of when this was started.
|
||||
@@ -0,0 +1 @@
|
||||
2dea514c13ca31e1
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"[\"default\", \"proc-macro\"]","declared_features":"[\"default\", \"nightly\", \"proc-macro\", \"span-locations\"]","target":369203346396300798,"profile":2225463790103693989,"path":2018616170207818342,"deps":[[373107762698212489,"build_script_build",false,3507093674611315338],[10637008577242657367,"unicode_ident",false,16544503007204026449]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/proc-macro2-b63889c6fd3b3dcd/dep-lib-proc_macro2","checksum":false}}],"rustflags":["-Awarnings"],"config":2069994364910194474,"compile_kind":0}
|
||||
@@ -0,0 +1 @@
|
||||
59ff7f21a1300150
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"[\"default\", \"proc-macro\"]","declared_features":"[\"default\", \"nightly\", \"proc-macro\", \"span-locations\"]","target":5408242616063297496,"profile":2225463790103693989,"path":14691176724777273933,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/proc-macro2-c001df32137bf4f2/dep-build-script-build-script-build","checksum":false}}],"rustflags":["-Awarnings"],"config":2069994364910194474,"compile_kind":0}
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
This file has an mtime of when this was started.
|
||||
@@ -0,0 +1 @@
|
||||
8a123a9d1db3ab30
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"","declared_features":"","target":0,"profile":0,"path":0,"deps":[[373107762698212489,"build_script_build",false,5764942466620850009]],"local":[{"RerunIfChanged":{"output":"debug/build/proc-macro2-ceb5054514a7dd7e/output","paths":["src/probe/proc_macro_span.rs","src/probe/proc_macro_span_location.rs","src/probe/proc_macro_span_file.rs"]}},{"RerunIfEnvChanged":{"var":"RUSTC_BOOTSTRAP","val":null}}],"rustflags":["-Awarnings"],"config":0,"compile_kind":0}
|
||||
@@ -0,0 +1 @@
|
||||
b6dc7bea9f22cb1d
|
||||
@@ -0,0 +1 @@
|
||||
{"rustc":16285725380928457773,"features":"","declared_features":"","target":0,"profile":0,"path":0,"deps":[[11082282709338087849,"build_script_build",false,3556112095485165420]],"local":[{"RerunIfChanged":{"output":"debug/build/quote-78b7cb83ae941fe5/output","paths":["build.rs"]}}],"rustflags":["-Awarnings"],"config":0,"compile_kind":0}
|
||||
@@ -0,0 +1 @@
|
||||
6cffdc181cd95931
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user