From 93cb1d5e2847de68e36c2bd108c70c46b7be537a Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Wed, 14 Jan 2026 11:23:59 -0500 Subject: [PATCH 01/39] added ability to trigger ADC flush, added call in Adc::read_channel() --- hal/src/peripherals/adc/d5x/mod.rs | 6 ++++++ hal/src/peripherals/adc/mod.rs | 1 + 2 files changed, 7 insertions(+) diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 9a8d87cbf993..828177eba54b 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -290,6 +290,12 @@ impl Adc { }); self.sync() } + + #[inline] + pub(super) fn flush(&mut self) { + self.adc.swtrig().modify(|_,w| w.flush().set_bit()); + self.sync(); + } } #[cfg(feature = "async")] diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index c34b560ad113..b3a04bf1457f 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -346,6 +346,7 @@ impl Adc { while !self.read_flags().contains(Flags::RESRDY) { core::hint::spin_loop(); } + self.flush(); self.conversion_result() } From 94936275cc44b5bb4f9557fcf50516389344efbf Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Fri, 30 Jan 2026 15:26:28 -0500 Subject: [PATCH 02/39] initial implementation to support differential sampling --- hal/src/peripherals/adc/d5x/channel.rs | 99 ++++++++++++++++ hal/src/peripherals/adc/d5x/mod.rs | 65 ++++++++--- hal/src/peripherals/adc/d5x/pin.rs | 127 ++++++++++++++------ hal/src/peripherals/adc/input.rs | 153 +++++++++++++++++++++++++ hal/src/peripherals/adc/mod.rs | 43 ++++--- 5 files changed, 413 insertions(+), 74 deletions(-) create mode 100644 hal/src/peripherals/adc/d5x/channel.rs create mode 100644 hal/src/peripherals/adc/input.rs diff --git a/hal/src/peripherals/adc/d5x/channel.rs b/hal/src/peripherals/adc/d5x/channel.rs new file mode 100644 index 000000000000..4e0cea7377cf --- /dev/null +++ b/hal/src/peripherals/adc/d5x/channel.rs @@ -0,0 +1,99 @@ +use core::marker::PhantomData; +use crate::adc::*; +use crate::{ + pac::adc0::inputctrl::{Muxposselect, Muxnegselect}, + typelevel::Sealed, +}; + +macro_rules! channel { + ( + $( + $CH:ident: ($($PMUX:path)?, $($NMUX:path)?) + ),+ + $(,)? + ) => { + crate::paste::paste!{ + $( + pub struct $CH { + adc: PhantomData, + } + + impl Sealed for $CH {} + + $( + impl PosChannel for $CH { + const MUXVAL: Muxposselect = $PMUX; + } + )? + $( + impl NegChannel for $CH { + const MUXVAL: Muxnegselect = $NMUX; + } + )? + + impl $CH { + pub fn get_channel() -> Self { + Self { + adc: PhantomData + } + } + } + )+ + } + }; +} + +channel! { + AIN0: (Muxposselect::Ain0, Muxnegselect::Ain0), + AIN1: (Muxposselect::Ain1, Muxnegselect::Ain1), + AIN2: (Muxposselect::Ain2, Muxnegselect::Ain2), + AIN3: (Muxposselect::Ain3, Muxnegselect::Ain3), + AIN4: (Muxposselect::Ain4, Muxnegselect::Ain4), + AIN5: (Muxposselect::Ain5, Muxnegselect::Ain5), + AIN6: (Muxposselect::Ain6, Muxnegselect::Ain6), + AIN7: (Muxposselect::Ain7, Muxnegselect::Ain7), + AIN8: (Muxposselect::Ain8, ), + AIN9: (Muxposselect::Ain9, ), + AIN10: (Muxposselect::Ain10, ), + AIN11: (Muxposselect::Ain11, ), + AIN12: (Muxposselect::Ain12, ), + AIN13: (Muxposselect::Ain13, ), + AIN14: (Muxposselect::Ain14, ), + AIN15: (Muxposselect::Ain15, ), + SCALEDCOREVCC: (Muxposselect::Scaledcorevcc, ), + SCALEDVBAT: (Muxposselect::Scaledvbat, ), + SCALEDIOVCC: (Muxposselect::Scalediovcc, ), + BANDGAP: (Muxposselect::Bandgap, ), + PTAT: (Muxposselect::Ptat, ), + CTAT: (Muxposselect::Ctat, ), + GND: (, Muxnegselect::Gnd), +} + +macro_rules! primary_channel { + ( + $( + $CH:ident: ($($PMUX:path)?, $($NMUX:path)?) + ),+ + $(,)? + ) => { + $( + pub struct $CH { + adc: PhantomData, + } + + impl Sealed for $CH {} + + $( + impl PosChannel for $CH { + const MUXVAL: Muxposselect = $PMUX; + } + )? + $( + impl PosChannel for $CH { + const MUXVAL: Muxnegselect = $NMUX; + } + )? + )+ + }; +} + diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 828177eba54b..bcb814627456 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -1,4 +1,5 @@ pub mod pin; +pub mod channel; use pac::Supc; @@ -7,7 +8,7 @@ use super::{FutureAdc, async_api}; use super::{ ADC_SETTINGS_INTERNAL_READ, Accumulation, Adc, AdcInstance, AdcSettings, CpuVoltageSource, - Error, Flags, PrimaryAdc, SampleCount, + Error, Flags, PrimaryAdc, SampleCount, SampleMode, }; use crate::{calibration, pac}; @@ -110,9 +111,7 @@ impl Adc { self.sync(); I::calibrate(&self.adc); self.sync(); - self.adc - .ctrla() - .modify(|_, w| w.prescaler().variant(cfg.clk_divider)); + self.adc.ctrla().modify(|_, w| { w.prescaler().variant(cfg.clk_divider) }); self.sync(); self.adc .ctrlb() @@ -123,11 +122,14 @@ impl Adc { .sampctrl() .modify(|_, w| unsafe { w.samplen().bits(cfg.sample_clock_cycles.saturating_sub(1)) }); // sample length self.sync(); - self.adc.inputctrl().modify(|_, w| { - w.muxneg().gnd(); - w.diffmode().clear_bit() - }); // No negative input (internal gnd) - self.sync(); +// let diffmode = match cfg.sample_mode { +// SampleMode::SingleEnded => false, +// SampleMode::Differential => true, +// }; +// self.adc.inputctrl().modify(|_, w| { +// w.diffmode().bit(diffmode) +// }); +// self.sync(); let (sample_cnt, adjres) = match cfg.accumulation { // 1 sample to be used as is Accumulation::Single(_) => (SampleCount::_1, 0), @@ -169,8 +171,14 @@ impl Adc { let (tp, tc) = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ, |adc| { ( - adc.read_channel(0x1C) as f32, // Tp - adc.read_channel(0x1D) as f32, // Tc + adc.read_channel( + pac::adc0::inputctrl::Muxposselect::Ptat, + pac::adc0::inputctrl::Muxnegselect::Gnd, + SampleMode::SingleEnded) as f32, // Tp + adc.read_channel( + pac::adc0::inputctrl::Muxposselect::Ctat, + pac::adc0::inputctrl::Muxnegselect::Gnd, + SampleMode::SingleEnded) as f32, // Tc ) }); // Restore vrefs old state @@ -181,11 +189,12 @@ impl Adc { #[inline] pub fn read_cpu_voltage(&mut self, src: CpuVoltageSource) -> u16 { - let voltage = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ, |adc| { - let res = adc.read_channel(src as u8); - adc.reading_to_f32(res) * 3.3 * 4.0 // x4 since the voltages are 1/4 scaled - }); - (voltage * 1000.0) as u16 + //let voltage = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ, |adc| { + // let res = adc.read_channel(src as u8, None); + // adc.reading_to_f32(res) * 3.3 * 4.0 // x4 since the voltages are 1/4 scaled + //}); + //(voltage * 1000.0) as u16 + 0 } } @@ -281,12 +290,30 @@ impl Adc { } #[inline] - pub(super) fn mux(&mut self, ch: u8) { + pub(super) fn mux( + &mut self, + pos_ch: pac::adc0::inputctrl::Muxposselect, + neg_ch: pac::adc0::inputctrl::Muxnegselect, + sample_mode: SampleMode, + ) { self.adc.inputctrl().modify(|r, w| { - if r.muxpos().bits() != ch { + if r.muxpos().bits() != pos_ch.into() { + self.discard = true; + } + if r.muxneg().bits() != neg_ch.into() { self.discard = true; } - unsafe { w.muxpos().bits(ch) } + + // Safe as pos_ch and neg_ch are derived from Muxposselect and Muxnegselect PAC enums + unsafe { + w.muxpos().bits(pos_ch.into()); + w.muxneg().bits(neg_ch.into()); + } + + match sample_mode { + SampleMode::SingleEnded => w.diffmode().clear_bit(), + SampleMode::Differential => w.diffmode().set_bit(), + } }); self.sync() } diff --git a/hal/src/peripherals/adc/d5x/pin.rs b/hal/src/peripherals/adc/d5x/pin.rs index e7664e509cd5..2757d198d876 100644 --- a/hal/src/peripherals/adc/d5x/pin.rs +++ b/hal/src/peripherals/adc/d5x/pin.rs @@ -2,89 +2,140 @@ use crate::adc::*; use crate::gpio::pin::*; use atsamd_hal_macros::hal_cfg; -macro_rules! adc_pins { +macro_rules! pos_adc_pins { ( $( $( #[$cfg:meta] )? - $PinId:ident: ($Adc:ident, $CHAN:literal) + $PinId:ident: ($Adc:ident, $CHAN:ident) ),+ $(,)? ) => { crate::paste::item! { $( $( #[$cfg] )? - impl AdcPin<$Adc> for Pin<$PinId, AlternateB> { - const CHANNEL: u8 = $CHAN; - } + impl PosAdcPin<$Adc, $CHAN<$Adc>> for Pin<$PinId, AlternateB> {} )+ } }; } -adc_pins! { +macro_rules! neg_adc_pins { + ( + $( + $( #[$cfg:meta] )? + $PinId:ident: ($Adc:ident, $CHAN:ident) + ),+ + $(,)? + ) => { + crate::paste::item! { + $( + $( #[$cfg] )? + impl NegAdcPin<$Adc, $CHAN<$Adc>> for Pin<$PinId, AlternateB> {} + )+ + } + }; +} + +pos_adc_pins! { #[hal_cfg("pa02")] - PA02: (Adc0, 0), + PA02: (Adc0, AIN0), #[hal_cfg("pa03")] - PA03: (Adc0, 1), + PA03: (Adc0, AIN1), #[hal_cfg("pb08")] - PB08: (Adc0, 2), + PB08: (Adc0, AIN2), #[hal_cfg("pb09")] - PB09: (Adc0, 3), + PB09: (Adc0, AIN3), #[hal_cfg("pa04")] - PA04: (Adc0, 4), + PA04: (Adc0, AIN4), #[hal_cfg("pa05")] - PA05: (Adc0, 5), + PA05: (Adc0, AIN5), #[hal_cfg("pa06")] - PA06: (Adc0, 6), + PA06: (Adc0, AIN6), #[hal_cfg("pa07")] - PA07: (Adc0, 7), + PA07: (Adc0, AIN7), #[hal_cfg("pa08")] - PA08: (Adc0, 8), + PA08: (Adc0, AIN8), #[hal_cfg("pa09")] - PA09: (Adc0, 9), + PA09: (Adc0, AIN9), #[hal_cfg("pa10")] - PA10: (Adc0, 10), + PA10: (Adc0, AIN10), #[hal_cfg("pa11")] - PA11: (Adc0, 11), + PA11: (Adc0, AIN11), #[hal_cfg("pb00")] - PB00: (Adc0, 12), + PB00: (Adc0, AIN12), #[hal_cfg("pb01")] - PB01: (Adc0, 13), + PB01: (Adc0, AIN13), #[hal_cfg("pb02")] - PB02: (Adc0, 14), + PB02: (Adc0, AIN14), #[hal_cfg("pb03")] - PB03: (Adc0, 15), + PB03: (Adc0, AIN15), #[hal_cfg("pb08")] - PB08: (Adc1, 0), + PB08: (Adc1, AIN0), #[hal_cfg("pb09")] - PB09: (Adc1, 1), + PB09: (Adc1, AIN1), #[hal_cfg("pa08")] - PA08: (Adc1, 2), + PA08: (Adc1, AIN2), #[hal_cfg("pa09")] - PA09: (Adc1, 3), + PA09: (Adc1, AIN3), #[hal_cfg("pc02")] - PC02: (Adc1, 4), + PC02: (Adc1, AIN4), #[hal_cfg("pc03")] - PC03: (Adc1, 5), + PC03: (Adc1, AIN5), #[hal_cfg("pb04")] - PB04: (Adc1, 6), + PB04: (Adc1, AIN6), #[hal_cfg("pb05")] - PB05: (Adc1, 7), + PB05: (Adc1, AIN7), #[hal_cfg("pb06")] - PB06: (Adc1, 8), + PB06: (Adc1, AIN8), #[hal_cfg("pb07")] - PB07: (Adc1, 9), + PB07: (Adc1, AIN9), #[hal_cfg("pc00")] - PC00: (Adc1, 10), + PC00: (Adc1, AIN10), #[hal_cfg("pc01")] - PC01: (Adc1, 11), + PC01: (Adc1, AIN11), #[hal_cfg("pc30")] - PC30: (Adc1, 12), + PC30: (Adc1, AIN12), #[hal_cfg("pc31")] - PC31: (Adc1, 13), + PC31: (Adc1, AIN13), #[hal_cfg("pd00")] - PD00: (Adc1, 14), + PD00: (Adc1, AIN14), #[hal_cfg("pd01")] - PD01: (Adc1, 15), + PD01: (Adc1, AIN15), +} + +neg_adc_pins! { + #[hal_cfg("pa02")] + PA02: (Adc0, AIN0), + #[hal_cfg("pa03")] + PA03: (Adc0, AIN1), + #[hal_cfg("pb08")] + PB08: (Adc0, AIN2), + #[hal_cfg("pb09")] + PB09: (Adc0, AIN3), + #[hal_cfg("pa04")] + PA04: (Adc0, AIN4), + #[hal_cfg("pa05")] + PA05: (Adc0, AIN5), + #[hal_cfg("pa06")] + PA06: (Adc0, AIN6), + #[hal_cfg("pa07")] + PA07: (Adc0, AIN7), + + #[hal_cfg("pb08")] + PB08: (Adc1, AIN0), + #[hal_cfg("pb09")] + PB09: (Adc1, AIN1), + #[hal_cfg("pa08")] + PA08: (Adc1, AIN2), + #[hal_cfg("pa09")] + PA09: (Adc1, AIN3), + #[hal_cfg("pc02")] + PC02: (Adc1, AIN4), + #[hal_cfg("pc03")] + PC03: (Adc1, AIN5), + #[hal_cfg("pb04")] + PB04: (Adc1, AIN6), + #[hal_cfg("pb05")] + PB05: (Adc1, AIN7), } diff --git a/hal/src/peripherals/adc/input.rs b/hal/src/peripherals/adc/input.rs new file mode 100644 index 000000000000..ffded7694a57 --- /dev/null +++ b/hal/src/peripherals/adc/input.rs @@ -0,0 +1,153 @@ +use core::marker::PhantomData; +use atsamd_hal_macros::hal_cfg; +use crate::{ + gpio::AnyPin, + typelevel::Sealed, +}; +use super::{ + AdcInstance, + channel::*, +}; + +#[hal_cfg(any("adc-d11", "adc-d21"))] +use crate::pac::adc as adc0; +#[hal_cfg("adc-d5x")] +use crate::pac::adc0; + + +/// Trait for positive ADC channels +pub trait PosChannel: Sealed { + const MUXVAL: adc0::inputctrl::Muxposselect; +} + +/// Trait for negative ADC channels +pub trait NegChannel: Sealed { + const MUXVAL: adc0::inputctrl::Muxnegselect; +} + +/// Marker trait for ADC pins which can be used as positive ADC inputs +pub trait PosAdcPin>: AnyPin + Sealed {} + +/// Marker trait for ADC pins which can be used as negative ADC inputs +pub trait NegAdcPin>: AnyPin + Sealed {} + +/// Sampling mode for the ADC +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum SampleMode { + SingleEnded, + Differential, +} + +/// Trait for ADC inputs +pub trait AdcInput { + const SAMPLE_MODE: SampleMode; + type Pos: PosChannel; + type Neg: NegChannel; +} + +/// Type representing a single ended input. +pub struct SingleEndedInput +where + I: AdcInstance, + P: PosChannel, +{ + adc: PhantomData, + pos: PhantomData

