use atsamd_hal_macros::hal_cfg;
#[hal_cfg("adc-d5x")]
use crate::pac::adc0;
#[hal_cfg(any("adc-d21", "adc-d11"))]
use crate::pac::adc as adc0;
#[hal_cfg(any("adc-d21", "adc-d11"))]
pub use adc0::ctrlb::Prescalerselect as Prescaler;
#[hal_cfg("adc-d5x")]
pub use adc0::ctrla::Prescalerselect as Prescaler;
pub use adc0::avgctrl::Samplenumselect as SampleCount;
pub use adc0::ctrlb::Resselselect as Resolution;
pub use adc0::refctrl::Refselselect as Reference;
use super::{Adc, AdcInstance};
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum AdcResolution {
_8,
_10,
_12,
}
impl From<AdcResolution> for Resolution {
fn from(val: AdcResolution) -> Self {
match val {
AdcResolution::_8 => Resolution::_8bit,
AdcResolution::_10 => Resolution::_10bit,
AdcResolution::_12 => Resolution::_12bit,
}
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Accumulation {
Single(AdcResolution),
Average(SampleCount),
Summed(SampleCount),
}
impl Accumulation {
pub const fn single(res: AdcResolution) -> Self {
Self::Single(res)
}
pub const fn average(count: SampleCount) -> Self {
Self::Average(count)
}
pub const fn summed(count: SampleCount) -> Self {
Self::Summed(count)
}
pub(crate) fn resolution(&self) -> Resolution {
if let Self::Single(res) = self {
(*res).into()
} else {
Resolution::_16bit
}
}
pub(crate) fn output_resolution(&self) -> Resolution {
if let Self::Single(res) = self {
(*res).into()
} else if let Self::Average(_) = self {
Resolution::_12bit
} else {
Resolution::_16bit
}
}
pub(crate) fn samples(&self) -> u16 {
match self {
Accumulation::Single(_) => 1,
Accumulation::Average(samplenumselect) => 2u16.pow(*samplenumselect as u32),
Accumulation::Summed(samplenumselect) => 2u16.pow(*samplenumselect as u32),
}
}
}
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum BuilderError {
MissingClockDiv,
MissingSampleClocks,
MissingVref,
AdcError(super::Error),
}
impl From<super::Error> for BuilderError {
fn from(value: super::Error) -> Self {
Self::AdcError(value)
}
}
#[derive(Copy, Clone)]
pub struct AdcBuilder {
pub clk_divider: Option<Prescaler>,
pub sample_clock_cycles: Option<u8>,
pub accumulation: Accumulation,
pub vref: Option<Reference>,
}
#[derive(Copy, Clone, PartialEq)]
pub(crate) struct AdcSettings {
pub clk_divider: Prescaler,
pub sample_clock_cycles: u8,
pub accumulation: Accumulation,
pub vref: Reference,
}
impl AdcBuilder {
pub fn new(accumulation_method: Accumulation) -> Self {
Self {
clk_divider: None,
sample_clock_cycles: None,
accumulation: accumulation_method,
vref: None,
}
}
pub(crate) fn check_params(&self) -> Result<(), BuilderError> {
self.clk_divider.ok_or(BuilderError::MissingClockDiv)?;
self.sample_clock_cycles
.ok_or(BuilderError::MissingSampleClocks)?;
self.vref.ok_or(BuilderError::MissingVref)?;
Ok(())
}
pub(crate) fn to_settings(self) -> Result<AdcSettings, BuilderError> {
self.check_params()?;
Ok(AdcSettings {
clk_divider: self.clk_divider.unwrap(),
sample_clock_cycles: self.sample_clock_cycles.unwrap(),
accumulation: self.accumulation,
vref: self.vref.unwrap(),
})
}
pub fn with_clock_divider(mut self, div: Prescaler) -> Self {
self.clk_divider = Some(div);
self
}
pub fn with_vref(mut self, reference: Reference) -> Self {
self.vref = Some(reference);
self
}
pub fn with_clock_cycles_per_sample(mut self, num: u8) -> Self {
self.sample_clock_cycles = Some(num.clamp(1, 63)); self
}
pub fn calculate_sps(&self, clock_freq: u32) -> Result<u32, BuilderError> {
self.check_params()?;
let div = self.clk_divider.unwrap() as u32;
let adc_clk_freq = clock_freq / div;
let bit_width = match self.accumulation.resolution() {
Resolution::_16bit => 16,
Resolution::_12bit => 12,
Resolution::_10bit => 10,
Resolution::_8bit => 8,
};
let mut clocks_per_sample = self.sample_clock_cycles.unwrap() as u32 + bit_width;
let samples = self.accumulation.samples();
clocks_per_sample *= samples as u32;
Ok(adc_clk_freq / clocks_per_sample)
}
#[hal_cfg("adc-d5x")]
#[inline]
pub fn enable<I: AdcInstance, PS: crate::clock::v2::pclk::PclkSourceId>(
self,
adc: I::Instance,
clk: crate::clock::v2::apb::ApbClk<I::ClockId>,
pclk: &crate::clock::v2::pclk::Pclk<I::ClockId, PS>,
) -> Result<Adc<I>, BuilderError> {
let settings = self.to_settings()?;
Adc::new(adc, settings, clk, pclk).map_err(|e| e.into())
}
#[hal_cfg(any("adc-d11", "adc-d21"))]
#[inline]
pub fn enable<I: AdcInstance>(
self,
adc: I::Instance,
pm: &mut crate::pac::Pm,
clock: &crate::clock::AdcClock,
) -> Result<Adc<I>, BuilderError> {
let settings = self.to_settings()?;
Adc::new(adc, settings, pm, clock).map_err(|e| e.into())
}
}