//! Analog to Digital Converter (ADC) driver. //! //! ## Examples //! //! - [ADC and DAC example](https://github.com/us-irs/va416xx-rs/blob/main/examples/simple/examples/dac-adc.rs) //! - [ADC](https://github.com/us-irs/va416xx-rs/blob/main/examples/simple/examples/adc.rs) use core::marker::PhantomData; use crate::clock::Clocks; use crate::pac; use crate::prelude::*; use crate::time::Hertz; use num_enum::{IntoPrimitive, TryFromPrimitive}; pub const ADC_MIN_CLK: Hertz = Hertz::from_raw(2_000_000); pub const ADC_MAX_CLK: Hertz = Hertz::from_raw(12_500_000); #[derive(Debug, PartialEq, Eq, Copy, Clone, TryFromPrimitive, IntoPrimitive)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] pub enum ChannelSelect { /// Analogue Input 0 external channel AnIn0 = 0, /// Analogue Input 1 external channel AnIn1 = 1, /// Analogue Input 2 external channel AnIn2 = 2, /// Analogue Input 3 external channel AnIn3 = 3, /// Analogue Input 4 external channel AnIn4 = 4, /// Analogue Input 5 external channel AnIn5 = 5, /// Analogue Input 6 external channel AnIn6 = 6, /// Analogue Input 7 external channel AnIn7 = 7, /// DAC 0 internal channel Dac0 = 8, /// DAC 1 internal channel Dac1 = 9, /// Internal temperature sensor TempSensor = 10, /// Internal bandgap 1 V reference Bandgap1V = 11, /// Internal bandgap 1.5 V reference Bandgap1_5V = 12, Avdd1_5 = 13, Dvdd1_5 = 14, /// Internally generated Voltage equal to VREFH / 2 Vrefp5 = 15, } bitflags::bitflags! { /// This structure is used by the ADC multi-select API to /// allow selecting multiple channels in a convenient manner. pub struct MultiChannelSelect: u16 { const AnIn0 = 1; const AnIn1 = 1 << 1; const AnIn2 = 1 << 2; const AnIn3 = 1 << 3; const AnIn4 = 1 << 4; const AnIn5 = 1 << 5; const AnIn6 = 1 << 6; const AnIn7 = 1 << 7; const Dac0 = 1 << 8; const Dac1 = 1 << 9; const TempSensor = 1 << 10; const Bandgap1V = 1 << 11; const Bandgap1_5V = 1 << 12; const Avdd1_5 = 1 << 13; const Dvdd1_5 = 1 << 14; const Vrefp5 = 1 << 15; } } #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct AdcEmptyError; #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct InvalidChannelRangeError; #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct BufferTooSmallError; #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AdcRangeReadError { InvalidChannelRange(InvalidChannelRangeError), BufferTooSmall(BufferTooSmallError), } impl From for AdcRangeReadError { fn from(value: InvalidChannelRangeError) -> Self { AdcRangeReadError::InvalidChannelRange(value) } } impl From for AdcRangeReadError { fn from(value: BufferTooSmallError) -> Self { AdcRangeReadError::BufferTooSmall(value) } } #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct ChannelValue { /// If the channel tag is enabled, this field will contain the determined channel tag. channel: ChannelSelect, /// Raw value. value: u16, } impl Default for ChannelValue { fn default() -> Self { Self { channel: ChannelSelect::AnIn0, value: Default::default(), } } } impl ChannelValue { #[inline] pub fn value(&self) -> u16 { self.value } #[inline] pub fn channel(&self) -> ChannelSelect { self.channel } } pub enum ChannelTagEnabled {} pub enum ChannelTagDisabled {} /// ADC driver structure. /// /// Currently, this structure supports three primary ways to measure channel value(s): /// /// * Trigger and read a single value /// * Trigger and read a range of ADC values using the sweep functionality /// * Trigger and read multiple ADC values using the sweep functionality /// /// The ADC channel tag feature is enabled or disabled at compile time using the /// [ChannelTagEnabled] and [ChannelTagDisabled]. The [Adc::new] method returns a driver instance /// with the channel tag enabled, while the [Adc::new_with_channel_tag] method can be used to /// return an instance with the channel tag enabled. pub struct Adc { adc: pac::Adc, phantom: PhantomData, } impl Adc {} impl Adc { pub fn new(syscfg: &mut pac::Sysconfig, adc: pac::Adc, clocks: &Clocks) -> Self { Self::generic_new(syscfg, adc, clocks) } pub fn trigger_and_read_single_channel(&self, ch: ChannelSelect) -> Result { self.generic_trigger_and_read_single_channel(ch) .map(|v| v & 0xfff) } /// Perform a sweep for a specified range of ADC channels. /// /// Returns the number of read values which were written to the passed RX buffer. pub fn sweep_and_read_range( &self, lower_bound_idx: u8, upper_bound_idx: u8, rx_buf: &mut [u16], ) -> Result { self.generic_prepare_range_sweep_and_wait_until_ready( lower_bound_idx, upper_bound_idx, rx_buf.len(), )?; let fifo_entry_count = self.adc.status().read().fifo_entry_cnt().bits(); for i in 0..core::cmp::min(fifo_entry_count, rx_buf.len() as u8) { rx_buf[i as usize] = self.adc.fifo_data().read().bits() as u16 & 0xfff; } Ok(fifo_entry_count as usize) } /// Perform a sweep for selected ADC channels. /// /// Returns the number of read values which were written to the passed RX buffer. pub fn sweep_and_read_multiselect( &self, ch_select: MultiChannelSelect, rx_buf: &mut [u16], ) -> Result { self.generic_prepare_multiselect_sweep_and_wait_until_ready(ch_select, rx_buf.len())?; let fifo_entry_count = self.adc.status().read().fifo_entry_cnt().bits(); for i in 0..core::cmp::min(fifo_entry_count, rx_buf.len() as u8) { rx_buf[i as usize] = self.adc.fifo_data().read().bits() as u16 & 0xfff; } Ok(fifo_entry_count as usize) } pub fn try_read_single_value(&self) -> nb::Result, ()> { self.generic_try_read_single_value() .map(|v| v.map(|v| v & 0xfff)) } #[inline(always)] pub fn channel_tag_enabled(&self) -> bool { false } } impl Adc { pub fn new_with_channel_tag( syscfg: &mut pac::Sysconfig, adc: pac::Adc, clocks: &Clocks, ) -> Self { let mut adc = Self::generic_new(syscfg, adc, clocks); adc.enable_channel_tag(); adc } pub fn trigger_and_read_single_channel( &self, ch: ChannelSelect, ) -> Result { self.generic_trigger_and_read_single_channel(ch) .map(|v| self.create_channel_value(v)) } pub fn try_read_single_value(&self) -> nb::Result, ()> { self.generic_try_read_single_value() .map(|v| v.map(|v| self.create_channel_value(v))) } /// Perform a sweep for a specified range of ADC channels. /// /// Returns the number of read values which were written to the passed RX buffer. pub fn sweep_and_read_range( &self, lower_bound_idx: u8, upper_bound_idx: u8, rx_buf: &mut [ChannelValue], ) -> Result { self.generic_prepare_range_sweep_and_wait_until_ready( lower_bound_idx, upper_bound_idx, rx_buf.len(), )?; let fifo_entry_count = self.adc.status().read().fifo_entry_cnt().bits(); for i in 0..core::cmp::min(fifo_entry_count, rx_buf.len() as u8) { rx_buf[i as usize] = self.create_channel_value(self.adc.fifo_data().read().bits() as u16); } Ok(fifo_entry_count as usize) } /// Perform a sweep for selected ADC channels. /// /// Returns the number of read values which were written to the passed RX buffer. pub fn sweep_and_read_multiselect( &self, ch_select: MultiChannelSelect, rx_buf: &mut [ChannelValue], ) -> Result { self.generic_prepare_multiselect_sweep_and_wait_until_ready(ch_select, rx_buf.len())?; let fifo_entry_count = self.adc.status().read().fifo_entry_cnt().bits(); for i in 0..core::cmp::min(fifo_entry_count, rx_buf.len() as u8) { rx_buf[i as usize] = self.create_channel_value(self.adc.fifo_data().read().bits() as u16); } Ok(fifo_entry_count as usize) } #[inline] pub fn create_channel_value(&self, raw_value: u16) -> ChannelValue { ChannelValue { value: raw_value & 0xfff, channel: ChannelSelect::try_from(((raw_value >> 12) & 0xf) as u8).unwrap(), } } #[inline(always)] pub fn channel_tag_enabled(&self) -> bool { true } } impl Adc { fn generic_new(syscfg: &mut pac::Sysconfig, adc: pac::Adc, _clocks: &Clocks) -> Self { syscfg.enable_peripheral_clock(crate::clock::PeripheralSelect::Adc); adc.ctrl().write(|w| unsafe { w.bits(0) }); let adc = Self { adc, phantom: PhantomData, }; adc.clear_fifo(); adc } #[inline(always)] fn enable_channel_tag(&mut self) { self.adc.ctrl().modify(|_, w| w.chan_tag_en().set_bit()); } #[inline(always)] fn disable_channel_tag(&mut self) { self.adc.ctrl().modify(|_, w| w.chan_tag_en().clear_bit()); } #[inline(always)] pub fn clear_fifo(&self) { self.adc.fifo_clr().write(|w| unsafe { w.bits(1) }); } pub fn generic_try_read_single_value(&self) -> nb::Result, ()> { if self.adc.status().read().adc_busy().bit_is_set() { return Err(nb::Error::WouldBlock); } if self.adc.status().read().fifo_entry_cnt().bits() == 0 { return Ok(None); } Ok(Some(self.adc.fifo_data().read().bits() as u16)) } fn generic_trigger_single_channel(&self, ch: ChannelSelect) { self.adc.ctrl().modify(|_, w| { w.ext_trig_en().clear_bit(); unsafe { // N + 1 conversions, so set set 0 here. w.conv_cnt().bits(0); w.chan_en().bits(1 << ch as u8) } }); self.clear_fifo(); self.adc.ctrl().modify(|_, w| w.manual_trig().set_bit()); } fn generic_prepare_range_sweep_and_wait_until_ready( &self, lower_bound_idx: u8, upper_bound_idx: u8, buf_len: usize, ) -> Result<(), AdcRangeReadError> { if (lower_bound_idx > 15 || upper_bound_idx > 15) || lower_bound_idx > upper_bound_idx { return Err(InvalidChannelRangeError.into()); } let ch_count = upper_bound_idx - lower_bound_idx + 1; if buf_len < ch_count as usize { return Err(BufferTooSmallError.into()); } let mut ch_select = 0; for i in lower_bound_idx..upper_bound_idx + 1 { ch_select |= 1 << i; } self.generic_trigger_sweep(ch_select); while self.adc.status().read().adc_busy().bit_is_set() { cortex_m::asm::nop(); } Ok(()) } fn generic_prepare_multiselect_sweep_and_wait_until_ready( &self, ch_select: MultiChannelSelect, buf_len: usize, ) -> Result<(), BufferTooSmallError> { let ch_select = ch_select.bits(); let ch_count = ch_select.count_ones(); if buf_len < ch_count as usize { return Err(BufferTooSmallError); } self.generic_trigger_sweep(ch_select); while self.adc.status().read().adc_busy().bit_is_set() { cortex_m::asm::nop(); } Ok(()) } fn generic_trigger_sweep(&self, ch_select: u16) { let ch_num = ch_select.count_ones() as u8; assert!(ch_num > 0); self.adc.ctrl().modify(|_, w| { w.ext_trig_en().clear_bit(); unsafe { // N + 1 conversions. w.conv_cnt().bits(0); w.chan_en().bits(ch_select); w.sweep_en().set_bit() } }); self.clear_fifo(); self.adc.ctrl().modify(|_, w| w.manual_trig().set_bit()); } fn generic_trigger_and_read_single_channel( &self, ch: ChannelSelect, ) -> Result { self.generic_trigger_single_channel(ch); nb::block!(self.generic_try_read_single_value()) .unwrap() .ok_or(AdcEmptyError) } } impl From> for Adc { fn from(value: Adc) -> Self { let mut adc = Self { adc: value.adc, phantom: PhantomData, }; adc.enable_channel_tag(); adc } } impl From> for Adc { fn from(value: Adc) -> Self { let mut adc = Self { adc: value.adc, phantom: PhantomData, }; adc.disable_channel_tag(); adc } }