Skip to main content

va416xx_hal/
adc.rs

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