diff options
| -rw-r--r-- | embassy-stm32/src/adc/mod.rs | 11 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/resolution.rs | 8 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/sample_time.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/v1.rs | 170 | ||||
| -rw-r--r-- | examples/stm32f0/src/bin/adc.rs | 35 |
5 files changed, 214 insertions, 12 deletions
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index ec49dace7..56ecd63ca 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs | |||
| @@ -7,21 +7,18 @@ | |||
| 7 | #[cfg_attr(adc_v4, path = "v4.rs")] | 7 | #[cfg_attr(adc_v4, path = "v4.rs")] |
| 8 | mod _version; | 8 | mod _version; |
| 9 | 9 | ||
| 10 | #[cfg(not(any(adc_f1, adc_v1)))] | 10 | #[cfg(not(adc_f1))] |
| 11 | mod resolution; | 11 | mod resolution; |
| 12 | #[cfg(not(adc_v1))] | ||
| 13 | mod sample_time; | 12 | mod sample_time; |
| 14 | 13 | ||
| 15 | #[allow(unused)] | 14 | #[allow(unused)] |
| 16 | pub use _version::*; | 15 | pub use _version::*; |
| 17 | #[cfg(not(any(adc_f1, adc_v1)))] | 16 | #[cfg(not(adc_f1))] |
| 18 | pub use resolution::Resolution; | 17 | pub use resolution::Resolution; |
| 19 | #[cfg(not(adc_v1))] | ||
| 20 | pub use sample_time::SampleTime; | 18 | pub use sample_time::SampleTime; |
| 21 | 19 | ||
| 22 | use crate::peripherals; | 20 | use crate::peripherals; |
| 23 | 21 | ||
| 24 | #[cfg(not(adc_v1))] | ||
| 25 | pub struct Adc<'d, T: Instance> { | 22 | pub struct Adc<'d, T: Instance> { |
| 26 | #[allow(unused)] | 23 | #[allow(unused)] |
| 27 | adc: crate::PeripheralRef<'d, T>, | 24 | adc: crate::PeripheralRef<'d, T>, |
| @@ -44,9 +41,9 @@ pub(crate) mod sealed { | |||
| 44 | } | 41 | } |
| 45 | } | 42 | } |
| 46 | 43 | ||
| 47 | #[cfg(not(any(adc_f1, adc_v2, adc_v4)))] | 44 | #[cfg(not(any(adc_f1, adc_v1, adc_v2, adc_v4)))] |
| 48 | pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {} | 45 | pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> {} |
| 49 | #[cfg(any(adc_f1, adc_v2, adc_v4))] | 46 | #[cfg(any(adc_f1, adc_v1, adc_v2, adc_v4))] |
| 50 | pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {} | 47 | pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {} |
| 51 | 48 | ||
| 52 | pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {} | 49 | pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {} |
diff --git a/embassy-stm32/src/adc/resolution.rs b/embassy-stm32/src/adc/resolution.rs index 62b52a46c..67fb9b8c0 100644 --- a/embassy-stm32/src/adc/resolution.rs +++ b/embassy-stm32/src/adc/resolution.rs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | #[cfg(any(adc_v2, adc_v3, adc_g0))] | 1 | #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] |
| 2 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] | 2 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] |
| 3 | pub enum Resolution { | 3 | pub enum Resolution { |
| 4 | TwelveBit, | 4 | TwelveBit, |
| @@ -19,7 +19,7 @@ pub enum Resolution { | |||
| 19 | 19 | ||
| 20 | impl Default for Resolution { | 20 | impl Default for Resolution { |
| 21 | fn default() -> Self { | 21 | fn default() -> Self { |
| 22 | #[cfg(any(adc_v2, adc_v3, adc_g0))] | 22 | #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] |
| 23 | { | 23 | { |
| 24 | Self::TwelveBit | 24 | Self::TwelveBit |
| 25 | } | 25 | } |
| @@ -40,7 +40,7 @@ impl From<Resolution> for crate::pac::adc::vals::Res { | |||
| 40 | Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT, | 40 | Resolution::TwelveBit => crate::pac::adc::vals::Res::TWELVEBIT, |
| 41 | Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT, | 41 | Resolution::TenBit => crate::pac::adc::vals::Res::TENBIT, |
| 42 | Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT, | 42 | Resolution::EightBit => crate::pac::adc::vals::Res::EIGHTBIT, |
| 43 | #[cfg(any(adc_v2, adc_v3, adc_g0))] | 43 | #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] |
| 44 | Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, | 44 | Resolution::SixBit => crate::pac::adc::vals::Res::SIXBIT, |
| 45 | } | 45 | } |
| 46 | } | 46 | } |
| @@ -56,7 +56,7 @@ impl Resolution { | |||
| 56 | Resolution::TwelveBit => (1 << 12) - 1, | 56 | Resolution::TwelveBit => (1 << 12) - 1, |
| 57 | Resolution::TenBit => (1 << 10) - 1, | 57 | Resolution::TenBit => (1 << 10) - 1, |
| 58 | Resolution::EightBit => (1 << 8) - 1, | 58 | Resolution::EightBit => (1 << 8) - 1, |
| 59 | #[cfg(any(adc_v2, adc_v3, adc_g0))] | 59 | #[cfg(any(adc_v1, adc_v2, adc_v3, adc_g0))] |
| 60 | Resolution::SixBit => (1 << 6) - 1, | 60 | Resolution::SixBit => (1 << 6) - 1, |
| 61 | } | 61 | } |
| 62 | } | 62 | } |
diff --git a/embassy-stm32/src/adc/sample_time.rs b/embassy-stm32/src/adc/sample_time.rs index bc5fb1d6f..0faa1e3c0 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 { | |||
| 25 | }; | 25 | }; |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | #[cfg(adc_f1)] | 28 | #[cfg(any(adc_f1, adc_v1))] |
| 29 | impl_sample_time!( | 29 | impl_sample_time!( |
| 30 | "1.5", | 30 | "1.5", |
| 31 | Cycles1_5, | 31 | Cycles1_5, |
diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 8b1378917..82a8c3efb 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs | |||
| @@ -1 +1,171 @@ | |||
| 1 | use embassy_hal_common::into_ref; | ||
| 2 | use embedded_hal_02::blocking::delay::DelayUs; | ||
| 1 | 3 | ||
| 4 | use crate::adc::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; | ||
| 5 | use crate::peripherals::ADC; | ||
| 6 | use crate::Peripheral; | ||
| 7 | |||
| 8 | pub const VDDA_CALIB_MV: u32 = 3300; | ||
| 9 | pub const VREF_INT: u32 = 1230; | ||
| 10 | |||
| 11 | pub struct Vbat; | ||
| 12 | impl InternalChannel<ADC> for Vbat {} | ||
| 13 | impl super::sealed::InternalChannel<ADC> for Vbat { | ||
| 14 | fn channel(&self) -> u8 { | ||
| 15 | 18 | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 19 | pub struct Vref; | ||
| 20 | impl InternalChannel<ADC> for Vref {} | ||
| 21 | impl super::sealed::InternalChannel<ADC> for Vref { | ||
| 22 | fn channel(&self) -> u8 { | ||
| 23 | 17 | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | pub struct Temperature; | ||
| 28 | impl InternalChannel<ADC> for Temperature {} | ||
| 29 | impl super::sealed::InternalChannel<ADC> for Temperature { | ||
| 30 | fn channel(&self) -> u8 { | ||
| 31 | 16 | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | impl<'d, T: Instance> Adc<'d, T> { | ||
| 36 | pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self { | ||
| 37 | into_ref!(adc); | ||
| 38 | T::enable(); | ||
| 39 | T::reset(); | ||
| 40 | |||
| 41 | // Delay 1μs when using HSI14 as the ADC clock. | ||
| 42 | // | ||
| 43 | // Table 57. ADC characteristics | ||
| 44 | // tstab = 14 * 1/fadc | ||
| 45 | delay.delay_us(1); | ||
| 46 | |||
| 47 | let s = Self { | ||
| 48 | adc, | ||
| 49 | sample_time: Default::default(), | ||
| 50 | }; | ||
| 51 | s.calibrate(); | ||
| 52 | s | ||
| 53 | } | ||
| 54 | |||
| 55 | pub fn enable_vbat(&self, _delay: &mut impl DelayUs<u32>) -> Vbat { | ||
| 56 | // SMP must be ≥ 56 ADC clock cycles when using HSI14. | ||
| 57 | // | ||
| 58 | // 6.3.20 Vbat monitoring characteristics | ||
| 59 | // ts_vbat ≥ 4μs | ||
| 60 | unsafe { | ||
| 61 | T::regs().ccr().modify(|reg| reg.set_vbaten(true)); | ||
| 62 | } | ||
| 63 | Vbat | ||
| 64 | } | ||
| 65 | |||
| 66 | pub fn enable_vref(&self, delay: &mut impl DelayUs<u32>) -> Vref { | ||
| 67 | // Table 28. Embedded internal reference voltage | ||
| 68 | // tstart = 10μs | ||
| 69 | unsafe { | ||
| 70 | T::regs().ccr().modify(|reg| reg.set_vrefen(true)); | ||
| 71 | } | ||
| 72 | delay.delay_us(10); | ||
| 73 | Vref | ||
| 74 | } | ||
| 75 | |||
| 76 | pub fn enable_temperature(&self, delay: &mut impl DelayUs<u32>) -> Temperature { | ||
| 77 | // SMP must be ≥ 56 ADC clock cycles when using HSI14. | ||
| 78 | // | ||
| 79 | // 6.3.19 Temperature sensor characteristics | ||
| 80 | // tstart ≤ 10μs | ||
| 81 | // ts_temp ≥ 4μs | ||
| 82 | unsafe { | ||
| 83 | T::regs().ccr().modify(|reg| reg.set_tsen(true)); | ||
| 84 | } | ||
| 85 | delay.delay_us(10); | ||
| 86 | Temperature | ||
| 87 | } | ||
| 88 | |||
| 89 | fn calibrate(&self) { | ||
| 90 | unsafe { | ||
| 91 | // A.7.1 ADC calibration code example | ||
| 92 | if T::regs().cr().read().aden() { | ||
| 93 | T::regs().cr().modify(|reg| reg.set_addis(true)); | ||
| 94 | } | ||
| 95 | while T::regs().cr().read().aden() { | ||
| 96 | // spin | ||
| 97 | } | ||
| 98 | T::regs().cfgr1().modify(|reg| reg.set_dmaen(false)); | ||
| 99 | T::regs().cr().modify(|reg| reg.set_adcal(true)); | ||
| 100 | while T::regs().cr().read().adcal() { | ||
| 101 | // spin | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | pub fn set_sample_time(&mut self, sample_time: SampleTime) { | ||
| 107 | self.sample_time = sample_time; | ||
| 108 | } | ||
| 109 | |||
| 110 | pub fn set_resolution(&mut self, resolution: Resolution) { | ||
| 111 | unsafe { | ||
| 112 | T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | pub fn read<P>(&mut self, pin: &mut P) -> u16 | ||
| 117 | where | ||
| 118 | P: AdcPin<T> + crate::gpio::sealed::Pin, | ||
| 119 | { | ||
| 120 | let channel = pin.channel(); | ||
| 121 | unsafe { | ||
| 122 | pin.set_as_analog(); | ||
| 123 | self.read_channel(channel) | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 { | ||
| 128 | let channel = channel.channel(); | ||
| 129 | unsafe { self.read_channel(channel) } | ||
| 130 | } | ||
| 131 | |||
| 132 | unsafe fn read_channel(&mut self, channel: u8) -> u16 { | ||
| 133 | // A.7.2 ADC enable sequence code example | ||
| 134 | if T::regs().isr().read().adrdy() { | ||
| 135 | T::regs().isr().modify(|reg| reg.set_adrdy(true)); | ||
| 136 | } | ||
| 137 | T::regs().cr().modify(|reg| reg.set_aden(true)); | ||
| 138 | while !T::regs().isr().read().adrdy() { | ||
| 139 | // ES0233, 2.4.3 ADEN bit cannot be set immediately after the ADC calibration | ||
| 140 | // Workaround: When the ADC calibration is complete (ADCAL = 0), keep setting the | ||
| 141 | // ADEN bit until the ADRDY flag goes high. | ||
| 142 | T::regs().cr().modify(|reg| reg.set_aden(true)); | ||
| 143 | } | ||
| 144 | |||
| 145 | T::regs().isr().modify(|reg| { | ||
| 146 | reg.set_eoc(true); | ||
| 147 | reg.set_eosmp(true); | ||
| 148 | }); | ||
| 149 | |||
| 150 | // A.7.5 Single conversion sequence code example - Software trigger | ||
| 151 | T::regs().chselr().write(|reg| reg.set_chselx(channel as usize, true)); | ||
| 152 | T::regs().smpr().modify(|reg| reg.set_smp(self.sample_time.into())); | ||
| 153 | T::regs().cr().modify(|reg| reg.set_adstart(true)); | ||
| 154 | while !T::regs().isr().read().eoc() { | ||
| 155 | // spin | ||
| 156 | } | ||
| 157 | let value = T::regs().dr().read().0 as u16; | ||
| 158 | |||
| 159 | // A.7.3 ADC disable code example | ||
| 160 | T::regs().cr().modify(|reg| reg.set_adstp(true)); | ||
| 161 | while T::regs().cr().read().adstp() { | ||
| 162 | // spin | ||
| 163 | } | ||
| 164 | T::regs().cr().modify(|reg| reg.set_addis(true)); | ||
| 165 | while T::regs().cr().read().aden() { | ||
| 166 | // spin | ||
| 167 | } | ||
| 168 | |||
| 169 | value | ||
| 170 | } | ||
| 171 | } | ||
diff --git a/examples/stm32f0/src/bin/adc.rs b/examples/stm32f0/src/bin/adc.rs new file mode 100644 index 000000000..8ed9f98f8 --- /dev/null +++ b/examples/stm32f0/src/bin/adc.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::adc::{Adc, SampleTime}; | ||
| 8 | use embassy_time::{Delay, Duration, Timer}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_stm32::init(Default::default()); | ||
| 14 | info!("Hello World!"); | ||
| 15 | |||
| 16 | let mut adc = Adc::new(p.ADC, &mut Delay); | ||
| 17 | adc.set_sample_time(SampleTime::Cycles71_5); | ||
| 18 | let mut pin = p.PA1; | ||
| 19 | |||
| 20 | let mut vrefint = adc.enable_vref(&mut Delay); | ||
| 21 | let vrefint_sample = adc.read_internal(&mut vrefint); | ||
| 22 | let convert_to_millivolts = |sample| { | ||
| 23 | // From https://www.st.com/resource/en/datasheet/stm32f031c6.pdf | ||
| 24 | // 6.3.4 Embedded reference voltage | ||
| 25 | const VREFINT_MV: u32 = 1230; // mV | ||
| 26 | |||
| 27 | (u32::from(sample) * VREFINT_MV / u32::from(vrefint_sample)) as u16 | ||
| 28 | }; | ||
| 29 | |||
| 30 | loop { | ||
| 31 | let v = adc.read(&mut pin); | ||
| 32 | info!("--> {} - {} mV", v, convert_to_millivolts(v)); | ||
| 33 | Timer::after(Duration::from_millis(100)).await; | ||
| 34 | } | ||
| 35 | } | ||
