py32_hal/adc/
v2.rs

1// The following code is modified from embassy-stm32
2// https://github.com/embassy-rs/embassy/tree/main/embassy-stm32
3// Special thanks to the Embassy Project and its contributors for their work!
4
5use embassy_hal_internal::into_ref;
6
7use super::blocking_delay_us;
8use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime};
9use crate::pac::adc::vals::Extsel;
10use crate::pac::RCC;
11use crate::peripherals::ADC1;
12use crate::time::Hertz;
13use crate::{rcc, Peripheral};
14
15mod ringbuffered_v2;
16pub use ringbuffered_v2::{RingBufferedAdc, Sequence};
17
18/// Default VREF voltage used for sample conversion to millivolts.
19pub const VREF_DEFAULT_MV: u32 = 3300;
20/// VREF voltage used for factory calibration of VREFINTCAL register.
21pub const VREF_CALIB_MV: u32 = 3300;
22
23pub struct VrefInt;
24impl AdcChannel<ADC1> for VrefInt {}
25impl super::SealedAdcChannel<ADC1> for VrefInt {
26    fn channel(&self) -> u8 {
27        17
28    }
29}
30
31impl VrefInt {
32    /// Time needed for internal voltage reference to stabilize
33    pub fn start_time_us() -> u32 {
34        10
35    }
36}
37
38pub struct Temperature;
39impl AdcChannel<ADC1> for Temperature {}
40impl super::SealedAdcChannel<ADC1> for Temperature {
41    fn channel(&self) -> u8 {
42        16
43    }
44}
45
46impl Temperature {
47    /// Time needed for temperature sensor readings to stabilize
48    pub fn start_time_us() -> u32 {
49        10
50    }
51}
52
53// pub struct Vbat;
54// impl AdcChannel<ADC1> for Vbat {}
55// impl super::SealedAdcChannel<ADC1> for Vbat {
56//     fn channel(&self) -> u8 {
57//         18
58//     }
59// }
60
61pub enum Prescaler {
62    Div2,
63    Div4,
64    Div6,
65    Div8,
66}
67
68impl Prescaler {
69    fn from_pclk(freq: Hertz) -> Self {
70        #[cfg(py32f072)]
71        const MAX_FREQUENCY: Hertz = Hertz(16_000_000);
72        let raw_div = freq.0 / MAX_FREQUENCY.0;
73        match raw_div {
74            0..=1 => Self::Div2,
75            2..=3 => Self::Div4,
76            4..=5 => Self::Div6,
77            6..=7 => Self::Div8,
78            _ => panic!(
79                "Selected PCLK frequency is too high for ADC with largest possible prescaler."
80            ),
81        }
82    }
83
84    fn adcdiv(&self) -> crate::pac::rcc::vals::Adcdiv {
85        match self {
86            Prescaler::Div2 => crate::pac::rcc::vals::Adcdiv::DIV2,
87            Prescaler::Div4 => crate::pac::rcc::vals::Adcdiv::DIV4,
88            Prescaler::Div6 => crate::pac::rcc::vals::Adcdiv::DIV6,
89            Prescaler::Div8 => crate::pac::rcc::vals::Adcdiv::DIV8,
90        }
91    }
92}
93
94impl<'d, T> Adc<'d, T>
95where
96    T: Instance,
97{
98    pub fn new(adc: impl Peripheral<P = T> + 'd) -> Self {
99        let presc = Prescaler::from_pclk(T::frequency());
100        Self::new_with_prediv(adc, presc)
101    }
102
103    /// adc_div: The PCLK division factor
104    pub fn new_with_prediv(adc: impl Peripheral<P = T> + 'd, adc_div: Prescaler) -> Self {
105        into_ref!(adc);
106        rcc::enable_and_reset::<T>();
107
108        RCC.cr().modify(|reg| {
109            reg.set_adcdiv(adc_div.adcdiv());
110        });
111        T::regs().cr2().modify(|reg| {
112            reg.set_extsel(Extsel::SWSTART);
113        });
114
115        Self::calibrate();
116
117        T::regs().cr2().modify(|reg| {
118            reg.set_adon(true);
119        });
120
121        blocking_delay_us(3);
122
123        Self {
124            adc,
125            sample_time: SampleTime::from_bits(0),
126        }
127    }
128
129    pub fn set_sample_time(&mut self, sample_time: SampleTime) {
130        self.sample_time = sample_time;
131    }
132
133    pub fn set_resolution(&mut self, resolution: Resolution) {
134        T::regs().cr1().modify(|reg| reg.set_res(resolution.into()));
135    }
136
137    /// Enables internal voltage reference and returns [VrefInt], which can be used in
138    /// [Adc::read_internal()] to perform conversion.
139    pub fn enable_vrefint(&self) -> VrefInt {
140        T::regs().cr2().modify(|reg| {
141            reg.set_tsvrefe(true);
142        });
143
144        VrefInt {}
145    }
146
147    /// Enables internal temperature sensor and returns [Temperature], which can be used in
148    /// [Adc::read_internal()] to perform conversion.
149    ///
150    /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled,
151    /// temperature sensor will return vbat value.
152    pub fn enable_temperature(&self) -> Temperature {
153        T::regs().cr2().modify(|reg| {
154            reg.set_tsvrefe(true);
155        });
156
157        Temperature {}
158    }
159
160    // /// Enables vbat input and returns [Vbat], which can be used in
161    // /// [Adc::read_internal()] to perform conversion.
162    // pub fn enable_vbat(&self) -> Vbat {
163    //     T::common_regs().ccr().modify(|reg| {
164    //         reg.set_vbate(true);
165    //     });
166
167    //     Vbat {}
168    // }
169
170    /// Perform a single conversion.
171    fn convert(&mut self) -> u16 {
172        // clear end of conversion flag
173        T::regs().sr().modify(|reg| {
174            reg.set_eoc(false);
175        });
176
177        // Start conversion
178        T::regs().cr2().modify(|reg| {
179            reg.set_swstart(true);
180            reg.set_exttrig(true);
181        });
182
183        while T::regs().sr().read().strt() == false {
184            // spin //wait for actual start
185        }
186        while T::regs().sr().read().eoc() == false {
187            // spin //wait for finish
188        }
189
190        T::regs().dr().read().0 as u16
191    }
192
193    pub fn blocking_read(&mut self, channel: &mut impl AdcChannel<T>) -> u16 {
194        channel.setup();
195
196        // Configure ADC
197        let channel = channel.channel();
198
199        // Select channel
200        T::regs().sqr3().write(|reg| reg.set_sq(0, channel));
201
202        // Configure channel
203        Self::set_channel_sample_time(channel, self.sample_time);
204
205        self.convert()
206    }
207
208    fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
209        let sample_time = sample_time.into();
210        match ch {
211            ..=9 => T::regs()
212                .smpr3()
213                .modify(|reg| reg.set_smp(ch as _, sample_time)),
214            ..=19 => T::regs()
215                .smpr2()
216                .modify(|reg| reg.set_smp((ch - 10) as _, sample_time)),
217            _ => T::regs()
218                .smpr3()
219                .modify(|reg| reg.set_smp((ch - 20) as _, sample_time)),
220        }
221    }
222
223    /// Perform ADC automatic self-calibration
224    pub fn calibrate() {
225        T::regs().cr2().modify(|reg| {
226            reg.set_adon(false);
227        });
228
229        while T::regs().cr2().read().adon() {}
230
231        // Wait for ADC to be fully disabled
232        // Compute and wait for required ADC clock cycles
233
234        let adc_clock_mhz = 72_u32; // MAX
235        let cpu_clock_mhz = unsafe { rcc::get_freqs() }.sys.to_hertz().unwrap().0 / 1_000_000;
236        #[cfg(py32f072)]
237        let precalibration_cycles = 2_u32;
238
239        let delay_us = (precalibration_cycles * adc_clock_mhz) / cpu_clock_mhz;
240        blocking_delay_us(delay_us);
241
242        // Check if ADC is enabled
243        if T::regs().cr2().read().adon() {
244            panic!();
245        }
246
247        // Reset calibration
248        T::regs().cr2().modify(|reg| {
249            reg.set_rstcal(true);
250        });
251
252        // Wait for calibration reset to complete
253        while T::regs().cr2().read().rstcal() {}
254
255        // Start calibration
256        T::regs().cr2().modify(|reg| {
257            reg.set_cal(true);
258        });
259
260        // Wait for calibration to complete
261        while T::regs().cr2().read().cal() {}
262    }
263}
264
265impl<'d, T: Instance> Drop for Adc<'d, T> {
266    fn drop(&mut self) {
267        T::regs().cr2().modify(|reg| {
268            reg.set_adon(false);
269        });
270
271        rcc::disable::<T>();
272    }
273}