, +} + +impl AdcInput for SingleEndedInput +where + I: AdcInstance, + P: PosChannel, +{ + const SAMPLE_MODE: SampleMode = SampleMode::SingleEnded; + type Pos = P; + /// Single ended inputs always referenced to internal ADC GND + type Neg = GND; +} + +impl SingleEndedInput +where + I: AdcInstance, + P: PosChannel, +{ + + fn new() -> Self { + Self { + adc: PhantomData, + pos: PhantomData, + } + } + + /// Creates a [`SingleEndedInput`] from a [`PosChannel`] + pub fn from_channel(_pos: P) -> Self { + Self::new() + } + + /// Creates a [`SingleEndedInput`] from a [`PosAdcPin`] + pub fn from_pin>(_pin: PP) -> Self { + Self::new() + } +} + +/// Type representing a differential input +pub struct DifferentialInput +where + I: AdcInstance, + P: PosChannel, + N: NegChannel, +{ + adc: PhantomData, + pos: PhantomData

, + neg: PhantomData, +} + +impl AdcInput for DifferentialInput +where + I: AdcInstance, + P: PosChannel, + N: NegChannel, +{ + const SAMPLE_MODE: SampleMode = SampleMode::Differential; + type Pos = P; + type Neg = N; +} + +impl DifferentialInput +where + I: AdcInstance, + P: PosChannel, + N: NegChannel, +{ + + fn new() -> Self { + Self { + adc: PhantomData, + pos: PhantomData, + neg: PhantomData, + } + } + + /// Create a [`DifferentialInput`] from two ADC channels + pub fn from_channels(_pos: P, _neg: N) -> Self { + Self::new() + } + + /// Create a [`DifferentialInput`] from two GPIO pins + pub fn from_pins(_pos: PP, _neg: PN) -> Self + where + PP: PosAdcPin, + PN: NegAdcPin, + { + Self::new() + } + + /// Create a [`DifferentialInput`] from a single GPIO pin and + /// a negative ADC channel + pub fn from_pos_pin(_pos: PP, _neg: N) -> Self + where + PP: PosAdcPin, + { + Self::new() + } +} diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index b3a04bf1457f..706e917715f0 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -27,7 +27,7 @@ use core::ops::Deref; use atsamd_hal_macros::{hal_cfg, hal_module}; use pac::Peripherals; -use crate::{gpio::AnyPin, pac, typelevel::Sealed}; +use crate::pac; #[hal_module( any("adc-d11", "adc-d21") => "d11/mod.rs", @@ -45,6 +45,9 @@ pub use async_api::*; mod builder; pub use builder::*; +mod input; +pub use input::*; + #[hal_cfg(any("adc-d11", "adc-d21"))] use crate::pac::adc as adc0; #[hal_cfg("adc-d5x")] @@ -52,6 +55,8 @@ use crate::pac::adc0; pub use adc0::refctrl::Refselselect as Reference; +use channel::*; + /// ADC Settings when reading Internal sensors (Like VREF and Temperatures) /// These settings are based on the minimums suggested in the datasheet const ADC_SETTINGS_INTERNAL_READ: AdcSettings = AdcSettings { @@ -154,13 +159,6 @@ pub trait AdcInstance { fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; } -/// Trait representing a GPIO pin which can be used as an input for an ADC -pub trait AdcPin: AnyPin + Sealed -where - I: AdcInstance, -{ - const CHANNEL: u8; -} /// ADC Instance #[hal_cfg(any("adc-d11", "adc-d21"))] @@ -328,19 +326,24 @@ impl Adc { /// Read a single value from the provided ADC pin. #[inline] - pub fn read>(&mut self, _pin: &mut P) -> u16 { - self.read_channel(P::CHANNEL) + pub fn read>(&mut self, _input: &mut IN) -> u16 { + self.read_channel(IN::Pos::MUXVAL, IN::Neg::MUXVAL, IN::SAMPLE_MODE) } /// Read a single value from the provided channel, in a blocking fashion #[inline] - fn read_channel(&mut self, ch: u8) -> u16 { + fn read_channel( + &mut self, + pos_ch: adc0::inputctrl::Muxposselect, + neg_ch: adc0::inputctrl::Muxnegselect, + sample_mode: SampleMode, + ) -> u16 { // Clear overrun errors that might've occured before we try to read anything self.clear_all_flags(); self.disable_interrupts(Flags::all()); self.disable_freerunning(); self.sync(); - self.mux(ch); + self.mux(pos_ch, neg_ch, sample_mode); self.check_read_discard(); self.start_conversion(); while !self.read_flags().contains(Flags::RESRDY) { @@ -365,21 +368,27 @@ impl Adc { /// Read into a buffer from the provided ADC pin, in a blocking fashion #[inline] - pub fn read_buffer>( + pub fn read_buffer>( &mut self, - _pin: &mut P, + _input: &mut IN, dst: &mut [u16], ) -> Result<(), Error> { - self.read_buffer_channel(P::CHANNEL, dst) + self.read_buffer_channel(IN::Pos::MUXVAL, IN::Neg::MUXVAL, IN::SAMPLE_MODE, dst) } /// Read into a buffer from the provided channel, in a blocking fashion #[inline] - fn read_buffer_channel(&mut self, ch: u8, dst: &mut [u16]) -> Result<(), Error> { + fn read_buffer_channel( + &mut self, + pos_ch: adc0::inputctrl::Muxposselect, + neg_ch: adc0::inputctrl::Muxnegselect, + sample_mode: SampleMode, + dst: &mut [u16], + ) -> Result<(), Error> { // Clear overrun errors that might've occured before we try to read anything self.clear_all_flags(); self.disable_interrupts(Flags::all()); - self.mux(ch); + self.mux(pos_ch, neg_ch, sample_mode); self.enable_freerunning(); self.start_conversion(); if self.discard { From e6498b84f876c691dba999a64f6673552d551ecc Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Mon, 2 Feb 2026 10:35:09 -0500 Subject: [PATCH 03/39] restored CPU voltage measurment --- hal/src/peripherals/adc/d5x/mod.rs | 22 ++++++--------- hal/src/peripherals/adc/mod.rs | 43 +++++++++++++++++++----------- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index bcb814627456..a1c7a0e29eeb 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -122,14 +122,6 @@ impl Adc { .sampctrl() .modify(|_, w| unsafe { w.samplen().bits(cfg.sample_clock_cycles.saturating_sub(1)) }); // sample length self.sync(); -// let diffmode = match cfg.sample_mode { -// SampleMode::SingleEnded => false, -// SampleMode::Differential => true, -// }; -// self.adc.inputctrl().modify(|_, w| { -// w.diffmode().bit(diffmode) -// }); -// self.sync(); let (sample_cnt, adjres) = match cfg.accumulation { // 1 sample to be used as is Accumulation::Single(_) => (SampleCount::_1, 0), @@ -189,12 +181,14 @@ impl Adc { #[inline] pub fn read_cpu_voltage(&mut self, src: CpuVoltageSource) -> u16 { - //let voltage = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ, |adc| { - // let res = adc.read_channel(src as u8, None); - // adc.reading_to_f32(res) * 3.3 * 4.0 // x4 since the voltages are 1/4 scaled - //}); - //(voltage * 1000.0) as u16 - 0 + let voltage = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ, |adc| { + let res = adc.read_channel( + src.into(), + pac::adc0::inputctrl::Muxnegselect::Gnd, + SampleMode::SingleEnded); + adc.reading_to_f32(res) * 3.3 * 4.0 // x4 since the voltages are 1/4 scaled + }); + (voltage * 1000.0) as u16 } } diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 706e917715f0..105d1e468632 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -96,31 +96,42 @@ pub enum Error { } /// Voltage source to use when using the ADC to measure the CPU voltage -#[hal_cfg("adc-d5x")] #[derive(Copy, Clone, PartialEq, Eq)] -#[repr(u8)] pub enum CpuVoltageSource { /// Core voltage - Core = 0x18, + Core, /// VBAT supply voltage - Vbat = 0x19, + Vbat, /// IO supply voltage - Io = 0x1A, + Io, + /// Bandgap reference voltage + Bandgap, } -/// Voltage source to use when using the ADC to measure the CPU voltage -#[hal_cfg(any("adc-d21", "adc-d11"))] -#[derive(Copy, Clone, PartialEq, Eq)] -#[repr(u8)] -pub enum CpuVoltageSource { - /// Bandgap reference voltage - 1.1V - Bandgap = 0x19, - /// Core voltage - 1.2V - Core = 0x1A, - /// IO voltage - 1.62V to 3.63V - Io = 0x1B, +impl Into for CpuVoltageSource { + fn into(self) -> adc0::inputctrl::Muxposselect { + match self { + CpuVoltageSource::Core => adc0::inputctrl::Muxposselect::Scaledcorevcc, + CpuVoltageSource::Vbat => adc0::inputctrl::Muxposselect::Scaledvbat, + CpuVoltageSource::Io => adc0::inputctrl::Muxposselect::Scalediovcc, + CpuVoltageSource::Bandgap => adc0::inputctrl::Muxposselect::Bandgap, + } + } } +///// Voltage source to use when using the ADC to measure the CPU voltage +//#[hal_cfg(any("adc-d21", "adc-d11"))] +//#[derive(Copy, Clone, PartialEq, Eq)] +//#[repr(u8)] +//pub enum CpuVoltageSource { +// /// Bandgap reference voltage - 1.1V +// Bandgap = 0x19, +// /// Core voltage - 1.2V +// Core = 0x1A, +// /// IO voltage - 1.62V to 3.63V +// Io = 0x1B, +//} + bitflags::bitflags! { /// ADC interrupt flags #[derive(Clone, Copy)] From 25be26893d19f8373c885f82bd7776870a0c554a Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Mon, 2 Feb 2026 15:06:04 -0500 Subject: [PATCH 04/39] updated d11 implementation to support differential sampling --- hal/src/peripherals/adc/d11/channel.rs | 97 ++++++++++++++++++ hal/src/peripherals/adc/d11/mod.rs | 59 ++++++++++- hal/src/peripherals/adc/d11/pin.rs | 131 ++++++++++++++++++------- hal/src/peripherals/adc/mod.rs | 36 ++++--- 4 files changed, 269 insertions(+), 54 deletions(-) create mode 100644 hal/src/peripherals/adc/d11/channel.rs diff --git a/hal/src/peripherals/adc/d11/channel.rs b/hal/src/peripherals/adc/d11/channel.rs new file mode 100644 index 000000000000..a11aab76a5a9 --- /dev/null +++ b/hal/src/peripherals/adc/d11/channel.rs @@ -0,0 +1,97 @@ +use core::marker::PhantomData; +use atsamd_hal_macros::hal_cfg; +use crate::{ + adc::*, + pac::adc::inputctrl::{Muxposselect, Muxnegselect}, + typelevel::Sealed, +}; + +macro_rules! channel { + ( + $( + $CH:ident: ($($PMUX:path)?, $($NMUX:path)?) + ),+ + $(,)? + ) => { + crate::paste::paste!{ + $( + pub struct $CH { + adc: PhantomData, + } + + impl Sealed for $CH {} + + $( + impl PosChannel for $CH { + const MUXVAL: Muxposselect = $PMUX; + } + )? + $( + impl NegChannel for $CH { + const MUXVAL: Muxnegselect = $NMUX; + } + )? + + impl $CH { + pub fn get_channel() -> Self { + Self { + adc: PhantomData + } + } + } + )+ + } + }; +} + +#[hal_cfg("adc-d21")] +channel! { + AIN0: (Muxposselect::Pin0, Muxnegselect::Pin0), + AIN1: (Muxposselect::Pin1, Muxnegselect::Pin1), + AIN2: (Muxposselect::Pin2, Muxnegselect::Pin2), + AIN3: (Muxposselect::Pin3, Muxnegselect::Pin3), + AIN4: (Muxposselect::Pin4, Muxnegselect::Pin4), + AIN5: (Muxposselect::Pin5, Muxnegselect::Pin5), + AIN6: (Muxposselect::Pin6, Muxnegselect::Pin6), + AIN7: (Muxposselect::Pin7, Muxnegselect::Pin7), + AIN8: (Muxposselect::Pin8, ), + AIN9: (Muxposselect::Pin9, ), + AIN10: (Muxposselect::Pin10, ), + AIN11: (Muxposselect::Pin11, ), + AIN12: (Muxposselect::Pin12, ), + AIN13: (Muxposselect::Pin13, ), + AIN14: (Muxposselect::Pin14, ), + AIN15: (Muxposselect::Pin15, ), + AIN16: (Muxposselect::Pin15, ), + AIN17: (Muxposselect::Pin15, ), + AIN18: (Muxposselect::Pin15, ), + AIN19: (Muxposselect::Pin15, ), + TEMP: (Muxposselect::Temp, ), + SCALEDCOREVCC: (Muxposselect::Scaledcorevcc, ), + SCALEDIOVCC: (Muxposselect::Scalediovcc, ), + BANDGAP: (Muxposselect::Bandgap, ), + DAC: (Muxposselect::Dac, ), + GND: (, Muxnegselect::Gnd), + IOGND: (, Muxnegselect::Iognd), +} + +#[hal_cfg("adc-d11")] +channel! { + AIN0: (Muxposselect::Pin0, Muxnegselect::Pin0), + AIN1: (Muxposselect::Pin1, Muxnegselect::Pin1), + AIN2: (Muxposselect::Pin2, Muxnegselect::Pin2), + AIN3: (Muxposselect::Pin3, Muxnegselect::Pin3), + AIN4: (Muxposselect::Pin4, Muxnegselect::Pin4), + AIN5: (Muxposselect::Pin5, Muxnegselect::Pin5), + AIN6: (Muxposselect::Pin6, Muxnegselect::Pin6), + AIN7: (Muxposselect::Pin7, Muxnegselect::Pin7), + AIN8: (Muxposselect::Pin8, ), + AIN9: (Muxposselect::Pin9, ), + TEMP: (Muxposselect::Temp, ), + SCALEDCOREVCC: (Muxposselect::Scaledcorevcc, ), + SCALEDIOVCC: (Muxposselect::Scalediovcc, ), + BANDGAP: (Muxposselect::Bandgap, ), + DAC: (Muxposselect::Dac, ), + GND: (, Muxnegselect::Gnd), + IOGND: (, Muxnegselect::Iognd), +} diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index d43d76012144..d42e03d49f2b 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -1,8 +1,11 @@ use super::{ ADC_SETTINGS_INTERNAL_READ, ADC_SETTINGS_INTERNAL_READ_D21_TEMP, Accumulation, Adc, AdcInstance, AdcSettings, CpuVoltageSource, Error, Flags, PrimaryAdc, SampleCount, + SampleMode }; +use atsamd_hal_macros::{hal_macro_helper}; + #[cfg(feature = "async")] use super::{FutureAdc, async_api}; @@ -11,6 +14,7 @@ use pac::Peripherals; use pac::Sysctrl; use pac::adc::inputctrl::Gainselect; pub mod pin; +pub mod channel; /// Wrapper around the ADC instance pub struct Adc0 { @@ -196,12 +200,51 @@ impl Adc { } #[inline] - pub(super) fn mux(&mut self, ch: u8) { + #[hal_macro_helper] + pub(super) fn mux( + &mut self, + pos_ch: pac::adc::inputctrl::Muxposselect, + neg_ch: pac::adc::inputctrl::Muxnegselect, + sample_mode: SampleMode, + ) { + let active_sample_mode = match self.adc.ctrlb().read().diffmode().bit() { + true => SampleMode::Differential, + false => SampleMode::SingleEnded, + }; + + if active_sample_mode != sample_mode { + // Disable the ADC if chip is SAMD11 family + // SAMD11 datasheet section 31.8.5 states that the ADC must be disabled to modify + // the DIFFMODE bit. + #[hal_cfg("adc-d11")] + self.power_down(); + + self.adc.ctrlb().write(|w| { + match sample_mode { + SampleMode::SingleEnded => w.diffmode().clear_bit(), + SampleMode::Differential => w.diffmode().set_bit(), + } + }); + self.sync(); + + // Re-enable the ADC if chip is SAMD11 family + #[hal_cfg("adc-d11")] + self.power_up(); + } + self.adc.inputctrl().modify(|r, w| { - if r.muxpos().bits() != ch { + if r.muxpos().bits() != pos_ch.into() { self.discard = true; } - unsafe { w.muxpos().bits(ch) } + if r.muxneg().bits() != neg_ch.into() { + self.discard = true; + } + + // Safe as pos_ch and neg_ch are derived from Muxposselect and Muxnegselect PAC enums + unsafe { + w.muxpos().bits(pos_ch.into()); + w.muxpos().bits(neg_ch.into()) + } }); self.sync() } @@ -252,7 +295,10 @@ impl Adc { // Settings adc.adc.inputctrl().modify(|_, w| w.gain()._1x()); adc.discard = true; - let res = adc.read_channel(0x18); + let res = adc.read_channel( + pac::adc::inputctrl::Muxposselect::Temp, + pac::adc::inputctrl::Muxnegselect::Gnd, + SampleMode::SingleEnded); // Set gain back to normal adc.adc.inputctrl().modify(|_, w| w.gain().div2()); adc.discard = true; @@ -267,7 +313,10 @@ impl Adc { #[inline] pub fn read_cpu_voltage(&mut self, src: CpuVoltageSource) -> u16 { let voltage = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ, |adc| { - let res = adc.read_channel(src as u8); + let res = adc.read_channel( + src.into(), + pac::adc::inputctrl::Muxnegselect::Gnd, + SampleMode::SingleEnded); let mut res_f = adc.reading_to_f32(res) * 3.3; if CpuVoltageSource::Bandgap != src { res_f *= 4.0; diff --git a/hal/src/peripherals/adc/d11/pin.rs b/hal/src/peripherals/adc/d11/pin.rs index c09d9e70a658..e142b7e101ff 100644 --- a/hal/src/peripherals/adc/d11/pin.rs +++ b/hal/src/peripherals/adc/d11/pin.rs @@ -2,94 +2,151 @@ use crate::adc::*; use crate::gpio::pin::*; use atsamd_hal_macros::hal_cfg; -macro_rules! adc_pins { +macro_rules! pos_adc_pins { ( $( $( #[$cfg:meta] )? - $PinId:ident: ($Adc:ident, $CHAN:literal) + $PinId:ident: ($Adc:ident, $CHAN:ident) ),+ $(,)? ) => { crate::paste::item! { $( $( #[$cfg] )? - impl AdcPin<$Adc> for Pin<$PinId, AlternateB> { - const CHANNEL: u8 = $CHAN; - } + impl PosAdcPin<$Adc, $CHAN<$Adc>> for Pin<$PinId, AlternateB> {} + )+ + } + }; +} + +macro_rules! neg_adc_pins { + ( + $( + $( #[$cfg:meta] )? + $PinId:ident: ($Adc:ident, $CHAN:ident) + ),+ + $(,)? + ) => { + crate::paste::item! { + $( + $( #[$cfg] )? + impl NegAdcPin<$Adc, $CHAN<$Adc>> for Pin<$PinId, AlternateB> {} )+ } }; } #[hal_cfg(any("adc-d21"))] -adc_pins! { +pos_adc_pins! { #[hal_cfg("pa02")] - PA02: (Adc0, 0), + PA02: (Adc0, AIN0), #[hal_cfg("pa03")] - PA03: (Adc0, 1), + PA03: (Adc0, AIN1), #[hal_cfg("pb08")] - PB08: (Adc0, 2), + PB08: (Adc0, AIN2), #[hal_cfg("pb09")] - PB09: (Adc0, 3), + PB09: (Adc0, AIN3), #[hal_cfg("pa04")] - PA04: (Adc0, 4), + PA04: (Adc0, AIN4), #[hal_cfg("pa05")] - PA05: (Adc0, 5), + PA05: (Adc0, AIN5), #[hal_cfg("pa06")] - PA06: (Adc0, 6), + PA06: (Adc0, AIN6), #[hal_cfg("pa07")] - PA07: (Adc0, 7), + PA07: (Adc0, AIN7), #[hal_cfg("pb00")] - PB00: (Adc0, 8), + PB00: (Adc0, AIN8), #[hal_cfg("pb01")] - PB01: (Adc0, 9), + PB01: (Adc0, AIN9), #[hal_cfg("pb02")] - PB02: (Adc0, 10), + PB02: (Adc0, AIN10), #[hal_cfg("pb03")] - PB03: (Adc0, 11), + PB03: (Adc0, AIN11), #[hal_cfg("pb04")] - PB04: (Adc0, 12), + PB04: (Adc0, AIN12), #[hal_cfg("pb05")] - PB05: (Adc0, 13), + PB05: (Adc0, AIN13), #[hal_cfg("pb06")] - PB06: (Adc0, 14), + PB06: (Adc0, AIN14), #[hal_cfg("pb07")] - PB07: (Adc0, 15), + PB07: (Adc0, AIN15), #[hal_cfg("pa08")] - PA08: (Adc0, 16), + PA08: (Adc0, AIN16), #[hal_cfg("pa09")] - PA09: (Adc0, 17), + PA09: (Adc0, AIN17), #[hal_cfg("pa10")] - PA10: (Adc0, 18), + PA10: (Adc0, AIN18), #[hal_cfg("pa11")] - PA11: (Adc0, 19), + PA11: (Adc0, AIN19), +} + +#[hal_cfg(any("adc-d21"))] +neg_adc_pins! { + #[hal_cfg("pa02")] + PA02: (Adc0, AIN0), + #[hal_cfg("pa03")] + PA03: (Adc0, AIN1), + + #[hal_cfg("pb08")] + PB08: (Adc0, AIN2), + #[hal_cfg("pb09")] + PB09: (Adc0, AIN3), + + #[hal_cfg("pa04")] + PA04: (Adc0, AIN4), + #[hal_cfg("pa05")] + PA05: (Adc0, AIN5), + #[hal_cfg("pa06")] + PA06: (Adc0, AIN6), + #[hal_cfg("pa07")] + PA07: (Adc0, AIN7), } #[hal_cfg(any("adc-d11"))] -adc_pins! { +pos_adc_pins! { #[hal_cfg("pa02")] - PA02: (Adc0, 0), + PA02: (Adc0, AIN0), #[hal_cfg("pa03")] - PA03: (Adc0, 1), + PA03: (Adc0, AIN1), #[hal_cfg("pa04")] - PA04: (Adc0, 2), + PA04: (Adc0, AIN2), #[hal_cfg("pa05")] - PA05: (Adc0, 3), + PA05: (Adc0, AIN3), #[hal_cfg("pa06")] - PA06: (Adc0, 4), + PA06: (Adc0, AIN4), #[hal_cfg("pa07")] - PA07: (Adc0, 5), + PA07: (Adc0, AIN5), #[hal_cfg("pa14")] - PA14: (Adc0, 6), + PA14: (Adc0, AIN6), #[hal_cfg("pa15")] - PA15: (Adc0, 7), + PA15: (Adc0, AIN7), #[hal_cfg("pa10")] - PA10: (Adc0, 8), + PA10: (Adc0, AIN8), #[hal_cfg("pa11")] - PA11: (Adc0, 9), + PA11: (Adc0, AIN9), +} + +#[hal_cfg(any("adc-d11"))] +neg_adc_pins! { + #[hal_cfg("pa02")] + PA02: (Adc0, AIN0), + #[hal_cfg("pa03")] + PA03: (Adc0, AIN1), + #[hal_cfg("pa04")] + PA04: (Adc0, AIN2), + #[hal_cfg("pa05")] + PA05: (Adc0, AIN3), + #[hal_cfg("pa06")] + PA06: (Adc0, AIN4), + #[hal_cfg("pa07")] + PA07: (Adc0, AIN5), + #[hal_cfg("pa14")] + PA14: (Adc0, AIN6), + #[hal_cfg("pa15")] + PA15: (Adc0, AIN7), } diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 105d1e468632..f1ff343b759e 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -96,6 +96,7 @@ pub enum Error { } /// Voltage source to use when using the ADC to measure the CPU voltage +#[hal_cfg("adc-d5x")] #[derive(Copy, Clone, PartialEq, Eq)] pub enum CpuVoltageSource { /// Core voltage @@ -108,6 +109,7 @@ pub enum CpuVoltageSource { Bandgap, } +#[hal_cfg("adc-d5x")] impl Into for CpuVoltageSource { fn into(self) -> adc0::inputctrl::Muxposselect { match self { @@ -119,18 +121,28 @@ impl Into for CpuVoltageSource { } } -///// Voltage source to use when using the ADC to measure the CPU voltage -//#[hal_cfg(any("adc-d21", "adc-d11"))] -//#[derive(Copy, Clone, PartialEq, Eq)] -//#[repr(u8)] -//pub enum CpuVoltageSource { -// /// Bandgap reference voltage - 1.1V -// Bandgap = 0x19, -// /// Core voltage - 1.2V -// Core = 0x1A, -// /// IO voltage - 1.62V to 3.63V -// Io = 0x1B, -//} +/// Voltage source to use when using the ADC to measure the CPU voltage +#[hal_cfg(any("adc-d21", "adc-d11"))] +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum CpuVoltageSource { + /// Bandgap reference voltage - 1.1V + Bandgap, + /// Core voltage - 1.2V + Core, + /// IO voltage - 1.62V to 3.63V + Io, +} + +#[hal_cfg(any("adc-d21", "adc-d11"))] +impl Into for CpuVoltageSource { + fn into(self) -> adc0::inputctrl::Muxposselect { + match self { + CpuVoltageSource::Core => adc0::inputctrl::Muxposselect::Scaledcorevcc, + CpuVoltageSource::Io => adc0::inputctrl::Muxposselect::Scalediovcc, + CpuVoltageSource::Bandgap => adc0::inputctrl::Muxposselect::Bandgap, + } + } +} bitflags::bitflags! { /// ADC interrupt flags From 698c958a444762b3cde75af9d35afef484f29cae Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Mon, 2 Feb 2026 15:06:27 -0500 Subject: [PATCH 05/39] removed unused macro --- hal/src/peripherals/adc/d5x/channel.rs | 29 -------------------------- 1 file changed, 29 deletions(-) diff --git a/hal/src/peripherals/adc/d5x/channel.rs b/hal/src/peripherals/adc/d5x/channel.rs index 4e0cea7377cf..350b340eac94 100644 --- a/hal/src/peripherals/adc/d5x/channel.rs +++ b/hal/src/peripherals/adc/d5x/channel.rs @@ -68,32 +68,3 @@ channel! { CTAT: (Muxposselect::Ctat, ), GND: (, Muxnegselect::Gnd), } - -macro_rules! primary_channel { - ( - $( - $CH:ident: ($($PMUX:path)?, $($NMUX:path)?) - ),+ - $(,)? - ) => { - $( - pub struct $CH { - adc: PhantomData, - } - - impl Sealed for $CH {} - - $( - impl PosChannel for $CH { - const MUXVAL: Muxposselect = $PMUX; - } - )? - $( - impl PosChannel for $CH { - const MUXVAL: Muxnegselect = $NMUX; - } - )? - )+ - }; -} - From a7eb6da802855a3b84372a2733197a8102d17e90 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Mon, 2 Feb 2026 15:07:04 -0500 Subject: [PATCH 06/39] added missing DAC0 analog input channel for D5x family chips --- hal/src/peripherals/adc/d5x/channel.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/hal/src/peripherals/adc/d5x/channel.rs b/hal/src/peripherals/adc/d5x/channel.rs index 350b340eac94..a0c4564d4b2e 100644 --- a/hal/src/peripherals/adc/d5x/channel.rs +++ b/hal/src/peripherals/adc/d5x/channel.rs @@ -66,5 +66,6 @@ channel! { BANDGAP: (Muxposselect::Bandgap, ), PTAT: (Muxposselect::Ptat, ), CTAT: (Muxposselect::Ctat, ), + DAC0: (Muxposselect::Dac0, ), GND: (, Muxnegselect::Gnd), } From 9a4a58cc8fde907705bbfffd53618f9a99f5722d Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Mon, 2 Feb 2026 15:41:04 -0500 Subject: [PATCH 07/39] fixed typo in Muxposselect enum variant name --- hal/src/peripherals/adc/d5x/channel.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hal/src/peripherals/adc/d5x/channel.rs b/hal/src/peripherals/adc/d5x/channel.rs index a0c4564d4b2e..76c6fb8cc4c4 100644 --- a/hal/src/peripherals/adc/d5x/channel.rs +++ b/hal/src/peripherals/adc/d5x/channel.rs @@ -66,6 +66,6 @@ channel! { BANDGAP: (Muxposselect::Bandgap, ), PTAT: (Muxposselect::Ptat, ), CTAT: (Muxposselect::Ctat, ), - DAC0: (Muxposselect::Dac0, ), + DAC0: (Muxposselect::Dac, ), GND: (, Muxnegselect::Gnd), } From 31901e21653afeeec7bc9cb6ef81a06420671828 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Tue, 3 Feb 2026 09:36:59 -0500 Subject: [PATCH 08/39] added method to retrieve ADC sample resolution --- hal/src/peripherals/adc/mod.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index c34b560ad113..5c63e5ee031f 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -412,6 +412,16 @@ impl Adc { Ok(()) } + /// Retrieve the configured ADC sample resolution + #[inline] + pub fn get_resolution(&self) -> Resolution { + match self.cfg.accumulation { + Accumulation::Single(res) => res.into(), + Accumulation::Average => Resolution::_12bit, + Accumulation::Summed => Resolution::_16bit, + } + } + /// Return the underlying ADC PAC object. #[hal_cfg(any("adc-d11", "adc-d21"))] #[inline] From e45ba54175bd04e449a3ebe2bb8cf67d936a54b3 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Tue, 3 Feb 2026 09:47:25 -0500 Subject: [PATCH 09/39] fixed enum variant match syntax --- hal/src/peripherals/adc/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 5c63e5ee031f..8069622543cb 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -417,8 +417,8 @@ impl Adc { pub fn get_resolution(&self) -> Resolution { match self.cfg.accumulation { Accumulation::Single(res) => res.into(), - Accumulation::Average => Resolution::_12bit, - Accumulation::Summed => Resolution::_16bit, + Accumulation::Average(_) => Resolution::_12bit, + Accumulation::Summed(_) => Resolution::_16bit, } } From 607694f1ed232212f69f80fa6f0c96c7819b1de6 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Thu, 5 Feb 2026 15:17:56 -0500 Subject: [PATCH 10/39] added associated type to AdcInput to handle signedness for ADC results from differential inputs --- hal/src/peripherals/adc/input.rs | 16 ++++++++++++++++ hal/src/peripherals/adc/mod.rs | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/hal/src/peripherals/adc/input.rs b/hal/src/peripherals/adc/input.rs index ffded7694a57..01c02d771d23 100644 --- a/hal/src/peripherals/adc/input.rs +++ b/hal/src/peripherals/adc/input.rs @@ -43,6 +43,10 @@ pub trait AdcInput { const SAMPLE_MODE: SampleMode; type Pos: PosChannel; type Neg: NegChannel; + type Output; + + /// Cast the ADC result to the appropriate output type + fn cast_result(result: u16) -> Self::Output; } /// Type representing a single ended input. @@ -64,6 +68,12 @@ where type Pos = P; /// Single ended inputs always referenced to internal ADC GND type Neg = GND; + type Output = u16; + + #[inline] + fn cast_result(result: u16) -> Self::Output { + result + } } impl SingleEndedInput @@ -111,6 +121,12 @@ where const SAMPLE_MODE: SampleMode = SampleMode::Differential; type Pos = P; type Neg = N; + type Output = i16; + + #[inline] + fn cast_result(result: u16) -> Self::Output { + result as i16 + } } impl DifferentialInput diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index da23fa498a6b..e2ae9ffdfcd0 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -349,8 +349,8 @@ impl Adc { /// Read a single value from the provided ADC pin. #[inline] - pub fn read>(&mut self, _input: &mut IN) -> u16 { - self.read_channel(IN::Pos::MUXVAL, IN::Neg::MUXVAL, IN::SAMPLE_MODE) + pub fn read>(&mut self, _input: &mut IN) -> IN::Output { + IN::cast_result(self.read_channel(IN::Pos::MUXVAL, IN::Neg::MUXVAL, IN::SAMPLE_MODE)) } /// Read a single value from the provided channel, in a blocking fashion From 4f5d2070b99db319173f9c6977a21397bed0f7cf Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Mon, 9 Feb 2026 10:45:32 -0500 Subject: [PATCH 11/39] set CTRLB.LEFTADJ when using differential sampling, added appropriate de-shifting when returning 12-bit signed result --- hal/src/peripherals/adc/d5x/mod.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index a1c7a0e29eeb..eebcc78d2cba 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -280,7 +280,10 @@ impl Adc { #[inline] pub(super) fn conversion_result(&self) -> u16 { - self.adc.result().read().result().bits() + match self.adc.ctrlb().read().leftadj().bit_is_clear() { + true => self.adc.result().read().result().bits(), + false => ((self.adc.result().read().result().bits() as i16) >> 4) as u16, + } } #[inline] @@ -309,6 +312,12 @@ impl Adc { SampleMode::Differential => w.diffmode().set_bit(), } }); + self.sync(); + + match sample_mode { + SampleMode::SingleEnded => self.adc.ctrlb().write(|w| w.leftadj().clear_bit()), + SampleMode::Differential => self.adc.ctrlb().write(|w| w.leftadj().set_bit()), + } self.sync() } From 52eb5c16f09b13f2cc4af72c6a0292ee1431ffc0 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Tue, 10 Feb 2026 14:05:21 -0500 Subject: [PATCH 12/39] added support for right-shifting 8-bit, 10-bit and 16-bit results when CTRLB.LEFTADJ is enabled (when using differential sampling) --- hal/src/peripherals/adc/d5x/mod.rs | 37 ++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index eebcc78d2cba..14f1d52070ac 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -8,10 +8,39 @@ use super::{FutureAdc, async_api}; use super::{ ADC_SETTINGS_INTERNAL_READ, Accumulation, Adc, AdcInstance, AdcSettings, CpuVoltageSource, - Error, Flags, PrimaryAdc, SampleCount, SampleMode, + Error, Flags, PrimaryAdc, SampleCount, SampleMode, Resolution, }; use crate::{calibration, pac}; +enum ResultShift { + _8bit, + _10bit, + _12bit, + _16bit, +} + +impl From for ResultShift { + fn from(item: Resolution) -> Self { + match item { + Resolution::_8bit => ResultShift::_8bit, + Resolution::_10bit => ResultShift::_10bit, + Resolution::_12bit => ResultShift::_12bit, + Resolution::_16bit => ResultShift::_16bit, + } + } +} + +impl ResultShift { + fn shift_amount(self) -> u8 { + match self { + ResultShift::_8bit => 7, + ResultShift::_10bit => 5, + ResultShift::_12bit => 3, + ResultShift::_16bit => 0, + } + } +} + /// ADC instance 0 pub struct Adc0 { _adc: pac::Adc0, @@ -282,7 +311,11 @@ impl Adc { pub(super) fn conversion_result(&self) -> u16 { match self.adc.ctrlb().read().leftadj().bit_is_clear() { true => self.adc.result().read().result().bits(), - false => ((self.adc.result().read().result().bits() as i16) >> 4) as u16, + false => + ((self.adc.result().read().result().bits() as i16) >> + ResultShift::from(self.cfg.accumulation.output_resolution()) + .shift_amount()) + as u16, } } From a13b4a48a88a3504a0648c807865e1ba39a5b72b Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Tue, 10 Feb 2026 15:38:04 -0500 Subject: [PATCH 13/39] added support for offset and reference compensation --- hal/src/peripherals/adc/builder.rs | 24 ++++++++++++++++++++++++ hal/src/peripherals/adc/d5x/mod.rs | 11 ++++++++++- hal/src/peripherals/adc/mod.rs | 4 ++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/hal/src/peripherals/adc/builder.rs b/hal/src/peripherals/adc/builder.rs index baaa62e74b83..d20c53a888b9 100644 --- a/hal/src/peripherals/adc/builder.rs +++ b/hal/src/peripherals/adc/builder.rs @@ -152,6 +152,8 @@ pub struct AdcBuilder { pub sample_clock_cycles: Option, pub accumulation: Accumulation, pub vref: Option, + pub offset_compensation: Option, + pub reference_compensation: Option, } /// Version of [AdcBuilder] without any optional settings. @@ -162,6 +164,8 @@ pub(crate) struct AdcSettings { pub sample_clock_cycles: u8, pub accumulation: Accumulation, pub vref: Reference, + pub offset_compensation: bool, + pub reference_compensation: bool, } impl AdcBuilder { @@ -172,6 +176,8 @@ impl AdcBuilder { sample_clock_cycles: None, accumulation: accumulation_method, vref: None, + offset_compensation: None, + reference_compensation: None, } } @@ -190,6 +196,8 @@ impl AdcBuilder { sample_clock_cycles: self.sample_clock_cycles.unwrap(), accumulation: self.accumulation, vref: self.vref.unwrap(), + offset_compensation: self.offset_compensation.unwrap_or(false), + reference_compensation: self.reference_compensation.unwrap_or(false), }) } @@ -244,6 +252,22 @@ impl AdcBuilder { Ok(adc_clk_freq / clocks_per_sample) } + /// Configure the ADC offset compensation + /// + /// ## Important + /// * Enabling offset compesation forces the clock cycles per sample to be 4 GCLK cycles, any change + /// to the cycles per sample via [`with_clock_cycles_per_sample()`] will be ignored. + pub fn enable_offset_compensation(mut self, enabled: bool) -> Self { + self.offset_compensation = Some(enabled); + self + } + + /// Configure the ADC reference compensation + pub fn enable_reference_compensation(mut self, enabled: bool) -> Self { + self.reference_compensation = Some(enabled); + self + } + /// Turn the builder into an ADC #[hal_cfg("adc-d5x")] #[inline] diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 14f1d52070ac..758387c650b6 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -147,9 +147,16 @@ impl Adc { .modify(|_, w| w.ressel().variant(cfg.accumulation.resolution())); self.sync(); + let samplen = match cfg.offset_compensation { + true => 0, // As per datasheet, SAMPLEN must be set to 0 when offset compensation is enabled + false => cfg.sample_clock_cycles.saturating_sub(1), + }; self.adc .sampctrl() - .modify(|_, w| unsafe { w.samplen().bits(cfg.sample_clock_cycles.saturating_sub(1)) }); // sample length + .modify(|_, w| { + unsafe { w.samplen().bits(samplen) }; + w.offcomp().bit(cfg.offset_compensation) + }); self.sync(); let (sample_cnt, adjres) = match cfg.accumulation { // 1 sample to be used as is @@ -169,6 +176,8 @@ impl Adc { self.sync(); self.set_reference(cfg.vref); self.sync(); + self.adc.refctrl().write(|w| w.refcomp().bit(cfg.reference_compensation)); + self.sync(); self.adc.ctrla().modify(|_, w| w.enable().set_bit()); self.sync(); self.cfg = cfg; diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index e2ae9ffdfcd0..62722da5c50f 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -64,6 +64,8 @@ const ADC_SETTINGS_INTERNAL_READ: AdcSettings = AdcSettings { sample_clock_cycles: 32, accumulation: Accumulation::average(SampleCount::_4), vref: Reference::Intvcc1, + offset_compensation: false, + reference_compensation: false, }; /// Based on Temperature log row information (NVM)x @@ -73,6 +75,8 @@ const ADC_SETTINGS_INTERNAL_READ_D21_TEMP: AdcSettings = AdcSettings { sample_clock_cycles: 32, accumulation: Accumulation::average(SampleCount::_4), vref: Reference::Int1v, + offset_compensation: false, + reference_compensation: false, }; /// Errors that may occur when operating the ADC From b55cf0be3e09c668a52a450fe2e44a960240ce55 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Wed, 11 Feb 2026 13:53:05 -0500 Subject: [PATCH 14/39] added support for automatically handling left-adjusting and enabling rail-to-rail operation for differential inputs --- hal/src/peripherals/adc/builder.rs | 38 +++++++++++++++++++++++---- hal/src/peripherals/adc/d5x/mod.rs | 42 +++++++++++++++++++++++++----- hal/src/peripherals/adc/mod.rs | 10 +++++-- 3 files changed, 77 insertions(+), 13 deletions(-) diff --git a/hal/src/peripherals/adc/builder.rs b/hal/src/peripherals/adc/builder.rs index d20c53a888b9..76080bf46aef 100644 --- a/hal/src/peripherals/adc/builder.rs +++ b/hal/src/peripherals/adc/builder.rs @@ -154,6 +154,8 @@ pub struct AdcBuilder { pub vref: Option, pub offset_compensation: Option, pub reference_compensation: Option, + pub auto_left_adjust: Option, + pub auto_rail_to_rail: Option, } /// Version of [AdcBuilder] without any optional settings. @@ -166,6 +168,8 @@ pub(crate) struct AdcSettings { pub vref: Reference, pub offset_compensation: bool, pub reference_compensation: bool, + pub auto_left_adjust: bool, + pub auto_rail_to_rail: bool, } impl AdcBuilder { @@ -178,6 +182,8 @@ impl AdcBuilder { vref: None, offset_compensation: None, reference_compensation: None, + auto_left_adjust: None, + auto_rail_to_rail: None, } } @@ -198,6 +204,8 @@ impl AdcBuilder { vref: self.vref.unwrap(), offset_compensation: self.offset_compensation.unwrap_or(false), reference_compensation: self.reference_compensation.unwrap_or(false), + auto_left_adjust: self.auto_left_adjust.unwrap_or(false), + auto_rail_to_rail: self.auto_rail_to_rail.unwrap_or(false), }) } @@ -256,15 +264,35 @@ impl AdcBuilder { /// /// ## Important /// * Enabling offset compesation forces the clock cycles per sample to be 4 GCLK cycles, any change - /// to the cycles per sample via [`with_clock_cycles_per_sample()`] will be ignored. - pub fn enable_offset_compensation(mut self, enabled: bool) -> Self { - self.offset_compensation = Some(enabled); + /// to the cycles per sample via [`Self::with_clock_cycles_per_sample()`] will be ignored. + pub fn enable_offset_compensation(mut self, enable: bool) -> Self { + self.offset_compensation = Some(enable); self } /// Configure the ADC reference compensation - pub fn enable_reference_compensation(mut self, enabled: bool) -> Self { - self.reference_compensation = Some(enabled); + pub fn enable_reference_compensation(mut self, enable: bool) -> Self { + self.reference_compensation = Some(enable); + self + } + + /// Enables automatic left-adjustment when measuring differential inputs. This allows + /// use of the ADC summation or averaging hardware with negative result values. Results are + /// automatically right-shifted back appropriately. + pub fn enable_auto_left_adjust(mut self, enable: bool) -> Self { + self.auto_left_adjust = Some(enable); + self + } + + /// Automatically enables rail-to-rail operation when measuring a differential input. This + /// relaxes common-mode input requirements on differential inputs and allows measurments closer + /// to supply rails. + /// + /// ## Important + /// * Enabling auto rail-to-rail incurs a slight runtime performance hit as the CTRLA.R2R bit is + /// enable-protected, meaning the ADC must be shut down and re-enabled to enable/disable rail-to-rail mode. + pub fn enable_auto_rail_to_rail(mut self, enable: bool) -> Self { + self.auto_rail_to_rail = Some(enable); self } diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 758387c650b6..ec04ffba3e60 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -333,7 +333,6 @@ impl Adc { &mut self, pos_ch: pac::adc0::inputctrl::Muxposselect, neg_ch: pac::adc0::inputctrl::Muxnegselect, - sample_mode: SampleMode, ) { self.adc.inputctrl().modify(|r, w| { if r.muxpos().bits() != pos_ch.into() { @@ -346,9 +345,15 @@ impl Adc { // Safe as pos_ch and neg_ch are derived from Muxposselect and Muxnegselect PAC enums unsafe { w.muxpos().bits(pos_ch.into()); - w.muxneg().bits(neg_ch.into()); + w.muxneg().bits(neg_ch.into()) } + }); + self.sync(); + } + #[inline] + pub(super) fn set_sample_mode(&mut self, sample_mode: SampleMode) { + self.adc.inputctrl().write(|w| { match sample_mode { SampleMode::SingleEnded => w.diffmode().clear_bit(), SampleMode::Differential => w.diffmode().set_bit(), @@ -356,11 +361,36 @@ impl Adc { }); self.sync(); - match sample_mode { - SampleMode::SingleEnded => self.adc.ctrlb().write(|w| w.leftadj().clear_bit()), - SampleMode::Differential => self.adc.ctrlb().write(|w| w.leftadj().set_bit()), + if self.cfg.auto_left_adjust == true { + self.adc.ctrlb().write(|w| { + match sample_mode { + SampleMode::SingleEnded => w.leftadj().clear_bit(), + SampleMode::Differential => w.leftadj().set_bit(), + } + }); + self.sync(); + } + + if self.cfg.auto_rail_to_rail == true { + match sample_mode { + SampleMode::SingleEnded => { + if self.adc.ctrla().read().r2r().bit_is_set() { + self.power_down(); + self.adc.ctrla().write(|w| w.r2r().clear_bit()); + self.sync(); + self.power_up(); + } + }, + SampleMode::Differential => { + if self.adc.ctrla().read().r2r().bit_is_clear() { + self.power_down(); + self.adc.ctrla().write(|w| w.r2r().set_bit()); + self.sync(); + self.power_up(); + } + } + } } - self.sync() } #[inline] diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 62722da5c50f..492e4b74bbe8 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -66,6 +66,8 @@ const ADC_SETTINGS_INTERNAL_READ: AdcSettings = AdcSettings { vref: Reference::Intvcc1, offset_compensation: false, reference_compensation: false, + auto_left_adjust: false, + auto_rail_to_rail: false, }; /// Based on Temperature log row information (NVM)x @@ -77,6 +79,8 @@ const ADC_SETTINGS_INTERNAL_READ_D21_TEMP: AdcSettings = AdcSettings { vref: Reference::Int1v, offset_compensation: false, reference_compensation: false, + auto_left_adjust: false, + auto_rail_to_rail: false, }; /// Errors that may occur when operating the ADC @@ -370,7 +374,8 @@ impl Adc { self.disable_interrupts(Flags::all()); self.disable_freerunning(); self.sync(); - self.mux(pos_ch, neg_ch, sample_mode); + self.set_sample_mode(sample_mode); + self.mux(pos_ch, neg_ch); self.check_read_discard(); self.start_conversion(); while !self.read_flags().contains(Flags::RESRDY) { @@ -415,7 +420,8 @@ impl Adc { // Clear overrun errors that might've occured before we try to read anything self.clear_all_flags(); self.disable_interrupts(Flags::all()); - self.mux(pos_ch, neg_ch, sample_mode); + self.set_sample_mode(sample_mode); + self.mux(pos_ch, neg_ch); self.enable_freerunning(); self.start_conversion(); if self.discard { From 5138ebe956dcddac770bf124546b8a5162eba978 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Wed, 11 Feb 2026 15:56:00 -0500 Subject: [PATCH 15/39] added read of RESULT register in check_read_discard() to discard ADC conversion result --- hal/src/peripherals/adc/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index c34b560ad113..fdd60c5bb10b 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -358,6 +358,7 @@ impl Adc { while !self.read_flags().contains(Flags::RESRDY) { core::hint::spin_loop(); } + let _ = self.conversion_result(); self.discard = false; } } From 9f019d3f1a2a435956967f785618f3a2dc550d0c Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Thu, 12 Feb 2026 10:24:55 -0500 Subject: [PATCH 16/39] added handling of enable/disable of offset compensation when auto rail-to-rail is enabled --- hal/src/peripherals/adc/d5x/mod.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index ec04ffba3e60..c0766ed2d592 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -380,6 +380,17 @@ impl Adc { self.sync(); self.power_up(); } + + // Disable offset compenstation only if it was enabled earlier for auto rail-to-rail + if self.cfg.offset_compensation == false { + self.adc.sampctrl().modify(|r, w| { + if r.offcomp().bit_is_set() { + w.offcomp().clear_bit() + } else { + w + } + }); + } }, SampleMode::Differential => { if self.adc.ctrla().read().r2r().bit_is_clear() { @@ -388,6 +399,18 @@ impl Adc { self.sync(); self.power_up(); } + + // It is required to enable offset compensation during rail-to-rail operation + // per SAM D5x/E5x datasheet section 45.6.3.2 + if self.cfg.offset_compensation == false { + self.adc.sampctrl().modify(|r, w| { + if r.offcomp().bit_is_clear() { + w.offcomp().set_bit() + } else { + w + } + }); + } } } } From 1047ef037a3e5811cf9683f0b4eea1528da4e44b Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Thu, 12 Feb 2026 15:10:29 -0500 Subject: [PATCH 17/39] fixed unintentional overwriting of various registers during operation --- hal/src/peripherals/adc/d5x/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index c0766ed2d592..b8b91e3b0c1e 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -176,7 +176,7 @@ impl Adc { self.sync(); self.set_reference(cfg.vref); self.sync(); - self.adc.refctrl().write(|w| w.refcomp().bit(cfg.reference_compensation)); + self.adc.refctrl().modify(|_, w| w.refcomp().bit(cfg.reference_compensation)); self.sync(); self.adc.ctrla().modify(|_, w| w.enable().set_bit()); self.sync(); @@ -353,7 +353,7 @@ impl Adc { #[inline] pub(super) fn set_sample_mode(&mut self, sample_mode: SampleMode) { - self.adc.inputctrl().write(|w| { + self.adc.inputctrl().modify(|_, w| { match sample_mode { SampleMode::SingleEnded => w.diffmode().clear_bit(), SampleMode::Differential => w.diffmode().set_bit(), @@ -362,7 +362,7 @@ impl Adc { self.sync(); if self.cfg.auto_left_adjust == true { - self.adc.ctrlb().write(|w| { + self.adc.ctrlb().modify(|_, w| { match sample_mode { SampleMode::SingleEnded => w.leftadj().clear_bit(), SampleMode::Differential => w.leftadj().set_bit(), @@ -376,7 +376,7 @@ impl Adc { SampleMode::SingleEnded => { if self.adc.ctrla().read().r2r().bit_is_set() { self.power_down(); - self.adc.ctrla().write(|w| w.r2r().clear_bit()); + self.adc.ctrla().modify(|_, w| w.r2r().clear_bit()); self.sync(); self.power_up(); } @@ -395,7 +395,7 @@ impl Adc { SampleMode::Differential => { if self.adc.ctrla().read().r2r().bit_is_clear() { self.power_down(); - self.adc.ctrla().write(|w| w.r2r().set_bit()); + self.adc.ctrla().modify(|_, w| w.r2r().set_bit()); self.sync(); self.power_up(); } From 429d7320c7668c8e993e085c05707ccb623ef752 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Thu, 12 Feb 2026 15:43:15 -0500 Subject: [PATCH 18/39] cleaned up the logic for handling left-shifted results --- hal/src/peripherals/adc/d5x/mod.rs | 54 ++++++++++-------------------- 1 file changed, 18 insertions(+), 36 deletions(-) diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index b8b91e3b0c1e..8bf957017216 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -12,35 +12,6 @@ use super::{ }; use crate::{calibration, pac}; -enum ResultShift { - _8bit, - _10bit, - _12bit, - _16bit, -} - -impl From for ResultShift { - fn from(item: Resolution) -> Self { - match item { - Resolution::_8bit => ResultShift::_8bit, - Resolution::_10bit => ResultShift::_10bit, - Resolution::_12bit => ResultShift::_12bit, - Resolution::_16bit => ResultShift::_16bit, - } - } -} - -impl ResultShift { - fn shift_amount(self) -> u8 { - match self { - ResultShift::_8bit => 7, - ResultShift::_10bit => 5, - ResultShift::_12bit => 3, - ResultShift::_16bit => 0, - } - } -} - /// ADC instance 0 pub struct Adc0 { _adc: pac::Adc0, @@ -318,13 +289,24 @@ impl Adc { #[inline] pub(super) fn conversion_result(&self) -> u16 { - match self.adc.ctrlb().read().leftadj().bit_is_clear() { - true => self.adc.result().read().result().bits(), - false => - ((self.adc.result().read().result().bits() as i16) >> - ResultShift::from(self.cfg.accumulation.output_resolution()) - .shift_amount()) - as u16, + let shift_amt = if self.cfg.auto_left_adjust == true + && self.adc.ctrlb().read().leftadj().bit_is_set() { + match self.cfg.accumulation.output_resolution() { + Resolution::_8bit => 8, + Resolution::_10bit => 6, + Resolution::_12bit => 4, + Resolution::_16bit => 0, + } + } else { + 0 + }; + + // If the result is signed (a.k.a. differential input), cast as signed value before shift + // to use the proper arithemtic shift + if self.adc.inputctrl().read().diffmode().bit_is_set() { + ((self.adc.result().read().result().bits() as i16) >> shift_amt) as u16 + } else { + self.adc.result().read().result().bits() >> shift_amt } } From 28d5cae2fac54a375f943566ae5f1f2c72301a95 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Fri, 13 Feb 2026 11:54:31 -0500 Subject: [PATCH 19/39] simplified get_resolution() and adjusted auto left-shit to only adjust result when ADC is configured with Accumulation::Single --- hal/src/peripherals/adc/d5x/mod.rs | 3 ++- hal/src/peripherals/adc/mod.rs | 6 +----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 8bf957017216..8eb1ac3c28a6 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -290,7 +290,8 @@ impl Adc { #[inline] pub(super) fn conversion_result(&self) -> u16 { let shift_amt = if self.cfg.auto_left_adjust == true - && self.adc.ctrlb().read().leftadj().bit_is_set() { + && self.adc.ctrlb().read().leftadj().bit_is_set() + && let Accumulation::Single(_) = self.cfg.accumulation { match self.cfg.accumulation.output_resolution() { Resolution::_8bit => 8, Resolution::_10bit => 6, diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index fce38d35cbf8..7de90851e05f 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -459,11 +459,7 @@ impl Adc { /// Retrieve the configured ADC sample resolution #[inline] pub fn get_resolution(&self) -> Resolution { - match self.cfg.accumulation { - Accumulation::Single(res) => res.into(), - Accumulation::Average(_) => Resolution::_12bit, - Accumulation::Summed(_) => Resolution::_16bit, - } + self.cfg.accumulation.output_resolution() } /// Return the underlying ADC PAC object. From 36fbecec0b2b08e0440f47266d27f7a660ea1a94 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Thu, 26 Feb 2026 15:38:24 -0500 Subject: [PATCH 20/39] removed now unecessary flush() after conversion --- hal/src/peripherals/adc/d5x/mod.rs | 6 ------ hal/src/peripherals/adc/mod.rs | 1 - 2 files changed, 7 deletions(-) diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 8eb1ac3c28a6..9e523dbb20ef 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -398,12 +398,6 @@ impl Adc { } } } - - #[inline] - pub(super) fn flush(&mut self) { - self.adc.swtrig().modify(|_,w| w.flush().set_bit()); - self.sync(); - } } #[cfg(feature = "async")] diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index f3d92f72921d..64c7830c1718 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -381,7 +381,6 @@ impl Adc { while !self.read_flags().contains(Flags::RESRDY) { core::hint::spin_loop(); } - self.flush(); self.conversion_result() } From e61a56248d38ab85ce00dfbb7db72097959d2cc7 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Tue, 3 Mar 2026 15:11:12 -0500 Subject: [PATCH 21/39] added support for d11 implementation --- hal/src/peripherals/adc/builder.rs | 7 ++-- hal/src/peripherals/adc/d11/mod.rs | 57 +++++++++++++++++------------- hal/src/peripherals/adc/mod.rs | 9 ++--- 3 files changed, 42 insertions(+), 31 deletions(-) diff --git a/hal/src/peripherals/adc/builder.rs b/hal/src/peripherals/adc/builder.rs index 76080bf46aef..e1fca4061234 100644 --- a/hal/src/peripherals/adc/builder.rs +++ b/hal/src/peripherals/adc/builder.rs @@ -1,4 +1,4 @@ -use atsamd_hal_macros::hal_cfg; +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; #[hal_cfg("adc-d5x")] use crate::pac::adc0; @@ -161,14 +161,17 @@ pub struct AdcBuilder { /// Version of [AdcBuilder] without any optional settings. /// [AdcBuilder] is converted to this when passed to the ADC #[derive(Copy, Clone, PartialEq)] +#[hal_macro_helper] pub(crate) struct AdcSettings { pub clk_divider: Prescaler, pub sample_clock_cycles: u8, pub accumulation: Accumulation, pub vref: Reference, - pub offset_compensation: bool, pub reference_compensation: bool, pub auto_left_adjust: bool, + #[hal_cfg("adc-d5x")] + pub offset_compensation: bool, + #[hal_cfg("adc-d5x")] pub auto_rail_to_rail: bool, } diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index d42e03d49f2b..80872749faad 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -104,6 +104,8 @@ impl Adc { self.sync(); self.set_reference(cfg.vref); self.sync(); + self.adc.refctrl().modify(|_, w| w.refcomp().bit(cfg.reference_compensation)); + self.sync(); self.adc.ctrla().modify(|_, w| w.enable().set_bit()); self.sync(); self.cfg = cfg; @@ -207,31 +209,6 @@ impl Adc { neg_ch: pac::adc::inputctrl::Muxnegselect, sample_mode: SampleMode, ) { - let active_sample_mode = match self.adc.ctrlb().read().diffmode().bit() { - true => SampleMode::Differential, - false => SampleMode::SingleEnded, - }; - - if active_sample_mode != sample_mode { - // Disable the ADC if chip is SAMD11 family - // SAMD11 datasheet section 31.8.5 states that the ADC must be disabled to modify - // the DIFFMODE bit. - #[hal_cfg("adc-d11")] - self.power_down(); - - self.adc.ctrlb().write(|w| { - match sample_mode { - SampleMode::SingleEnded => w.diffmode().clear_bit(), - SampleMode::Differential => w.diffmode().set_bit(), - } - }); - self.sync(); - - // Re-enable the ADC if chip is SAMD11 family - #[hal_cfg("adc-d11")] - self.power_up(); - } - self.adc.inputctrl().modify(|r, w| { if r.muxpos().bits() != pos_ch.into() { self.discard = true; @@ -249,6 +226,36 @@ impl Adc { self.sync() } + + #[inline] + #[hal_macro_helper] + pub(super) fn set_sample_mode(&mut self, sample_mode: SampleMode) { + // Disable the ADC if chip is SAMD11 family + // SAMD11 datasheet section 31.8.5 states that the ADC must be disabled to modify + // the DIFFMODE and LEFTADJ bits. + #[hal_cfg("adc-d11")] + self.power_down(); + + self.adc.ctrlb().modify(|_, w| { + match sample_mode { + SampleMode::SingleEnded => w.diffmode().clear_bit(), + SampleMode::Differential => w.diffmode().set_bit(), + } + + if self.cfg.auto_left_adjust == true { + match sample_mode { + SampleMode::SingleEnded => w.leftadj().clear_bit(), + SampleMode::Differential => w.leftadj().set_bit(), + } + } + }); + self.sync(); + + // Re-enable the ADC if chip is SAMD11 family + #[hal_cfg("adc-d11")] + self.power_up(); + } + #[inline] fn cpu_raw_to_temp(&self, reading: u16) -> f32 { // Source: diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 64c7830c1718..78e7e3b522f1 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -24,7 +24,7 @@ use core::ops::Deref; -use atsamd_hal_macros::{hal_cfg, hal_module}; +use atsamd_hal_macros::{hal_cfg, hal_module, hal_macro_helper}; use pac::Peripherals; use crate::pac; @@ -59,14 +59,17 @@ use channel::*; /// ADC Settings when reading Internal sensors (Like VREF and Temperatures) /// These settings are based on the minimums suggested in the datasheet +#[hal_macro_helper] const ADC_SETTINGS_INTERNAL_READ: AdcSettings = AdcSettings { clk_divider: Prescaler::Div64, sample_clock_cycles: 32, accumulation: Accumulation::average(SampleCount::_4), vref: Reference::Intvcc1, - offset_compensation: false, reference_compensation: false, auto_left_adjust: false, + #[hal_cfg("adc-d5x")] + offset_compensation: false, + #[hal_cfg("adc-d5x")] auto_rail_to_rail: false, }; @@ -77,10 +80,8 @@ const ADC_SETTINGS_INTERNAL_READ_D21_TEMP: AdcSettings = AdcSettings { sample_clock_cycles: 32, accumulation: Accumulation::average(SampleCount::_4), vref: Reference::Int1v, - offset_compensation: false, reference_compensation: false, auto_left_adjust: false, - auto_rail_to_rail: false, }; /// Errors that may occur when operating the ADC From 6e3fdb0985cd345e969fd292601eb9cc085210d8 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Thu, 5 Mar 2026 15:32:20 -0500 Subject: [PATCH 22/39] refactored read* functions and elevated CpuSourceVoltage to type-level marker trait, updated async API to be compatible with prior changes --- hal/src/peripherals/adc/d11/channel.rs | 17 ++-- hal/src/peripherals/adc/d11/mod.rs | 26 ++--- hal/src/peripherals/adc/d5x/channel.rs | 14 ++- hal/src/peripherals/adc/d5x/mod.rs | 30 +++--- hal/src/peripherals/adc/input.rs | 8 +- hal/src/peripherals/adc/mod.rs | 126 +++++-------------------- 6 files changed, 65 insertions(+), 156 deletions(-) diff --git a/hal/src/peripherals/adc/d11/channel.rs b/hal/src/peripherals/adc/d11/channel.rs index a11aab76a5a9..658d7d2cf2ba 100644 --- a/hal/src/peripherals/adc/d11/channel.rs +++ b/hal/src/peripherals/adc/d11/channel.rs @@ -9,7 +9,7 @@ use crate::{ macro_rules! channel { ( $( - $CH:ident: ($($PMUX:path)?, $($NMUX:path)?) + $CH:ident: ($($PMUX:path)?, $($NMUX:path)?) $(+ $MARKER:ident)* ),+ $(,)? ) => { @@ -31,6 +31,9 @@ macro_rules! channel { const MUXVAL: Muxnegselect = $NMUX; } )? + $( + impl $MARKER for $CH {} + )* impl $CH { pub fn get_channel() -> Self { @@ -67,9 +70,9 @@ channel! { AIN18: (Muxposselect::Pin15, ), AIN19: (Muxposselect::Pin15, ), TEMP: (Muxposselect::Temp, ), - SCALEDCOREVCC: (Muxposselect::Scaledcorevcc, ), - SCALEDIOVCC: (Muxposselect::Scalediovcc, ), - BANDGAP: (Muxposselect::Bandgap, ), + SCALEDCOREVCC: (Muxposselect::Scaledcorevcc, ) + CpuVoltageSource, + SCALEDIOVCC: (Muxposselect::Scalediovcc, ) + CpuVoltageSource, + BANDGAP: (Muxposselect::Bandgap, ) + CpuVoltageSource, DAC: (Muxposselect::Dac, ), GND: (, Muxnegselect::Gnd), IOGND: (, Muxnegselect::Iognd), @@ -88,9 +91,9 @@ channel! { AIN8: (Muxposselect::Pin8, ), AIN9: (Muxposselect::Pin9, ), TEMP: (Muxposselect::Temp, ), - SCALEDCOREVCC: (Muxposselect::Scaledcorevcc, ), - SCALEDIOVCC: (Muxposselect::Scalediovcc, ), - BANDGAP: (Muxposselect::Bandgap, ), + SCALEDCOREVCC: (Muxposselect::Scaledcorevcc, ) + CpuVoltageSource, + SCALEDIOVCC: (Muxposselect::Scalediovcc, ) + CpuVoltageSource, + BANDGAP: (Muxposselect::Bandgap, ) + CpuVoltageSource, DAC: (Muxposselect::Dac, ), GND: (, Muxnegselect::Gnd), IOGND: (, Muxnegselect::Iognd), diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 80872749faad..ed954bb3200a 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -1,7 +1,7 @@ use super::{ ADC_SETTINGS_INTERNAL_READ, ADC_SETTINGS_INTERNAL_READ_D21_TEMP, Accumulation, Adc, AdcInstance, AdcSettings, CpuVoltageSource, Error, Flags, PrimaryAdc, SampleCount, - SampleMode + SampleMode, SingleEndedInput, TEMP, input::CpuVoltageSource, }; use atsamd_hal_macros::{hal_macro_helper}; @@ -80,11 +80,6 @@ impl Adc { .sampctrl() .modify(|_, w| unsafe { w.samplen().bits(cfg.sample_clock_cycles.saturating_sub(1)) }); // sample length self.sync(); - self.adc.inputctrl().modify(|_, w| { - w.muxneg().gnd(); - w.gain().variant(Gainselect::Div2) - }); // No negative input (internal gnd) - self.sync(); let (sample_cnt, adjres) = match cfg.accumulation { // 1 sample to be used as is Accumulation::Single(_) => (SampleCount::_1, 0), @@ -207,7 +202,6 @@ impl Adc { &mut self, pos_ch: pac::adc::inputctrl::Muxposselect, neg_ch: pac::adc::inputctrl::Muxnegselect, - sample_mode: SampleMode, ) { self.adc.inputctrl().modify(|r, w| { if r.muxpos().bits() != pos_ch.into() { @@ -302,10 +296,7 @@ impl Adc { // Settings adc.adc.inputctrl().modify(|_, w| w.gain()._1x()); adc.discard = true; - let res = adc.read_channel( - pac::adc::inputctrl::Muxposselect::Temp, - pac::adc::inputctrl::Muxnegselect::Gnd, - SampleMode::SingleEnded); + let res = adc.read(&mut SingleEndedInput::from_channel(TEMP::get_channel())); // Set gain back to normal adc.adc.inputctrl().modify(|_, w| w.gain().div2()); adc.discard = true; @@ -318,12 +309,9 @@ impl Adc { } #[inline] - pub fn read_cpu_voltage(&mut self, src: CpuVoltageSource) -> u16 { + pub fn read_cpu_voltage(&mut self, src: C) -> u16 { let voltage = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ, |adc| { - let res = adc.read_channel( - src.into(), - pac::adc::inputctrl::Muxnegselect::Gnd, - SampleMode::SingleEnded); + let res = adc.read(&mut SingleEndedInput::from_channel(src)); let mut res_f = adc.reading_to_f32(res) * 3.3; if CpuVoltageSource::Bandgap != src { res_f *= 4.0; @@ -349,7 +337,7 @@ where self.inner.adc.inputctrl().modify(|_, w| w.gain()._1x()); self.inner.discard = true; - let res = self.read_channel(0x18).await; + let res = self.read(&mut SingleEndedInput::from_channel(TEMP::get_channel())).await; self.inner.adc.inputctrl().modify(|_, w| w.gain().div2()); self.inner.configure(old_adc_settings); @@ -361,11 +349,11 @@ where } /// Reads a CPU voltage source. Value returned is in millivolts (mV) - pub async fn read_cpu_voltage(&mut self, src: CpuVoltageSource) -> u16 { + pub async fn read_cpu_voltage(&mut self, src: C) -> u16 { let old_adc_settings = self.inner.cfg; self.inner.configure(ADC_SETTINGS_INTERNAL_READ); - let res = self.read_channel(src as u8).await; + let res = self.read(&mut SingleEndedInput::from_channel(src)).await; let mut voltage = self.inner.reading_to_f32(res) * 3.3; if CpuVoltageSource::Bandgap != src { voltage *= 4.0; diff --git a/hal/src/peripherals/adc/d5x/channel.rs b/hal/src/peripherals/adc/d5x/channel.rs index 76c6fb8cc4c4..a43ec7069bcd 100644 --- a/hal/src/peripherals/adc/d5x/channel.rs +++ b/hal/src/peripherals/adc/d5x/channel.rs @@ -1,5 +1,6 @@ use core::marker::PhantomData; use crate::adc::*; +use crate::adc::input::CpuVoltageSource; use crate::{ pac::adc0::inputctrl::{Muxposselect, Muxnegselect}, typelevel::Sealed, @@ -8,7 +9,7 @@ use crate::{ macro_rules! channel { ( $( - $CH:ident: ($($PMUX:path)?, $($NMUX:path)?) + $CH:ident: ($($PMUX:path)?, $($NMUX:path)?) $(+ $MARKER:ident)* ),+ $(,)? ) => { @@ -30,6 +31,9 @@ macro_rules! channel { const MUXVAL: Muxnegselect = $NMUX; } )? + $( + impl $MARKER for $CH {} + )* impl $CH { pub fn get_channel() -> Self { @@ -60,10 +64,10 @@ channel! { AIN13: (Muxposselect::Ain13, ), AIN14: (Muxposselect::Ain14, ), AIN15: (Muxposselect::Ain15, ), - SCALEDCOREVCC: (Muxposselect::Scaledcorevcc, ), - SCALEDVBAT: (Muxposselect::Scaledvbat, ), - SCALEDIOVCC: (Muxposselect::Scalediovcc, ), - BANDGAP: (Muxposselect::Bandgap, ), + SCALEDCOREVCC: (Muxposselect::Scaledcorevcc, ) + CpuVoltageSource, + SCALEDVBAT: (Muxposselect::Scaledvbat, ) + CpuVoltageSource, + SCALEDIOVCC: (Muxposselect::Scalediovcc, ) + CpuVoltageSource, + BANDGAP: (Muxposselect::Bandgap, ) + CpuVoltageSource, PTAT: (Muxposselect::Ptat, ), CTAT: (Muxposselect::Ctat, ), DAC0: (Muxposselect::Dac, ), diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 9e523dbb20ef..7de88dc910a3 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -7,8 +7,9 @@ use pac::Supc; use super::{FutureAdc, async_api}; use super::{ - ADC_SETTINGS_INTERNAL_READ, Accumulation, Adc, AdcInstance, AdcSettings, CpuVoltageSource, - Error, Flags, PrimaryAdc, SampleCount, SampleMode, Resolution, + ADC_SETTINGS_INTERNAL_READ, Accumulation, Adc, AdcInstance, AdcSettings, Error, Flags, + PrimaryAdc, SampleCount, SampleMode, Resolution, SingleEndedInput, PTAT, CTAT, + input::CpuVoltageSource, }; use crate::{calibration, pac}; @@ -172,14 +173,8 @@ impl Adc { let (tp, tc) = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ, |adc| { ( - adc.read_channel( - pac::adc0::inputctrl::Muxposselect::Ptat, - pac::adc0::inputctrl::Muxnegselect::Gnd, - SampleMode::SingleEnded) as f32, // Tp - adc.read_channel( - pac::adc0::inputctrl::Muxposselect::Ctat, - pac::adc0::inputctrl::Muxnegselect::Gnd, - SampleMode::SingleEnded) as f32, // Tc + adc.read(&mut SingleEndedInput::from_channel(PTAT::get_channel())) as f32, // Tp + adc.read(&mut SingleEndedInput::from_channel(CTAT::get_channel())) as f32, // Tc ) }); // Restore vrefs old state @@ -189,12 +184,9 @@ impl Adc { } #[inline] - pub fn read_cpu_voltage(&mut self, src: CpuVoltageSource) -> u16 { + pub fn read_cpu_voltage>(&mut self, src: C) -> u16 { let voltage = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ, |adc| { - let res = adc.read_channel( - src.into(), - pac::adc0::inputctrl::Muxnegselect::Gnd, - SampleMode::SingleEnded); + let res = adc.read(&mut SingleEndedInput::from_channel(src)); adc.reading_to_f32(res) * 3.3 * 4.0 // x4 since the voltages are 1/4 scaled }); (voltage * 1000.0) as u16 @@ -421,8 +413,8 @@ where }); self.inner.configure(ADC_SETTINGS_INTERNAL_READ); - let tp = self.read_channel(0x1C).await as f32; - let tc = self.read_channel(0x1D).await as f32; + let tp = self.read(&mut SingleEndedInput::from_channel(PTAT::get_channel())).await as f32; + let tc = self.read(&mut SingleEndedInput::from_channel(CTAT::get_channel())).await as f32; // Restore vrefs old state supc.vref().write(|w| unsafe { w.bits(old_state) }); self.inner.configure(old_adc_settings); @@ -430,11 +422,11 @@ where } /// Reads a CPU voltage source. Value returned is in millivolts (mV) - pub async fn read_cpu_voltage(&mut self, src: CpuVoltageSource) -> u16 { + pub async fn read_cpu_voltage>(&mut self, src: C) -> u16 { let old_adc_settings = self.inner.cfg; self.inner.configure(ADC_SETTINGS_INTERNAL_READ); - let res = self.read_channel(src as u8).await; + let res = self.read(&mut SingleEndedInput::from_channel(src)).await; // x4 since the voltages are 1/4 scaled let voltage = self.inner.reading_to_f32(res) * 3.3 * 4.0; diff --git a/hal/src/peripherals/adc/input.rs b/hal/src/peripherals/adc/input.rs index 01c02d771d23..1ad3c0ec632d 100644 --- a/hal/src/peripherals/adc/input.rs +++ b/hal/src/peripherals/adc/input.rs @@ -1,5 +1,6 @@ use core::marker::PhantomData; use atsamd_hal_macros::hal_cfg; +use num_traits::int::PrimInt; use crate::{ gpio::AnyPin, typelevel::Sealed, @@ -31,6 +32,9 @@ pub trait PosAdcPin>: AnyPin>: AnyPin + Sealed {} +/// Marker trait representing [`PosChannel`]'s which measures one of the various CPU voltages +pub trait CpuVoltageSource: PosChannel {} + /// Sampling mode for the ADC #[derive(Copy, Clone, PartialEq, Eq)] pub enum SampleMode { @@ -43,7 +47,7 @@ pub trait AdcInput { const SAMPLE_MODE: SampleMode; type Pos: PosChannel; type Neg: NegChannel; - type Output; + type Output: PrimInt; /// Cast the ADC result to the appropriate output type fn cast_result(result: u16) -> Self::Output; @@ -125,7 +129,7 @@ where #[inline] fn cast_result(result: u16) -> Self::Output { - result as i16 + result as Self::Output } } diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 78e7e3b522f1..69aba3a727e4 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -104,55 +104,6 @@ pub enum Error { BufferOverrun, } -/// Voltage source to use when using the ADC to measure the CPU voltage -#[hal_cfg("adc-d5x")] -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum CpuVoltageSource { - /// Core voltage - Core, - /// VBAT supply voltage - Vbat, - /// IO supply voltage - Io, - /// Bandgap reference voltage - Bandgap, -} - -#[hal_cfg("adc-d5x")] -impl Into for CpuVoltageSource { - fn into(self) -> adc0::inputctrl::Muxposselect { - match self { - CpuVoltageSource::Core => adc0::inputctrl::Muxposselect::Scaledcorevcc, - CpuVoltageSource::Vbat => adc0::inputctrl::Muxposselect::Scaledvbat, - CpuVoltageSource::Io => adc0::inputctrl::Muxposselect::Scalediovcc, - CpuVoltageSource::Bandgap => adc0::inputctrl::Muxposselect::Bandgap, - } - } -} - -/// Voltage source to use when using the ADC to measure the CPU voltage -#[hal_cfg(any("adc-d21", "adc-d11"))] -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum CpuVoltageSource { - /// Bandgap reference voltage - 1.1V - Bandgap, - /// Core voltage - 1.2V - Core, - /// IO voltage - 1.62V to 3.63V - Io, -} - -#[hal_cfg(any("adc-d21", "adc-d11"))] -impl Into for CpuVoltageSource { - fn into(self) -> adc0::inputctrl::Muxposselect { - match self { - CpuVoltageSource::Core => adc0::inputctrl::Muxposselect::Scaledcorevcc, - CpuVoltageSource::Io => adc0::inputctrl::Muxposselect::Scalediovcc, - CpuVoltageSource::Bandgap => adc0::inputctrl::Muxposselect::Bandgap, - } - } -} - bitflags::bitflags! { /// ADC interrupt flags #[derive(Clone, Copy)] @@ -356,33 +307,22 @@ impl Adc { self.sync(); } - /// Read a single value from the provided ADC pin. + /// Read a single value from the provided ADC input. #[inline] - pub fn read>(&mut self, _input: &mut IN) -> IN::Output { - IN::cast_result(self.read_channel(IN::Pos::MUXVAL, IN::Neg::MUXVAL, IN::SAMPLE_MODE)) - } - - /// Read a single value from the provided channel, in a blocking fashion - #[inline] - fn read_channel( - &mut self, - pos_ch: adc0::inputctrl::Muxposselect, - neg_ch: adc0::inputctrl::Muxnegselect, - sample_mode: SampleMode, - ) -> u16 { + pub fn read>(&mut self, _input: &mut P) -> P::Output { // Clear overrun errors that might've occured before we try to read anything self.clear_all_flags(); self.disable_interrupts(Flags::all()); self.disable_freerunning(); self.sync(); - self.set_sample_mode(sample_mode); - self.mux(pos_ch, neg_ch); + self.set_sample_mode(P::SAMPLE_MODE); + self.mux(P::Pos::MUXVAL, P::Neg::MUXVAL); self.check_read_discard(); self.start_conversion(); while !self.read_flags().contains(Flags::RESRDY) { core::hint::spin_loop(); } - self.conversion_result() + P::cast_result(self.conversion_result()) } // If the ADC has to discard the next value, then we try to read it @@ -399,30 +339,18 @@ impl Adc { } } - /// Read into a buffer from the provided ADC pin, in a blocking fashion - #[inline] - pub fn read_buffer>( - &mut self, - _input: &mut IN, - dst: &mut [u16], - ) -> Result<(), Error> { - self.read_buffer_channel(IN::Pos::MUXVAL, IN::Neg::MUXVAL, IN::SAMPLE_MODE, dst) - } - /// Read into a buffer from the provided channel, in a blocking fashion #[inline] - fn read_buffer_channel( + pub fn read_buffer>( &mut self, - pos_ch: adc0::inputctrl::Muxposselect, - neg_ch: adc0::inputctrl::Muxnegselect, - sample_mode: SampleMode, - dst: &mut [u16], + _input: &mut P, + dst: &mut [P::Output], ) -> Result<(), Error> { // Clear overrun errors that might've occured before we try to read anything self.clear_all_flags(); self.disable_interrupts(Flags::all()); - self.set_sample_mode(sample_mode); - self.mux(pos_ch, neg_ch); + self.set_sample_mode(P::SAMPLE_MODE); + self.mux(P::Pos::MUXVAL, P::Neg::MUXVAL); self.enable_freerunning(); self.start_conversion(); self.check_read_discard(); @@ -440,7 +368,7 @@ impl Adc { return Err(e); } - *result = self.conversion_result(); + *result = P::cast_result(self.conversion_result()); } //self.power_down(); self.disable_freerunning(); @@ -492,19 +420,14 @@ where (self.inner, self.irqs) } - /// Read a single value from the provided ADC pin. - #[inline] - pub async fn read>(&mut self, _pin: &mut P) -> u16 { - self.read_channel(P::CHANNEL).await - } - - /// Read a single value from the provided channel ID + /// Read a single value from the provided ADC input #[inline] - async fn read_channel(&mut self, ch: u8) -> u16 { + pub async fn read>(&mut self, _input: &mut P) -> P::Output { // Clear overrun errors that might've occured before we try to read anything self.inner.clear_all_flags(); self.inner.disable_freerunning(); - self.inner.mux(ch); + self.inner.set_sample_mode(P::SAMPLE_MODE); + self.inner.mux(P::Pos::MUXVAL, P::Neg::MUXVAL); if self.inner.discard { // Read and discard if something was changed self.inner.start_conversion(); @@ -516,28 +439,23 @@ where // Here we explicitly ignore the result, because we know that // overrun errors are impossible since the ADC is configured in one-shot mode. let _ = self.wait_flags(Flags::RESRDY).await; - let res = self.inner.conversion_result(); + let res = P::cast_result(self.inner.conversion_result()); //self.inner.power_down(); self.inner.sync(); res } - /// Read into a buffer from the provided ADC pin + /// Read into a buffer from the provided ADC input #[inline] - pub async fn read_buffer>( + pub async fn read_buffer>( &mut self, _pin: &mut P, - dst: &mut [u16], + dst: &mut [P::Output], ) -> Result<(), Error> { - self.read_buffer_channel(P::CHANNEL, dst).await - } - - /// Read into a buffer from the provided channel ID - #[inline] - async fn read_buffer_channel(&mut self, ch: u8, dst: &mut [u16]) -> Result<(), Error> { // Clear overrun errors that might've occured before we try to read anything self.inner.clear_all_flags(); - self.inner.mux(ch); + self.inner.set_sample_mode(P::SAMPLE_MODE); + self.inner.mux(P::Pos::MUXVAL, P::Neg::MUXVAL); self.inner.enable_freerunning(); if self.inner.discard { @@ -556,7 +474,7 @@ where return Err(e); } - *result = self.inner.conversion_result(); + *result = P::cast_result(self.inner.conversion_result()); } //self.inner.power_down(); From a46c8ce9e3293efa9579279a73c8433026d26d4c Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Mon, 9 Mar 2026 09:10:24 -0400 Subject: [PATCH 23/39] removed conditonally-compiled fields in AdcSettings --- hal/src/peripherals/adc/builder.rs | 7 ++----- hal/src/peripherals/adc/mod.rs | 7 +++---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/hal/src/peripherals/adc/builder.rs b/hal/src/peripherals/adc/builder.rs index e1fca4061234..76080bf46aef 100644 --- a/hal/src/peripherals/adc/builder.rs +++ b/hal/src/peripherals/adc/builder.rs @@ -1,4 +1,4 @@ -use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; +use atsamd_hal_macros::hal_cfg; #[hal_cfg("adc-d5x")] use crate::pac::adc0; @@ -161,17 +161,14 @@ pub struct AdcBuilder { /// Version of [AdcBuilder] without any optional settings. /// [AdcBuilder] is converted to this when passed to the ADC #[derive(Copy, Clone, PartialEq)] -#[hal_macro_helper] pub(crate) struct AdcSettings { pub clk_divider: Prescaler, pub sample_clock_cycles: u8, pub accumulation: Accumulation, pub vref: Reference, + pub offset_compensation: bool, pub reference_compensation: bool, pub auto_left_adjust: bool, - #[hal_cfg("adc-d5x")] - pub offset_compensation: bool, - #[hal_cfg("adc-d5x")] pub auto_rail_to_rail: bool, } diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 69aba3a727e4..ad0fae4aff46 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -59,17 +59,14 @@ use channel::*; /// ADC Settings when reading Internal sensors (Like VREF and Temperatures) /// These settings are based on the minimums suggested in the datasheet -#[hal_macro_helper] const ADC_SETTINGS_INTERNAL_READ: AdcSettings = AdcSettings { clk_divider: Prescaler::Div64, sample_clock_cycles: 32, accumulation: Accumulation::average(SampleCount::_4), vref: Reference::Intvcc1, + offset_compensation: false, reference_compensation: false, auto_left_adjust: false, - #[hal_cfg("adc-d5x")] - offset_compensation: false, - #[hal_cfg("adc-d5x")] auto_rail_to_rail: false, }; @@ -80,8 +77,10 @@ const ADC_SETTINGS_INTERNAL_READ_D21_TEMP: AdcSettings = AdcSettings { sample_clock_cycles: 32, accumulation: Accumulation::average(SampleCount::_4), vref: Reference::Int1v, + offset_compensation: false, reference_compensation: false, auto_left_adjust: false, + auto_rail_to_rail: false, }; /// Errors that may occur when operating the ADC From 49efeb2f2b2daa6ccb63fd2bda42696fe78a10e0 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Tue, 10 Mar 2026 08:46:29 -0400 Subject: [PATCH 24/39] updated API to use pin parameters and inputs as mutable borrows and channel inputs as immutable borrows --- hal/src/peripherals/adc/d11/mod.rs | 23 ++++++++++++----------- hal/src/peripherals/adc/d5x/mod.rs | 12 ++++++------ hal/src/peripherals/adc/input.rs | 10 +++++----- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index ed954bb3200a..9c0ad16a8390 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -1,7 +1,7 @@ use super::{ ADC_SETTINGS_INTERNAL_READ, ADC_SETTINGS_INTERNAL_READ_D21_TEMP, Accumulation, Adc, - AdcInstance, AdcSettings, CpuVoltageSource, Error, Flags, PrimaryAdc, SampleCount, - SampleMode, SingleEndedInput, TEMP, input::CpuVoltageSource, + AdcInstance, AdcSettings, Error, Flags, PrimaryAdc, SampleCount, SampleMode, SingleEndedInput, + TEMP, input::CpuVoltageSource, }; use atsamd_hal_macros::{hal_macro_helper}; @@ -12,7 +12,7 @@ use super::{FutureAdc, async_api}; use crate::{calibration, pac}; use pac::Peripherals; use pac::Sysctrl; -use pac::adc::inputctrl::Gainselect; +use pac::adc::inputctrl::Muxposselect; pub mod pin; pub mod channel; @@ -234,14 +234,15 @@ impl Adc { match sample_mode { SampleMode::SingleEnded => w.diffmode().clear_bit(), SampleMode::Differential => w.diffmode().set_bit(), - } + }; if self.cfg.auto_left_adjust == true { match sample_mode { SampleMode::SingleEnded => w.leftadj().clear_bit(), SampleMode::Differential => w.leftadj().set_bit(), - } + }; } + w }); self.sync(); @@ -296,7 +297,7 @@ impl Adc { // Settings adc.adc.inputctrl().modify(|_, w| w.gain()._1x()); adc.discard = true; - let res = adc.read(&mut SingleEndedInput::from_channel(TEMP::get_channel())); + let res = adc.read(&mut SingleEndedInput::from_channel(&TEMP::get_channel())); // Set gain back to normal adc.adc.inputctrl().modify(|_, w| w.gain().div2()); adc.discard = true; @@ -309,11 +310,11 @@ impl Adc { } #[inline] - pub fn read_cpu_voltage(&mut self, src: C) -> u16 { + pub fn read_cpu_voltage>(&mut self, src: &C) -> u16 { let voltage = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ, |adc| { let res = adc.read(&mut SingleEndedInput::from_channel(src)); let mut res_f = adc.reading_to_f32(res) * 3.3; - if CpuVoltageSource::Bandgap != src { + if C::MUXVAL != Muxposselect::Bandgap { res_f *= 4.0; } res_f @@ -337,7 +338,7 @@ where self.inner.adc.inputctrl().modify(|_, w| w.gain()._1x()); self.inner.discard = true; - let res = self.read(&mut SingleEndedInput::from_channel(TEMP::get_channel())).await; + let res = self.read(&mut SingleEndedInput::from_channel(&TEMP::get_channel())).await; self.inner.adc.inputctrl().modify(|_, w| w.gain().div2()); self.inner.configure(old_adc_settings); @@ -349,13 +350,13 @@ where } /// Reads a CPU voltage source. Value returned is in millivolts (mV) - pub async fn read_cpu_voltage(&mut self, src: C) -> u16 { + pub async fn read_cpu_voltage>(&mut self, src: &C) -> u16 { let old_adc_settings = self.inner.cfg; self.inner.configure(ADC_SETTINGS_INTERNAL_READ); let res = self.read(&mut SingleEndedInput::from_channel(src)).await; let mut voltage = self.inner.reading_to_f32(res) * 3.3; - if CpuVoltageSource::Bandgap != src { + if C::MUXVAL != Muxposselect::Bandgap { voltage *= 4.0; } diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 7de88dc910a3..7c50b3880445 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -173,8 +173,8 @@ impl Adc { let (tp, tc) = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ, |adc| { ( - adc.read(&mut SingleEndedInput::from_channel(PTAT::get_channel())) as f32, // Tp - adc.read(&mut SingleEndedInput::from_channel(CTAT::get_channel())) as f32, // Tc + adc.read(&mut SingleEndedInput::from_channel(&PTAT::get_channel())) as f32, // Tp + adc.read(&mut SingleEndedInput::from_channel(&CTAT::get_channel())) as f32, // Tc ) }); // Restore vrefs old state @@ -184,7 +184,7 @@ impl Adc { } #[inline] - pub fn read_cpu_voltage>(&mut self, src: C) -> u16 { + pub fn read_cpu_voltage>(&mut self, src: &C) -> u16 { let voltage = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ, |adc| { let res = adc.read(&mut SingleEndedInput::from_channel(src)); adc.reading_to_f32(res) * 3.3 * 4.0 // x4 since the voltages are 1/4 scaled @@ -413,8 +413,8 @@ where }); self.inner.configure(ADC_SETTINGS_INTERNAL_READ); - let tp = self.read(&mut SingleEndedInput::from_channel(PTAT::get_channel())).await as f32; - let tc = self.read(&mut SingleEndedInput::from_channel(CTAT::get_channel())).await as f32; + let tp = self.read(&mut SingleEndedInput::from_channel(&PTAT::get_channel())).await as f32; + let tc = self.read(&mut SingleEndedInput::from_channel(&CTAT::get_channel())).await as f32; // Restore vrefs old state supc.vref().write(|w| unsafe { w.bits(old_state) }); self.inner.configure(old_adc_settings); @@ -422,7 +422,7 @@ where } /// Reads a CPU voltage source. Value returned is in millivolts (mV) - pub async fn read_cpu_voltage>(&mut self, src: C) -> u16 { + pub async fn read_cpu_voltage>(&mut self, src: &C) -> u16 { let old_adc_settings = self.inner.cfg; self.inner.configure(ADC_SETTINGS_INTERNAL_READ); diff --git a/hal/src/peripherals/adc/input.rs b/hal/src/peripherals/adc/input.rs index 1ad3c0ec632d..cab911a5a9f0 100644 --- a/hal/src/peripherals/adc/input.rs +++ b/hal/src/peripherals/adc/input.rs @@ -94,12 +94,12 @@ where } /// Creates a [`SingleEndedInput`] from a [`PosChannel`] - pub fn from_channel(_pos: P) -> Self { + pub fn from_channel(_pos: &P) -> Self { Self::new() } /// Creates a [`SingleEndedInput`] from a [`PosAdcPin`] - pub fn from_pin>(_pin: PP) -> Self { + pub fn from_pin>(_pin: &mut PP) -> Self { Self::new() } } @@ -149,12 +149,12 @@ where } /// Create a [`DifferentialInput`] from two ADC channels - pub fn from_channels(_pos: P, _neg: N) -> Self { + pub fn from_channels(_pos: &P, _neg: &N) -> Self { Self::new() } /// Create a [`DifferentialInput`] from two GPIO pins - pub fn from_pins(_pos: PP, _neg: PN) -> Self + pub fn from_pins(_pos: &mut PP, _neg: &mut PN) -> Self where PP: PosAdcPin, PN: NegAdcPin, @@ -164,7 +164,7 @@ where /// Create a [`DifferentialInput`] from a single GPIO pin and /// a negative ADC channel - pub fn from_pos_pin(_pos: PP, _neg: N) -> Self + pub fn from_pos_pin(_pos: &mut PP, _neg: &N) -> Self where PP: PosAdcPin, { From c3d1d40a41674db2af306b0d1317a0f923a2996e Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Tue, 10 Mar 2026 15:38:08 -0400 Subject: [PATCH 25/39] updated impacted board examples --- boards/feather_m0/examples/adc.rs | 7 ++++--- boards/feather_m0/examples/async_adc.rs | 5 +++-- boards/feather_m4/examples/adc.rs | 5 +++-- boards/metro_m4/examples/adc.rs | 5 +++-- boards/metro_m4/examples/async_adc.rs | 5 +++-- boards/pygamer/src/pins.rs | 6 +++--- boards/samd11_bare/examples/adc.rs | 5 +++-- 7 files changed, 22 insertions(+), 16 deletions(-) diff --git a/boards/feather_m0/examples/adc.rs b/boards/feather_m0/examples/adc.rs index 6194e9d26b28..69854ebf5d60 100644 --- a/boards/feather_m0/examples/adc.rs +++ b/boards/feather_m0/examples/adc.rs @@ -17,7 +17,7 @@ use bsp::Pins; use pac::{CorePeripherals, Peripherals}; use hal::{ - adc::{Accumulation, Adc, Prescaler, Resolution}, + adc::{Accumulation, Adc, Prescaler, Resolution, SingleEndedInput}, clock::GenericClockController, }; @@ -44,10 +44,11 @@ fn main() -> ! { .enable(peripherals.adc, &mut peripherals.pm, &adc_clock) .unwrap(); let mut adc_pin = pins.a0.into_alternate(); + let mut adc_input = SingleEndedInput::from_pin(&mut adc_pin); loop { - let res = adc.read(&mut adc_pin); + let res = adc.read(&mut adc_input); #[cfg(feature = "use_semihosting")] - cortex_m_semihosting::hprintln!("ADC value: {}", read).unwrap(); + cortex_m_semihosting::hprintln!("ADC value: {}", res).unwrap(); } } diff --git a/boards/feather_m0/examples/async_adc.rs b/boards/feather_m0/examples/async_adc.rs index 56139256ba01..838313bf958d 100644 --- a/boards/feather_m0/examples/async_adc.rs +++ b/boards/feather_m0/examples/async_adc.rs @@ -16,7 +16,7 @@ use bsp::Pins; use pac::{CorePeripherals, Peripherals}; use hal::{ - adc::{Accumulation, Adc, Adc0, Prescaler, Resolution}, + adc::{Accumulation, Adc, Adc0, Prescaler, Resolution, SingleEndedInput}, clock::GenericClockController, }; @@ -49,9 +49,10 @@ async fn main(_s: embassy_executor::Spawner) -> ! { .into_future(Irqs); let mut adc_pin = pins.a0.into_alternate(); + let mut adc_input = SingleEndedInput::from_pin(&mut adc_pin); loop { - let res = adc.read(&mut adc_pin).await; + let res = adc.read(&mut adc_input).await; #[cfg(feature = "use_semihosting")] cortex_m_semihosting::hprintln!("ADC Result: {}", res).unwrap(); } diff --git a/boards/feather_m4/examples/adc.rs b/boards/feather_m4/examples/adc.rs index b6b892cf43d3..9ce50d44fe14 100644 --- a/boards/feather_m4/examples/adc.rs +++ b/boards/feather_m4/examples/adc.rs @@ -17,7 +17,7 @@ use bsp::Pins; use pac::{CorePeripherals, Peripherals}; use hal::{ - adc::{Accumulation, Adc, Prescaler, Resolution}, + adc::{Accumulation, Adc, Prescaler, Resolution, SingleEndedInput}, clock::v2::{clock_system_at_reset, pclk::Pclk}, }; @@ -50,9 +50,10 @@ fn main() -> ! { .enable(peripherals.adc0, apb_adc0, &pclk_adc0) .unwrap(); let mut adc_pin = pins.a0.into_alternate(); + let mut adc_input = SingleEndedInput::from_pin(&mut adc_pin); loop { - let res = adc.read(&mut adc_pin); + let res = adc.read(&mut adc_input); #[cfg(feature = "use_semihosting")] cortex_m_semihosting::hprintln!("ADC value: {}", res); } diff --git a/boards/metro_m4/examples/adc.rs b/boards/metro_m4/examples/adc.rs index 56c46bbd97f7..e931cc567253 100644 --- a/boards/metro_m4/examples/adc.rs +++ b/boards/metro_m4/examples/adc.rs @@ -17,7 +17,7 @@ use bsp::Pins; use pac::{CorePeripherals, Peripherals}; use hal::{ - adc::{Accumulation, Adc, Prescaler, Resolution}, + adc::{Accumulation, Adc, Prescaler, Resolution, SingleEndedInput}, clock::v2::{clock_system_at_reset, pclk::Pclk}, }; @@ -49,9 +49,10 @@ fn main() -> ! { .enable(peripherals.adc0, apb_adc0, &pclk_adc0) .unwrap(); let mut adc_pin = pins.a0.into_alternate(); + let mut adc_input = SingleEndedInput::from_pin(&mut adc_pin); loop { - let res = adc.read(&mut adc_pin); + let res = adc.read(&mut adc_input); #[cfg(feature = "use_semihosting")] cortex_m_semihosting::hprintln!("ADC Result: {}", res).unwrap(); } diff --git a/boards/metro_m4/examples/async_adc.rs b/boards/metro_m4/examples/async_adc.rs index 818e395734d7..c78aee9b5cd6 100644 --- a/boards/metro_m4/examples/async_adc.rs +++ b/boards/metro_m4/examples/async_adc.rs @@ -16,7 +16,7 @@ use bsp::Pins; use pac::{CorePeripherals, Peripherals}; use hal::{ - adc::{Accumulation, Adc, Adc0, Prescaler, Resolution}, + adc::{Accumulation, Adc, Adc0, Prescaler, Resolution, SingleEndedInput}, clock::v2::{clock_system_at_reset, pclk::Pclk}, }; @@ -54,9 +54,10 @@ async fn main(_s: embassy_executor::Spawner) -> ! { .into_future(Irqs); let mut adc_pin = pins.a0.into_alternate(); + let mut adc_input = SingleEndedInput::from_pin(&mut adc_pin); loop { - let res = adc.read(&mut adc_pin).await; + let res = adc.read(&mut adc_input).await; #[cfg(feature = "use_semihosting")] cortex_m_semihosting::hprintln!("ADC Result: {}", res).unwrap(); } diff --git a/boards/pygamer/src/pins.rs b/boards/pygamer/src/pins.rs index 2f1f5de4c63c..1f552b62a2e9 100644 --- a/boards/pygamer/src/pins.rs +++ b/boards/pygamer/src/pins.rs @@ -934,8 +934,8 @@ impl JoystickReader { // unnecessary? note adafruit recenters around zero.. Im not doing that // either atm. - let y_data: u16 = adc.read(&mut self.joy_y); - let x_data: u16 = adc.read(&mut self.joy_x); + let y_data: u16 = adc.read(&mut hal::adc::SingleEndedInput::from_pin(&mut self.joy_y)); + let x_data: u16 = adc.read(&mut hal::adc::SingleEndedInput::from_pin(&mut self.joy_x)); (x_data, y_data) } @@ -969,7 +969,7 @@ pub struct BatteryReader { impl BatteryReader { /// Returns a float for voltage of battery pub fn read(&mut self, adc: &mut hal::adc::Adc) -> f32 { - let data: u16 = adc.read(&mut self.battery); + let data: u16 = adc.read(&mut hal::adc::SingleEndedInput::from_pin(&mut self.battery)); let result: f32 = (data as f32 / 4095.0) * 2.0 * 3.3; result } diff --git a/boards/samd11_bare/examples/adc.rs b/boards/samd11_bare/examples/adc.rs index 8a4416357b2b..94c41f56d1c1 100644 --- a/boards/samd11_bare/examples/adc.rs +++ b/boards/samd11_bare/examples/adc.rs @@ -17,7 +17,7 @@ use bsp::Pins; use pac::{CorePeripherals, Peripherals}; use hal::{ - adc::{Accumulation, Adc, Prescaler, Resolution}, + adc::{Accumulation, Adc, Prescaler, Resolution, SingleEndedInput}, clock::GenericClockController, }; @@ -45,9 +45,10 @@ fn main() -> ! { .unwrap(); let mut adc_pin = pins.d1.into_alternate(); + let mut adc_input = SingleEndedInput::from_pin(&mut adc_pin); loop { - let res = adc.read(&mut adc_pin); + let res = adc.read(&mut adc_input); #[cfg(feature = "use_semihosting")] cortex_m_semihosting::hprintln!("ADC Result: {}", res); } From 89f06dafd59801cb4b1e2c9af1af3c77d28dffa7 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Wed, 11 Mar 2026 08:27:51 -0400 Subject: [PATCH 26/39] added back initialization of INPUTCTRL.GAIN for D11/D21 family chips --- hal/src/peripherals/adc/d11/mod.rs | 6 +++++- hal/src/peripherals/adc/mod.rs | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 9c0ad16a8390..ba10c82cc761 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -12,7 +12,7 @@ use super::{FutureAdc, async_api}; use crate::{calibration, pac}; use pac::Peripherals; use pac::Sysctrl; -use pac::adc::inputctrl::Muxposselect; +use pac::adc::inputctrl::{Gainselect, Muxposselect}; pub mod pin; pub mod channel; @@ -80,6 +80,10 @@ impl Adc { .sampctrl() .modify(|_, w| unsafe { w.samplen().bits(cfg.sample_clock_cycles.saturating_sub(1)) }); // sample length self.sync(); + self.adc + .inputctrl() + .modify(|_, w| w.gain().variant(Gainselect::Div2)); + self.sync(); let (sample_cnt, adjres) = match cfg.accumulation { // 1 sample to be used as is Accumulation::Single(_) => (SampleCount::_1, 0), diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index ad0fae4aff46..d0a40e08eb08 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -24,7 +24,7 @@ use core::ops::Deref; -use atsamd_hal_macros::{hal_cfg, hal_module, hal_macro_helper}; +use atsamd_hal_macros::{hal_cfg, hal_module}; use pac::Peripherals; use crate::pac; From f58a86ea0ff4a5fc74db75fdaa59c152684ce7b2 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Wed, 11 Mar 2026 15:05:41 -0400 Subject: [PATCH 27/39] minor formatting changes --- hal/src/peripherals/adc/builder.rs | 11 +++++++---- hal/src/peripherals/adc/d11/mod.rs | 5 +---- hal/src/peripherals/adc/d5x/mod.rs | 7 ++----- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/hal/src/peripherals/adc/builder.rs b/hal/src/peripherals/adc/builder.rs index 76080bf46aef..c83b7df1f913 100644 --- a/hal/src/peripherals/adc/builder.rs +++ b/hal/src/peripherals/adc/builder.rs @@ -263,8 +263,9 @@ impl AdcBuilder { /// Configure the ADC offset compensation /// /// ## Important - /// * Enabling offset compesation forces the clock cycles per sample to be 4 GCLK cycles, any change - /// to the cycles per sample via [`Self::with_clock_cycles_per_sample()`] will be ignored. + /// * (D5x) Enabling offset compesation forces the clock cycles per sample to be 4 GCLK cycles, + /// any change to the cycles per sample via [`Self::with_clock_cycles_per_sample()`] will be ignored. + /// * (For D11/D21) Not supported. pub fn enable_offset_compensation(mut self, enable: bool) -> Self { self.offset_compensation = Some(enable); self @@ -289,8 +290,10 @@ impl AdcBuilder { /// to supply rails. /// /// ## Important - /// * Enabling auto rail-to-rail incurs a slight runtime performance hit as the CTRLA.R2R bit is - /// enable-protected, meaning the ADC must be shut down and re-enabled to enable/disable rail-to-rail mode. + /// * (D5x) Enabling auto rail-to-rail incurs a slight runtime performance hit as the CTRLA.R2R + /// bit is enable-protected, meaning the ADC must be shut down and re-enabled to + /// enable/disable rail-to-rail mode. + /// * (D11/D21) Not supported. pub fn enable_auto_rail_to_rail(mut self, enable: bool) -> Self { self.auto_rail_to_rail = Some(enable); self diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index ba10c82cc761..15bf9a1dba38 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -208,10 +208,7 @@ impl Adc { neg_ch: pac::adc::inputctrl::Muxnegselect, ) { self.adc.inputctrl().modify(|r, w| { - if r.muxpos().bits() != pos_ch.into() { - self.discard = true; - } - if r.muxneg().bits() != neg_ch.into() { + if (r.muxpos().bits() != pos_ch.into()) || (r.muxneg().bits() != neg_ch.into()) { self.discard = true; } diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 7c50b3880445..665aae361fdd 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -295,7 +295,7 @@ impl Adc { }; // If the result is signed (a.k.a. differential input), cast as signed value before shift - // to use the proper arithemtic shift + // to use the proper (arithemtic) shift if self.adc.inputctrl().read().diffmode().bit_is_set() { ((self.adc.result().read().result().bits() as i16) >> shift_amt) as u16 } else { @@ -310,10 +310,7 @@ impl Adc { neg_ch: pac::adc0::inputctrl::Muxnegselect, ) { self.adc.inputctrl().modify(|r, w| { - if r.muxpos().bits() != pos_ch.into() { - self.discard = true; - } - if r.muxneg().bits() != neg_ch.into() { + if (r.muxpos().bits() != pos_ch.into()) || (r.muxneg().bits() != neg_ch.into()) { self.discard = true; } From 8da29df7abc97127c5ea14d91893f09193ea0549 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Wed, 11 Mar 2026 15:55:22 -0400 Subject: [PATCH 28/39] updated doc example --- hal/src/peripherals/adc/builder.rs | 2 +- hal/src/peripherals/adc/mod.rs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/hal/src/peripherals/adc/builder.rs b/hal/src/peripherals/adc/builder.rs index c83b7df1f913..3e630c4530ae 100644 --- a/hal/src/peripherals/adc/builder.rs +++ b/hal/src/peripherals/adc/builder.rs @@ -265,7 +265,7 @@ impl AdcBuilder { /// ## Important /// * (D5x) Enabling offset compesation forces the clock cycles per sample to be 4 GCLK cycles, /// any change to the cycles per sample via [`Self::with_clock_cycles_per_sample()`] will be ignored. - /// * (For D11/D21) Not supported. + /// * (D11/D21) Not supported. pub fn enable_offset_compensation(mut self, enable: bool) -> Self { self.offset_compensation = Some(enable); self diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index d0a40e08eb08..13fcaa66d9dd 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -5,7 +5,7 @@ //! conversions. Async functionality can be enabled via the `async` feature. //! //! ``` -//! # use atsamd_hal::adc::{AdcResolution, Reference}; +//! # use atsamd_hal::adc::{AdcResolution, Reference, SingleEndedInput}; //! let apb_adc0 = buses.apb.enable(tokens.apbs.adc0); //! let (pclk_adc0, _gclk0) = Pclk::enable(tokens.pclks.adc0, clocks.gclk0); //! @@ -17,9 +17,10 @@ //! .unwrap(); //! //! let mut adc_pin = pins.a0.into_alternate(); +//! let mut adc_input = SingleEndedInput::from_pin(adc_pin); //! //! let mut _buffer = [0; 16]; -//! adc.read_buffer(&mut adc_pin, &mut _buffer).unwrap(); +//! adc.read_buffer(&mut adc_input, &mut _buffer).unwrap(); //! ``` use core::ops::Deref; From 505122654b5e3396eb6f31f5d3576042ce513665 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Thu, 12 Mar 2026 14:49:16 -0400 Subject: [PATCH 29/39] rustfmt changes and additional documentation comments --- hal/src/peripherals/adc/builder.rs | 24 +++++++++++++----------- hal/src/peripherals/adc/d11/mod.rs | 3 ++- hal/src/peripherals/adc/d5x/mod.rs | 2 ++ hal/src/peripherals/adc/input.rs | 28 +++++++++++++--------------- hal/src/peripherals/adc/mod.rs | 1 - 5 files changed, 30 insertions(+), 28 deletions(-) diff --git a/hal/src/peripherals/adc/builder.rs b/hal/src/peripherals/adc/builder.rs index 3e630c4530ae..8b98b84540f4 100644 --- a/hal/src/peripherals/adc/builder.rs +++ b/hal/src/peripherals/adc/builder.rs @@ -263,8 +263,9 @@ impl AdcBuilder { /// Configure the ADC offset compensation /// /// ## Important - /// * (D5x) Enabling offset compesation forces the clock cycles per sample to be 4 GCLK cycles, - /// any change to the cycles per sample via [`Self::with_clock_cycles_per_sample()`] will be ignored. + /// * (D5x) Enabling offset compesation forces the clock cycles per sample + /// to be 4 GCLK cycles, any change to the cycles per sample via + /// [`Self::with_clock_cycles_per_sample()`] will be ignored. /// * (D11/D21) Not supported. pub fn enable_offset_compensation(mut self, enable: bool) -> Self { self.offset_compensation = Some(enable); @@ -277,22 +278,23 @@ impl AdcBuilder { self } - /// Enables automatic left-adjustment when measuring differential inputs. This allows - /// use of the ADC summation or averaging hardware with negative result values. Results are - /// automatically right-shifted back appropriately. + /// Enables automatic left-adjustment when measuring differential inputs. + /// This allows use of the ADC summation or averaging hardware with + /// negative result values. Results are automatically right-shifted back + /// appropriately. pub fn enable_auto_left_adjust(mut self, enable: bool) -> Self { self.auto_left_adjust = Some(enable); self } - /// Automatically enables rail-to-rail operation when measuring a differential input. This - /// relaxes common-mode input requirements on differential inputs and allows measurments closer - /// to supply rails. + /// Automatically enables rail-to-rail operation when measuring a + /// differential input. This relaxes common-mode input requirements on + /// differential inputs and allows measurments closer to supply rails. /// /// ## Important - /// * (D5x) Enabling auto rail-to-rail incurs a slight runtime performance hit as the CTRLA.R2R - /// bit is enable-protected, meaning the ADC must be shut down and re-enabled to - /// enable/disable rail-to-rail mode. + /// * (D5x) Enabling auto rail-to-rail incurs a slight runtime performance + /// hit as the CTRLA.R2R bit is enable-protected, meaning the ADC must be + /// shut down and re-enabled to enable/disable rail-to-rail mode. /// * (D11/D21) Not supported. pub fn enable_auto_rail_to_rail(mut self, enable: bool) -> Self { self.auto_rail_to_rail = Some(enable); diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 15bf9a1dba38..1f51936952d4 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -201,7 +201,6 @@ impl Adc { } #[inline] - #[hal_macro_helper] pub(super) fn mux( &mut self, pos_ch: pac::adc::inputctrl::Muxposselect, @@ -222,6 +221,8 @@ impl Adc { } + /// Sets the sample mode and various ADC settings based on sample mode & the user supplied + /// [`AdcSettings`]. #[inline] #[hal_macro_helper] pub(super) fn set_sample_mode(&mut self, sample_mode: SampleMode) { diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 665aae361fdd..51031257c937 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -323,6 +323,8 @@ impl Adc { self.sync(); } + /// Sets the sample mode and various ADC settings based on sample mode & the user supplied + /// [`AdcSettings`]. #[inline] pub(super) fn set_sample_mode(&mut self, sample_mode: SampleMode) { self.adc.inputctrl().modify(|_, w| { diff --git a/hal/src/peripherals/adc/input.rs b/hal/src/peripherals/adc/input.rs index cab911a5a9f0..31e1c4532ad5 100644 --- a/hal/src/peripherals/adc/input.rs +++ b/hal/src/peripherals/adc/input.rs @@ -1,21 +1,14 @@ -use core::marker::PhantomData; +use super::{AdcInstance, channel::*}; +use crate::{gpio::AnyPin, typelevel::Sealed}; use atsamd_hal_macros::hal_cfg; +use core::marker::PhantomData; use num_traits::int::PrimInt; -use crate::{ - gpio::AnyPin, - typelevel::Sealed, -}; -use super::{ - AdcInstance, - channel::*, -}; #[hal_cfg(any("adc-d11", "adc-d21"))] use crate::pac::adc as adc0; #[hal_cfg("adc-d5x")] use crate::pac::adc0; - /// Trait for positive ADC channels pub trait PosChannel: Sealed { const MUXVAL: adc0::inputctrl::Muxposselect; @@ -27,12 +20,19 @@ pub trait NegChannel: Sealed { } /// Marker trait for ADC pins which can be used as positive ADC inputs -pub trait PosAdcPin>: AnyPin + Sealed {} +pub trait PosAdcPin>: + AnyPin + Sealed +{ +} /// Marker trait for ADC pins which can be used as negative ADC inputs -pub trait NegAdcPin>: AnyPin + Sealed {} +pub trait NegAdcPin>: + AnyPin + Sealed +{ +} -/// Marker trait representing [`PosChannel`]'s which measures one of the various CPU voltages +/// Marker trait representing [`PosChannel`]'s which measures one of the various +/// CPU voltages pub trait CpuVoltageSource: PosChannel {} /// Sampling mode for the ADC @@ -85,7 +85,6 @@ where I: AdcInstance, P: PosChannel, { - fn new() -> Self { Self { adc: PhantomData, @@ -139,7 +138,6 @@ where P: PosChannel, N: NegChannel, { - fn new() -> Self { Self { adc: PhantomData, diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 13fcaa66d9dd..7e4cb373f670 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -142,7 +142,6 @@ pub trait AdcInstance { fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; } - /// ADC Instance #[hal_cfg(any("adc-d11", "adc-d21"))] pub struct Adc { From 2ba7322f6cf82ef484fbe8c9872a4ad4e85f0729 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Thu, 12 Mar 2026 14:54:13 -0400 Subject: [PATCH 30/39] fixed clippy errors --- hal/src/peripherals/adc/d5x/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 51031257c937..79a05b9439d6 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -281,7 +281,7 @@ impl Adc { #[inline] pub(super) fn conversion_result(&self) -> u16 { - let shift_amt = if self.cfg.auto_left_adjust == true + let shift_amt = if self.cfg.auto_left_adjust && self.adc.ctrlb().read().leftadj().bit_is_set() && let Accumulation::Single(_) = self.cfg.accumulation { match self.cfg.accumulation.output_resolution() { @@ -335,7 +335,7 @@ impl Adc { }); self.sync(); - if self.cfg.auto_left_adjust == true { + if self.cfg.auto_left_adjust { self.adc.ctrlb().modify(|_, w| { match sample_mode { SampleMode::SingleEnded => w.leftadj().clear_bit(), @@ -345,7 +345,7 @@ impl Adc { self.sync(); } - if self.cfg.auto_rail_to_rail == true { + if self.cfg.auto_rail_to_rail { match sample_mode { SampleMode::SingleEnded => { if self.adc.ctrla().read().r2r().bit_is_set() { @@ -356,7 +356,7 @@ impl Adc { } // Disable offset compenstation only if it was enabled earlier for auto rail-to-rail - if self.cfg.offset_compensation == false { + if !self.cfg.offset_compensation { self.adc.sampctrl().modify(|r, w| { if r.offcomp().bit_is_set() { w.offcomp().clear_bit() @@ -376,7 +376,7 @@ impl Adc { // It is required to enable offset compensation during rail-to-rail operation // per SAM D5x/E5x datasheet section 45.6.3.2 - if self.cfg.offset_compensation == false { + if !self.cfg.offset_compensation { self.adc.sampctrl().modify(|r, w| { if r.offcomp().bit_is_clear() { w.offcomp().set_bit() From c60c17ac92edeb7186779f49ed150ac4664a1f77 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Thu, 12 Mar 2026 15:00:05 -0400 Subject: [PATCH 31/39] fixed same clippy error in d11 implementation --- hal/src/peripherals/adc/d11/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 1f51936952d4..5545914f89b2 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -238,7 +238,7 @@ impl Adc { SampleMode::Differential => w.diffmode().set_bit(), }; - if self.cfg.auto_left_adjust == true { + if self.cfg.auto_left_adjust { match sample_mode { SampleMode::SingleEnded => w.leftadj().clear_bit(), SampleMode::Differential => w.leftadj().set_bit(), From c0eeb9d1085c0ae5f7af9914601eac937375c55b Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Mon, 16 Mar 2026 11:06:32 -0400 Subject: [PATCH 32/39] refactored to remove AdcInput wrapper type, separated functionality into read() and read_differential() function --- hal/src/peripherals/adc/d11/channel.rs | 20 ++- hal/src/peripherals/adc/d11/mod.rs | 16 +- hal/src/peripherals/adc/d11/pin.rs | 9 +- hal/src/peripherals/adc/d5x/channel.rs | 20 ++- hal/src/peripherals/adc/d5x/mod.rs | 38 +++-- hal/src/peripherals/adc/d5x/pin.rs | 8 +- hal/src/peripherals/adc/input.rs | 141 +---------------- hal/src/peripherals/adc/mod.rs | 203 ++++++++++++++++++++++--- 8 files changed, 259 insertions(+), 196 deletions(-) diff --git a/hal/src/peripherals/adc/d11/channel.rs b/hal/src/peripherals/adc/d11/channel.rs index 658d7d2cf2ba..68e61f7721c3 100644 --- a/hal/src/peripherals/adc/d11/channel.rs +++ b/hal/src/peripherals/adc/d11/channel.rs @@ -24,24 +24,28 @@ macro_rules! channel { $( impl PosChannel for $CH { const MUXVAL: Muxposselect = $PMUX; + + fn get_channel() -> Self { + Self { + adc: PhantomData + } + } } )? $( impl NegChannel for $CH { const MUXVAL: Muxnegselect = $NMUX; + + fn get_channel() -> Self { + Self { + adc: PhantomData + } + } } )? $( impl $MARKER for $CH {} )* - - impl $CH { - pub fn get_channel() -> Self { - Self { - adc: PhantomData - } - } - } )+ } }; diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 5545914f89b2..8e2fa534caf5 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -1,7 +1,7 @@ use super::{ ADC_SETTINGS_INTERNAL_READ, ADC_SETTINGS_INTERNAL_READ_D21_TEMP, Accumulation, Adc, - AdcInstance, AdcSettings, Error, Flags, PrimaryAdc, SampleCount, SampleMode, SingleEndedInput, - TEMP, input::CpuVoltageSource, + AdcInstance, AdcSettings, Error, Flags, PrimaryAdc, SampleCount, SampleMode, TEMP, GND, + input::CpuVoltageSource, PosChannel, NegChannel, }; use atsamd_hal_macros::{hal_macro_helper}; @@ -299,7 +299,7 @@ impl Adc { // Settings adc.adc.inputctrl().modify(|_, w| w.gain()._1x()); adc.discard = true; - let res = adc.read(&mut SingleEndedInput::from_channel(&TEMP::get_channel())); + let res = adc.read_channel(TEMP::get_channel(), GND::get_channel()); // Set gain back to normal adc.adc.inputctrl().modify(|_, w| w.gain().div2()); adc.discard = true; @@ -312,9 +312,9 @@ impl Adc { } #[inline] - pub fn read_cpu_voltage>(&mut self, src: &C) -> u16 { + pub fn read_cpu_voltage>(&mut self, src: C) -> u16 { let voltage = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ, |adc| { - let res = adc.read(&mut SingleEndedInput::from_channel(src)); + let res = adc.read_channel(src); let mut res_f = adc.reading_to_f32(res) * 3.3; if C::MUXVAL != Muxposselect::Bandgap { res_f *= 4.0; @@ -340,7 +340,7 @@ where self.inner.adc.inputctrl().modify(|_, w| w.gain()._1x()); self.inner.discard = true; - let res = self.read(&mut SingleEndedInput::from_channel(&TEMP::get_channel())).await; + let res = self.read_channel(TEMP::get_channel(), GND::get_channel()).await; self.inner.adc.inputctrl().modify(|_, w| w.gain().div2()); self.inner.configure(old_adc_settings); @@ -352,11 +352,11 @@ where } /// Reads a CPU voltage source. Value returned is in millivolts (mV) - pub async fn read_cpu_voltage>(&mut self, src: &C) -> u16 { + pub async fn read_cpu_voltage>(&mut self, src: C) -> u16 { let old_adc_settings = self.inner.cfg; self.inner.configure(ADC_SETTINGS_INTERNAL_READ); - let res = self.read(&mut SingleEndedInput::from_channel(src)).await; + let res = self.read_channel(src, GND::get_channel()).await; let mut voltage = self.inner.reading_to_f32(res) * 3.3; if C::MUXVAL != Muxposselect::Bandgap { voltage *= 4.0; diff --git a/hal/src/peripherals/adc/d11/pin.rs b/hal/src/peripherals/adc/d11/pin.rs index e142b7e101ff..e9fd844152de 100644 --- a/hal/src/peripherals/adc/d11/pin.rs +++ b/hal/src/peripherals/adc/d11/pin.rs @@ -13,7 +13,10 @@ macro_rules! pos_adc_pins { crate::paste::item! { $( $( #[$cfg] )? - impl PosAdcPin<$Adc, $CHAN<$Adc>> for Pin<$PinId, AlternateB> {} + impl PosAdcPin<$Adc> for Pin<$PinId, AlternateB> { + type Channel = $CHAN<$Adc>; + + } )+ } }; @@ -30,7 +33,9 @@ macro_rules! neg_adc_pins { crate::paste::item! { $( $( #[$cfg] )? - impl NegAdcPin<$Adc, $CHAN<$Adc>> for Pin<$PinId, AlternateB> {} + impl NegAdcPin<$Adc> for Pin<$PinId, AlternateB> { + type Channel = $CHAN<$Adc>; + } )+ } }; diff --git a/hal/src/peripherals/adc/d5x/channel.rs b/hal/src/peripherals/adc/d5x/channel.rs index a43ec7069bcd..8013e36c9411 100644 --- a/hal/src/peripherals/adc/d5x/channel.rs +++ b/hal/src/peripherals/adc/d5x/channel.rs @@ -24,24 +24,28 @@ macro_rules! channel { $( impl PosChannel for $CH { const MUXVAL: Muxposselect = $PMUX; + + fn get_channel() -> Self { + Self { + adc: PhantomData + } + } } )? $( impl NegChannel for $CH { const MUXVAL: Muxnegselect = $NMUX; + + fn get_channel() -> Self { + Self { + adc: PhantomData + } + } } )? $( impl $MARKER for $CH {} )* - - impl $CH { - pub fn get_channel() -> Self { - Self { - adc: PhantomData - } - } - } )+ } }; diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 79a05b9439d6..e4f6bf9c8330 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -8,8 +8,8 @@ use super::{FutureAdc, async_api}; use super::{ ADC_SETTINGS_INTERNAL_READ, Accumulation, Adc, AdcInstance, AdcSettings, Error, Flags, - PrimaryAdc, SampleCount, SampleMode, Resolution, SingleEndedInput, PTAT, CTAT, - input::CpuVoltageSource, + PrimaryAdc, SampleCount, SampleMode, Resolution, PTAT, CTAT, GND, input::CpuVoltageSource, + PosChannel, NegChannel, }; use crate::{calibration, pac}; @@ -173,8 +173,16 @@ impl Adc { let (tp, tc) = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ, |adc| { ( - adc.read(&mut SingleEndedInput::from_channel(&PTAT::get_channel())) as f32, // Tp - adc.read(&mut SingleEndedInput::from_channel(&CTAT::get_channel())) as f32, // Tc + adc.read_channel( + PTAT::get_channel(), + GND::get_channel(), + SampleMode::SingleEnded + ) as f32, // Tp + adc.read_channel( + CTAT::get_channel(), + GND::get_channel(), + SampleMode::SingleEnded + ) as f32, // Tc ) }); // Restore vrefs old state @@ -184,9 +192,9 @@ impl Adc { } #[inline] - pub fn read_cpu_voltage>(&mut self, src: &C) -> u16 { + pub fn read_cpu_voltage>(&mut self, src: C) -> u16 { let voltage = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ, |adc| { - let res = adc.read(&mut SingleEndedInput::from_channel(src)); + let res = adc.read_channel(src, GND::get_channel(), SampleMode::SingleEnded); adc.reading_to_f32(res) * 3.3 * 4.0 // x4 since the voltages are 1/4 scaled }); (voltage * 1000.0) as u16 @@ -412,8 +420,18 @@ where }); self.inner.configure(ADC_SETTINGS_INTERNAL_READ); - let tp = self.read(&mut SingleEndedInput::from_channel(&PTAT::get_channel())).await as f32; - let tc = self.read(&mut SingleEndedInput::from_channel(&CTAT::get_channel())).await as f32; + let tp = self.read_channel( + PTAT::get_channel(), + GND::get_channel(), + SampleMode::SingleEnded + ).await as f32; + + let tc = self.read_channel( + CTAT::get_channel(), + GND::get_channel(), + SampleMode::SingleEnded + ).await as f32; + // Restore vrefs old state supc.vref().write(|w| unsafe { w.bits(old_state) }); self.inner.configure(old_adc_settings); @@ -421,11 +439,11 @@ where } /// Reads a CPU voltage source. Value returned is in millivolts (mV) - pub async fn read_cpu_voltage>(&mut self, src: &C) -> u16 { + pub async fn read_cpu_voltage>(&mut self, src: C) -> u16 { let old_adc_settings = self.inner.cfg; self.inner.configure(ADC_SETTINGS_INTERNAL_READ); - let res = self.read(&mut SingleEndedInput::from_channel(src)).await; + let res = self.read_channel(src, GND::get_channel(), SampleMode::SingleEnded).await; // x4 since the voltages are 1/4 scaled let voltage = self.inner.reading_to_f32(res) * 3.3 * 4.0; diff --git a/hal/src/peripherals/adc/d5x/pin.rs b/hal/src/peripherals/adc/d5x/pin.rs index 2757d198d876..94cc8440425c 100644 --- a/hal/src/peripherals/adc/d5x/pin.rs +++ b/hal/src/peripherals/adc/d5x/pin.rs @@ -13,7 +13,9 @@ macro_rules! pos_adc_pins { crate::paste::item! { $( $( #[$cfg] )? - impl PosAdcPin<$Adc, $CHAN<$Adc>> for Pin<$PinId, AlternateB> {} + impl PosAdcPin<$Adc> for Pin<$PinId, AlternateB> { + type Channel = $CHAN<$Adc>; + } )+ } }; @@ -30,7 +32,9 @@ macro_rules! neg_adc_pins { crate::paste::item! { $( $( #[$cfg] )? - impl NegAdcPin<$Adc, $CHAN<$Adc>> for Pin<$PinId, AlternateB> {} + impl NegAdcPin<$Adc> for Pin<$PinId, AlternateB> { + type Channel = $CHAN<$Adc>; + } )+ } }; diff --git a/hal/src/peripherals/adc/input.rs b/hal/src/peripherals/adc/input.rs index 31e1c4532ad5..cb981dd4696d 100644 --- a/hal/src/peripherals/adc/input.rs +++ b/hal/src/peripherals/adc/input.rs @@ -12,23 +12,25 @@ use crate::pac::adc0; /// Trait for positive ADC channels pub trait PosChannel: Sealed { const MUXVAL: adc0::inputctrl::Muxposselect; + + fn get_channel() -> Self; } /// Trait for negative ADC channels pub trait NegChannel: Sealed { const MUXVAL: adc0::inputctrl::Muxnegselect; + + fn get_channel() -> Self; } /// Marker trait for ADC pins which can be used as positive ADC inputs -pub trait PosAdcPin>: - AnyPin + Sealed -{ +pub trait PosAdcPin: AnyPin + Sealed { + type Channel: PosChannel; } /// Marker trait for ADC pins which can be used as negative ADC inputs -pub trait NegAdcPin>: - AnyPin + Sealed -{ +pub trait NegAdcPin: AnyPin + Sealed { + type Channel: NegChannel; } /// Marker trait representing [`PosChannel`]'s which measures one of the various @@ -42,130 +44,3 @@ pub enum SampleMode { Differential, } -/// Trait for ADC inputs -pub trait AdcInput { - const SAMPLE_MODE: SampleMode; - type Pos: PosChannel; - type Neg: NegChannel; - type Output: PrimInt; - - /// Cast the ADC result to the appropriate output type - fn cast_result(result: u16) -> Self::Output; -} - -/// Type representing a single ended input. -pub struct SingleEndedInput -where - I: AdcInstance, - P: PosChannel, -{ - adc: PhantomData, - pos: PhantomData

, -} - -impl AdcInput for SingleEndedInput -where - I: AdcInstance, - P: PosChannel, -{ - const SAMPLE_MODE: SampleMode = SampleMode::SingleEnded; - type Pos = P; - /// Single ended inputs always referenced to internal ADC GND - type Neg = GND; - type Output = u16; - - #[inline] - fn cast_result(result: u16) -> Self::Output { - result - } -} - -impl SingleEndedInput -where - I: AdcInstance, - P: PosChannel, -{ - fn new() -> Self { - Self { - adc: PhantomData, - pos: PhantomData, - } - } - - /// Creates a [`SingleEndedInput`] from a [`PosChannel`] - pub fn from_channel(_pos: &P) -> Self { - Self::new() - } - - /// Creates a [`SingleEndedInput`] from a [`PosAdcPin`] - pub fn from_pin>(_pin: &mut PP) -> Self { - Self::new() - } -} - -/// Type representing a differential input -pub struct DifferentialInput -where - I: AdcInstance, - P: PosChannel, - N: NegChannel, -{ - adc: PhantomData, - pos: PhantomData

, - neg: PhantomData, -} - -impl AdcInput for DifferentialInput -where - I: AdcInstance, - P: PosChannel, - N: NegChannel, -{ - const SAMPLE_MODE: SampleMode = SampleMode::Differential; - type Pos = P; - type Neg = N; - type Output = i16; - - #[inline] - fn cast_result(result: u16) -> Self::Output { - result as Self::Output - } -} - -impl DifferentialInput -where - I: AdcInstance, - P: PosChannel, - N: NegChannel, -{ - fn new() -> Self { - Self { - adc: PhantomData, - pos: PhantomData, - neg: PhantomData, - } - } - - /// Create a [`DifferentialInput`] from two ADC channels - pub fn from_channels(_pos: &P, _neg: &N) -> Self { - Self::new() - } - - /// Create a [`DifferentialInput`] from two GPIO pins - pub fn from_pins(_pos: &mut PP, _neg: &mut PN) -> Self - where - PP: PosAdcPin, - PN: NegAdcPin, - { - Self::new() - } - - /// Create a [`DifferentialInput`] from a single GPIO pin and - /// a negative ADC channel - pub fn from_pos_pin(_pos: &mut PP, _neg: &N) -> Self - where - PP: PosAdcPin, - { - Self::new() - } -} diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 7e4cb373f670..eb1603cb551a 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -306,22 +306,55 @@ impl Adc { self.sync(); } - /// Read a single value from the provided ADC input. + /// Reads a single value from the provided ADC pin, referenced to internal + /// GND (single ended) #[inline] - pub fn read>(&mut self, _input: &mut P) -> P::Output { + pub fn read

(&mut self, _pin: &mut P) -> u16 + where + P: PosAdcPin, + { + self.read_channel( + P::Channel::get_channel(), + GND::get_channel(), + SampleMode::SingleEnded, + ) + } + + /// Reads a single value from a pair of ADC pins (differentially) + #[inline] + pub fn read_differential(&mut self, _pos_pin: &mut P, _neg_pin: &mut N) -> i16 + where + P: PosAdcPin, + N: NegAdcPin, + { + self.read_channel( + P::Channel::get_channel(), + N::Channel::get_channel(), + SampleMode::Differential, + ) + .cast_signed() + } + + /// Read a single value from the provided ADC channels. + #[inline] + fn read_channel(&mut self, _pos: P, _neg: N, sample_mode: SampleMode) -> u16 + where + P: PosChannel, + N: NegChannel, + { // Clear overrun errors that might've occured before we try to read anything self.clear_all_flags(); self.disable_interrupts(Flags::all()); self.disable_freerunning(); self.sync(); - self.set_sample_mode(P::SAMPLE_MODE); - self.mux(P::Pos::MUXVAL, P::Neg::MUXVAL); + self.set_sample_mode(sample_mode); + self.mux(P::MUXVAL, N::MUXVAL); self.check_read_discard(); self.start_conversion(); while !self.read_flags().contains(Flags::RESRDY) { core::hint::spin_loop(); } - P::cast_result(self.conversion_result()) + self.conversion_result() } // If the ADC has to discard the next value, then we try to read it @@ -338,18 +371,60 @@ impl Adc { } } + /// Read into a buffer from the provided pin (single-ended, referenced to + /// GND), in a blocking fashion + #[inline] + pub fn read_buffer

(&mut self, _pin: P, dst: &mut [u16]) -> Result<(), Error> + where + P: PosAdcPin, + { + self.read_buffer_channel( + P::Channel::get_channel(), + GND::get_channel(), + SampleMode::SingleEnded, + dst, + ) + } + + /// Read into a buffer from the provided pins (differentially), in a + /// blocking fashion + #[inline] + pub fn read_buffer_differential( + &mut self, + _pos: P, + _neg: N, + dst: &mut [i16], + ) -> Result<(), Error> + where + P: PosAdcPin, + N: NegAdcPin, + { + self.read_buffer_channel( + P::Channel::get_channel(), + N::Channel::get_channel(), + SampleMode::Differential, + unsafe { &mut *(dst as *mut [i16] as *mut [u16]) }, + ) + } + /// Read into a buffer from the provided channel, in a blocking fashion #[inline] - pub fn read_buffer>( + fn read_buffer_channel( &mut self, - _input: &mut P, - dst: &mut [P::Output], - ) -> Result<(), Error> { + _pos: P, + _neg: N, + sample_mode: SampleMode, + dst: &mut [u16], + ) -> Result<(), Error> + where + P: PosChannel, + N: NegChannel, + { // Clear overrun errors that might've occured before we try to read anything self.clear_all_flags(); self.disable_interrupts(Flags::all()); - self.set_sample_mode(P::SAMPLE_MODE); - self.mux(P::Pos::MUXVAL, P::Neg::MUXVAL); + self.set_sample_mode(sample_mode); + self.mux(P::MUXVAL, N::MUXVAL); self.enable_freerunning(); self.start_conversion(); self.check_read_discard(); @@ -367,7 +442,7 @@ impl Adc { return Err(e); } - *result = P::cast_result(self.conversion_result()); + *result = self.conversion_result(); } //self.power_down(); self.disable_freerunning(); @@ -419,14 +494,49 @@ where (self.inner, self.irqs) } - /// Read a single value from the provided ADC input + /// Read a single value from the provided ADC pin (single-ended, referenced + /// to GND) #[inline] - pub async fn read>(&mut self, _input: &mut P) -> P::Output { + pub async fn read

(&mut self, _pin: &mut P) -> u16 + where + P: PosAdcPin, + { + self.read_channel( + P::Channel::get_channel(), + GND::get_channel(), + SampleMode::SingleEnded, + ) + .await + } + + /// Read a single value from the provided ADC pins (differentially) + #[inline] + pub async fn read_differential(&mut self, _pos: &mut P, _neg: &mut N) -> i16 + where + P: PosAdcPin, + N: NegAdcPin, + { + self.read_channel( + P::Channel::get_channel(), + N::Channel::get_channel(), + SampleMode::Differential, + ) + .await + .cast_signed() + } + + /// Read a single value from the provided ADC channels + #[inline] + async fn read_channel(&mut self, _pos: P, _neg: N, sample_mode: SampleMode) -> u16 + where + P: PosChannel, + N: NegChannel, + { // Clear overrun errors that might've occured before we try to read anything self.inner.clear_all_flags(); self.inner.disable_freerunning(); - self.inner.set_sample_mode(P::SAMPLE_MODE); - self.inner.mux(P::Pos::MUXVAL, P::Neg::MUXVAL); + self.inner.set_sample_mode(sample_mode); + self.inner.mux(P::MUXVAL, N::MUXVAL); if self.inner.discard { // Read and discard if something was changed self.inner.start_conversion(); @@ -438,23 +548,66 @@ where // Here we explicitly ignore the result, because we know that // overrun errors are impossible since the ADC is configured in one-shot mode. let _ = self.wait_flags(Flags::RESRDY).await; - let res = P::cast_result(self.inner.conversion_result()); + let res = self.inner.conversion_result(); //self.inner.power_down(); self.inner.sync(); res } - /// Read into a buffer from the provided ADC input + /// Read into a buffer from the provided ADC pin (single-ended, referenced + /// to GND) #[inline] - pub async fn read_buffer>( + pub async fn read_buffer

(&mut self, _pos: &mut P, dst: &mut [u16]) -> Result<(), Error> + where + P: PosAdcPin, + { + self.read_buffer_channel( + P::Channel::get_channel(), + GND::get_channel(), + SampleMode::SingleEnded, + dst, + ) + .await + } + + /// Read into a buffer from the provided ADC pin (differentially) + #[inline] + pub async fn read_buffer_differential( + &mut self, + _pos: &mut P, + _neg: &mut N, + dst: &mut [i16], + ) -> Result<(), Error> + where + P: PosAdcPin, + N: NegAdcPin, + { + self.read_buffer_channel( + P::Channel::get_channel(), + N::Channel::get_channel(), + SampleMode::Differential, + unsafe { &mut *(dst as *mut [i16] as *mut [u16]) }, + ) + .await + } + + /// Read into a buffer from the provided ADC channels + #[inline] + async fn read_buffer_channel( &mut self, - _pin: &mut P, - dst: &mut [P::Output], - ) -> Result<(), Error> { + _pos: P, + _neg: N, + sample_mode: SampleMode, + dst: &mut [u16], + ) -> Result<(), Error> + where + P: PosChannel, + N: NegChannel, + { // Clear overrun errors that might've occured before we try to read anything self.inner.clear_all_flags(); - self.inner.set_sample_mode(P::SAMPLE_MODE); - self.inner.mux(P::Pos::MUXVAL, P::Neg::MUXVAL); + self.inner.set_sample_mode(sample_mode); + self.inner.mux(P::MUXVAL, N::MUXVAL); self.inner.enable_freerunning(); if self.inner.discard { @@ -473,7 +626,7 @@ where return Err(e); } - *result = P::cast_result(self.inner.conversion_result()); + *result = self.inner.conversion_result(); } //self.inner.power_down(); From 3e1f35beeb082eb9816be41824394069d16ce97a Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Mon, 16 Mar 2026 11:17:30 -0400 Subject: [PATCH 33/39] moved remaining traits to mod.rs, removed input.rs --- hal/src/peripherals/adc/d11/mod.rs | 2 +- hal/src/peripherals/adc/d5x/channel.rs | 3 +- hal/src/peripherals/adc/d5x/mod.rs | 4 +-- hal/src/peripherals/adc/input.rs | 46 -------------------------- hal/src/peripherals/adc/mod.rs | 39 ++++++++++++++++++++-- 5 files changed, 40 insertions(+), 54 deletions(-) delete mode 100644 hal/src/peripherals/adc/input.rs diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 8e2fa534caf5..8deb3b201e26 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -1,7 +1,7 @@ use super::{ ADC_SETTINGS_INTERNAL_READ, ADC_SETTINGS_INTERNAL_READ_D21_TEMP, Accumulation, Adc, AdcInstance, AdcSettings, Error, Flags, PrimaryAdc, SampleCount, SampleMode, TEMP, GND, - input::CpuVoltageSource, PosChannel, NegChannel, + CpuVoltageSource, PosChannel, NegChannel, }; use atsamd_hal_macros::{hal_macro_helper}; diff --git a/hal/src/peripherals/adc/d5x/channel.rs b/hal/src/peripherals/adc/d5x/channel.rs index 8013e36c9411..5d3fd168424b 100644 --- a/hal/src/peripherals/adc/d5x/channel.rs +++ b/hal/src/peripherals/adc/d5x/channel.rs @@ -1,7 +1,6 @@ use core::marker::PhantomData; -use crate::adc::*; -use crate::adc::input::CpuVoltageSource; use crate::{ + adc::*, pac::adc0::inputctrl::{Muxposselect, Muxnegselect}, typelevel::Sealed, }; diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index e4f6bf9c8330..1a71ebd6d3de 100644 --- a/hal/src/peripherals/adc/d5x/mod.rs +++ b/hal/src/peripherals/adc/d5x/mod.rs @@ -8,8 +8,8 @@ use super::{FutureAdc, async_api}; use super::{ ADC_SETTINGS_INTERNAL_READ, Accumulation, Adc, AdcInstance, AdcSettings, Error, Flags, - PrimaryAdc, SampleCount, SampleMode, Resolution, PTAT, CTAT, GND, input::CpuVoltageSource, - PosChannel, NegChannel, + PrimaryAdc, SampleCount, SampleMode, Resolution, PTAT, CTAT, GND, CpuVoltageSource, PosChannel, + NegChannel, }; use crate::{calibration, pac}; diff --git a/hal/src/peripherals/adc/input.rs b/hal/src/peripherals/adc/input.rs deleted file mode 100644 index cb981dd4696d..000000000000 --- a/hal/src/peripherals/adc/input.rs +++ /dev/null @@ -1,46 +0,0 @@ -use super::{AdcInstance, channel::*}; -use crate::{gpio::AnyPin, typelevel::Sealed}; -use atsamd_hal_macros::hal_cfg; -use core::marker::PhantomData; -use num_traits::int::PrimInt; - -#[hal_cfg(any("adc-d11", "adc-d21"))] -use crate::pac::adc as adc0; -#[hal_cfg("adc-d5x")] -use crate::pac::adc0; - -/// Trait for positive ADC channels -pub trait PosChannel: Sealed { - const MUXVAL: adc0::inputctrl::Muxposselect; - - fn get_channel() -> Self; -} - -/// Trait for negative ADC channels -pub trait NegChannel: Sealed { - const MUXVAL: adc0::inputctrl::Muxnegselect; - - fn get_channel() -> Self; -} - -/// Marker trait for ADC pins which can be used as positive ADC inputs -pub trait PosAdcPin: AnyPin + Sealed { - type Channel: PosChannel; -} - -/// Marker trait for ADC pins which can be used as negative ADC inputs -pub trait NegAdcPin: AnyPin + Sealed { - type Channel: NegChannel; -} - -/// Marker trait representing [`PosChannel`]'s which measures one of the various -/// CPU voltages -pub trait CpuVoltageSource: PosChannel {} - -/// Sampling mode for the ADC -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum SampleMode { - SingleEnded, - Differential, -} - diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index eb1603cb551a..007a6367e502 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -23,6 +23,7 @@ //! adc.read_buffer(&mut adc_input, &mut _buffer).unwrap(); //! ``` +use crate::{gpio::AnyPin, typelevel::Sealed}; use core::ops::Deref; use atsamd_hal_macros::{hal_cfg, hal_module}; @@ -46,9 +47,6 @@ pub use async_api::*; mod builder; pub use builder::*; -mod input; -pub use input::*; - #[hal_cfg(any("adc-d11", "adc-d21"))] use crate::pac::adc as adc0; #[hal_cfg("adc-d5x")] @@ -117,6 +115,41 @@ bitflags::bitflags! { } } +/// Trait for positive ADC channels +pub trait PosChannel: Sealed { + const MUXVAL: adc0::inputctrl::Muxposselect; + + fn get_channel() -> Self; +} + +/// Trait for negative ADC channels +pub trait NegChannel: Sealed { + const MUXVAL: adc0::inputctrl::Muxnegselect; + + fn get_channel() -> Self; +} + +/// Marker trait for ADC pins which can be used as positive ADC inputs +pub trait PosAdcPin: AnyPin + Sealed { + type Channel: PosChannel; +} + +/// Marker trait for ADC pins which can be used as negative ADC inputs +pub trait NegAdcPin: AnyPin + Sealed { + type Channel: NegChannel; +} + +/// Marker trait representing [`PosChannel`]'s which measures one of the various +/// CPU voltages +pub trait CpuVoltageSource: PosChannel {} + +/// Sampling mode for the ADC +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum SampleMode { + SingleEnded, + Differential, +} + /// Marker for which ADC has access to the CPUs internal sensors pub trait PrimaryAdc {} From feb6f7762899d15ce84c5d85bccd5345351b6d52 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Mon, 16 Mar 2026 11:19:22 -0400 Subject: [PATCH 34/39] Revert "updated impacted board examples" This reverts commit c3d1d40a41674db2af306b0d1317a0f923a2996e. --- boards/feather_m0/examples/adc.rs | 7 +++---- boards/feather_m0/examples/async_adc.rs | 5 ++--- boards/feather_m4/examples/adc.rs | 5 ++--- boards/metro_m4/examples/adc.rs | 5 ++--- boards/metro_m4/examples/async_adc.rs | 5 ++--- boards/pygamer/src/pins.rs | 6 +++--- boards/samd11_bare/examples/adc.rs | 5 ++--- 7 files changed, 16 insertions(+), 22 deletions(-) diff --git a/boards/feather_m0/examples/adc.rs b/boards/feather_m0/examples/adc.rs index 69854ebf5d60..6194e9d26b28 100644 --- a/boards/feather_m0/examples/adc.rs +++ b/boards/feather_m0/examples/adc.rs @@ -17,7 +17,7 @@ use bsp::Pins; use pac::{CorePeripherals, Peripherals}; use hal::{ - adc::{Accumulation, Adc, Prescaler, Resolution, SingleEndedInput}, + adc::{Accumulation, Adc, Prescaler, Resolution}, clock::GenericClockController, }; @@ -44,11 +44,10 @@ fn main() -> ! { .enable(peripherals.adc, &mut peripherals.pm, &adc_clock) .unwrap(); let mut adc_pin = pins.a0.into_alternate(); - let mut adc_input = SingleEndedInput::from_pin(&mut adc_pin); loop { - let res = adc.read(&mut adc_input); + let res = adc.read(&mut adc_pin); #[cfg(feature = "use_semihosting")] - cortex_m_semihosting::hprintln!("ADC value: {}", res).unwrap(); + cortex_m_semihosting::hprintln!("ADC value: {}", read).unwrap(); } } diff --git a/boards/feather_m0/examples/async_adc.rs b/boards/feather_m0/examples/async_adc.rs index 838313bf958d..56139256ba01 100644 --- a/boards/feather_m0/examples/async_adc.rs +++ b/boards/feather_m0/examples/async_adc.rs @@ -16,7 +16,7 @@ use bsp::Pins; use pac::{CorePeripherals, Peripherals}; use hal::{ - adc::{Accumulation, Adc, Adc0, Prescaler, Resolution, SingleEndedInput}, + adc::{Accumulation, Adc, Adc0, Prescaler, Resolution}, clock::GenericClockController, }; @@ -49,10 +49,9 @@ async fn main(_s: embassy_executor::Spawner) -> ! { .into_future(Irqs); let mut adc_pin = pins.a0.into_alternate(); - let mut adc_input = SingleEndedInput::from_pin(&mut adc_pin); loop { - let res = adc.read(&mut adc_input).await; + let res = adc.read(&mut adc_pin).await; #[cfg(feature = "use_semihosting")] cortex_m_semihosting::hprintln!("ADC Result: {}", res).unwrap(); } diff --git a/boards/feather_m4/examples/adc.rs b/boards/feather_m4/examples/adc.rs index 9ce50d44fe14..b6b892cf43d3 100644 --- a/boards/feather_m4/examples/adc.rs +++ b/boards/feather_m4/examples/adc.rs @@ -17,7 +17,7 @@ use bsp::Pins; use pac::{CorePeripherals, Peripherals}; use hal::{ - adc::{Accumulation, Adc, Prescaler, Resolution, SingleEndedInput}, + adc::{Accumulation, Adc, Prescaler, Resolution}, clock::v2::{clock_system_at_reset, pclk::Pclk}, }; @@ -50,10 +50,9 @@ fn main() -> ! { .enable(peripherals.adc0, apb_adc0, &pclk_adc0) .unwrap(); let mut adc_pin = pins.a0.into_alternate(); - let mut adc_input = SingleEndedInput::from_pin(&mut adc_pin); loop { - let res = adc.read(&mut adc_input); + let res = adc.read(&mut adc_pin); #[cfg(feature = "use_semihosting")] cortex_m_semihosting::hprintln!("ADC value: {}", res); } diff --git a/boards/metro_m4/examples/adc.rs b/boards/metro_m4/examples/adc.rs index e931cc567253..56c46bbd97f7 100644 --- a/boards/metro_m4/examples/adc.rs +++ b/boards/metro_m4/examples/adc.rs @@ -17,7 +17,7 @@ use bsp::Pins; use pac::{CorePeripherals, Peripherals}; use hal::{ - adc::{Accumulation, Adc, Prescaler, Resolution, SingleEndedInput}, + adc::{Accumulation, Adc, Prescaler, Resolution}, clock::v2::{clock_system_at_reset, pclk::Pclk}, }; @@ -49,10 +49,9 @@ fn main() -> ! { .enable(peripherals.adc0, apb_adc0, &pclk_adc0) .unwrap(); let mut adc_pin = pins.a0.into_alternate(); - let mut adc_input = SingleEndedInput::from_pin(&mut adc_pin); loop { - let res = adc.read(&mut adc_input); + let res = adc.read(&mut adc_pin); #[cfg(feature = "use_semihosting")] cortex_m_semihosting::hprintln!("ADC Result: {}", res).unwrap(); } diff --git a/boards/metro_m4/examples/async_adc.rs b/boards/metro_m4/examples/async_adc.rs index c78aee9b5cd6..818e395734d7 100644 --- a/boards/metro_m4/examples/async_adc.rs +++ b/boards/metro_m4/examples/async_adc.rs @@ -16,7 +16,7 @@ use bsp::Pins; use pac::{CorePeripherals, Peripherals}; use hal::{ - adc::{Accumulation, Adc, Adc0, Prescaler, Resolution, SingleEndedInput}, + adc::{Accumulation, Adc, Adc0, Prescaler, Resolution}, clock::v2::{clock_system_at_reset, pclk::Pclk}, }; @@ -54,10 +54,9 @@ async fn main(_s: embassy_executor::Spawner) -> ! { .into_future(Irqs); let mut adc_pin = pins.a0.into_alternate(); - let mut adc_input = SingleEndedInput::from_pin(&mut adc_pin); loop { - let res = adc.read(&mut adc_input).await; + let res = adc.read(&mut adc_pin).await; #[cfg(feature = "use_semihosting")] cortex_m_semihosting::hprintln!("ADC Result: {}", res).unwrap(); } diff --git a/boards/pygamer/src/pins.rs b/boards/pygamer/src/pins.rs index 1f552b62a2e9..2f1f5de4c63c 100644 --- a/boards/pygamer/src/pins.rs +++ b/boards/pygamer/src/pins.rs @@ -934,8 +934,8 @@ impl JoystickReader { // unnecessary? note adafruit recenters around zero.. Im not doing that // either atm. - let y_data: u16 = adc.read(&mut hal::adc::SingleEndedInput::from_pin(&mut self.joy_y)); - let x_data: u16 = adc.read(&mut hal::adc::SingleEndedInput::from_pin(&mut self.joy_x)); + let y_data: u16 = adc.read(&mut self.joy_y); + let x_data: u16 = adc.read(&mut self.joy_x); (x_data, y_data) } @@ -969,7 +969,7 @@ pub struct BatteryReader { impl BatteryReader { /// Returns a float for voltage of battery pub fn read(&mut self, adc: &mut hal::adc::Adc) -> f32 { - let data: u16 = adc.read(&mut hal::adc::SingleEndedInput::from_pin(&mut self.battery)); + let data: u16 = adc.read(&mut self.battery); let result: f32 = (data as f32 / 4095.0) * 2.0 * 3.3; result } diff --git a/boards/samd11_bare/examples/adc.rs b/boards/samd11_bare/examples/adc.rs index 94c41f56d1c1..8a4416357b2b 100644 --- a/boards/samd11_bare/examples/adc.rs +++ b/boards/samd11_bare/examples/adc.rs @@ -17,7 +17,7 @@ use bsp::Pins; use pac::{CorePeripherals, Peripherals}; use hal::{ - adc::{Accumulation, Adc, Prescaler, Resolution, SingleEndedInput}, + adc::{Accumulation, Adc, Prescaler, Resolution}, clock::GenericClockController, }; @@ -45,10 +45,9 @@ fn main() -> ! { .unwrap(); let mut adc_pin = pins.d1.into_alternate(); - let mut adc_input = SingleEndedInput::from_pin(&mut adc_pin); loop { - let res = adc.read(&mut adc_input); + let res = adc.read(&mut adc_pin); #[cfg(feature = "use_semihosting")] cortex_m_semihosting::hprintln!("ADC Result: {}", res); } From 1e014e77946b298a77fd257c16dcaab198a1af9e Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Mon, 16 Mar 2026 11:30:09 -0400 Subject: [PATCH 35/39] fix d11 implementation --- hal/src/peripherals/adc/d11/mod.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 8deb3b201e26..382f3f2d842b 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -299,7 +299,11 @@ impl Adc { // Settings adc.adc.inputctrl().modify(|_, w| w.gain()._1x()); adc.discard = true; - let res = adc.read_channel(TEMP::get_channel(), GND::get_channel()); + let res = adc.read_channel( + TEMP::get_channel(), + GND::get_channel(), + SampleMode::SingleEnded + ); // Set gain back to normal adc.adc.inputctrl().modify(|_, w| w.gain().div2()); adc.discard = true; @@ -314,7 +318,7 @@ impl Adc { #[inline] pub fn read_cpu_voltage>(&mut self, src: C) -> u16 { let voltage = self.with_specific_settings(ADC_SETTINGS_INTERNAL_READ, |adc| { - let res = adc.read_channel(src); + let res = adc.read_channel(src, GND::get_channel(), SampleMode::SingleEnded); let mut res_f = adc.reading_to_f32(res) * 3.3; if C::MUXVAL != Muxposselect::Bandgap { res_f *= 4.0; @@ -340,7 +344,11 @@ where self.inner.adc.inputctrl().modify(|_, w| w.gain()._1x()); self.inner.discard = true; - let res = self.read_channel(TEMP::get_channel(), GND::get_channel()).await; + let res = self.read_channel( + TEMP::get_channel(), + GND::get_channel(), + SampleMode::SingleEnded + ).await; self.inner.adc.inputctrl().modify(|_, w| w.gain().div2()); self.inner.configure(old_adc_settings); @@ -356,7 +364,7 @@ where let old_adc_settings = self.inner.cfg; self.inner.configure(ADC_SETTINGS_INTERNAL_READ); - let res = self.read_channel(src, GND::get_channel()).await; + let res = self.read_channel(src, GND::get_channel(), SampleMode::SingleEnded).await; let mut voltage = self.inner.reading_to_f32(res) * 3.3; if C::MUXVAL != Muxposselect::Bandgap { voltage *= 4.0; From c81757fb20d13465054aa04ee237fc60c3bd6964 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Mon, 16 Mar 2026 11:30:41 -0400 Subject: [PATCH 36/39] fix feather_m0 ADC example --- boards/feather_m0/examples/adc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boards/feather_m0/examples/adc.rs b/boards/feather_m0/examples/adc.rs index 6194e9d26b28..b72043fe9e25 100644 --- a/boards/feather_m0/examples/adc.rs +++ b/boards/feather_m0/examples/adc.rs @@ -48,6 +48,6 @@ fn main() -> ! { loop { let res = adc.read(&mut adc_pin); #[cfg(feature = "use_semihosting")] - cortex_m_semihosting::hprintln!("ADC value: {}", read).unwrap(); + cortex_m_semihosting::hprintln!("ADC value: {}", res).unwrap(); } } From 51d3b2f0d4adaa30d8b53000cade049fa1c37e44 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Mon, 16 Mar 2026 12:48:34 -0400 Subject: [PATCH 37/39] added feature gates for enable_offset_compensation() and enable_auto_rail_to_rail() --- hal/src/peripherals/adc/builder.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hal/src/peripherals/adc/builder.rs b/hal/src/peripherals/adc/builder.rs index 8b98b84540f4..d8362f017e3b 100644 --- a/hal/src/peripherals/adc/builder.rs +++ b/hal/src/peripherals/adc/builder.rs @@ -263,10 +263,10 @@ impl AdcBuilder { /// Configure the ADC offset compensation /// /// ## Important - /// * (D5x) Enabling offset compesation forces the clock cycles per sample - /// to be 4 GCLK cycles, any change to the cycles per sample via + /// * Enabling offset compesation forces the clock cycles per sample to be 4 + /// GCLK cycles, any change to the cycles per sample via /// [`Self::with_clock_cycles_per_sample()`] will be ignored. - /// * (D11/D21) Not supported. + #[hal_cfg("adc-d5x")] pub fn enable_offset_compensation(mut self, enable: bool) -> Self { self.offset_compensation = Some(enable); self @@ -292,10 +292,10 @@ impl AdcBuilder { /// differential inputs and allows measurments closer to supply rails. /// /// ## Important - /// * (D5x) Enabling auto rail-to-rail incurs a slight runtime performance - /// hit as the CTRLA.R2R bit is enable-protected, meaning the ADC must be - /// shut down and re-enabled to enable/disable rail-to-rail mode. - /// * (D11/D21) Not supported. + /// * Enabling auto rail-to-rail incurs a slight runtime performance hit as + /// the CTRLA.R2R bit is enable-protected, meaning the ADC must be shut + /// down and re-enabled to enable/disable rail-to-rail mode. + #[hal_cfg("adc-d5x")] pub fn enable_auto_rail_to_rail(mut self, enable: bool) -> Self { self.auto_rail_to_rail = Some(enable); self From 9d8b0c519448f75489c96feadb2f1a9282312bd4 Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Mon, 16 Mar 2026 13:44:57 -0400 Subject: [PATCH 38/39] updated documentation comments --- hal/src/peripherals/adc/d11/mod.rs | 4 ++++ hal/src/peripherals/adc/mod.rs | 7 +++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/hal/src/peripherals/adc/d11/mod.rs b/hal/src/peripherals/adc/d11/mod.rs index 382f3f2d842b..921d25ca1315 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -223,6 +223,10 @@ impl Adc { /// Sets the sample mode and various ADC settings based on sample mode & the user supplied /// [`AdcSettings`]. + /// + /// ## Important + /// * (For D11) The ADC is automatically powered down before modifying the the enable-protected + /// CTRLB.DIFFMODE and CTRLB.LEFTADJ bits, then turned back on. #[inline] #[hal_macro_helper] pub(super) fn set_sample_mode(&mut self, sample_mode: SampleMode) { diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 007a6367e502..e37df7be4dde 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -17,10 +17,9 @@ //! .unwrap(); //! //! let mut adc_pin = pins.a0.into_alternate(); -//! let mut adc_input = SingleEndedInput::from_pin(adc_pin); //! //! let mut _buffer = [0; 16]; -//! adc.read_buffer(&mut adc_input, &mut _buffer).unwrap(); +//! adc.read_buffer(&mut adc_pin, &mut _buffer).unwrap(); //! ``` use crate::{gpio::AnyPin, typelevel::Sealed}; @@ -129,12 +128,12 @@ pub trait NegChannel: Sealed { fn get_channel() -> Self; } -/// Marker trait for ADC pins which can be used as positive ADC inputs +/// Trait for ADC pins which can be used as positive ADC inputs pub trait PosAdcPin: AnyPin + Sealed { type Channel: PosChannel; } -/// Marker trait for ADC pins which can be used as negative ADC inputs +/// Trait for ADC pins which can be used as negative ADC inputs pub trait NegAdcPin: AnyPin + Sealed { type Channel: NegChannel; } From c4334d104488fdb8a5b925236a9b22bf955b027c Mon Sep 17 00:00:00 2001 From: Noah Lutz Date: Mon, 23 Mar 2026 09:28:46 -0400 Subject: [PATCH 39/39] replaced cast_signed() with cast using as --- hal/src/peripherals/adc/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index e37df7be4dde..a3f05e64748c 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -363,8 +363,7 @@ impl Adc { P::Channel::get_channel(), N::Channel::get_channel(), SampleMode::Differential, - ) - .cast_signed() + ) as i16 } /// Read a single value from the provided ADC channels. @@ -553,8 +552,7 @@ where N::Channel::get_channel(), SampleMode::Differential, ) - .await - .cast_signed() + .await as i16 } /// Read a single value from the provided ADC channels