Skip to main content

gd32e2_hal/
adc.rs

1//! 12-bit analog-to-digital converter.
2//!
3//! Single-channel conversions, software triggered: blocking through
4//! [`Adc::read`], or started with [`Adc::start`] and collected with
5//! [`Adc::result`] on [`Event::Eoc`]. The ADC needs its own clock, which is
6//! *not* started by default — call
7//! [`ClockConfig::adc_sel`](crate::rcu::ClockConfig::adc_sel) before
8//! constructing an [`Adc`].
9//!
10//! ```ignore
11//! let config = ClockConfig::default().adc_sel(AdcSel::Prescaled(AdcPsc::Apb2Div8));
12//! let mut rcu = dp.rcu.constrain().freeze(&mut fmc, config);
13//! let mut adc = dp.adc.constrain(&mut rcu);
14//! let pin = parts.pa0.into_analog();
15//! let raw = adc.read(&pin, SampTime::Cycles55_5);
16//! ```
17
18use crate::gpio::{Analog, Pin};
19use crate::pac;
20use crate::rcu::{Clocks, Enable, Rcu, Reset};
21
22const VREFINT_CAL_ADDR: *const u16 = 0x1FFF_F7C0 as *const u16;
23/// Erased-flash value: the factory VREFINT calibration is missing on this part.
24const VREFINT_CAL_BLANK: u16 = 0xFFFF;
25/// Typical VREFINT (datasheet 4.13): ~1.2 V. Used only when the factory
26/// calibration is blank, so `VDDA` can still be estimated ratiometrically.
27const VREFINT_TYP_MV: i32 = 1200;
28const NOMINAL_VDDA_MV: i32 = 3300;
29const ADC_MAX_CODE: i32 = 4095;
30/// Temperature sensor output at 25 °C.
31const V25_MV: i32 = 1450;
32/// Temperature sensor slope, 4.3 mV/°C, scaled by 10 to stay integer.
33const AVG_SLOPE_X10: i32 = 43;
34
35const TEMP_CHANNEL: u8 = 16;
36const VREF_CHANNEL: u8 = 17;
37
38/// Cycles of `CK_ADC` to wait between ADCON and calibration (manual, 11.4.1).
39const CALIBRATION_DELAY_CYCLES: u32 = 14;
40/// Minimum sampling time for the temperature sensor: 17.1 us (manual, 10.4.11).
41const TEMP_MIN_SAMPTIME_US_X10: u64 = 171;
42/// The longest available `SampTime` (`Cycles239_5`), scaled by 10 to stay integer.
43const MAX_SAMPTIME_CYCLES_X10: u64 = 2395;
44const US_PER_S: u64 = 1_000_000;
45
46/// How long the input is sampled before conversion, in `CK_ADC` cycles.
47///
48/// Longer sampling suits higher-impedance sources; the internal temperature
49/// sensor has a minimum requirement expressed in *time*, so how long is long
50/// enough depends on the actual `CK_ADC` frequency.
51#[derive(Clone, Copy, PartialEq, Eq)]
52#[cfg_attr(feature = "defmt", derive(defmt::Format))]
53#[allow(missing_docs)]
54pub enum SampTime {
55    Cycles1_5 = 0b000,
56    Cycles7_5 = 0b001,
57    Cycles13_5 = 0b010,
58    Cycles28_5 = 0b011,
59    Cycles41_5 = 0b100,
60    Cycles55_5 = 0b101,
61    Cycles71_5 = 0b110,
62    Cycles239_5 = 0b111,
63}
64
65/// Binds a pin to the ADC input number it is wired to.
66///
67/// Implemented only for pins in [`Analog`] mode, so a pin that hasn't been
68/// through [`into_analog`](crate::gpio::Pin::into_analog) can't be measured.
69pub trait Channel {
70    /// The ADC input number for this pin.
71    const CHANNEL: u8;
72}
73
74macro_rules! channel {
75    ($($(#[$cfg:meta])? $port:literal $pin:literal => $channel:literal),+ $(,)?) => {
76        $($(#[$cfg])? impl Channel for Pin<$port, $pin, Analog> { const CHANNEL: u8 = $channel; })+
77    };
78}
79
80channel!(
81    'A' 0 => 0,
82    'A' 1 => 1,
83    'A' 2 => 2,
84    'A' 3 => 3,
85    'A' 4 => 4,
86    'A' 5 => 5,
87    'A' 6 => 6,
88    'A' 7 => 7,
89    #[cfg(pads_ge_24)] 'B' 0 => 8,
90    'B' 1 => 9,
91);
92
93/// An ADC event that can raise an interrupt.
94#[derive(Clone, Copy, PartialEq, Eq)]
95#[cfg_attr(feature = "defmt", derive(defmt::Format))]
96pub enum Event {
97    /// A conversion finished; `RDATA` holds the result. `EOC` clears on
98    /// reading it, the same read a handler makes to fetch the value.
99    Eoc,
100}
101
102/// A calibrated ADC, ready to convert.
103pub struct Adc {
104    adc: pac::Adc,
105    clocks: Clocks,
106}
107
108impl Adc {
109    /// Returns the peripheral, powered down, with its interrupt off and its
110    /// clock stopped. A conversion still running is abandoned.
111    pub fn release(mut self, rcu: &mut Rcu) -> pac::Adc {
112        self.unlisten(Event::Eoc);
113        self.adc.ctl1().modify(|_, w| w.adcon().disabled());
114        <pac::Adc as Enable>::disable(rcu);
115        self.adc
116    }
117
118    fn set_channel(&mut self, channel: u8) {
119        self.adc.rsq0().modify(|_, w| w.rl().bits(0b0));
120        // SAFETY: `channel` is an input number — 0–9 from a `Channel` impl, or
121        // `TEMP_CHANNEL`/`VREF_CHANNEL` — each one the field can select.
122        self.adc
123            .rsq2()
124            .modify(|_, w| unsafe { w.rsq0().bits(channel) });
125    }
126    fn set_sample_time(&mut self, channel: u8, time: SampTime) {
127        self.adc.sampt1().modify(|_, w| match channel {
128            0 => w.spt0().bits(time as u8),
129            1 => w.spt1().bits(time as u8),
130            2 => w.spt2().bits(time as u8),
131            3 => w.spt3().bits(time as u8),
132            4 => w.spt4().bits(time as u8),
133            5 => w.spt5().bits(time as u8),
134            6 => w.spt6().bits(time as u8),
135            7 => w.spt7().bits(time as u8),
136            8 => w.spt8().bits(time as u8),
137            9 => w.spt9().bits(time as u8),
138            _ => unreachable!(),
139        });
140    }
141    fn set_internal_channel(&mut self, channel: u8) {
142        self.set_channel(channel);
143    }
144    fn sample_time_sufficient(&self) -> bool {
145        TEMP_MIN_SAMPTIME_US_X10 * self.clocks.ck_adc().to_Hz() as u64
146            <= MAX_SAMPTIME_CYCLES_X10 * US_PER_S
147    }
148    fn set_internal_sample_time(&mut self, channel: u8, time: SampTime) {
149        self.adc.sampt0().modify(|_, w| match channel {
150            TEMP_CHANNEL => w.spt16().bits(time as u8),
151            VREF_CHANNEL => w.spt17().bits(time as u8),
152            _ => unreachable!(),
153        });
154    }
155    fn convert(&mut self) -> u16 {
156        self.start_conversion();
157        while self.adc.stat().read().eoc().is_not_complete() {}
158        self.read_result()
159    }
160    fn start_conversion(&mut self) {
161        self.adc.ctl1().modify(|_, w| w.swrcst().start());
162        while self.adc.ctl1().read().swrcst().is_not_started() {}
163    }
164    fn read_result(&mut self) -> u16 {
165        self.adc.rdata().read().rdata().bits()
166    }
167    fn with_internal<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
168        let was_enabled = self.adc.ctl1().read().tsvren().is_enabled();
169        if !was_enabled {
170            self.adc.ctl1().modify(|_, w| w.tsvren().enabled());
171        }
172        let result = f(self);
173        if !was_enabled {
174            self.adc.ctl1().modify(|_, w| w.tsvren().disabled());
175        }
176        result
177    }
178
179    /// Triggers a conversion, blocking only until the trigger is acknowledged —
180    /// not until it finishes. Pairs with [`listen`](Self::listen) and
181    /// [`result`](Self::result) for the interrupt-driven path.
182    pub fn start<PIN: Channel>(&mut self, _pin: &PIN, time: SampTime) {
183        self.set_channel(PIN::CHANNEL);
184        self.set_sample_time(PIN::CHANNEL, time);
185        self.start_conversion();
186    }
187    /// Reads the conversion result out of `RDATA`, clearing `EOC`.
188    ///
189    /// Does not check `EOC` itself — call this once notified of it (an `Eoc`
190    /// handler after [`listen`](Self::listen)), not on a guess.
191    pub fn result(&mut self) -> u16 {
192        self.read_result()
193    }
194
195    /// Lets `event` raise an interrupt. Unmasking the line in the NVIC is the
196    /// caller's.
197    pub fn listen(&mut self, event: Event) {
198        match event {
199            Event::Eoc => self.adc.ctl0().modify(|_, w| w.eocie().enabled()),
200        }
201    }
202    /// Stops `event` from raising an interrupt.
203    pub fn unlisten(&mut self, event: Event) {
204        match event {
205            Event::Eoc => self.adc.ctl0().modify(|_, w| w.eocie().disabled()),
206        }
207    }
208
209    /// Converts one channel and returns the raw 12-bit code (0..=4095).
210    ///
211    /// The pin is borrowed only to identify the channel — nothing is read from
212    /// the value itself. Blocks until the conversion finishes.
213    pub fn read<PIN: Channel>(&mut self, _pin: &PIN, time: SampTime) -> u16 {
214        self.set_channel(PIN::CHANNEL);
215        self.set_sample_time(PIN::CHANNEL, time);
216        self.convert()
217    }
218    /// Reads the internal temperature sensor, in tenths of a degree Celsius.
219    ///
220    /// Scaled against the real supply from [`read_vref`](Self::read_vref).
221    ///
222    /// Returns `None` when `CK_ADC` runs too fast for the sensor's minimum
223    /// sampling time: [`SampTime`] is counted in cycles, so above roughly 14
224    /// MHz even the longest no longer spans the required 17.1 µs.
225    pub fn read_temperature(&mut self) -> Option<i32> {
226        if !self.sample_time_sufficient() {
227            return None;
228        }
229        let vdda_mv = self.read_vref();
230        let raw = self.with_internal(|s| {
231            s.set_internal_channel(TEMP_CHANNEL);
232            s.set_internal_sample_time(TEMP_CHANNEL, SampTime::Cycles239_5);
233            s.convert()
234        });
235        let v_temperature_mv = raw as i32 * vdda_mv / ADC_MAX_CODE;
236        let temperature_x10 = 100 * (V25_MV - v_temperature_mv) / AVG_SLOPE_X10 + 250;
237        Some(temperature_x10)
238    }
239    /// Measures the actual analog supply voltage `VDDA`, in millivolts.
240    ///
241    /// The internal reference is fixed, so its raw code moves only because `VDDA`
242    /// — the ADC's own reference — moved; comparing it against the factory
243    /// calibration in flash gives the real supply.
244    ///
245    /// If that calibration is blank (`0xFFFF`, seen on some parts), falls back to
246    /// the typical VREFINT of ~1.2 V: less accurate, but not wildly wrong.
247    pub fn read_vref(&mut self) -> i32 {
248        // SAFETY: `VREFINT_CAL_ADDR` is an aligned half-word of system memory,
249        // always readable and never written.
250        let vrefint_cal = unsafe { core::ptr::read_volatile(VREFINT_CAL_ADDR) };
251        let raw = self.with_internal(|s| {
252            s.set_internal_channel(VREF_CHANNEL);
253            s.set_internal_sample_time(VREF_CHANNEL, SampTime::Cycles239_5);
254            s.convert()
255        }) as i32;
256        if vrefint_cal == VREFINT_CAL_BLANK {
257            VREFINT_TYP_MV * ADC_MAX_CODE / raw
258        } else {
259            NOMINAL_VDDA_MV * vrefint_cal as i32 / raw
260        }
261    }
262}
263
264/// Entry point on the raw peripheral.
265pub trait AdcExt {
266    /// Enables the peripheral, powers it up and runs the calibration sequence.
267    ///
268    /// Blocks until calibration completes.
269    ///
270    /// # Panics
271    ///
272    /// If the ADC clock was never selected —
273    /// [`Clocks::ck_adc`](crate::rcu::Clocks::ck_adc) is then zero and the
274    /// calibration delay divides by it. Configure the clock with
275    /// [`ClockConfig::adc_sel`](crate::rcu::ClockConfig::adc_sel) first.
276    fn constrain(self, rcu: &mut Rcu) -> Adc;
277}
278
279impl AdcExt for pac::Adc {
280    fn constrain(self, rcu: &mut Rcu) -> Adc {
281        let clocks = rcu.clocks();
282        <pac::Adc as Enable>::enable(rcu);
283        <pac::Adc as Reset>::reset(rcu);
284        self.ctl1().modify(|_, w| w.adcon().enabled());
285        cortex_m::asm::delay(
286            (CALIBRATION_DELAY_CYCLES * clocks.hclk().to_Hz()).div_ceil(clocks.ck_adc().to_Hz()),
287        );
288        self.ctl1().modify(|_, w| w.rstclb().start());
289        self.ctl1().modify(|_, w| w.clb().start());
290        while self.ctl1().read().clb().is_not_complete() {}
291        self.ctl1()
292            .modify(|_, w| w.eterc().enabled().etsrc().swrcst());
293        Adc { adc: self, clocks }
294    }
295}