rcc and pwr needs PAC map fixes

This commit is contained in:
Priec
2025-10-19 13:18:08 +02:00
parent 24ad631416
commit ef77eb629b
262 changed files with 5026 additions and 0 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
/target /target
stm32u5/

44
Cargo.lock generated
View File

@@ -56,6 +56,15 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" 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]] [[package]]
name = "embedded-hal" name = "embedded-hal"
version = "0.2.7" version = "0.2.7"
@@ -72,6 +81,21 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" 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]] [[package]]
name = "nb" name = "nb"
version = "0.1.3" version = "0.1.3"
@@ -129,13 +153,33 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 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]] [[package]]
name = "stm32u575-hal" name = "stm32u575-hal"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cortex-m", "cortex-m",
"cortex-m-rt", "cortex-m-rt",
"embedded-dma",
"embedded-hal 1.0.0", "embedded-hal 1.0.0",
"fugit",
"stm32u5",
"vcell", "vcell",
] ]

View File

@@ -8,6 +8,9 @@ description = "Hardware Abstraction Layer for STM32U575 microcontrollers"
[dependencies] [dependencies]
cortex-m = { version = "0.7", features = ["critical-section-single-core"] } cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
embedded-dma = "0.2.0"
embedded-hal = "1.0.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" vcell = "0.1.3"

27
flake.lock generated Normal file
View 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
}

View File

@@ -21,6 +21,8 @@
curl curl
wget wget
unzip unzip
svd2rust
form
# ARM embedded cross toolchain # ARM embedded cross toolchain
gcc-arm-embedded gcc-arm-embedded

955
src/adc/adc.rs Normal file
View 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
View File

9
src/adc/mod.rs Normal file
View 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
View 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(())
}
}

View File

@@ -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
View 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
View 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
View 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
View 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
}
}

View 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
View 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/

View File

View File

@@ -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}

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -0,0 +1 @@
12cc29099771db9d

View File

@@ -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}

View File

@@ -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}

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -0,0 +1 @@
9e693c4e92963c4d

View File

@@ -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}

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -0,0 +1 @@
b6790a74456d93f5

View File

@@ -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}

View File

@@ -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}

View File

@@ -0,0 +1 @@
8e3a00bc2f6d36d0

View File

@@ -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}

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -0,0 +1 @@
b1b796e7e9f50419

View File

@@ -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}

View File

@@ -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}

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -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}

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -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}

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -0,0 +1 @@
bc236d4362fb9c5f

View File

@@ -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}

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -0,0 +1 @@
c2b412224227c8c2

View File

@@ -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}

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -0,0 +1 @@
28a5a7b19089120c

View File

@@ -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}

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -0,0 +1 @@
4872e185b9b73f0f

View File

@@ -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}

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -0,0 +1 @@
df6404cb29880783

View File

@@ -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}

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -0,0 +1 @@
2e71890b2ad70340

View File

@@ -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}

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -0,0 +1 @@
bffd6c8919cbfac4

View File

@@ -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}

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -0,0 +1 @@
9c4980a5a109f072

View File

@@ -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}

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -0,0 +1 @@
2dea514c13ca31e1

View File

@@ -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}

View File

@@ -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}

View File

@@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@@ -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}

View File

@@ -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}

View File

@@ -0,0 +1 @@
6cffdc181cd95931

Some files were not shown because too many files have changed in this diff Show More