va416xx_hal/
adc.rs

1//! Analog to Digital Converter (ADC) driver.
2//!
3//! ## Examples
4//!
5//! - [ADC and DAC example](https://github.com/us-irs/va416xx-rs/blob/main/examples/simple/examples/dac-adc.rs)
6//! - [ADC](https://github.com/us-irs/va416xx-rs/blob/main/examples/simple/examples/adc.rs)
7use core::marker::PhantomData;
8
9use crate::clock::Clocks;
10use crate::pac;
11use crate::time::Hertz;
12use num_enum::{IntoPrimitive, TryFromPrimitive};
13use vorago_shared_hal::{enable_peripheral_clock, PeripheralSelect};
14
15pub const ADC_MIN_CLK: Hertz = Hertz::from_raw(2_000_000);
16pub const ADC_MAX_CLK: Hertz = Hertz::from_raw(12_500_000);
17
18#[derive(Debug, PartialEq, Eq, Copy, Clone, TryFromPrimitive, IntoPrimitive)]
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20#[repr(u8)]
21pub enum ChannelSelect {
22    /// Analogue Input 0 external channel
23    AnIn0 = 0,
24    /// Analogue Input 1 external channel
25    AnIn1 = 1,
26    /// Analogue Input 2 external channel
27    AnIn2 = 2,
28    /// Analogue Input 3 external channel
29    AnIn3 = 3,
30    /// Analogue Input 4 external channel
31    AnIn4 = 4,
32    /// Analogue Input 5 external channel
33    AnIn5 = 5,
34    /// Analogue Input 6 external channel
35    AnIn6 = 6,
36    /// Analogue Input 7 external channel
37    AnIn7 = 7,
38    /// DAC 0 internal channel
39    Dac0 = 8,
40    /// DAC 1 internal channel
41    Dac1 = 9,
42    /// Internal temperature sensor
43    TempSensor = 10,
44    /// Internal bandgap 1 V reference
45    Bandgap1V = 11,
46    /// Internal bandgap 1.5 V reference
47    Bandgap1_5V = 12,
48    Avdd1_5 = 13,
49    Dvdd1_5 = 14,
50    /// Internally generated Voltage equal to VREFH / 2
51    Vrefp5 = 15,
52}
53
54bitflags::bitflags! {
55    /// This structure is used by the ADC multi-select API to
56    /// allow selecting multiple channels in a convenient manner.
57    #[derive(Debug)]
58    pub struct MultiChannelSelect: u16 {
59        const AnIn0 = 1;
60        const AnIn1 = 1 << 1;
61        const AnIn2 = 1 << 2;
62        const AnIn3 = 1 << 3;
63        const AnIn4 = 1 << 4;
64        const AnIn5 = 1 << 5;
65        const AnIn6 = 1 << 6;
66        const AnIn7 = 1 << 7;
67        const Dac0 = 1 << 8;
68        const Dac1 = 1 << 9;
69        const TempSensor = 1 << 10;
70        const Bandgap1V = 1 << 11;
71        const Bandgap1_5V = 1 << 12;
72        const Avdd1_5 = 1 << 13;
73        const Dvdd1_5 = 1 << 14;
74        const Vrefp5 = 1 << 15;
75    }
76}
77
78#[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)]
79#[cfg_attr(feature = "defmt", derive(defmt::Format))]
80#[error("ADC empty error")]
81pub struct AdcEmptyError;
82
83#[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)]
84#[cfg_attr(feature = "defmt", derive(defmt::Format))]
85#[error("invalid channel range error")]
86pub struct InvalidChannelRangeError;
87
88#[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)]
89#[cfg_attr(feature = "defmt", derive(defmt::Format))]
90#[error("buffer too small")]
91pub struct BufferTooSmallError;
92
93#[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)]
94#[cfg_attr(feature = "defmt", derive(defmt::Format))]
95pub enum AdcRangeReadError {
96    #[error("invalid channel range: {0}")]
97    InvalidChannelRange(#[from] InvalidChannelRangeError),
98    #[error("buffer too small: {0}")]
99    BufferTooSmall(#[from] BufferTooSmallError),
100}
101
102#[derive(Debug, PartialEq, Eq, Copy, Clone)]
103#[cfg_attr(feature = "defmt", derive(defmt::Format))]
104pub struct ChannelValue {
105    /// If the channel tag is enabled, this field will contain the determined channel tag.
106    channel: ChannelSelect,
107    /// Raw value.
108    value: u16,
109}
110
111impl Default for ChannelValue {
112    fn default() -> Self {
113        Self {
114            channel: ChannelSelect::AnIn0,
115            value: Default::default(),
116        }
117    }
118}
119
120impl ChannelValue {
121    #[inline]
122    pub fn value(&self) -> u16 {
123        self.value
124    }
125
126    #[inline]
127    pub fn channel(&self) -> ChannelSelect {
128        self.channel
129    }
130}
131
132pub enum ChannelTagEnabled {}
133pub enum ChannelTagDisabled {}
134
135/// ADC driver structure.
136///
137/// Currently, this structure supports three primary ways to measure channel value(s):
138///
139/// * Trigger and read a single value
140/// * Trigger and read a range of ADC values using the sweep functionality
141/// * Trigger and read multiple ADC values using the sweep functionality
142///
143/// The ADC channel tag feature is enabled or disabled at compile time using the
144/// [ChannelTagEnabled] and [ChannelTagDisabled]. The [Adc::new] method returns a driver instance
145/// with the channel tag enabled, while the [Adc::new_with_channel_tag] method can be used to
146/// return an instance with the channel tag enabled.
147pub struct Adc<TagEnabled = ChannelTagDisabled> {
148    adc: pac::Adc,
149    phantom: PhantomData<TagEnabled>,
150}
151
152impl Adc<ChannelTagEnabled> {}
153
154impl Adc<ChannelTagDisabled> {
155    pub fn new(adc: pac::Adc, clocks: &Clocks) -> Self {
156        Self::generic_new(adc, clocks)
157    }
158
159    pub fn trigger_and_read_single_channel(&self, ch: ChannelSelect) -> Result<u16, AdcEmptyError> {
160        self.generic_trigger_and_read_single_channel(ch)
161            .map(|v| v & 0xfff)
162    }
163
164    /// Perform a sweep for a specified range of ADC channels.
165    ///
166    /// Returns the number of read values which were written to the passed RX buffer.
167    pub fn sweep_and_read_range(
168        &self,
169        lower_bound_idx: u8,
170        upper_bound_idx: u8,
171        rx_buf: &mut [u16],
172    ) -> Result<usize, AdcRangeReadError> {
173        self.generic_prepare_range_sweep_and_wait_until_ready(
174            lower_bound_idx,
175            upper_bound_idx,
176            rx_buf.len(),
177        )?;
178        let fifo_entry_count = self.adc.status().read().fifo_entry_cnt().bits();
179        for i in 0..core::cmp::min(fifo_entry_count, rx_buf.len() as u8) {
180            rx_buf[i as usize] = self.adc.fifo_data().read().bits() as u16 & 0xfff;
181        }
182        Ok(fifo_entry_count as usize)
183    }
184
185    /// Perform a sweep for selected ADC channels.
186    ///
187    /// Returns the number of read values which were written to the passed RX buffer.
188    pub fn sweep_and_read_multiselect(
189        &self,
190        ch_select: MultiChannelSelect,
191        rx_buf: &mut [u16],
192    ) -> Result<usize, BufferTooSmallError> {
193        self.generic_prepare_multiselect_sweep_and_wait_until_ready(ch_select, rx_buf.len())?;
194        let fifo_entry_count = self.adc.status().read().fifo_entry_cnt().bits();
195        for i in 0..core::cmp::min(fifo_entry_count, rx_buf.len() as u8) {
196            rx_buf[i as usize] = self.adc.fifo_data().read().bits() as u16 & 0xfff;
197        }
198        Ok(fifo_entry_count as usize)
199    }
200
201    pub fn try_read_single_value(&self) -> nb::Result<Option<u16>, ()> {
202        self.generic_try_read_single_value()
203            .map(|v| v.map(|v| v & 0xfff))
204    }
205
206    #[inline(always)]
207    pub fn channel_tag_enabled(&self) -> bool {
208        false
209    }
210}
211
212impl Adc<ChannelTagEnabled> {
213    pub fn new_with_channel_tag(adc: pac::Adc, clocks: &Clocks) -> Self {
214        let mut adc = Self::generic_new(adc, clocks);
215        adc.enable_channel_tag();
216        adc
217    }
218
219    pub fn trigger_and_read_single_channel(
220        &self,
221        ch: ChannelSelect,
222    ) -> Result<ChannelValue, AdcEmptyError> {
223        self.generic_trigger_and_read_single_channel(ch)
224            .map(|v| self.create_channel_value(v))
225    }
226
227    pub fn try_read_single_value(&self) -> nb::Result<Option<ChannelValue>, ()> {
228        self.generic_try_read_single_value()
229            .map(|v| v.map(|v| self.create_channel_value(v)))
230    }
231
232    /// Perform a sweep for a specified range of ADC channels.
233    ///
234    /// Returns the number of read values which were written to the passed RX buffer.
235    pub fn sweep_and_read_range(
236        &self,
237        lower_bound_idx: u8,
238        upper_bound_idx: u8,
239        rx_buf: &mut [ChannelValue],
240    ) -> Result<usize, AdcRangeReadError> {
241        self.generic_prepare_range_sweep_and_wait_until_ready(
242            lower_bound_idx,
243            upper_bound_idx,
244            rx_buf.len(),
245        )?;
246        let fifo_entry_count = self.adc.status().read().fifo_entry_cnt().bits();
247        for i in 0..core::cmp::min(fifo_entry_count, rx_buf.len() as u8) {
248            rx_buf[i as usize] =
249                self.create_channel_value(self.adc.fifo_data().read().bits() as u16);
250        }
251        Ok(fifo_entry_count as usize)
252    }
253
254    /// Perform a sweep for selected ADC channels.
255    ///
256    /// Returns the number of read values which were written to the passed RX buffer.
257    pub fn sweep_and_read_multiselect(
258        &self,
259        ch_select: MultiChannelSelect,
260        rx_buf: &mut [ChannelValue],
261    ) -> Result<usize, BufferTooSmallError> {
262        self.generic_prepare_multiselect_sweep_and_wait_until_ready(ch_select, rx_buf.len())?;
263        let fifo_entry_count = self.adc.status().read().fifo_entry_cnt().bits();
264        for i in 0..core::cmp::min(fifo_entry_count, rx_buf.len() as u8) {
265            rx_buf[i as usize] =
266                self.create_channel_value(self.adc.fifo_data().read().bits() as u16);
267        }
268        Ok(fifo_entry_count as usize)
269    }
270
271    #[inline]
272    pub fn create_channel_value(&self, raw_value: u16) -> ChannelValue {
273        ChannelValue {
274            value: raw_value & 0xfff,
275            channel: ChannelSelect::try_from(((raw_value >> 12) & 0xf) as u8).unwrap(),
276        }
277    }
278
279    #[inline(always)]
280    pub fn channel_tag_enabled(&self) -> bool {
281        true
282    }
283}
284
285impl<TagEnabled> Adc<TagEnabled> {
286    fn generic_new(adc: pac::Adc, _clocks: &Clocks) -> Self {
287        enable_peripheral_clock(PeripheralSelect::Adc);
288        adc.ctrl().write(|w| unsafe { w.bits(0) });
289        let adc = Self {
290            adc,
291            phantom: PhantomData,
292        };
293        adc.clear_fifo();
294        adc
295    }
296
297    #[inline(always)]
298    fn enable_channel_tag(&mut self) {
299        self.adc.ctrl().modify(|_, w| w.chan_tag_en().set_bit());
300    }
301
302    #[inline(always)]
303    fn disable_channel_tag(&mut self) {
304        self.adc.ctrl().modify(|_, w| w.chan_tag_en().clear_bit());
305    }
306
307    #[inline(always)]
308    pub fn clear_fifo(&self) {
309        self.adc.fifo_clr().write(|w| unsafe { w.bits(1) });
310    }
311
312    pub fn generic_try_read_single_value(&self) -> nb::Result<Option<u16>, ()> {
313        if self.adc.status().read().adc_busy().bit_is_set() {
314            return Err(nb::Error::WouldBlock);
315        }
316        if self.adc.status().read().fifo_entry_cnt().bits() == 0 {
317            return Ok(None);
318        }
319        Ok(Some(self.adc.fifo_data().read().bits() as u16))
320    }
321
322    fn generic_trigger_single_channel(&self, ch: ChannelSelect) {
323        self.adc.ctrl().modify(|_, w| {
324            w.ext_trig_en().clear_bit();
325            unsafe {
326                // N + 1 conversions, so set set 0 here.
327                w.conv_cnt().bits(0);
328                w.chan_en().bits(1 << ch as u8)
329            }
330        });
331        self.clear_fifo();
332
333        self.adc.ctrl().modify(|_, w| w.manual_trig().set_bit());
334    }
335
336    fn generic_prepare_range_sweep_and_wait_until_ready(
337        &self,
338        lower_bound_idx: u8,
339        upper_bound_idx: u8,
340        buf_len: usize,
341    ) -> Result<(), AdcRangeReadError> {
342        if (lower_bound_idx > 15 || upper_bound_idx > 15) || lower_bound_idx > upper_bound_idx {
343            return Err(InvalidChannelRangeError.into());
344        }
345        let ch_count = upper_bound_idx - lower_bound_idx + 1;
346        if buf_len < ch_count as usize {
347            return Err(BufferTooSmallError.into());
348        }
349        let mut ch_select = 0;
350        for i in lower_bound_idx..upper_bound_idx + 1 {
351            ch_select |= 1 << i;
352        }
353        self.generic_trigger_sweep(ch_select);
354        while self.adc.status().read().adc_busy().bit_is_set() {
355            cortex_m::asm::nop();
356        }
357        Ok(())
358    }
359
360    fn generic_prepare_multiselect_sweep_and_wait_until_ready(
361        &self,
362        ch_select: MultiChannelSelect,
363        buf_len: usize,
364    ) -> Result<(), BufferTooSmallError> {
365        let ch_select = ch_select.bits();
366        let ch_count = ch_select.count_ones();
367        if buf_len < ch_count as usize {
368            return Err(BufferTooSmallError);
369        }
370        self.generic_trigger_sweep(ch_select);
371        while self.adc.status().read().adc_busy().bit_is_set() {
372            cortex_m::asm::nop();
373        }
374        Ok(())
375    }
376
377    fn generic_trigger_sweep(&self, ch_select: u16) {
378        let ch_num = ch_select.count_ones() as u8;
379        assert!(ch_num > 0);
380        self.adc.ctrl().modify(|_, w| {
381            w.ext_trig_en().clear_bit();
382            unsafe {
383                // N + 1 conversions.
384                w.conv_cnt().bits(0);
385                w.chan_en().bits(ch_select);
386                w.sweep_en().set_bit()
387            }
388        });
389        self.clear_fifo();
390
391        self.adc.ctrl().modify(|_, w| w.manual_trig().set_bit());
392    }
393
394    fn generic_trigger_and_read_single_channel(
395        &self,
396        ch: ChannelSelect,
397    ) -> Result<u16, AdcEmptyError> {
398        self.generic_trigger_single_channel(ch);
399        nb::block!(self.generic_try_read_single_value())
400            .unwrap()
401            .ok_or(AdcEmptyError)
402    }
403}
404
405impl From<Adc<ChannelTagDisabled>> for Adc<ChannelTagEnabled> {
406    fn from(value: Adc<ChannelTagDisabled>) -> Self {
407        let mut adc = Self {
408            adc: value.adc,
409            phantom: PhantomData,
410        };
411        adc.enable_channel_tag();
412        adc
413    }
414}
415
416impl From<Adc<ChannelTagEnabled>> for Adc<ChannelTagDisabled> {
417    fn from(value: Adc<ChannelTagEnabled>) -> Self {
418        let mut adc = Self {
419            adc: value.adc,
420            phantom: PhantomData,
421        };
422        adc.disable_channel_tag();
423        adc
424    }
425}