From 5d9ae3dbdbfe8ba6e1008cd2eadc09743cfc6284 Mon Sep 17 00:00:00 2001 From: "Matthew W. Samsonoff" Date: Tue, 13 Sep 2022 23:08:03 -0400 Subject: [PATCH 01/12] Add implementation of STM32 v1 ADC --- embassy-stm32/src/adc/v1.rs | 277 ++++++++++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 8b137891..923a1d97 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -1 +1,278 @@ +use core::marker::PhantomData; +use embassy_hal_common::into_ref; +use embedded_hal_02::blocking::delay::DelayUs; + +use crate::adc::{AdcPin, Instance}; +use crate::{pac, Peripheral}; + +pub const VDDA_CALIB_MV: u32 = 3300; +pub const VREF_INT: u32 = 1230; + +fn enable() { + critical_section::with(|_| unsafe { + crate::pac::RCC.apb2enr().modify(|reg| reg.set_adcen(true)); + }); +} + +pub enum Resolution { + TwelveBit, + TenBit, + EightBit, + SixBit, +} + +impl Default for Resolution { + fn default() -> Self { + Self::TwelveBit + } +} + +impl Resolution { + fn res(&self) -> pac::adc::vals::Res { + match self { + Resolution::TwelveBit => pac::adc::vals::Res::TWELVEBIT, + Resolution::TenBit => pac::adc::vals::Res::TENBIT, + Resolution::EightBit => pac::adc::vals::Res::EIGHTBIT, + Resolution::SixBit => pac::adc::vals::Res::SIXBIT, + } + } + + pub fn to_max_count(&self) -> u32 { + match self { + Resolution::TwelveBit => (1 << 12) - 1, + Resolution::TenBit => (1 << 10) - 1, + Resolution::EightBit => (1 << 8) - 1, + Resolution::SixBit => (1 << 6) - 1, + } + } +} + +pub struct Vbat; +impl AdcPin for Vbat {} +impl super::sealed::AdcPin for Vbat { + fn channel(&self) -> u8 { + 18 + } +} + +pub struct Vref; +impl AdcPin for Vref {} +impl super::sealed::AdcPin for Vref { + fn channel(&self) -> u8 { + 17 + } +} + +pub struct Temperature; +impl AdcPin for Temperature {} +impl super::sealed::AdcPin for Temperature { + fn channel(&self) -> u8 { + 16 + } +} + +mod sample_time { + #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] + pub enum SampleTime { + /// 1.5 ADC clock cycles + Cycles1_5 = 0b000, + + /// 7.5 ADC clock cycles + Cycles7_5 = 0b001, + + /// 13.5 ADC clock cycles + Cycles13_5 = 0b010, + + /// 28.5 ADC clock cycles + Cycles28_5 = 0b011, + + /// 41.5 ADC clock cycles + Cycles41_5 = 0b100, + + /// 55.5 ADC clock cycles + Cycles55_5 = 0b101, + + /// 71.5 ADC clock cycles + Cycles71_5 = 0b110, + + /// 239.5 ADC clock cycles + Cycles239_5 = 0b111, + } + + impl SampleTime { + pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::Smp { + match self { + SampleTime::Cycles1_5 => crate::pac::adc::vals::Smp::CYCLES1_5, + SampleTime::Cycles7_5 => crate::pac::adc::vals::Smp::CYCLES7_5, + SampleTime::Cycles13_5 => crate::pac::adc::vals::Smp::CYCLES13_5, + SampleTime::Cycles28_5 => crate::pac::adc::vals::Smp::CYCLES28_5, + SampleTime::Cycles41_5 => crate::pac::adc::vals::Smp::CYCLES41_5, + SampleTime::Cycles55_5 => crate::pac::adc::vals::Smp::CYCLES55_5, + SampleTime::Cycles71_5 => crate::pac::adc::vals::Smp::CYCLES71_5, + SampleTime::Cycles239_5 => crate::pac::adc::vals::Smp::CYCLES239_5, + } + } + } + + impl Default for SampleTime { + fn default() -> Self { + Self::Cycles1_5 + } + } +} + +pub use sample_time::SampleTime; + +pub struct Adc<'d, T: Instance> { + sample_time: SampleTime, + vref_mv: u32, + resolution: Resolution, + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: Instance> Adc<'d, T> { + pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + into_ref!(_peri); + enable(); + + // Delay 1μs when using HSI14 as the ADC clock. + // + // Table 57. ADC characteristics + // tstab = 14 * 1/fadc + delay.delay_us(1); + + let s = Self { + sample_time: Default::default(), + vref_mv: VDDA_CALIB_MV, + resolution: Resolution::default(), + phantom: PhantomData, + }; + s.calibrate(); + s + } + + pub fn enable_vbat(&self, _delay: &mut impl DelayUs) -> Vbat { + // SMP must be ≥ 56 ADC clock cycles when using HSI14. + // + // 6.3.20 Vbat monitoring characteristics + // ts_vbat ≥ 4μs + unsafe { + T::regs().ccr().modify(|reg| reg.set_vbaten(true)); + } + Vbat + } + + pub fn enable_vref(&self, delay: &mut impl DelayUs) -> Vref { + // Table 28. Embedded internal reference voltage + // tstart = 10μs + unsafe { + T::regs().ccr().modify(|reg| reg.set_vrefen(true)); + } + delay.delay_us(10); + Vref + } + + pub fn enable_temperature(&self, delay: &mut impl DelayUs) -> Temperature { + // SMP must be ≥ 56 ADC clock cycles when using HSI14. + // + // 6.3.19 Temperature sensor characteristics + // tstart ≤ 10μs + // ts_temp ≥ 4μs + unsafe { + T::regs().ccr().modify(|reg| reg.set_tsen(true)); + } + delay.delay_us(10); + Temperature + } + + fn calibrate(&self) { + unsafe { + // A.7.1 ADC calibration code example + if T::regs().cr().read().aden() { + T::regs().cr().modify(|reg| reg.set_addis(true)); + } + while T::regs().cr().read().aden() { + // spin + } + T::regs().cfgr1().modify(|reg| reg.set_dmaen(false)); + T::regs().cr().modify(|reg| reg.set_adcal(true)); + while T::regs().cr().read().adcal() { + // spin + } + } + } + + pub fn set_sample_time(&mut self, sample_time: SampleTime) { + self.sample_time = sample_time; + } + + pub fn set_vref_mv(&mut self, vref_mv: u32) { + self.vref_mv = vref_mv; + } + + pub fn set_resolution(&mut self, resolution: Resolution) { + self.resolution = resolution; + } + + pub fn to_millivolts(&self, sample: u16) -> u16 { + ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 + } + + fn convert(&mut self) -> u16 { + unsafe { + T::regs().isr().modify(|reg| { + reg.set_eoc(true); + reg.set_eosmp(true); + }); + + // A.7.5 Single conversion sequence code example - Software trigger + T::regs().cr().modify(|reg| reg.set_adstart(true)); + while !T::regs().isr().read().eoc() { + // spin + } + + T::regs().dr().read().0 as u16 + } + } + + pub fn read(&mut self, pin: &mut impl AdcPin) -> u16 { + unsafe { + // A.7.2 ADC enable sequence code example + if T::regs().isr().read().adrdy() { + T::regs().isr().modify(|reg| reg.set_adrdy(true)); + } + T::regs().cr().modify(|reg| reg.set_aden(true)); + while !T::regs().isr().read().adrdy() { + // ES0233, 2.4.3 ADEN bit cannot be set immediately after the ADC calibration + // Workaround: When the ADC calibration is complete (ADCAL = 0), keep setting the + // ADEN bit until the ADRDY flag goes high. + T::regs().cr().modify(|reg| reg.set_aden(true)); + } + + T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); + Self::set_channel_sample_time(pin.channel(), self.sample_time); + T::regs() + .chselr() + .write(|reg| reg.set_chselx(pin.channel() as usize, true)); + + let value = self.convert(); + + // A.7.3 ADC disable code example + T::regs().cr().modify(|reg| reg.set_adstp(true)); + while T::regs().cr().read().adstp() { + // spin + } + T::regs().cr().modify(|reg| reg.set_addis(true)); + while T::regs().cr().read().aden() { + // spin + } + + value + } + } + + unsafe fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { + T::regs().smpr().modify(|reg| reg.set_smp(sample_time.sample_time())); + } +} From 7e9e628eb9e63598a7d15ecae21af5a94ee93be4 Mon Sep 17 00:00:00 2001 From: "Matthew W. Samsonoff" Date: Tue, 13 Sep 2022 23:10:29 -0400 Subject: [PATCH 02/12] Add ADC example for STM32F0 --- examples/stm32f0/src/bin/adc.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 examples/stm32f0/src/bin/adc.rs diff --git a/examples/stm32f0/src/bin/adc.rs b/examples/stm32f0/src/bin/adc.rs new file mode 100644 index 00000000..b69bc3c3 --- /dev/null +++ b/examples/stm32f0/src/bin/adc.rs @@ -0,0 +1,24 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::adc::Adc; +use embassy_time::{Delay, Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let mut adc = Adc::new(p.ADC, &mut Delay); + let mut pin = p.PA1; + + loop { + let v = adc.read(&mut pin); + info!("--> {} - {} mV", v, adc.to_millivolts(v)); + Timer::after(Duration::from_millis(100)).await; + } +} From a0b60966106f96e2915d429319a347e239eb1a5f Mon Sep 17 00:00:00 2001 From: "Matthew W. Samsonoff" Date: Mon, 3 Oct 2022 13:03:42 -0400 Subject: [PATCH 03/12] Put ADC input pin into analog mode --- embassy-stm32/src/adc/v1.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 923a1d97..224d1717 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -236,7 +236,10 @@ impl<'d, T: Instance> Adc<'d, T> { } } - pub fn read(&mut self, pin: &mut impl AdcPin) -> u16 { + pub fn read

(&mut self, pin: &mut P) -> u16 + where + P: AdcPin + crate::gpio::sealed::Pin, + { unsafe { // A.7.2 ADC enable sequence code example if T::regs().isr().read().adrdy() { @@ -252,6 +255,7 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); Self::set_channel_sample_time(pin.channel(), self.sample_time); + pin.set_as_analog(); T::regs() .chselr() .write(|reg| reg.set_chselx(pin.channel() as usize, true)); From 511a95124690be253dc9b789abeae1bdea3aa83a Mon Sep 17 00:00:00 2001 From: "Matthew W. Samsonoff" Date: Mon, 3 Oct 2022 14:18:37 -0400 Subject: [PATCH 04/12] Differentiate between `read` and `read_internal` for STM32F0 ADC The internal channels (vbat, vref, and temperature) are not real pins and do not have the `set_as_analog` method. They must be read using the `read_internal` method. --- embassy-stm32/src/adc/v1.rs | 120 +++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 56 deletions(-) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 224d1717..f787f72a 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -48,25 +48,33 @@ impl Resolution { } } +pub trait InternalChannel: sealed::InternalChannel {} + +mod sealed { + pub trait InternalChannel { + fn channel(&self) -> u8; + } +} + pub struct Vbat; -impl AdcPin for Vbat {} -impl super::sealed::AdcPin for Vbat { +impl InternalChannel for Vbat {} +impl sealed::InternalChannel for Vbat { fn channel(&self) -> u8 { 18 } } pub struct Vref; -impl AdcPin for Vref {} -impl super::sealed::AdcPin for Vref { +impl InternalChannel for Vref {} +impl sealed::InternalChannel for Vref { fn channel(&self) -> u8 { 17 } } pub struct Temperature; -impl AdcPin for Temperature {} -impl super::sealed::AdcPin for Temperature { +impl InternalChannel for Temperature {} +impl sealed::InternalChannel for Temperature { fn channel(&self) -> u8 { 16 } @@ -219,64 +227,64 @@ impl<'d, T: Instance> Adc<'d, T> { ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 } - fn convert(&mut self) -> u16 { - unsafe { - T::regs().isr().modify(|reg| { - reg.set_eoc(true); - reg.set_eosmp(true); - }); - - // A.7.5 Single conversion sequence code example - Software trigger - T::regs().cr().modify(|reg| reg.set_adstart(true)); - while !T::regs().isr().read().eoc() { - // spin - } - - T::regs().dr().read().0 as u16 - } - } - pub fn read

(&mut self, pin: &mut P) -> u16 where P: AdcPin + crate::gpio::sealed::Pin, { + let channel = pin.channel(); unsafe { - // A.7.2 ADC enable sequence code example - if T::regs().isr().read().adrdy() { - T::regs().isr().modify(|reg| reg.set_adrdy(true)); - } - T::regs().cr().modify(|reg| reg.set_aden(true)); - while !T::regs().isr().read().adrdy() { - // ES0233, 2.4.3 ADEN bit cannot be set immediately after the ADC calibration - // Workaround: When the ADC calibration is complete (ADCAL = 0), keep setting the - // ADEN bit until the ADRDY flag goes high. - T::regs().cr().modify(|reg| reg.set_aden(true)); - } - - T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); - Self::set_channel_sample_time(pin.channel(), self.sample_time); pin.set_as_analog(); - T::regs() - .chselr() - .write(|reg| reg.set_chselx(pin.channel() as usize, true)); - - let value = self.convert(); - - // A.7.3 ADC disable code example - T::regs().cr().modify(|reg| reg.set_adstp(true)); - while T::regs().cr().read().adstp() { - // spin - } - T::regs().cr().modify(|reg| reg.set_addis(true)); - while T::regs().cr().read().aden() { - // spin - } - - value + self.read_channel(channel) } } - unsafe fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { - T::regs().smpr().modify(|reg| reg.set_smp(sample_time.sample_time())); + pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 { + let channel = channel.channel(); + unsafe { + self.read_channel(channel) + } + } + + unsafe fn read_channel(&mut self, channel: u8) -> u16 { + // A.7.2 ADC enable sequence code example + if T::regs().isr().read().adrdy() { + T::regs().isr().modify(|reg| reg.set_adrdy(true)); + } + T::regs().cr().modify(|reg| reg.set_aden(true)); + while !T::regs().isr().read().adrdy() { + // ES0233, 2.4.3 ADEN bit cannot be set immediately after the ADC calibration + // Workaround: When the ADC calibration is complete (ADCAL = 0), keep setting the + // ADEN bit until the ADRDY flag goes high. + T::regs().cr().modify(|reg| reg.set_aden(true)); + } + + T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); + T::regs().isr().modify(|reg| { + reg.set_eoc(true); + reg.set_eosmp(true); + }); + + // A.7.5 Single conversion sequence code example - Software trigger + T::regs() + .chselr() + .write(|reg| reg.set_chselx(channel as usize, true)); + T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.sample_time())); + T::regs().cr().modify(|reg| reg.set_adstart(true)); + while !T::regs().isr().read().eoc() { + // spin + } + let value = T::regs().dr().read().0 as u16; + + // A.7.3 ADC disable code example + T::regs().cr().modify(|reg| reg.set_adstp(true)); + while T::regs().cr().read().adstp() { + // spin + } + T::regs().cr().modify(|reg| reg.set_addis(true)); + while T::regs().cr().read().aden() { + // spin + } + + value } } From 28b8ac4b62d952918ddfe143cf6925e1402fc2ce Mon Sep 17 00:00:00 2001 From: "Matthew W. Samsonoff" Date: Mon, 3 Oct 2022 14:23:31 -0400 Subject: [PATCH 05/12] Update STM32F0 ADC example to use `read_internal` --- examples/stm32f0/src/bin/adc.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/stm32f0/src/bin/adc.rs b/examples/stm32f0/src/bin/adc.rs index b69bc3c3..6205596f 100644 --- a/examples/stm32f0/src/bin/adc.rs +++ b/examples/stm32f0/src/bin/adc.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::Adc; +use embassy_stm32::adc::{Adc, SampleTime}; use embassy_time::{Delay, Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -14,11 +14,14 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let mut adc = Adc::new(p.ADC, &mut Delay); + adc.set_sample_time(SampleTime::Cycles71_5); let mut pin = p.PA1; + let mut vref = adc.enable_temperature(&mut Delay); loop { let v = adc.read(&mut pin); - info!("--> {} - {} mV", v, adc.to_millivolts(v)); + let r = adc.read_internal(&mut vref); + info!("--> {} - {} mV / vref = {} - {} mV", v, adc.to_millivolts(v), r, adc.to_millivolts(r)); Timer::after(Duration::from_millis(100)).await; } } From f5881054297713f6224d5d81f407f8a6cf591ef4 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 14:31:32 -0500 Subject: [PATCH 06/12] wip --- embassy-stm32/src/adc/mod.rs | 11 +-- embassy-stm32/src/adc/resolution.rs | 8 +- embassy-stm32/src/adc/sample_time.rs | 2 +- embassy-stm32/src/adc/v1.rs | 129 +++------------------------ 4 files changed, 20 insertions(+), 130 deletions(-) diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index ec49dace..56ecd63c 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -7,21 +7,18 @@ #[cfg_attr(adc_v4, path = "v4.rs")] mod _version; -#[cfg(not(any(adc_f1, adc_v1)))] +#[cfg(not(adc_f1))] mod resolution; -#[cfg(not(adc_v1))] mod sample_time; #[allow(unused)] pub use _version::*; -#[cfg(not(any(adc_f1, adc_v1)))] +#[cfg(not(adc_f1))] pub use resolution::Resolution; -#[cfg(not(adc_v1))] pub use sample_time::SampleTime; use crate::peripherals; -#[cfg(not(adc_v1))] pub struct Adc<'d, T: Instance> { #[allow(unused)] adc: crate::PeripheralRef<'d, T>, @@ -44,9 +41,9 @@ pub(crate) mod sealed { } } -#[cfg(not(any(adc_f1, adc_v2, adc_v4)))] +#[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v4)))] pub trait Instance: sealed::Instance + crate::Peripheral

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

+ crate::rcc::RccPeripheral {} pub trait AdcPin: sealed::AdcPin {} diff --git a/embassy-stm32/src/adc/resolution.rs b/embassy-stm32/src/adc/resolution.rs index 62b52a46..67fb9b8c 100644 --- a/embassy-stm32/src/adc/resolution.rs +++ b/embassy-stm32/src/adc/resolution.rs @@ -1,4 +1,4 @@ -#[cfg(any(adc_v2, adc_v3, adc_g0))] +#[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Resolution { TwelveBit, @@ -19,7 +19,7 @@ pub enum Resolution { impl Default for Resolution { fn default() -> Self { - #[cfg(any(adc_v2, adc_v3, adc_g0))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] { Self::TwelveBit } @@ -40,7 +40,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_v2, adc_v3, adc_g0))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, } } @@ -56,7 +56,7 @@ impl Resolution { Resolution::TwelveBit => (1 << 12) - 1, Resolution::TenBit => (1 << 10) - 1, Resolution::EightBit => (1 << 8) - 1, - #[cfg(any(adc_v2, adc_v3, adc_g0))] + #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] Resolution::SixBit => (1 << 6) - 1, } } diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs index bc5fb1d6..0faa1e3c 100644 --- a/embassy-stm32/src/adc/sample_time.rs +++ b/embassy-stm32/src/adc/sample_time.rs @@ -25,7 +25,7 @@ macro_rules! impl_sample_time { }; } -#[cfg(adc_f1)] +#[cfg(any(adc_f1, adc_v1))] impl_sample_time!( "1.5", Cycles1_5, diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index f787f72a..0fdb9556 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -1,10 +1,8 @@ -use core::marker::PhantomData; - use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; -use crate::adc::{AdcPin, Instance}; -use crate::{pac, Peripheral}; +use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; +use crate::Peripheral; pub const VDDA_CALIB_MV: u32 = 3300; pub const VREF_INT: u32 = 1230; @@ -15,39 +13,6 @@ fn enable() { }); } -pub enum Resolution { - TwelveBit, - TenBit, - EightBit, - SixBit, -} - -impl Default for Resolution { - fn default() -> Self { - Self::TwelveBit - } -} - -impl Resolution { - fn res(&self) -> pac::adc::vals::Res { - match self { - Resolution::TwelveBit => pac::adc::vals::Res::TWELVEBIT, - Resolution::TenBit => pac::adc::vals::Res::TENBIT, - Resolution::EightBit => pac::adc::vals::Res::EIGHTBIT, - Resolution::SixBit => pac::adc::vals::Res::SIXBIT, - } - } - - pub fn to_max_count(&self) -> u32 { - match self { - Resolution::TwelveBit => (1 << 12) - 1, - Resolution::TenBit => (1 << 10) - 1, - Resolution::EightBit => (1 << 8) - 1, - Resolution::SixBit => (1 << 6) - 1, - } - } -} - pub trait InternalChannel: sealed::InternalChannel {} mod sealed { @@ -80,68 +45,9 @@ impl sealed::InternalChannel for Temperature { } } -mod sample_time { - #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] - pub enum SampleTime { - /// 1.5 ADC clock cycles - Cycles1_5 = 0b000, - - /// 7.5 ADC clock cycles - Cycles7_5 = 0b001, - - /// 13.5 ADC clock cycles - Cycles13_5 = 0b010, - - /// 28.5 ADC clock cycles - Cycles28_5 = 0b011, - - /// 41.5 ADC clock cycles - Cycles41_5 = 0b100, - - /// 55.5 ADC clock cycles - Cycles55_5 = 0b101, - - /// 71.5 ADC clock cycles - Cycles71_5 = 0b110, - - /// 239.5 ADC clock cycles - Cycles239_5 = 0b111, - } - - impl SampleTime { - pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::Smp { - match self { - SampleTime::Cycles1_5 => crate::pac::adc::vals::Smp::CYCLES1_5, - SampleTime::Cycles7_5 => crate::pac::adc::vals::Smp::CYCLES7_5, - SampleTime::Cycles13_5 => crate::pac::adc::vals::Smp::CYCLES13_5, - SampleTime::Cycles28_5 => crate::pac::adc::vals::Smp::CYCLES28_5, - SampleTime::Cycles41_5 => crate::pac::adc::vals::Smp::CYCLES41_5, - SampleTime::Cycles55_5 => crate::pac::adc::vals::Smp::CYCLES55_5, - SampleTime::Cycles71_5 => crate::pac::adc::vals::Smp::CYCLES71_5, - SampleTime::Cycles239_5 => crate::pac::adc::vals::Smp::CYCLES239_5, - } - } - } - - impl Default for SampleTime { - fn default() -> Self { - Self::Cycles1_5 - } - } -} - -pub use sample_time::SampleTime; - -pub struct Adc<'d, T: Instance> { - sample_time: SampleTime, - vref_mv: u32, - resolution: Resolution, - phantom: PhantomData<&'d mut T>, -} - impl<'d, T: Instance> Adc<'d, T> { - pub fn new(_peri: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { - into_ref!(_peri); + pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { + into_ref!(adc); enable(); // Delay 1μs when using HSI14 as the ADC clock. @@ -151,10 +57,8 @@ impl<'d, T: Instance> Adc<'d, T> { delay.delay_us(1); let s = Self { + adc, sample_time: Default::default(), - vref_mv: VDDA_CALIB_MV, - resolution: Resolution::default(), - phantom: PhantomData, }; s.calibrate(); s @@ -215,16 +119,10 @@ impl<'d, T: Instance> Adc<'d, T> { self.sample_time = sample_time; } - pub fn set_vref_mv(&mut self, vref_mv: u32) { - self.vref_mv = vref_mv; - } - pub fn set_resolution(&mut self, resolution: Resolution) { - self.resolution = resolution; - } - - pub fn to_millivolts(&self, sample: u16) -> u16 { - ((u32::from(sample) * self.vref_mv) / self.resolution.to_max_count()) as u16 + unsafe { + T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); + } } pub fn read

(&mut self, pin: &mut P) -> u16 @@ -240,9 +138,7 @@ impl<'d, T: Instance> Adc<'d, T> { pub fn read_internal(&mut self, channel: &mut impl InternalChannel) -> u16 { let channel = channel.channel(); - unsafe { - self.read_channel(channel) - } + unsafe { self.read_channel(channel) } } unsafe fn read_channel(&mut self, channel: u8) -> u16 { @@ -258,17 +154,14 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().cr().modify(|reg| reg.set_aden(true)); } - T::regs().cfgr1().modify(|reg| reg.set_res(self.resolution.res())); T::regs().isr().modify(|reg| { reg.set_eoc(true); reg.set_eosmp(true); }); // A.7.5 Single conversion sequence code example - Software trigger - T::regs() - .chselr() - .write(|reg| reg.set_chselx(channel as usize, true)); - T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.sample_time())); + T::regs().chselr().write(|reg| reg.set_chselx(channel as usize, true)); + T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.into())); T::regs().cr().modify(|reg| reg.set_adstart(true)); while !T::regs().isr().read().eoc() { // spin From efd9e18321c258a188fa675804d59346a4c11cc2 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 15:12:27 -0500 Subject: [PATCH 07/12] Fix example --- examples/stm32f0/src/bin/adc.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/examples/stm32f0/src/bin/adc.rs b/examples/stm32f0/src/bin/adc.rs index 6205596f..c639299d 100644 --- a/examples/stm32f0/src/bin/adc.rs +++ b/examples/stm32f0/src/bin/adc.rs @@ -16,12 +16,21 @@ async fn main(_spawner: Spawner) { let mut adc = Adc::new(p.ADC, &mut Delay); adc.set_sample_time(SampleTime::Cycles71_5); let mut pin = p.PA1; - let mut vref = adc.enable_temperature(&mut Delay); + + let mut vrefint = adc.enable_vref(&mut Delay); + let vrefint_sample = adc.read_internal(&mut vrefint); + let convert_to_millivolts = |sample| { + // FIXME: use proper datasheet and value + // From http://www.st.com/resource/en/datasheet/CD00161566.pdf + // 5.3.4 Embedded reference voltage + const VREFINT_MV: u32 = 1200; // mV + + (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 + }; loop { let v = adc.read(&mut pin); - let r = adc.read_internal(&mut vref); - info!("--> {} - {} mV / vref = {} - {} mV", v, adc.to_millivolts(v), r, adc.to_millivolts(r)); + info!("--> {} - {} mV", v, convert_to_millivolts(v)); Timer::after(Duration::from_millis(100)).await; } } From 37d8f2e51256403cc4839c3b45e5c2253e3a09f1 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 15:28:42 -0500 Subject: [PATCH 08/12] Properly enable and reset adc --- embassy-stm32/src/adc/v1.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 0fdb9556..a699d8f7 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -7,12 +7,6 @@ use crate::Peripheral; pub const VDDA_CALIB_MV: u32 = 3300; pub const VREF_INT: u32 = 1230; -fn enable() { - critical_section::with(|_| unsafe { - crate::pac::RCC.apb2enr().modify(|reg| reg.set_adcen(true)); - }); -} - pub trait InternalChannel: sealed::InternalChannel {} mod sealed { @@ -48,7 +42,8 @@ impl sealed::InternalChannel for Temperature { impl<'d, T: Instance> Adc<'d, T> { pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { into_ref!(adc); - enable(); + T::enable(); + T::reset(); // Delay 1μs when using HSI14 as the ADC clock. // From 20e7b5e2965f718aa1a0ece6009356ba50d2d780 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 16:11:21 -0500 Subject: [PATCH 09/12] InternalChannel --- embassy-stm32/src/adc/v1.rs | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index a699d8f7..5f0f8c14 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -1,39 +1,32 @@ use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; -use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; +use crate::adc::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; use crate::Peripheral; +use crate::peripherals::ADC1; pub const VDDA_CALIB_MV: u32 = 3300; pub const VREF_INT: u32 = 1230; -pub trait InternalChannel: sealed::InternalChannel {} - -mod sealed { - pub trait InternalChannel { - fn channel(&self) -> u8; - } -} - pub struct Vbat; -impl InternalChannel for Vbat {} -impl sealed::InternalChannel for Vbat { +impl InternalChannel for Vbat {} +impl super::sealed::InternalChannel for Vbat { fn channel(&self) -> u8 { 18 } } pub struct Vref; -impl InternalChannel for Vref {} -impl sealed::InternalChannel for Vref { +impl InternalChannel for Vref {} +impl super::sealed::::InternalChannel for Vref { fn channel(&self) -> u8 { 17 } } pub struct Temperature; -impl InternalChannel for Temperature {} -impl sealed::InternalChannel for Temperature { +impl InternalChannel for Temperature {} +impl super::sealed::InternalChannel for Temperature { fn channel(&self) -> u8 { 16 } From 7c53ebd5766fc75dbb5ddd1953b5bff13f1cd853 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 16:28:28 -0500 Subject: [PATCH 10/12] Fix example reference voltage --- examples/stm32f0/src/bin/adc.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/stm32f0/src/bin/adc.rs b/examples/stm32f0/src/bin/adc.rs index c639299d..8ed9f98f 100644 --- a/examples/stm32f0/src/bin/adc.rs +++ b/examples/stm32f0/src/bin/adc.rs @@ -20,10 +20,9 @@ async fn main(_spawner: Spawner) { let mut vrefint = adc.enable_vref(&mut Delay); let vrefint_sample = adc.read_internal(&mut vrefint); let convert_to_millivolts = |sample| { - // FIXME: use proper datasheet and value - // From http://www.st.com/resource/en/datasheet/CD00161566.pdf - // 5.3.4 Embedded reference voltage - const VREFINT_MV: u32 = 1200; // mV + // From https://www.st.com/resource/en/datasheet/stm32f031c6.pdf + // 6.3.4 Embedded reference voltage + const VREFINT_MV: u32 = 1230; // mV (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 }; From 92e96bd601e8e663114b1efccc4a1e8d309332d9 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 16:38:06 -0500 Subject: [PATCH 11/12] Fix typo --- embassy-stm32/src/adc/v1.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 5f0f8c14..17114732 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -2,8 +2,8 @@ use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use crate::adc::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; -use crate::Peripheral; use crate::peripherals::ADC1; +use crate::Peripheral; pub const VDDA_CALIB_MV: u32 = 3300; pub const VREF_INT: u32 = 1230; @@ -18,7 +18,7 @@ impl super::sealed::InternalChannel for Vbat { pub struct Vref; impl InternalChannel for Vref {} -impl super::sealed::::InternalChannel for Vref { +impl super::sealed::InternalChannel for Vref { fn channel(&self) -> u8 { 17 } From 0ef419bee4afc5a984cab3d5f16e1f1382fa6bbf Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 5 Apr 2023 16:52:32 -0500 Subject: [PATCH 12/12] Change ADC1 to ADC --- embassy-stm32/src/adc/v1.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 17114732..82a8c3ef 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -2,31 +2,31 @@ use embassy_hal_common::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use crate::adc::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; -use crate::peripherals::ADC1; +use crate::peripherals::ADC; use crate::Peripheral; pub const VDDA_CALIB_MV: u32 = 3300; pub const VREF_INT: u32 = 1230; pub struct Vbat; -impl InternalChannel for Vbat {} -impl super::sealed::InternalChannel for Vbat { +impl InternalChannel for Vbat {} +impl super::sealed::InternalChannel for Vbat { fn channel(&self) -> u8 { 18 } } pub struct Vref; -impl InternalChannel for Vref {} -impl super::sealed::InternalChannel for Vref { +impl InternalChannel for Vref {} +impl super::sealed::InternalChannel for Vref { fn channel(&self) -> u8 { 17 } } pub struct Temperature; -impl InternalChannel for Temperature {} -impl super::sealed::InternalChannel for Temperature { +impl InternalChannel for Temperature {} +impl super::sealed::InternalChannel for Temperature { fn channel(&self) -> u8 { 16 }