Skip to main content

ra_hal/adc/
channel.rs

1use embassy_hal_internal::{Peri, PeripheralType};
2use embassy_time::{Duration, block_for};
3
4use crate::{
5    adc::{AdcChannelConfig, AverageMode, Resolution},
6    constants::{TSN_INTERCEPT, TSN_SLOPE},
7    gpio::{Flex, Pin},
8    pac,
9};
10
11#[cfg(adc12)]
12use pac::adc12::{self as adc_pac};
13#[cfg(adc14)]
14use pac::adc14::{self as adc_pac};
15#[cfg(adc16)]
16use pac::adc16::{self as adc_pac};
17
18use adc_pac::regs::Adans;
19
20#[cfg(adc16)]
21use pac::adc16::{regs::Addiscr, vals::Vrefadcg};
22
23/// Pseudo-channel for the on-die temperature sensor.
24///
25/// # Notes
26///
27/// This is mutually exclusive with all other channels.
28/// §35.2.13 `TSSA` bit.
29pub struct Temperature;
30
31/// Pseudo-channel for `Vref` measurement.
32///
33/// # Notes
34///
35/// This is mutually exclusive with all other channels.
36/// §35.2.13 `OCSA` bit.
37pub struct Vref;
38
39/// GPIO pin that can be used as an `ADC` input channel.
40///
41/// This struct is used because there is a bit of setup needed to configure the pin for ADC usage.
42/// In theory, at some point in the future, we could reset this to GPIO use on drop.
43#[allow(private_bounds)]
44#[cfg_attr(feature = "defmt", derive(defmt::Format))]
45pub struct AdcPin {
46    adc_channel: u8,
47}
48
49/// Multiple GPIO pins that can be used as `ADC` input channels.
50///
51/// # Notes
52///
53/// `ADC14` doesn't support arbitrary sequences but does support reading from arbitrary channels in order by channel index.
54/// See the [docs](crate::adc::AdcInputPin) for each pin to reveal its associated ADC channel.
55#[cfg_attr(feature = "defmt", derive(defmt::Format))]
56pub struct AdcSequence<const N: usize> {
57    channels: [u8; N],
58}
59
60/// For the physical GPIO pins connected to `ADC` input channels.
61#[allow(private_bounds)]
62pub trait AdcInputPin: SealedAdcInputPin {
63    /// ADC channel index `ANnnn`
64    const CHANNEL: u8;
65}
66
67/// For the physical GPIO pins connected to `ADC` input channels.
68pub(crate) trait SealedAdcInputPin: Pin + PeripheralType {}
69
70/// `ADC` channel instance.
71///
72/// An `AdcChannel` may be:
73/// * An individual ADC channel (`ANnnn` in Renesas speak)
74/// * A pseudo-channel such as the on-die temperature sensor
75/// * A collection of individual channels to be read in sequence
76///
77/// So R may be either `u16` or `[u16; N]`.
78#[allow(private_bounds)]
79pub trait AdcChannel<R>: SealedAdcChannel<R> {}
80
81pub(super) trait SealedAdcChannel<R> {
82    fn enable<I: super::Instance>(&self, config: AdcChannelConfig);
83    fn disable<I: super::Instance>(&self);
84    fn read_one<I: super::Instance>(&self) -> R;
85    fn address<I: super::Instance>(&self) -> *const R;
86}
87
88#[allow(private_bounds)]
89impl AdcPin {
90    /// Takes ownership of a pin for ADC use.
91    #[inline]
92    pub fn new<'d, P: AdcInputPin>(pin: Peri<'d, P>) -> Self {
93        let mut flex = Flex::new_basic(pin);
94        flex.set_as_analog();
95
96        #[cfg(feature = "strict-assert")]
97        assert!(P::CHANNEL <= 14 || (P::CHANNEL >= 16 && P::CHANNEL < 25));
98
99        let adc_channel = P::CHANNEL;
100
101        Self { adc_channel }
102    }
103}
104
105impl<const N: usize> AdcSequence<N> {
106    /// Takes ownership of an array of pins for ADC use.
107    pub fn new(pins: [AdcPin; N]) -> Self {
108        // There are only 26 ADC channels (0..=14, 16..=26).
109        #[cfg(feature = "strict-assert")]
110        assert!(N <= 26);
111
112        // Taking an array of AdcPin is nice here because they're type erased and properly configured
113        // for ADC input.  Will have to revisit this if we ever reset things on AdcPin::drop.  An
114        // added benefit is that this won't accept the temp or vref pseudo-channels.
115
116        let channels: [u8; N] = pins.map(|pin| pin.adc_channel);
117
118        Self { channels }
119    }
120
121    /// Returns a slice containing the indices of the channels to be read.
122    #[inline]
123    pub fn channels(&self) -> &[u8] {
124        &self.channels
125    }
126}
127
128impl<const N: usize> AdcChannel<[u16; N]> for AdcSequence<N> {}
129
130impl<const N: usize> SealedAdcChannel<[u16; N]> for AdcSequence<N> {
131    #[inline]
132    fn enable<I: super::Instance>(&self, config: AdcChannelConfig) {
133        // ADC14 lets us independently adjust the sample time for some, but not all channels.
134        // For now, let's just assign the same sample time.
135
136        trace!("ADC14: enable_channel(sequence)");
137
138        let adc = I::regs();
139
140        adc.adexicr().modify(|w| {
141            w.set_ocsa(false);
142            w.set_tssa(false);
143        });
144
145        for channel in self.channels.iter() {
146            trace!("  {} enable", channel);
147
148            let index = *channel as usize / 16;
149            let offset = *channel as usize % 16;
150
151            adc.adansa(index).write(|w| w.set_ans(offset, true));
152
153            if *channel <= 14 {
154                adc.adsstr(*channel as _).write_value(config.sample_time);
155            } else {
156                adc.adsstrl().write_value(config.sample_time);
157            }
158        }
159
160        if config.average_mode != AverageMode::Off {
161            for channel in self.channels.iter() {
162                trace!("  {} add/avg", channel);
163
164                let index = *channel as usize / 16;
165                let offset = *channel as usize % 16;
166
167                adc.adads(index).write(|w| w.set_ads(offset, true));
168            }
169        }
170    }
171
172    #[inline]
173    fn disable<I: super::Instance>(&self) {
174        trace!("ADC14: disable_channel(sequence)");
175
176        let adc = I::regs();
177
178        if adc.adcsr().read().adst() {
179            warn!("ADC14: Trying to disable sequence with a conversion running");
180        }
181
182        // Turn everything off.
183        adc.adansa(0).write_value(Adans(0));
184        adc.adansa(1).write_value(Adans(0));
185    }
186
187    #[inline]
188    fn read_one<I: super::Instance>(&self) -> [u16; N] {
189        let adc = I::regs();
190
191        self.channels.map(|chan| adc.addr(chan as _).read())
192    }
193
194    #[inline]
195    fn address<I: super::Instance>(&self) -> *const [u16; N] {
196        // This should probably just get the address of each enabled channel and
197        // return that in the array.  This will likely require a DTC implementation
198        // instead of DMAC.
199        //
200        // TODO: Benchmark this.
201
202        todo!("DMA xfers for sequences not yet supported")
203    }
204}
205
206impl AdcChannel<u16> for AdcPin {}
207
208impl SealedAdcChannel<u16> for AdcPin {
209    #[inline]
210    fn enable<I: super::Instance>(&self, config: AdcChannelConfig) {
211        let adc = I::regs();
212
213        trace!("ADC14: enable_channel({})", self.adc_channel);
214
215        adc.adexicr().modify(|w| {
216            w.set_ocsa(false);
217            w.set_tssa(false);
218        });
219
220        let index = self.adc_channel as usize / 16;
221        let offset = self.adc_channel as usize % 16;
222
223        adc.adansa(index).write(|w| w.set_ans(offset, true));
224
225        if config.average_mode != AverageMode::Off {
226            adc.adads(index).write(|w| w.set_ads(offset, true))
227        }
228
229        if self.adc_channel <= 14 {
230            adc.adsstr(self.adc_channel as _)
231                .write_value(config.sample_time);
232        } else {
233            adc.adsstrl().write_value(config.sample_time);
234        }
235    }
236
237    #[inline]
238    fn disable<I: super::Instance>(&self) {
239        let adc = I::regs();
240
241        trace!("ADC14: disable_channel({})", self.adc_channel);
242
243        if adc.adcsr().read().adst() {
244            warn!(
245                "ADC14: Trying to disable chan={} with a conversion running",
246                self.adc_channel
247            );
248        }
249
250        let index = self.adc_channel as usize / 16;
251        let offset = self.adc_channel as usize % 16;
252
253        adc.adansa(index).modify(|w| w.set_ans(offset, false));
254    }
255
256    #[inline]
257    fn read_one<I: super::Instance>(&self) -> u16 {
258        let adc = I::regs();
259
260        trace!("ADC14: read_one({})", self.adc_channel);
261
262        adc.addr(self.adc_channel as _).read()
263    }
264
265    #[inline]
266    fn address<I: super::Instance>(&self) -> *const u16 {
267        let adc = I::regs();
268        adc.addr(self.adc_channel as _).as_ptr()
269    }
270}
271
272impl Temperature {
273    /// Converts an ADC reading to a temperature in degrees celsius.
274    pub fn raw_to_celsius(&self, v_s: u16, vref_mv: u16, width: Resolution) -> i32 {
275        // RA4M1: § 48.7 TSN Characteristics
276
277        let tsn = pac::TSN;
278        let v_1 = tsn.tscdr().read();
279
280        let calibration_width = cfg_select! {
281          adc12 => 4096, // 12-bit
282          adc14 => 4096, // 12-bit
283          adc16 => 32768, // 15-bit
284          _ => compile_error!()
285        };
286
287        let measurement_width = match width {
288            #[cfg(any(adc12, adc14))]
289            Resolution::_12bit => 4_096,
290            #[cfg(adc14)]
291            Resolution::_14bit => 16_384,
292            #[cfg(adc16)]
293            Resolution::_16bit => 32_768,
294        };
295
296        let v_1 = (u64::from(v_1) * 3_300_000) / calibration_width;
297        let v_s = (u64::from(v_s) * u64::from(vref_mv) * 1_000) / measurement_width;
298
299        i32::try_from(((v_s - v_1) as i64 / i64::from(TSN_SLOPE)) + i64::from(TSN_INTERCEPT))
300            .expect("Temperature reading overflowed the bounds of an i32")
301    }
302
303    /// Converts an ADC reading to a temperature in degrees celsius.
304    pub fn raw_to_celsius_float(&self, v_s: u16, vref_mv: u16, width: Resolution) -> f32 {
305        // RA4M1: § 48.7 TSN Characteristics
306
307        let tsn = pac::TSN;
308        let v_1 = tsn.tscdr().read();
309
310        let calibration_width = cfg_select! {
311          adc12 => 4096, // 12-bit
312          adc14 => 4096, // 12-bit
313          adc16 => 32768, // 15-bit
314          _ => compile_error!()
315        };
316
317        let measurement_width = match width {
318            #[cfg(any(adc12, adc14))]
319            Resolution::_12bit => 4_096,
320            #[cfg(adc14)]
321            Resolution::_14bit => 16_384,
322            #[cfg(adc16)]
323            Resolution::_16bit => 32_768,
324        };
325
326        let v_1 = (u64::from(v_1) * 3_300_000) / calibration_width;
327        let v_s = (u64::from(v_s) * u64::from(vref_mv) * 1_000) / measurement_width;
328
329        let v_1 = v_1 * 100;
330        let v_s = v_s * 100;
331        let intercept = TSN_INTERCEPT * 100;
332
333        (((v_s - v_1) as i64 / i64::from(TSN_SLOPE)) + i64::from(intercept)) as f32 / 100.0
334    }
335}
336
337impl AdcChannel<u16> for Temperature {}
338
339impl SealedAdcChannel<u16> for Temperature {
340    #[inline]
341    fn enable<I: super::Instance>(&self, config: AdcChannelConfig) {
342        let adc = I::regs();
343
344        trace!("ADC14: enable_channel(TEMPERATURE)");
345
346        if adc.adcsr().read().adst() {
347            warn!("ADC14: Trying to enable temp with a conversion running");
348        }
349
350        adc.adsstrt().write_value(config.sample_time);
351
352        #[cfg(any(adc12, adc14))]
353        {
354            adc.adhvrefcnt().modify(|r| {
355                use adc_pac::vals::Hvsel;
356
357                r.set_hvsel(Hvsel::Avcc0);
358            });
359        }
360
361        #[cfg(adc16)]
362        {
363            adc.addiscr().write_value(Addiscr(0xF));
364
365            adc.vrefampcnt().modify(|r| {
366                r.set_vrefadcg(Vrefadcg::_11);
367                r.set_bgren(true);
368            });
369
370            // Table 47.45 Internal reference voltage for 16-bit ADC (VREFADC) characteristics
371            block_for(Duration::from_micros(2000));
372
373            adc.vrefampcnt().modify(|r| {
374                r.set_oldeten(true);
375                r.set_vrefadcen(true);
376                r.set_adslp(false);
377            });
378            block_for(Duration::from_micros(2000));
379        }
380
381        adc.adexicr().write(|w| w.set_tssa(true));
382        block_for(Duration::from_micros(2000));
383
384        if config.average_mode != AverageMode::Off {
385            todo!()
386        }
387    }
388
389    #[inline]
390    fn disable<I: super::Instance>(&self) {
391        let adc = I::regs();
392
393        trace!("ADC14: disable_channel(TEMPERATURE)");
394
395        if adc.adcsr().read().adst() {
396            warn!("ADC14: Trying to disable temp with a conversion running");
397        }
398
399        adc.adexicr().modify(|w| w.set_tssa(false));
400    }
401
402    #[inline]
403    fn read_one<I: super::Instance>(&self) -> u16 {
404        let adc = I::regs();
405
406        trace!("ADC14: read_one(TEMPERATURE)");
407        adc.adtsdr().read()
408    }
409
410    #[inline]
411    fn address<I: super::Instance>(&self) -> *const u16 {
412        let adc = I::regs();
413        adc.adtsdr().as_ptr()
414    }
415}
416
417impl AdcChannel<u16> for Vref {}
418
419impl SealedAdcChannel<u16> for Vref {
420    #[inline]
421    fn enable<I: super::Instance>(&self, config: AdcChannelConfig) {
422        let adc = I::regs();
423
424        trace!("ADC14: enable_channel(VREF)");
425
426        if adc.adcsr().read().adst() {
427            warn!("ADC14: Trying to enable Vref with a conversion running");
428        }
429
430        adc.adsstro().write_value(config.sample_time);
431
432        adc.adexicr().modify(|w| w.set_ocsa(true));
433
434        if config.average_mode != AverageMode::Off {
435            todo!()
436        }
437    }
438
439    #[inline]
440    fn disable<I: super::Instance>(&self) {
441        let adc = I::regs();
442
443        trace!("ADC14: disable_channel(VREF)");
444
445        if adc.adcsr().read().adst() {
446            warn!("ADC14: Trying to disable Vref with a conversion running");
447        }
448
449        adc.adexicr().modify(|w| w.set_ocsa(false));
450    }
451
452    #[inline]
453    fn read_one<I: super::Instance>(&self) -> u16 {
454        let adc = I::regs();
455
456        trace!("ADC14: read_one(VREF)");
457        adc.adocdr().read()
458    }
459
460    #[inline]
461    fn address<I: super::Instance>(&self) -> *const u16 {
462        let adc = I::regs();
463        adc.adocdr().as_ptr()
464    }
465}
466
467macro_rules! adc_pin {
468    ($channel:literal, $pin:ident) => {
469        impl crate::adc::channel::AdcInputPin for crate::peripherals::$pin {
470            const CHANNEL: u8 = $channel;
471        }
472        impl crate::adc::channel::SealedAdcInputPin for crate::peripherals::$pin {}
473    };
474}
475pub(crate) use adc_pin;