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 00000000..0bbfd313 --- /dev/null +++ b/embassy-stm32/src/adc/f3_v1_1.rs @@ -0,0 +1,408 @@ +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 { + T::regs().cr1().read().res().into() + } + + 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 3e2980bf..dbe53c80 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 5668137b..b1597a82 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,12 +42,28 @@ 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, } } } +impl From for Resolution { + fn from(res: crate::pac::adc::vals::Res) -> Resolution { + match res { + #[cfg(adc_v4)] + crate::pac::adc::vals::Res::SIXTEENBIT => Resolution::SixteenBit, + #[cfg(adc_v4)] + crate::pac::adc::vals::Res::FOURTEENBITV => Resolution::FourteenBit, + crate::pac::adc::vals::Res::TWELVEBIT => Resolution::TwelveBit, + crate::pac::adc::vals::Res::TENBIT => Resolution::TenBit, + crate::pac::adc::vals::Res::EIGHTBIT => Resolution::EightBit, + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0, adc_f3, adc_f3_v1_1, adc_f3_v3))] + crate::pac::adc::vals::Res::SIXBIT => Resolution::SixBit, + } + } +} + impl Resolution { pub fn to_max_count(&self) -> u32 { match self { @@ -56,7 +74,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 6a661929..5a06f1a5 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) + ) +);