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(); } } diff --git a/hal/src/peripherals/adc/builder.rs b/hal/src/peripherals/adc/builder.rs index baaa62e74b83..d8362f017e3b 100644 --- a/hal/src/peripherals/adc/builder.rs +++ b/hal/src/peripherals/adc/builder.rs @@ -152,6 +152,10 @@ pub struct AdcBuilder { pub sample_clock_cycles: Option, pub accumulation: Accumulation, 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. @@ -162,6 +166,10 @@ pub(crate) struct AdcSettings { pub sample_clock_cycles: u8, pub accumulation: Accumulation, pub vref: Reference, + pub offset_compensation: bool, + pub reference_compensation: bool, + pub auto_left_adjust: bool, + pub auto_rail_to_rail: bool, } impl AdcBuilder { @@ -172,6 +180,10 @@ impl AdcBuilder { sample_clock_cycles: None, accumulation: accumulation_method, vref: None, + offset_compensation: None, + reference_compensation: None, + auto_left_adjust: None, + auto_rail_to_rail: None, } } @@ -190,6 +202,10 @@ 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), + auto_left_adjust: self.auto_left_adjust.unwrap_or(false), + auto_rail_to_rail: self.auto_rail_to_rail.unwrap_or(false), }) } @@ -244,6 +260,47 @@ 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 + /// [`Self::with_clock_cycles_per_sample()`] will be ignored. + #[hal_cfg("adc-d5x")] + 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, 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. + #[hal_cfg("adc-d5x")] + pub fn enable_auto_rail_to_rail(mut self, enable: bool) -> Self { + self.auto_rail_to_rail = Some(enable); + self + } + /// Turn the builder into an ADC #[hal_cfg("adc-d5x")] #[inline] diff --git a/hal/src/peripherals/adc/d11/channel.rs b/hal/src/peripherals/adc/d11/channel.rs new file mode 100644 index 000000000000..68e61f7721c3 --- /dev/null +++ b/hal/src/peripherals/adc/d11/channel.rs @@ -0,0 +1,104 @@ +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)?) $(+ $MARKER:ident)* + ),+ + $(,)? + ) => { + crate::paste::paste!{ + $( + pub struct $CH { + adc: PhantomData, + } + + impl Sealed for $CH {} + + $( + 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 {} + )* + )+ + } + }; +} + +#[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, ) + CpuVoltageSource, + SCALEDIOVCC: (Muxposselect::Scalediovcc, ) + CpuVoltageSource, + BANDGAP: (Muxposselect::Bandgap, ) + CpuVoltageSource, + 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, ) + 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 d43d76012144..921d25ca1315 100644 --- a/hal/src/peripherals/adc/d11/mod.rs +++ b/hal/src/peripherals/adc/d11/mod.rs @@ -1,16 +1,20 @@ use super::{ ADC_SETTINGS_INTERNAL_READ, ADC_SETTINGS_INTERNAL_READ_D21_TEMP, Accumulation, Adc, - AdcInstance, AdcSettings, CpuVoltageSource, Error, Flags, PrimaryAdc, SampleCount, + AdcInstance, AdcSettings, Error, Flags, PrimaryAdc, SampleCount, SampleMode, TEMP, GND, + CpuVoltageSource, PosChannel, NegChannel, }; +use atsamd_hal_macros::{hal_macro_helper}; + #[cfg(feature = "async")] use super::{FutureAdc, async_api}; use crate::{calibration, pac}; use pac::Peripherals; use pac::Sysctrl; -use pac::adc::inputctrl::Gainselect; +use pac::adc::inputctrl::{Gainselect, Muxposselect}; pub mod pin; +pub mod channel; /// Wrapper around the ADC instance pub struct Adc0 { @@ -76,10 +80,9 @@ 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.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 @@ -100,6 +103,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; @@ -196,16 +201,62 @@ impl Adc { } #[inline] - pub(super) fn mux(&mut self, ch: u8) { + pub(super) fn mux( + &mut self, + pos_ch: pac::adc::inputctrl::Muxposselect, + neg_ch: pac::adc::inputctrl::Muxnegselect, + ) { self.adc.inputctrl().modify(|r, w| { - if r.muxpos().bits() != ch { + if (r.muxpos().bits() != pos_ch.into()) || (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.muxpos().bits(neg_ch.into()) + } }); self.sync() } + + /// 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) { + // 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 { + match sample_mode { + SampleMode::SingleEnded => w.leftadj().clear_bit(), + SampleMode::Differential => w.leftadj().set_bit(), + }; + } + w + }); + 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: @@ -252,7 +303,11 @@ 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( + TEMP::get_channel(), + GND::get_channel(), + SampleMode::SingleEnded + ); // Set gain back to normal adc.adc.inputctrl().modify(|_, w| w.gain().div2()); adc.discard = true; @@ -265,11 +320,11 @@ 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 as u8); + let res = adc.read_channel(src, GND::get_channel(), SampleMode::SingleEnded); 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 @@ -293,7 +348,11 @@ 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_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); @@ -305,13 +364,13 @@ 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_channel(src, GND::get_channel(), SampleMode::SingleEnded).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/d11/pin.rs b/hal/src/peripherals/adc/d11/pin.rs index c09d9e70a658..e9fd844152de 100644 --- a/hal/src/peripherals/adc/d11/pin.rs +++ b/hal/src/peripherals/adc/d11/pin.rs @@ -2,19 +2,39 @@ 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> for Pin<$PinId, AlternateB> { + type Channel = $CHAN<$Adc>; + + } + )+ + } + }; +} + +macro_rules! neg_adc_pins { + ( + $( + $( #[$cfg:meta] )? + $PinId:ident: ($Adc:ident, $CHAN:ident) + ),+ + $(,)? + ) => { + crate::paste::item! { + $( + $( #[$cfg] )? + impl NegAdcPin<$Adc> for Pin<$PinId, AlternateB> { + type Channel = $CHAN<$Adc>; } )+ } @@ -22,74 +42,116 @@ macro_rules! adc_pins { } #[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/d5x/channel.rs b/hal/src/peripherals/adc/d5x/channel.rs new file mode 100644 index 000000000000..5d3fd168424b --- /dev/null +++ b/hal/src/peripherals/adc/d5x/channel.rs @@ -0,0 +1,78 @@ +use core::marker::PhantomData; +use crate::{ + adc::*, + pac::adc0::inputctrl::{Muxposselect, Muxnegselect}, + typelevel::Sealed, +}; + +macro_rules! channel { + ( + $( + $CH:ident: ($($PMUX:path)?, $($NMUX:path)?) $(+ $MARKER:ident)* + ),+ + $(,)? + ) => { + crate::paste::paste!{ + $( + pub struct $CH { + adc: PhantomData, + } + + impl Sealed for $CH {} + + $( + 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 {} + )* + )+ + } + }; +} + +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, ) + CpuVoltageSource, + SCALEDVBAT: (Muxposselect::Scaledvbat, ) + CpuVoltageSource, + SCALEDIOVCC: (Muxposselect::Scalediovcc, ) + CpuVoltageSource, + BANDGAP: (Muxposselect::Bandgap, ) + CpuVoltageSource, + PTAT: (Muxposselect::Ptat, ), + CTAT: (Muxposselect::Ctat, ), + DAC0: (Muxposselect::Dac, ), + GND: (, Muxnegselect::Gnd), +} diff --git a/hal/src/peripherals/adc/d5x/mod.rs b/hal/src/peripherals/adc/d5x/mod.rs index 9a8d87cbf993..1a71ebd6d3de 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; @@ -6,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, + ADC_SETTINGS_INTERNAL_READ, Accumulation, Adc, AdcInstance, AdcSettings, Error, Flags, + PrimaryAdc, SampleCount, SampleMode, Resolution, PTAT, CTAT, GND, CpuVoltageSource, PosChannel, + NegChannel, }; use crate::{calibration, pac}; @@ -110,23 +112,23 @@ 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() .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 - self.sync(); - self.adc.inputctrl().modify(|_, w| { - w.muxneg().gnd(); - w.diffmode().clear_bit() - }); // No negative input (internal gnd) + .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 @@ -146,6 +148,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; @@ -169,8 +173,16 @@ 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( + 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 @@ -180,9 +192,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 as u8); + 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 @@ -277,18 +289,113 @@ impl Adc { #[inline] pub(super) fn conversion_result(&self) -> u16 { - self.adc.result().read().result().bits() + 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() { + 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 + } } #[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, + ) { self.adc.inputctrl().modify(|r, w| { - if r.muxpos().bits() != ch { + if (r.muxpos().bits() != pos_ch.into()) || (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()) + } }); - self.sync() + 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| { + match sample_mode { + SampleMode::SingleEnded => w.diffmode().clear_bit(), + SampleMode::Differential => w.diffmode().set_bit(), + } + }); + self.sync(); + + if self.cfg.auto_left_adjust { + self.adc.ctrlb().modify(|_, 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 { + match sample_mode { + SampleMode::SingleEnded => { + if self.adc.ctrla().read().r2r().bit_is_set() { + self.power_down(); + self.adc.ctrla().modify(|_, w| w.r2r().clear_bit()); + self.sync(); + self.power_up(); + } + + // Disable offset compenstation only if it was enabled earlier for auto rail-to-rail + if !self.cfg.offset_compensation { + 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() { + self.power_down(); + self.adc.ctrla().modify(|_, w| w.r2r().set_bit()); + 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 { + self.adc.sampctrl().modify(|r, w| { + if r.offcomp().bit_is_clear() { + w.offcomp().set_bit() + } else { + w + } + }); + } + } + } + } } } @@ -313,8 +420,18 @@ 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_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); @@ -322,11 +439,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_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 e7664e509cd5..94cc8440425c 100644 --- a/hal/src/peripherals/adc/d5x/pin.rs +++ b/hal/src/peripherals/adc/d5x/pin.rs @@ -2,89 +2,144 @@ 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> for Pin<$PinId, AlternateB> { + type Channel = $CHAN<$Adc>; } )+ } }; } -adc_pins! { +macro_rules! neg_adc_pins { + ( + $( + $( #[$cfg:meta] )? + $PinId:ident: ($Adc:ident, $CHAN:ident) + ),+ + $(,)? + ) => { + crate::paste::item! { + $( + $( #[$cfg] )? + impl NegAdcPin<$Adc> for Pin<$PinId, AlternateB> { + type Channel = $CHAN<$Adc>; + } + )+ + } + }; +} + +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/mod.rs b/hal/src/peripherals/adc/mod.rs index a5c6eb0cb729..a3f05e64748c 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); //! @@ -22,12 +22,13 @@ //! adc.read_buffer(&mut adc_pin, &mut _buffer).unwrap(); //! ``` +use crate::{gpio::AnyPin, typelevel::Sealed}; 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", @@ -52,6 +53,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 { @@ -59,6 +62,10 @@ 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, + auto_left_adjust: false, + auto_rail_to_rail: false, }; /// Based on Temperature log row information (NVM)x @@ -68,6 +75,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 @@ -90,32 +101,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)] -#[repr(u8)] -pub enum CpuVoltageSource { - /// Core voltage - Core = 0x18, - /// VBAT supply voltage - Vbat = 0x19, - /// IO supply voltage - Io = 0x1A, -} - -/// 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)] @@ -129,6 +114,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; +} + +/// Trait for ADC pins which can be used as positive ADC inputs +pub trait PosAdcPin: AnyPin + Sealed { + type Channel: PosChannel; +} + +/// 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 {} @@ -154,14 +174,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"))] pub struct Adc { @@ -326,21 +338,48 @@ impl Adc { self.sync(); } - /// Read a single value from the provided ADC pin. + /// Reads a single value from the provided ADC pin, referenced to internal + /// GND (single ended) #[inline] - pub fn read>(&mut self, _pin: &mut P) -> u16 { - self.read_channel(P::CHANNEL) + pub fn read

