va416xx-rs/va416xx-hal/src/adc.rs
Robin Mueller a2a4b5ff01
All checks were successful
Rust/va416xx-rs/pipeline/head This commit looks good
small improvements and fixes
2024-07-01 16:02:10 +02:00

435 lines
14 KiB
Rust

//! 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<InvalidChannelRangeError> for AdcRangeReadError {
fn from(value: InvalidChannelRangeError) -> Self {
AdcRangeReadError::InvalidChannelRange(value)
}
}
impl From<BufferTooSmallError> 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<TagEnabled = ChannelTagDisabled> {
adc: pac::Adc,
phantom: PhantomData<TagEnabled>,
}
impl Adc<ChannelTagEnabled> {}
impl Adc<ChannelTagDisabled> {
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<u16, AdcEmptyError> {
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<usize, AdcRangeReadError> {
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<usize, BufferTooSmallError> {
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<Option<u16>, ()> {
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<ChannelTagEnabled> {
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<ChannelValue, AdcEmptyError> {
self.generic_trigger_and_read_single_channel(ch)
.map(|v| self.create_channel_value(v))
}
pub fn try_read_single_value(&self) -> nb::Result<Option<ChannelValue>, ()> {
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<usize, AdcRangeReadError> {
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<usize, BufferTooSmallError> {
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<TagEnabled> Adc<TagEnabled> {
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<Option<u16>, ()> {
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<u16, AdcEmptyError> {
self.generic_trigger_single_channel(ch);
nb::block!(self.generic_try_read_single_value())
.unwrap()
.ok_or(AdcEmptyError)
}
}
impl From<Adc<ChannelTagDisabled>> for Adc<ChannelTagEnabled> {
fn from(value: Adc<ChannelTagDisabled>) -> Self {
let mut adc = Self {
adc: value.adc,
phantom: PhantomData,
};
adc.enable_channel_tag();
adc
}
}
impl From<Adc<ChannelTagEnabled>> for Adc<ChannelTagDisabled> {
fn from(value: Adc<ChannelTagEnabled>) -> Self {
let mut adc = Self {
adc: value.adc,
phantom: PhantomData,
};
adc.disable_channel_tag();
adc
}
}