diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index a8790ce63..979d15c0c 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -58,7 +58,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7117ad49c06fa00c388130a34977e029910083bd" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-019a5da1c47c092c199bc39a7f84fb444f2adcdf" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -76,7 +76,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7117ad49c06fa00c388130a34977e029910083bd", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-019a5da1c47c092c199bc39a7f84fb444f2adcdf", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 7bfd290d2..0eef43ac4 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -930,6 +930,10 @@ fn main() { } else if pin.signal.starts_with("INN") { // TODO handle in the future when embassy supports differential measurements None + } else if pin.signal.starts_with("IN") && pin.signal.ends_with("b") { + // we number STM32L1 ADC bank 1 as 0..=31, bank 2 as 32..=63 + let signal = pin.signal.strip_prefix("IN").unwrap().strip_suffix("b").unwrap(); + Some(32u8 + signal.parse::().unwrap()) } else if pin.signal.starts_with("IN") { Some(pin.signal.strip_prefix("IN").unwrap().parse().unwrap()) } else { diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index ad0f13826..fb27bb87b 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -148,7 +148,7 @@ impl<'d, T: Instance> Adc<'d, T> { reg.set_cont(false); reg.set_exttrig(true); reg.set_swstart(false); - reg.set_extsel(crate::pac::adc::vals::Extsel::SWSTART); + reg.set_extsel(7); // SWSTART }); // Configure the channel to sample diff --git a/embassy-stm32/src/adc/f3_v1_1.rs b/embassy-stm32/src/adc/f3_v1_1.rs new file mode 100644 index 000000000..6915a8f1c --- /dev/null +++ b/embassy-stm32/src/adc/f3_v1_1.rs @@ -0,0 +1,413 @@ +use core::future::poll_fn; +use core::marker::PhantomData; +use core::task::Poll; + +use embassy_futures::yield_now; +use embassy_hal_internal::into_ref; +use embassy_time::Instant; + +use super::Resolution; +use crate::adc::{Adc, AdcPin, Instance, SampleTime}; +use crate::interrupt::typelevel::Interrupt; +use crate::time::Hertz; +use crate::{interrupt, Peripheral}; + +const ADC_FREQ: Hertz = crate::rcc::HSI_FREQ; + +pub const VDDA_CALIB_MV: u32 = 3300; +pub const ADC_MAX: u32 = (1 << 12) - 1; +pub const VREF_INT: u32 = 1230; + +pub enum AdcPowerMode { + AlwaysOn, + DelayOff, + IdleOff, + DelayIdleOff, +} + +pub enum Prescaler { + Div1, + Div2, + Div3, + Div4, +} + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + if T::regs().sr().read().eoc() { + T::regs().cr1().modify(|w| w.set_eocie(false)); + } else { + return; + } + + T::state().waker.wake(); + } +} + +fn update_vref(op: i8) { + static VREF_STATUS: core::sync::atomic::AtomicU8 = core::sync::atomic::AtomicU8::new(0); + + if op > 0 { + if VREF_STATUS.fetch_add(1, core::sync::atomic::Ordering::SeqCst) == 0 { + T::regs().ccr().modify(|w| w.set_tsvrefe(true)); + } + } else { + if VREF_STATUS.fetch_sub(1, core::sync::atomic::Ordering::SeqCst) == 1 { + T::regs().ccr().modify(|w| w.set_tsvrefe(false)); + } + } +} + +pub struct Vref(core::marker::PhantomData); +impl AdcPin for Vref {} +impl super::sealed::AdcPin for Vref { + fn channel(&self) -> u8 { + 17 + } +} + +impl Vref { + /// The value that vref would be if vdda was at 3000mv + pub fn calibrated_value(&self) -> u16 { + crate::pac::VREFINTCAL.data().read().value() + } + + pub async fn calibrate(&mut self, adc: &mut Adc<'_, T>) -> Calibration { + let vref_val = adc.read(self).await; + Calibration { + vref_cal: self.calibrated_value(), + vref_val, + } + } +} + +pub struct Calibration { + vref_cal: u16, + vref_val: u16, +} + +impl Calibration { + /// The millivolts that the calibration value was measured at + pub const CALIBRATION_UV: u32 = 3_000_000; + + /// Returns the measured VddA in microvolts (uV) + pub fn vdda_uv(&self) -> u32 { + (Self::CALIBRATION_UV * self.vref_cal as u32) / self.vref_val as u32 + } + + /// Returns the measured VddA as an f32 + pub fn vdda_f32(&self) -> f32 { + (Self::CALIBRATION_UV as f32 / 1_000.0) * (self.vref_cal as f32 / self.vref_val as f32) + } + + /// Returns a calibrated voltage value as in microvolts (uV) + pub fn cal_uv(&self, raw: u16, resolution: super::Resolution) -> u32 { + (self.vdda_uv() / resolution.to_max_count()) * raw as u32 + } + + /// Returns a calibrated voltage value as an f32 + pub fn cal_f32(&self, raw: u16, resolution: super::Resolution) -> f32 { + raw as f32 * self.vdda_f32() / resolution.to_max_count() as f32 + } +} + +impl Drop for Vref { + fn drop(&mut self) { + update_vref::(-1) + } +} + +pub struct Temperature(core::marker::PhantomData); +impl AdcPin for Temperature {} +impl super::sealed::AdcPin for Temperature { + fn channel(&self) -> u8 { + 16 + } +} + +impl Drop for Temperature { + fn drop(&mut self) { + update_vref::(-1) + } +} + +impl<'d, T: Instance> Adc<'d, T> { + pub fn new( + adc: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + ) -> Self { + into_ref!(adc); + + T::enable_and_reset(); + + //let r = T::regs(); + //r.cr2().write(|w| w.set_align(true)); + + T::Interrupt::unpend(); + unsafe { + T::Interrupt::enable(); + } + + Self { adc } + } + + fn freq() -> Hertz { + let div = T::regs().ccr().read().adcpre() + 1; + ADC_FREQ / div as u32 + } + + pub async fn set_resolution(&mut self, res: Resolution) { + let was_on = Self::is_on(); + if was_on { + self.stop_adc().await; + } + + T::regs().cr1().modify(|w| w.set_res(res.into())); + + if was_on { + self.start_adc().await; + } + } + + pub fn resolution(&self) -> Resolution { + match T::regs().cr1().read().res() { + crate::pac::adc::vals::Res::TWELVEBIT => Resolution::TwelveBit, + crate::pac::adc::vals::Res::TENBIT => Resolution::TenBit, + crate::pac::adc::vals::Res::EIGHTBIT => Resolution::EightBit, + crate::pac::adc::vals::Res::SIXBIT => Resolution::SixBit, + } + } + + pub fn enable_vref(&self) -> Vref { + update_vref::(1); + + Vref(core::marker::PhantomData) + } + + pub fn enable_temperature(&self) -> Temperature { + T::regs().ccr().modify(|w| w.set_tsvrefe(true)); + + Temperature::(core::marker::PhantomData) + } + + /// Perform a single conversion. + async fn convert(&mut self) -> u16 { + let was_on = Self::is_on(); + + if !was_on { + self.start_adc().await; + } + + self.wait_sample_ready().await; + + T::regs().sr().write(|_| {}); + T::regs().cr1().modify(|w| { + w.set_eocie(true); + w.set_scan(false); + }); + T::regs().cr2().modify(|w| { + w.set_swstart(true); + w.set_cont(false); + }); // swstart cleared by HW + + let res = poll_fn(|cx| { + T::state().waker.register(cx.waker()); + + if T::regs().sr().read().eoc() { + let res = T::regs().dr().read().rdata(); + Poll::Ready(res) + } else { + Poll::Pending + } + }) + .await; + + if !was_on { + self.stop_adc().await; + } + + res + } + + #[inline(always)] + fn is_on() -> bool { + T::regs().sr().read().adons() || T::regs().cr2().read().adon() + } + + pub async fn start_adc(&self) { + //defmt::trace!("Turn ADC on"); + T::regs().cr2().modify(|w| w.set_adon(true)); + //defmt::trace!("Waiting for ADC to turn on"); + + let mut t = Instant::now(); + + while !T::regs().sr().read().adons() { + yield_now().await; + if t.elapsed() > embassy_time::Duration::from_millis(1000) { + t = Instant::now(); + //defmt::trace!("ADC still not on"); + } + } + + //defmt::trace!("ADC on"); + } + + pub async fn stop_adc(&self) { + if T::regs().cr2().read().adon() { + //defmt::trace!("ADC should be on, wait for it to start"); + while !T::regs().csr().read().adons1() { + yield_now().await; + } + } + + //defmt::trace!("Turn ADC off"); + + T::regs().cr2().modify(|w| w.set_adon(false)); + + //defmt::trace!("Waiting for ADC to turn off"); + + while T::regs().csr().read().adons1() { + yield_now().await; + } + } + + pub async fn read(&mut self, pin: &mut impl AdcPin) -> u16 { + self.set_sample_sequence(&[pin.channel()]).await; + self.convert().await + } + + async fn wait_sample_ready(&self) { + //trace!("Waiting for sample channel to be ready"); + while T::regs().sr().read().rcnr() { + yield_now().await; + } + } + + pub async fn set_sample_time(&mut self, pin: &mut impl AdcPin, sample_time: SampleTime) { + if Self::get_channel_sample_time(pin.channel()) != sample_time { + self.stop_adc().await; + unsafe { + Self::set_channel_sample_time(pin.channel(), sample_time); + } + self.start_adc().await; + } + } + + pub fn get_sample_time(&self, pin: &impl AdcPin) -> SampleTime { + Self::get_channel_sample_time(pin.channel()) + } + + /// Sets the channel sample time + /// + /// ## SAFETY: + /// - ADON == 0 i.e ADC must not be enabled when this is called. + unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { + let sample_time = sample_time.into(); + + match ch { + 0..=9 => T::regs().smpr3().modify(|reg| reg.set_smp(ch as _, sample_time)), + 10..=19 => T::regs() + .smpr2() + .modify(|reg| reg.set_smp(ch as usize - 10, sample_time)), + 20..=29 => T::regs() + .smpr1() + .modify(|reg| reg.set_smp(ch as usize - 20, sample_time)), + 30..=31 => T::regs() + .smpr0() + .modify(|reg| reg.set_smp(ch as usize - 30, sample_time)), + _ => panic!("Invalid channel to sample"), + } + } + + fn get_channel_sample_time(ch: u8) -> SampleTime { + match ch { + 0..=9 => T::regs().smpr3().read().smp(ch as _), + 10..=19 => T::regs().smpr2().read().smp(ch as usize - 10), + 20..=29 => T::regs().smpr1().read().smp(ch as usize - 20), + 30..=31 => T::regs().smpr0().read().smp(ch as usize - 30), + _ => panic!("Invalid channel to sample"), + } + .into() + } + + /// Sets the sequence to sample the ADC. Must be less than 28 elements. + async fn set_sample_sequence(&self, sequence: &[u8]) { + assert!(sequence.len() <= 28); + let mut iter = sequence.iter(); + T::regs().sqr1().modify(|w| w.set_l((sequence.len() - 1) as _)); + for (idx, ch) in iter.by_ref().take(6).enumerate() { + T::regs().sqr5().modify(|w| w.set_sq(idx, *ch)); + } + for (idx, ch) in iter.by_ref().take(6).enumerate() { + T::regs().sqr4().modify(|w| w.set_sq(idx, *ch)); + } + for (idx, ch) in iter.by_ref().take(6).enumerate() { + T::regs().sqr3().modify(|w| w.set_sq(idx, *ch)); + } + for (idx, ch) in iter.by_ref().take(6).enumerate() { + T::regs().sqr2().modify(|w| w.set_sq(idx, *ch)); + } + for (idx, ch) in iter.by_ref().take(4).enumerate() { + T::regs().sqr1().modify(|w| w.set_sq(idx, *ch)); + } + } + + fn get_res_clks(res: Resolution) -> u32 { + match res { + Resolution::TwelveBit => 12, + Resolution::TenBit => 11, + Resolution::EightBit => 9, + Resolution::SixBit => 7, + } + } + + fn get_sample_time_clks(sample_time: SampleTime) -> u32 { + match sample_time { + SampleTime::Cycles4 => 4, + SampleTime::Cycles9 => 9, + SampleTime::Cycles16 => 16, + SampleTime::Cycles24 => 24, + SampleTime::Cycles48 => 48, + SampleTime::Cycles96 => 96, + SampleTime::Cycles192 => 192, + SampleTime::Cycles384 => 384, + } + } + + pub fn sample_time_for_us(&self, us: u32) -> SampleTime { + let res_clks = Self::get_res_clks(self.resolution()); + let us_clks = us * Self::freq().0 / 1_000_000; + let clks = us_clks.saturating_sub(res_clks); + match clks { + 0..=4 => SampleTime::Cycles4, + 5..=9 => SampleTime::Cycles9, + 10..=16 => SampleTime::Cycles16, + 17..=24 => SampleTime::Cycles24, + 25..=48 => SampleTime::Cycles48, + 49..=96 => SampleTime::Cycles96, + 97..=192 => SampleTime::Cycles192, + 193.. => SampleTime::Cycles384, + } + } + + pub fn us_for_cfg(&self, res: Resolution, sample_time: SampleTime) -> u32 { + let res_clks = Self::get_res_clks(res); + let sample_clks = Self::get_sample_time_clks(sample_time); + (res_clks + sample_clks) * 1_000_000 / Self::freq().0 + } +} + +impl<'d, T: Instance> Drop for Adc<'d, T> { + fn drop(&mut self) { + while !T::regs().sr().read().adons() {} + + T::regs().cr2().modify(|w| w.set_adon(false)); + + T::disable(); + } +} diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 3e2980bf4..dbe53c807 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -3,6 +3,7 @@ #[cfg(not(adc_f3_v2))] #[cfg_attr(adc_f1, path = "f1.rs")] #[cfg_attr(adc_f3, path = "f3.rs")] +#[cfg_attr(adc_f3_v1_1, path = "f3_v1_1.rs")] #[cfg_attr(adc_v1, path = "v1.rs")] #[cfg_attr(adc_v2, path = "v2.rs")] #[cfg_attr(any(adc_v3, adc_g0), path = "v3.rs")] @@ -26,20 +27,20 @@ use crate::peripherals; pub struct Adc<'d, T: Instance> { #[allow(unused)] adc: crate::PeripheralRef<'d, T>, - #[cfg(not(adc_f3_v2))] + #[cfg(not(any(adc_f3_v2, adc_f3_v1_1)))] sample_time: SampleTime, } pub(crate) mod sealed { - #[cfg(any(adc_f1, adc_f3, adc_v1))] + #[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))] use embassy_sync::waitqueue::AtomicWaker; - #[cfg(any(adc_f1, adc_f3, adc_v1))] + #[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))] pub struct State { pub waker: AtomicWaker, } - #[cfg(any(adc_f1, adc_f3, adc_v1))] + #[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))] impl State { pub const fn new() -> Self { Self { @@ -54,11 +55,11 @@ pub(crate) mod sealed { pub trait Instance: InterruptableInstance { fn regs() -> crate::pac::adc::Adc; - #[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_g0)))] + #[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_f3_v1_1, adc_g0)))] fn common_regs() -> crate::pac::adccommon::AdcCommon; #[cfg(adc_f3)] fn frequency() -> crate::time::Hertz; - #[cfg(any(adc_f1, adc_f3, adc_v1))] + #[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))] fn state() -> &'static State; } @@ -74,9 +75,9 @@ pub(crate) mod sealed { } } -#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_g0)))] +#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0)))] pub trait Instance: sealed::Instance + crate::Peripheral

{} -#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_g0))] +#[cfg(any(adc_f1, adc_v1, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0))] pub trait Instance: sealed::Instance + crate::Peripheral

+ crate::rcc::RccPeripheral {} pub trait AdcPin: sealed::AdcPin {} @@ -89,7 +90,7 @@ foreach_adc!( crate::pac::$inst } - #[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_g0)))] + #[cfg(not(any(adc_f1, adc_v1, adc_f3_v2, adc_f3_v1_1, adc_g0)))] fn common_regs() -> crate::pac::adccommon::AdcCommon { return crate::pac::$common_inst } @@ -99,7 +100,7 @@ foreach_adc!( unsafe { crate::rcc::get_freqs() }.$clock.unwrap() } - #[cfg(any(adc_f1, adc_f3, adc_v1))] + #[cfg(any(adc_f1, adc_f3, adc_v1, adc_f3_v1_1))] fn state() -> &'static sealed::State { static STATE: sealed::State = sealed::State::new(); &STATE diff --git a/embassy-stm32/src/adc/resolution.rs b/embassy-stm32/src/adc/resolution.rs index 5668137b5..383980b5a 100644 --- a/embassy-stm32/src/adc/resolution.rs +++ b/embassy-stm32/src/adc/resolution.rs @@ -1,5 +1,6 @@ -#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] +#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Resolution { TwelveBit, TenBit, @@ -9,6 +10,7 @@ pub enum Resolution { #[cfg(adc_v4)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Resolution { SixteenBit, FourteenBit, @@ -19,7 +21,7 @@ pub enum Resolution { impl Default for Resolution { fn default() -> Self { - #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))] { Self::TwelveBit } @@ -40,7 +42,7 @@ impl From for crate::pac::adc::vals::Res { Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT, Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT, Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT, - #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))] Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, } } @@ -56,7 +58,7 @@ impl Resolution { Resolution::TwelveBit => (1 << 12) - 1, Resolution::TenBit => (1 << 10) - 1, Resolution::EightBit => (1 << 8) - 1, - #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1))] Resolution::SixBit => (1 << 6) - 1, } } diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs index 6a6619299..5a06f1a5a 100644 --- a/embassy-stm32/src/adc/sample_time.rs +++ b/embassy-stm32/src/adc/sample_time.rs @@ -3,6 +3,7 @@ macro_rules! impl_sample_time { ($default_doc:expr, $default:ident, ($(($doc:expr, $variant:ident, $pac_variant:ident)),*)) => { #[doc = concat!("ADC sample time\n\nThe default setting is ", $default_doc, " ADC clock cycles.")] #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SampleTime { $( #[doc = concat!($doc, " ADC clock cycles.")] @@ -18,6 +19,14 @@ macro_rules! impl_sample_time { } } + impl From for SampleTime { + fn from(sample_time: crate::pac::adc::vals::SampleTime) -> SampleTime { + match sample_time { + $(crate::pac::adc::vals::SampleTime::$pac_variant => SampleTime::$variant),* + } + } + } + impl Default for SampleTime { fn default() -> Self { Self::$default @@ -121,3 +130,19 @@ impl_sample_time!( ("601.5", Cycles601_5, CYCLES601_5) ) ); + +#[cfg(any(adc_f3_v1_1))] +impl_sample_time!( + "4", + Cycles4, + ( + ("4", Cycles4, CYCLES4), + ("9", Cycles9, CYCLES9), + ("16", Cycles16, CYCLES16), + ("24", Cycles24, CYCLES24), + ("48", Cycles48, CYCLES48), + ("96", Cycles96, CYCLES96), + ("192", Cycles192, CYCLES192), + ("384", Cycles384, CYCLES384) + ) +); diff --git a/embassy-stm32/src/can/mod.rs b/embassy-stm32/src/can/mod.rs index 4ff5aa0de..425f9ac2e 100644 --- a/embassy-stm32/src/can/mod.rs +++ b/embassy-stm32/src/can/mod.rs @@ -1,6 +1,6 @@ #![macro_use] #[cfg_attr(can_bxcan, path = "bxcan.rs")] -#[cfg_attr(can_fdcan, path = "fdcan.rs")] +#[cfg_attr(any(can_fdcan_v1, can_fdcan_h7), path = "fdcan.rs")] mod _version; pub use _version::*; diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 48b27255d..7af05b485 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -315,6 +315,8 @@ pub(crate) unsafe fn init(config: Config) { adc: adc12_ck, adc34: adc345_ck, pll1_p: None, + pll1_q: None, // TODO + hse: None, // TODO rtc, }); } diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 2e144dc77..40a5a619d 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -119,7 +119,7 @@ pub struct Clocks { #[cfg(any(stm32g4, rcc_l4))] pub pll1_p: Option, - #[cfg(any(stm32h5, stm32h7, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_l4))] + #[cfg(any(stm32h5, stm32h7, stm32f2, stm32f4, stm32f7, rcc_l4, stm32g4))] pub pll1_q: Option, #[cfg(any(stm32h5, stm32h7))] pub pll2_p: Option, @@ -167,7 +167,7 @@ pub struct Clocks { #[cfg(any(stm32h5, stm32h7, rcc_l4, rcc_c0))] pub lse: Option, - #[cfg(any(stm32h5, stm32h7))] + #[cfg(any(stm32h5, stm32h7, stm32g4))] pub hse: Option, #[cfg(stm32h5)] diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index c37d89a40..4f53e84f0 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -10,14 +10,14 @@ stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"] stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] stm32f207zg = ["embassy-stm32/stm32f207zg", "chrono", "not-gpdma", "eth", "rng"] stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"] -stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "eth", "stop", "can", "not-gpdma", "dac-adc-pin", "rng"] -stm32f446re = ["embassy-stm32/stm32f446re", "chrono", "stop", "can", "not-gpdma", "dac-adc-pin", "sdmmc"] +stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "eth", "stop", "can", "not-gpdma", "dac", "rng"] +stm32f446re = ["embassy-stm32/stm32f446re", "chrono", "stop", "can", "not-gpdma", "dac", "sdmmc"] stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"] -stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac-adc-pin"] +stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac"] stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng"] stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng"] stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng"] -stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac-adc-pin", "rng"] +stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng"] stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng"] stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng"] stm32l152re = ["embassy-stm32/stm32l152re", "chrono", "not-gpdma"] @@ -41,7 +41,7 @@ ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/ble"] mac = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/mac"] embassy-stm32-wpan = [] not-gpdma = [] -dac-adc-pin = [] +dac = [] cm0 = ["portable-atomic/unsafe-assume-single-core"] @@ -84,7 +84,12 @@ required-features = [ "can",] [[bin]] name = "dac" path = "src/bin/dac.rs" -required-features = [ "dac-adc-pin",] +required-features = [ "dac",] + +[[bin]] +name = "dac_l1" +path = "src/bin/dac_l1.rs" +required-features = [ "stm32l152re",] [[bin]] name = "eth" diff --git a/tests/stm32/src/bin/dac.rs b/tests/stm32/src/bin/dac.rs index cf43106b3..9d64742df 100644 --- a/tests/stm32/src/bin/dac.rs +++ b/tests/stm32/src/bin/dac.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -// required-features: dac-adc-pin +// required-features: dac #[path = "../common.rs"] mod common; @@ -22,12 +22,13 @@ async fn main(_spawner: Spawner) { // Initialize the board and obtain a Peripherals instance let p: embassy_stm32::Peripherals = embassy_stm32::init(config()); + let adc = peri!(p, ADC); let dac = peri!(p, DAC); let dac_pin = peri!(p, DAC_PIN); let mut adc_pin = unsafe { core::ptr::read(&dac_pin) }; let mut dac = DacCh1::new(dac, NoDma, dac_pin); - let mut adc = Adc::new(p.ADC1, &mut Delay); + let mut adc = Adc::new(adc, &mut Delay); #[cfg(feature = "stm32h755zi")] let normalization_factor = 256; diff --git a/tests/stm32/src/bin/dac_l1.rs b/tests/stm32/src/bin/dac_l1.rs new file mode 100644 index 000000000..f8b00aaef --- /dev/null +++ b/tests/stm32/src/bin/dac_l1.rs @@ -0,0 +1,86 @@ +#![no_std] +#![no_main] + +// required-features: stm32l152re + +#[path = "../common.rs"] +mod common; +use core::f32::consts::PI; + +use common::*; +use defmt::assert; +use embassy_executor::Spawner; +use embassy_stm32::adc::Adc; +use embassy_stm32::dac::{DacCh1, Value}; +use embassy_stm32::dma::NoDma; +use embassy_stm32::{bind_interrupts, peripherals}; +use embassy_time::Timer; +use micromath::F32Ext; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + ADC1 => embassy_stm32::adc::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Initialize the board and obtain a Peripherals instance + let p: embassy_stm32::Peripherals = embassy_stm32::init(config()); + + let adc = peri!(p, ADC); + let dac = peri!(p, DAC); + let dac_pin = peri!(p, DAC_PIN); + let mut adc_pin = unsafe { core::ptr::read(&dac_pin) }; + + let mut dac = DacCh1::new(dac, NoDma, dac_pin); + let mut adc = Adc::new(adc, Irqs); + + #[cfg(feature = "stm32h755zi")] + let normalization_factor = 256; + #[cfg(any( + feature = "stm32f429zi", + feature = "stm32f446re", + feature = "stm32g071rb", + feature = "stm32l152re", + ))] + let normalization_factor: i32 = 16; + + dac.set(Value::Bit8(0)); + // Now wait a little to obtain a stable value + Timer::after_millis(30).await; + let offset = adc.read(&mut adc_pin).await; + + for v in 0..=255 { + // First set the DAC output value + let dac_output_val = to_sine_wave(v); + dac.set(Value::Bit8(dac_output_val)); + + // Now wait a little to obtain a stable value + Timer::after_millis(30).await; + + // Need to steal the peripherals here because PA4 is obviously in use already + let measured = adc.read(&mut unsafe { embassy_stm32::Peripherals::steal() }.PA4).await; + // Calibrate and normalize the measurement to get close to the dac_output_val + let measured_normalized = ((measured as i32 - offset as i32) / normalization_factor) as i16; + + info!("value / measured: {} / {}", dac_output_val, measured_normalized); + + // The deviations are quite enormous but that does not matter since this is only a quick test + assert!((dac_output_val as i16 - measured_normalized).abs() < 15); + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +fn to_sine_wave(v: u8) -> u8 { + if v >= 128 { + // top half + let r = PI * ((v - 128) as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } else { + // bottom half + let r = PI + PI * (v as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } +} diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 155e1d9df..313380b35 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -101,14 +101,14 @@ define_peris!( define_peris!( UART = USART1, UART_TX = PC4, UART_RX = PC5, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2, SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2, - DAC = DAC1, DAC_PIN = PA4, + ADC = ADC1, DAC = DAC1, DAC_PIN = PA4, @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler;}, ); #[cfg(feature = "stm32f429zi")] define_peris!( UART = USART6, UART_TX = PG14, UART_RX = PG9, UART_TX_DMA = DMA2_CH6, UART_RX_DMA = DMA2_CH1, SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA2_CH3, SPI_RX_DMA = DMA2_CH2, - DAC = DAC, DAC_PIN = PA4, + ADC = ADC1, DAC = DAC, DAC_PIN = PA4, CAN = CAN1, CAN_RX = PD0, CAN_TX = PD1, @irq UART = {USART6 => embassy_stm32::usart::InterruptHandler;}, ); @@ -116,7 +116,7 @@ define_peris!( define_peris!( UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA2_CH7, UART_RX_DMA = DMA2_CH5, SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA2_CH3, SPI_RX_DMA = DMA2_CH2, - DAC = DAC, DAC_PIN = PA4, + ADC = ADC1, DAC = DAC, DAC_PIN = PA4, CAN = CAN1, CAN_RX = PA11, CAN_TX = PA12, @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler;}, ); @@ -130,7 +130,7 @@ define_peris!( define_peris!( UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH0, UART_RX_DMA = DMA1_CH1, SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PB5, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH0, SPI_RX_DMA = DMA1_CH1, - DAC = DAC1, DAC_PIN = PA4, + ADC = ADC1, DAC = DAC1, DAC_PIN = PA4, @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler;}, ); #[cfg(feature = "stm32h7a3zi")] @@ -191,6 +191,7 @@ define_peris!( define_peris!( UART = USART3, UART_TX = PB10, UART_RX = PB11, UART_TX_DMA = DMA1_CH2, UART_RX_DMA = DMA1_CH3, SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, + ADC = ADC, DAC = DAC, DAC_PIN = PA4, @irq UART = {USART3 => embassy_stm32::usart::InterruptHandler;}, ); #[cfg(feature = "stm32l552ze")]