(&mut self, _pin: &mut P) -> u16 + where + P: PosAdcPin, + { + self.read_channel( + P::Channel::get_channel(), + GND::get_channel(), + SampleMode::SingleEnded, + ) } - /// Read a single value from the provided channel, in a blocking fashion + /// Reads a single value from a pair of ADC pins (differentially) #[inline] - fn read_channel(&mut self, ch: u8) -> u16 { + 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, + ) as i16 + } + + /// 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.mux(ch); + 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) { @@ -363,23 +402,60 @@ impl Adc { } } - /// Read into a buffer from the provided ADC pin, in a blocking fashion + /// Read into a buffer from the provided pin (single-ended, referenced to + /// GND), in a blocking fashion #[inline] - pub fn read_buffer>( + 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, - _pin: &mut P, - dst: &mut [u16], - ) -> Result<(), Error> { - self.read_buffer_channel(P::CHANNEL, dst) + _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] - fn read_buffer_channel(&mut self, ch: u8, dst: &mut [u16]) -> Result<(), Error> { + fn read_buffer_channel( + &mut self, + _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.mux(ch); + self.set_sample_mode(sample_mode); + self.mux(P::MUXVAL, N::MUXVAL); self.enable_freerunning(); self.start_conversion(); self.check_read_discard(); @@ -405,6 +481,12 @@ impl Adc { Ok(()) } + /// Retrieve the configured ADC sample resolution + #[inline] + pub fn get_resolution(&self) -> Resolution { + self.cfg.accumulation.output_resolution() + } + /// Return the underlying ADC PAC object. #[hal_cfg(any("adc-d11", "adc-d21"))] #[inline] @@ -443,19 +525,48 @@ where (self.inner, self.irqs) } - /// Read a single value from the provided ADC pin. + /// Read a single value from the provided ADC pin (single-ended, referenced + /// to GND) + #[inline] + 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>(&mut self, _pin: &mut P) -> u16 { - self.read_channel(P::CHANNEL).await + 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 as i16 } - /// Read a single value from the provided channel ID + /// Read a single value from the provided ADC channels #[inline] - async fn read_channel(&mut self, ch: u8) -> u16 { + 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.mux(ch); + 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(); @@ -473,22 +584,60 @@ where res } - /// Read into a buffer from the provided ADC pin + /// Read into a buffer from the provided ADC pin (single-ended, referenced + /// to GND) + #[inline] + 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>( + pub async fn read_buffer_differential( &mut self, - _pin: &mut P, - dst: &mut [u16], - ) -> Result<(), Error> { - self.read_buffer_channel(P::CHANNEL, dst).await + _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 channel ID + /// Read into a buffer from the provided ADC channels #[inline] - async fn read_buffer_channel(&mut self, ch: u8, dst: &mut [u16]) -> Result<(), Error> { + async fn read_buffer_channel( + &mut self, + _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.mux(ch); + self.inner.set_sample_mode(sample_mode); + self.inner.mux(P::MUXVAL, N::MUXVAL); self.inner.enable_freerunning(); if self.inner.discard {