use core::ops::Deref;
use atsamd_hal_macros::{hal_cfg, hal_module};
use pac::Peripherals;
use crate::{gpio::AnyPin, pac, typelevel::Sealed};
#[hal_module(
any("adc-d11", "adc-d21") => "d11/mod.rs",
"adc-d5x" => "d5x/mod.rs",
)]
mod impls {}
pub use impls::*;
#[cfg(feature = "async")]
mod async_api;
#[cfg(feature = "async")]
pub use async_api::*;
mod builder;
pub use builder::*;
#[hal_cfg(any("adc-d11", "adc-d21"))]
use crate::pac::adc as adc0;
#[hal_cfg("adc-d5x")]
use crate::pac::adc0;
pub use adc0::refctrl::Refselselect as Reference;
const ADC_SETTINGS_INTERNAL_READ: AdcSettings = AdcSettings {
clk_divider: Prescaler::Div64,
sample_clock_cycles: 32,
accumulation: Accumulation::average(SampleCount::_4),
vref: Reference::Intvcc1,
};
#[hal_cfg(any("adc-d21", "adc-d11"))]
const ADC_SETTINGS_INTERNAL_READ_D21_TEMP: AdcSettings = AdcSettings {
clk_divider: Prescaler::Div64,
sample_clock_cycles: 32,
accumulation: Accumulation::average(SampleCount::_4),
vref: Reference::Int1v,
};
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
ClockTooFast,
BufferOverrun,
}
#[hal_cfg("adc-d5x")]
#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(u8)]
pub enum CpuVoltageSource {
Core = 0x18,
Vbat = 0x19,
Io = 0x1A,
}
#[hal_cfg(any("adc-d21", "adc-d11"))]
#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(u8)]
pub enum CpuVoltageSource {
Bandgap = 0x19,
Core = 0x1A,
Io = 0x1B,
}
bitflags::bitflags! {
#[derive(Clone, Copy)]
pub struct Flags: u8 {
const WINMON = 0x04;
const OVERRUN = 0x02;
const RESRDY = 0x01;
}
}
pub trait PrimaryAdc {}
pub trait AdcInstance {
#[cfg(feature = "async")]
type Interrupt: crate::async_hal::interrupts::InterruptSource;
type Instance: Deref<Target = adc0::RegisterBlock>;
#[hal_cfg("adc-d5x")]
type ClockId: crate::clock::v2::apb::ApbId + crate::clock::v2::pclk::PclkId;
fn peripheral_reg_block(p: &mut Peripherals) -> &adc0::RegisterBlock;
#[hal_cfg(any("adc-d11", "adc-d21"))]
fn enable_pm(pm: &mut pac::Pm);
fn calibrate(instance: &Self::Instance);
#[cfg(feature = "async")]
fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker;
}
pub trait AdcPin<I>: AnyPin<Mode = crate::gpio::AlternateB> + Sealed
where
I: AdcInstance,
{
const CHANNEL: u8;
}
#[hal_cfg(any("adc-d11", "adc-d21"))]
pub struct Adc<I: AdcInstance> {
adc: I::Instance,
cfg: AdcSettings,
discard: bool,
}
#[hal_cfg("adc-d5x")]
pub struct Adc<I: AdcInstance> {
adc: I::Instance,
_apbclk: crate::clock::v2::apb::ApbClk<I::ClockId>,
cfg: AdcSettings,
discard: bool,
}
#[cfg(feature = "async")]
pub struct FutureAdc<I: AdcInstance, F> {
inner: Adc<I>,
irqs: F,
}
impl<I: AdcInstance> Adc<I> {
#[hal_cfg("adc-d5x")]
#[inline]
pub(crate) fn new<PS: crate::clock::v2::pclk::PclkSourceId>(
adc: I::Instance,
settings: AdcSettings,
clk: crate::clock::v2::apb::ApbClk<I::ClockId>,
pclk: &crate::clock::v2::pclk::Pclk<I::ClockId, PS>,
) -> Result<Self, Error> {
if pclk.freq() > fugit::HertzU32::from_raw(100_000_000) {
return Err(Error::ClockTooFast);
}
let mut new_adc = Self {
adc,
_apbclk: clk,
cfg: settings,
discard: true,
};
new_adc.configure(settings);
Ok(new_adc)
}
#[hal_cfg(any("adc-d11", "adc-d21"))]
#[inline]
pub(crate) fn new(
adc: I::Instance,
settings: AdcSettings,
pm: &mut pac::Pm,
clock: &crate::clock::AdcClock,
) -> Result<Self, Error> {
if (clock.freq() as crate::time::Hertz).to_Hz() > 48_000_000 {
return Err(Error::ClockTooFast);
}
I::enable_pm(pm);
let mut new_adc = Self {
adc,
cfg: settings,
discard: true,
};
new_adc.configure(settings);
Ok(new_adc)
}
#[cfg(feature = "async")]
#[atsamd_hal_macros::hal_macro_helper]
#[inline]
pub fn into_future<F>(self, irqs: F) -> FutureAdc<I, F>
where
F: crate::async_hal::interrupts::Binding<I::Interrupt, async_api::InterruptHandler<I>>,
{
use crate::async_hal::interrupts::InterruptSource;
unsafe {
I::Interrupt::unpend();
I::Interrupt::enable();
}
FutureAdc { inner: self, irqs }
}
}
impl<I: AdcInstance> Adc<I> {
fn reading_to_f32(&self, raw: u16) -> f32 {
let max = match self.cfg.accumulation.output_resolution() {
Resolution::_16bit => 65535,
Resolution::_12bit => 4095,
Resolution::_10bit => 1023,
Resolution::_8bit => 255,
};
raw as f32 / max as f32
}
pub(crate) fn with_specific_settings<F: FnOnce(&mut Adc<I>) -> T, T>(
&mut self,
settings: AdcSettings,
f: F,
) -> T {
let old_cfg = self.cfg;
self.configure(settings);
let ret = f(self);
self.configure(old_cfg);
ret
}
#[inline]
fn set_reference(&mut self, reference: Reference) {
self.adc
.refctrl()
.modify(|_, w| w.refsel().variant(reference));
self.sync();
}
#[inline]
pub fn read<P: AdcPin<I>>(&mut self, _pin: &mut P) -> u16 {
self.read_channel(P::CHANNEL)
}
#[inline]
fn read_channel(&mut self, ch: u8) -> u16 {
self.clear_all_flags();
self.disable_interrupts(Flags::all());
self.disable_freerunning();
self.sync();
self.mux(ch);
self.check_read_discard();
self.start_conversion();
while !self.read_flags().contains(Flags::RESRDY) {
core::hint::spin_loop();
}
self.conversion_result()
}
#[inline]
pub fn check_read_discard(&mut self) {
if self.discard {
self.start_conversion();
while !self.read_flags().contains(Flags::RESRDY) {
core::hint::spin_loop();
}
self.clear_all_flags();
self.discard = false;
}
}
#[inline]
pub fn read_buffer<P: AdcPin<I>>(
&mut self,
_pin: &mut P,
dst: &mut [u16],
) -> Result<(), Error> {
self.read_buffer_channel(P::CHANNEL, dst)
}
#[inline]
fn read_buffer_channel(&mut self, ch: u8, dst: &mut [u16]) -> Result<(), Error> {
self.clear_all_flags();
self.disable_interrupts(Flags::all());
self.mux(ch);
self.enable_freerunning();
self.start_conversion();
self.check_read_discard();
for result in dst.iter_mut() {
while !self.read_flags().contains(Flags::RESRDY) {
core::hint::spin_loop();
}
let flags = self.read_flags();
self.clear_all_flags();
if let Err(e) = self.check_overrun(&flags) {
self.disable_freerunning();
return Err(e);
}
*result = self.conversion_result();
}
self.disable_freerunning();
Ok(())
}
#[hal_cfg(any("adc-d11", "adc-d21"))]
#[inline]
pub fn free(mut self) -> I::Instance {
self.software_reset();
self.adc
}
#[hal_cfg("adc-d5x")]
#[inline]
pub fn free(mut self) -> (I::Instance, crate::clock::v2::apb::ApbClk<I::ClockId>) {
self.software_reset();
(self.adc, self._apbclk)
}
#[inline]
fn software_reset(&mut self) {
self.adc.ctrla().modify(|_, w| w.swrst().set_bit());
self.sync();
}
}
#[cfg(feature = "async")]
impl<I: AdcInstance, F> FutureAdc<I, F>
where
F: crate::async_hal::interrupts::Binding<I::Interrupt, async_api::InterruptHandler<I>>,
{
pub fn into_blocking(self) -> (Adc<I>, F) {
(self.inner, self.irqs)
}
#[inline]
pub async fn read<P: AdcPin<I>>(&mut self, _pin: &mut P) -> u16 {
self.read_channel(P::CHANNEL).await
}
#[inline]
async fn read_channel(&mut self, ch: u8) -> u16 {
self.inner.clear_all_flags();
self.inner.disable_freerunning();
self.inner.mux(ch);
if self.inner.discard {
self.inner.start_conversion();
let _ = self.wait_flags(Flags::RESRDY).await;
self.inner.discard = false;
self.inner.clear_all_flags();
}
self.inner.start_conversion();
let _ = self.wait_flags(Flags::RESRDY).await;
let res = self.inner.conversion_result();
self.inner.sync();
res
}
#[inline]
pub async fn read_buffer<P: AdcPin<I>>(
&mut self,
_pin: &mut P,
dst: &mut [u16],
) -> Result<(), Error> {
self.read_buffer_channel(P::CHANNEL, dst).await
}
#[inline]
async fn read_buffer_channel(&mut self, ch: u8, dst: &mut [u16]) -> Result<(), Error> {
self.inner.clear_all_flags();
self.inner.mux(ch);
self.inner.enable_freerunning();
if self.inner.discard {
let _ = self.wait_flags(Flags::RESRDY).await;
let _ = self.inner.conversion_result();
self.inner.discard = false;
self.inner.clear_all_flags();
}
for result in dst.iter_mut() {
if let Err(e) = self.wait_flags(Flags::RESRDY).await {
self.inner.disable_freerunning();
return Err(e);
}
*result = self.inner.conversion_result();
}
self.inner.disable_freerunning();
Ok(())
}
}