From e2e0464d04ba2fa16ec5217dd651dae157279efb Mon Sep 17 00:00:00 2001 From: Ben Gamari Date: Mon, 30 Aug 2021 15:33:42 -0400 Subject: [PATCH 1/7] stm32/adc: Factor out conversion logic Also guard errata workaround correctly. --- embassy-stm32/src/adc/v3.rs | 61 ++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 2781fc4d..d80c9ffc 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -229,6 +229,27 @@ impl<'d, T: Instance> Adc<'d, T> { } */ + /// Perform a single conversion. + fn convert(&mut self) -> u16 { + unsafe { + T::regs().isr().modify(|reg| { + reg.set_eos(true); + reg.set_eoc(true); + }); + + // Start conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); + + while !T::regs().isr().read().eos() { + // spin + } + + T::regs().dr().read().0 as u16 + } + } + pub fn read(&mut self, pin: &mut impl AdcPin) -> u16 { unsafe { // Make sure bits are off @@ -259,38 +280,16 @@ impl<'d, T: Instance> Adc<'d, T> { // Select channel T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel())); - // Start conversion - T::regs().isr().modify(|reg| { - reg.set_eos(true); - reg.set_eoc(true); - }); - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); + // Some models are affected by an erratum: + // If we perform conversions slower than 1 kHz, the first read ADC value can be + // corrupted, so we discard it and measure again. + // + // STM32L471xx: Section 2.7.3 + // STM32G4: Section 2.7.3 + #[cfg(any(rcc_l4, rcc_g4))] + let _ = self.convert(); - while !T::regs().isr().read().eos() { - // spin - } - - // Read ADC value first time and discard it, as per errata sheet. - // The errata states 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 _ = T::regs().dr().read(); - - T::regs().isr().modify(|reg| { - reg.set_eos(true); - reg.set_eoc(true); - }); - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); - - while !T::regs().isr().read().eos() { - // spin - } - - let val = T::regs().dr().read().0 as u16; + let val = self.convert(); T::regs().cr().modify(|reg| reg.set_addis(true)); From aa4069fe1032c68977bea9c006a3be83304d8427 Mon Sep 17 00:00:00 2001 From: Ben Gamari Date: Mon, 30 Aug 2021 15:34:37 -0400 Subject: [PATCH 2/7] stm32/adc: Fix ADC support for STM32G0 --- embassy-stm32/src/adc/g0.rs | 1 - embassy-stm32/src/adc/mod.rs | 2 +- embassy-stm32/src/adc/v3.rs | 177 +++++++++++++++++++++++++++-------- 3 files changed, 139 insertions(+), 41 deletions(-) delete mode 100644 embassy-stm32/src/adc/g0.rs diff --git a/embassy-stm32/src/adc/g0.rs b/embassy-stm32/src/adc/g0.rs deleted file mode 100644 index 8b137891..00000000 --- a/embassy-stm32/src/adc/g0.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 9955502a..af98d177 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -1,7 +1,7 @@ #![macro_use] #[cfg_attr(adc_v3, path = "v3.rs")] -#[cfg_attr(adc_g0, path = "g0.rs")] +#[cfg_attr(adc_g0, path = "v3.rs")] mod _version; #[allow(unused)] diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index d80c9ffc..5fa5b9bc 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -43,7 +43,11 @@ pub struct Vref; impl AdcPin for Vref {} impl super::sealed::AdcPin for Vref { fn channel(&self) -> u8 { - 0 + #[cfg(not(rcc_g0))] + let val = 0; + #[cfg(rcc_g0)] + let val = 13; + val } } @@ -51,7 +55,11 @@ pub struct Temperature; impl AdcPin for Temperature {} impl super::sealed::AdcPin for Temperature { fn channel(&self) -> u8 { - 17 + #[cfg(not(rcc_g0))] + let val = 17; + #[cfg(rcc_g0)] + let val = 12; + val } } @@ -59,61 +67,124 @@ pub struct Vbat; impl AdcPin for Vbat {} impl super::sealed::AdcPin for Vbat { fn channel(&self) -> u8 { - 18 + #[cfg(not(rcc_g0))] + let val = 18; + #[cfg(rcc_g0)] + let val = 14; + val } } -/// ADC sample time -/// -/// The default setting is 2.5 ADC clock cycles. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] -pub enum SampleTime { - /// 2.5 ADC clock cycles - Cycles2_5 = 0b000, +#[cfg(not(adc_g0))] +mod sample_time { + /// ADC sample time + /// + /// The default setting is 2.5 ADC clock cycles. + #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] + pub enum SampleTime { + /// 2.5 ADC clock cycles + Cycles2_5 = 0b000, - /// 6.5 ADC clock cycles - Cycles6_5 = 0b001, + /// 6.5 ADC clock cycles + Cycles6_5 = 0b001, - /// 12.5 ADC clock cycles - Cycles12_5 = 0b010, + /// 12.5 ADC clock cycles + Cycles12_5 = 0b010, - /// 24.5 ADC clock cycles - Cycles24_5 = 0b011, + /// 24.5 ADC clock cycles + Cycles24_5 = 0b011, - /// 47.5 ADC clock cycles - Cycles47_5 = 0b100, + /// 47.5 ADC clock cycles + Cycles47_5 = 0b100, - /// 92.5 ADC clock cycles - Cycles92_5 = 0b101, + /// 92.5 ADC clock cycles + Cycles92_5 = 0b101, - /// 247.5 ADC clock cycles - Cycles247_5 = 0b110, + /// 247.5 ADC clock cycles + Cycles247_5 = 0b110, - /// 640.5 ADC clock cycles - Cycles640_5 = 0b111, -} + /// 640.5 ADC clock cycles + Cycles640_5 = 0b111, + } -impl SampleTime { - fn sample_time(&self) -> crate::pac::adc::vals::SampleTime { - match self { - SampleTime::Cycles2_5 => crate::pac::adc::vals::SampleTime::CYCLES2_5, - SampleTime::Cycles6_5 => crate::pac::adc::vals::SampleTime::CYCLES6_5, - SampleTime::Cycles12_5 => crate::pac::adc::vals::SampleTime::CYCLES12_5, - SampleTime::Cycles24_5 => crate::pac::adc::vals::SampleTime::CYCLES24_5, - SampleTime::Cycles47_5 => crate::pac::adc::vals::SampleTime::CYCLES47_5, - SampleTime::Cycles92_5 => crate::pac::adc::vals::SampleTime::CYCLES92_5, - SampleTime::Cycles247_5 => crate::pac::adc::vals::SampleTime::CYCLES247_5, - SampleTime::Cycles640_5 => crate::pac::adc::vals::SampleTime::CYCLES640_5, + impl SampleTime { + pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::SampleTime { + match self { + SampleTime::Cycles2_5 => crate::pac::adc::vals::SampleTime::CYCLES2_5, + SampleTime::Cycles6_5 => crate::pac::adc::vals::SampleTime::CYCLES6_5, + SampleTime::Cycles12_5 => crate::pac::adc::vals::SampleTime::CYCLES12_5, + SampleTime::Cycles24_5 => crate::pac::adc::vals::SampleTime::CYCLES24_5, + SampleTime::Cycles47_5 => crate::pac::adc::vals::SampleTime::CYCLES47_5, + SampleTime::Cycles92_5 => crate::pac::adc::vals::SampleTime::CYCLES92_5, + SampleTime::Cycles247_5 => crate::pac::adc::vals::SampleTime::CYCLES247_5, + SampleTime::Cycles640_5 => crate::pac::adc::vals::SampleTime::CYCLES640_5, + } + } + } + + impl Default for SampleTime { + fn default() -> Self { + Self::Cycles2_5 } } } -impl Default for SampleTime { - fn default() -> Self { - Self::Cycles2_5 +#[cfg(adc_g0)] +mod sample_time { + /// ADC sample time + /// + /// The default setting is 1.5 ADC clock cycles. + #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] + pub enum SampleTime { + /// 1.5 ADC clock cycles + Cycles1_5 = 0b000, + + /// 3.5 ADC clock cycles + Cycles3_5 = 0b001, + + /// 7.5 ADC clock cycles + Cycles7_5 = 0b010, + + /// 12.5 ADC clock cycles + Cycles12_5 = 0b011, + + /// 19.5 ADC clock cycles + Cycles19_5 = 0b100, + + /// 39.5 ADC clock cycles + Cycles39_5 = 0b101, + + /// 79.5 ADC clock cycles + Cycles79_5 = 0b110, + + /// 160.5 ADC clock cycles + Cycles160_5 = 0b111, + } + + impl SampleTime { + pub(crate) fn sample_time(&self) -> crate::pac::adc::vals::SampleTime { + match self { + SampleTime::Cycles1_5 => crate::pac::adc::vals::SampleTime::CYCLES1_5, + SampleTime::Cycles3_5 => crate::pac::adc::vals::SampleTime::CYCLES3_5, + SampleTime::Cycles7_5 => crate::pac::adc::vals::SampleTime::CYCLES7_5, + SampleTime::Cycles12_5 => crate::pac::adc::vals::SampleTime::CYCLES12_5, + SampleTime::Cycles19_5 => crate::pac::adc::vals::SampleTime::CYCLES19_5, + SampleTime::Cycles39_5 => crate::pac::adc::vals::SampleTime::CYCLES39_5, + SampleTime::Cycles79_5 => crate::pac::adc::vals::SampleTime::CYCLES79_5, + SampleTime::Cycles160_5 => crate::pac::adc::vals::SampleTime::CYCLES160_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, calibrated_vdda: u32, @@ -126,14 +197,24 @@ impl<'d, T: Instance> Adc<'d, T> { unborrow!(_peri); unsafe { T::regs().cr().modify(|reg| { + #[cfg(not(adc_g0))] reg.set_deeppwd(false); reg.set_advregen(true); }); + + #[cfg(adc_g0)] + T::regs().cfgr1().modify(|reg| { + reg.set_chselrmod(true); + }); } delay.delay_us(20); unsafe { + T::regs().cr().modify(|reg| { + reg.set_adcal(true); + }); + while T::regs().cr().read().adcal() { // spin } @@ -270,15 +351,25 @@ impl<'d, T: Instance> Adc<'d, T> { } // Configure ADC + #[cfg(not(rcc_g0))] T::regs() .cfgr() .modify(|reg| reg.set_res(self.resolution.res())); + #[cfg(rcc_g0)] + T::regs() + .cfgr1() + .modify(|reg| reg.set_res(self.resolution.res())); // Configure channel Self::set_channel_sample_time(pin.channel(), self.sample_time); // Select channel + #[cfg(not(rcc_g0))] T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel())); + #[cfg(rcc_g0)] + T::regs() + .chselr() + .write(|reg| reg.set_chsel(pin.channel() as u32)); // Some models are affected by an erratum: // If we perform conversions slower than 1 kHz, the first read ADC value can be @@ -297,6 +388,14 @@ impl<'d, T: Instance> Adc<'d, T> { } } + #[cfg(rcc_g0)] + unsafe fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { + T::regs() + .smpr() + .modify(|reg| reg.set_smp1(sample_time.sample_time())); + } + + #[cfg(not(rcc_g0))] unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { if ch <= 9 { T::regs() From 794798e225cbe9cf690938aaf802ad258dc996e2 Mon Sep 17 00:00:00 2001 From: Ben Gamari Date: Tue, 31 Aug 2021 01:48:22 -0400 Subject: [PATCH 3/7] stm32g0: Add support for HSI divider --- embassy-stm32/src/rcc/g0/mod.rs | 42 +++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/rcc/g0/mod.rs b/embassy-stm32/src/rcc/g0/mod.rs index 863db709..74577917 100644 --- a/embassy-stm32/src/rcc/g0/mod.rs +++ b/embassy-stm32/src/rcc/g0/mod.rs @@ -18,10 +18,38 @@ pub const LSI_FREQ: u32 = 32_000; #[derive(Clone, Copy)] pub enum ClockSrc { HSE(Hertz), - HSI16, + HSI16(HSI16Prescaler), LSI, } +#[derive(Clone, Copy)] +pub enum HSI16Prescaler { + NotDivided, + Div2, + Div4, + Div8, + Div16, + Div32, + Div64, + Div128, +} + +impl Into for HSI16Prescaler { + fn into(self) -> u8 { + match self { + HSI16Prescaler::NotDivided => 0x00, + HSI16Prescaler::Div2 => 0x01, + HSI16Prescaler::Div4 => 0x02, + HSI16Prescaler::Div8 => 0x03, + HSI16Prescaler::Div16 => 0x04, + HSI16Prescaler::Div32 => 0x05, + HSI16Prescaler::Div64 => 0x06, + HSI16Prescaler::Div128 => 0x07, + } + } +} + + impl Into for APBPrescaler { fn into(self) -> u8 { match self { @@ -61,7 +89,7 @@ impl Default for Config { #[inline] fn default() -> Config { Config { - mux: ClockSrc::HSI16, + mux: ClockSrc::HSI16(HSI16Prescaler::NotDivided), ahb_pre: AHBPrescaler::NotDivided, apb_pre: APBPrescaler::NotDivided, } @@ -119,14 +147,18 @@ impl RccExt for RCC { fn freeze(self, cfgr: Config) -> Clocks { let rcc = pac::RCC; let (sys_clk, sw) = match cfgr.mux { - ClockSrc::HSI16 => { + ClockSrc::HSI16(div) => { // Enable HSI16 + let div: u8 = div.into(); unsafe { - rcc.cr().write(|w| w.set_hsion(true)); + rcc.cr().write(|w| { + w.set_hsidiv(div); + w.set_hsion(true) + }); while !rcc.cr().read().hsirdy() {} } - (HSI_FREQ, 0x00) + (HSI_FREQ >> div, 0x00) } ClockSrc::HSE(freq) => { // Enable HSE From 573e6ec373c92e1c85f9d84b3b68f36f1e4d0dc9 Mon Sep 17 00:00:00 2001 From: Ben Gamari Date: Tue, 31 Aug 2021 01:51:49 -0400 Subject: [PATCH 4/7] stm32g0: Add support for low-power run --- embassy-stm32/src/rcc/g0/mod.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/g0/mod.rs b/embassy-stm32/src/rcc/g0/mod.rs index 74577917..c0b5b14e 100644 --- a/embassy-stm32/src/rcc/g0/mod.rs +++ b/embassy-stm32/src/rcc/g0/mod.rs @@ -49,7 +49,6 @@ impl Into for HSI16Prescaler { } } - impl Into for APBPrescaler { fn into(self) -> u8 { match self { @@ -83,6 +82,7 @@ pub struct Config { mux: ClockSrc, ahb_pre: AHBPrescaler, apb_pre: APBPrescaler, + low_power_run: bool, } impl Default for Config { @@ -92,6 +92,7 @@ impl Default for Config { mux: ClockSrc::HSI16(HSI16Prescaler::NotDivided), ahb_pre: AHBPrescaler::NotDivided, apb_pre: APBPrescaler::NotDivided, + low_power_run: false, } } } @@ -114,6 +115,12 @@ impl Config { self.apb_pre = pre; self } + + #[inline] + pub fn low_power_run(mut self, on: bool) -> Self { + self.low_power_run = on; + self + } } /// RCC peripheral @@ -206,6 +213,14 @@ impl RccExt for RCC { } }; + let pwr = pac::PWR; + if cfgr.low_power_run { + assert!(sys_clk.hz() <= 2_000_000.hz()); + unsafe { + pwr.cr1().modify(|w| w.set_lpr(true)); + } + } + Clocks { sys: sys_clk.hz(), ahb: ahb_freq.hz(), From 0b9961584b366e6d3cc83a218c8150800793cb1d Mon Sep 17 00:00:00 2001 From: Ben Gamari Date: Tue, 28 Sep 2021 20:29:46 -0400 Subject: [PATCH 5/7] stm32/adc: Ensure that clock is enabled Sadly due to the inconsistency in clocking configuration across devices we cannot use RccPeripheral. --- embassy-stm32/src/adc/v3.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 5fa5b9bc..ddf06dea 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -6,6 +6,17 @@ use embedded_hal::blocking::delay::DelayUs; pub const VDDA_CALIB_MV: u32 = 3000; +/// Sadly we cannot use `RccPeripheral::enable` since devices are quite inconsistent ADC clock +/// configuration. +unsafe fn enable() { + #[cfg(rcc_h7)] + crate::pac::RCC.apb2enr().modify(|w| w.set_adcen(true)); + #[cfg(rcc_g0)] + crate::pac::RCC.apbenr2().modify(|w| w.set_adcen(true)); + #[cfg(rcc_l4)] + crate::pac::RCC.ahb2enr().modify(|w| w.set_adcen(true)); +} + pub enum Resolution { TwelveBit, TenBit, @@ -196,6 +207,7 @@ impl<'d, T: Instance> Adc<'d, T> { pub fn new(_peri: impl Unborrow + 'd, delay: &mut impl DelayUs) -> Self { unborrow!(_peri); unsafe { + enable(); T::regs().cr().modify(|reg| { #[cfg(not(adc_g0))] reg.set_deeppwd(false); From 5a38cc214079aed4c8778bd210c1ece2fe64fdf7 Mon Sep 17 00:00:00 2001 From: Ben Gamari Date: Tue, 28 Sep 2021 20:56:19 -0400 Subject: [PATCH 6/7] stm32/dac: Ensure that clock is enabled --- embassy-stm32/src/dac/v2.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/embassy-stm32/src/dac/v2.rs b/embassy-stm32/src/dac/v2.rs index f46145b8..55a881d8 100644 --- a/embassy-stm32/src/dac/v2.rs +++ b/embassy-stm32/src/dac/v2.rs @@ -5,6 +5,17 @@ use core::marker::PhantomData; use embassy::util::Unborrow; use embassy_hal_common::unborrow; +/// Sadly we cannot use `RccPeripheral::enable` since devices are quite inconsistent DAC clock +/// configuration. +unsafe fn enable() { + #[cfg(rcc_h7)] + crate::pac::RCC.apb1lenr().modify(|w| w.set_dac12en(true)); + #[cfg(rcc_g0)] + crate::pac::RCC.apbenr1().modify(|w| w.set_dac1en(true)); + #[cfg(rcc_l4)] + crate::pac::RCC.apb1enr1().modify(|w| w.set_dac1en(true)); +} + #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { @@ -91,6 +102,10 @@ impl<'d, T: Instance> Dac<'d, T> { ) -> Self { unborrow!(ch1, ch2); + unsafe { + enable(); + } + let ch1 = ch1.degrade_optional(); if ch1.is_some() { unsafe { From 006bbea51afeeb01fac8c7adb3ae0ff549d4ecf0 Mon Sep 17 00:00:00 2001 From: Ben Gamari Date: Wed, 29 Sep 2021 00:31:42 -0400 Subject: [PATCH 7/7] stm32/adc: Add IN0 channel --- embassy-stm32/src/adc/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index af98d177..f292d020 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -72,6 +72,9 @@ macro_rules! impl_pin { } crate::pac::peripheral_pins!( + ($inst:ident, adc, ADC, $pin:ident, IN0) => { + impl_pin!($inst, $pin, 0); + }; ($inst:ident, adc, ADC, $pin:ident, IN1) => { impl_pin!($inst, $pin, 1); };