diff --git a/hal/src/thumbv7em/adc.rs b/hal/src/thumbv7em/adc.rs index e8b8a3d6de7d..12b2f60be190 100644 --- a/hal/src/thumbv7em/adc.rs +++ b/hal/src/thumbv7em/adc.rs @@ -1,5 +1,8 @@ //! Analogue-to-Digital Conversion -use crate::clock::GenericClockController; + +use core::ops::Deref; + +use crate::clock::{GClock, GenericClockController}; #[rustfmt::skip] use crate::gpio::*; use crate::ehal::adc::{Channel, OneShot}; @@ -8,6 +11,7 @@ use crate::pac::gclk::pchctrl::GEN_A; use crate::pac::{adc0, ADC0, ADC1, MCLK}; use crate::calibration; +use crate::typelevel::Sealed; /// Samples per reading pub use adc0::avgctrl::SAMPLENUM_A as SampleRate; @@ -18,9 +22,45 @@ pub use adc0::ctrlb::RESSEL_A as Resolution; /// Reference voltage (or its source) pub use adc0::refctrl::REFSEL_A as Reference; +pub trait AdcPeripheral: Sealed + Deref { + /// ADC number + const NUM: usize; + /// Enable the corresponding APB clock + fn enable_apb_clock(mclk: &mut MCLK); + /// Enable the corresponding peripheral clock + fn enable_peripheral_clock(clocks: &mut GenericClockController, gclock: &GClock); +} + +impl Sealed for ADC0 {} +impl AdcPeripheral for ADC0 { + const NUM: usize = 0; + #[inline] + fn enable_apb_clock(mclk: &mut MCLK) { + mclk.apbdmask.modify(|_, w| w.adc0_().set_bit()); + } + #[inline] + fn enable_peripheral_clock(clocks: &mut GenericClockController, gclock: &GClock) { + clocks.adc0(gclock).expect("adc clock setup failed"); + } +} + +impl Sealed for ADC1 {} +impl AdcPeripheral for ADC1 { + const NUM: usize = 1; + #[inline] + fn enable_apb_clock(mclk: &mut MCLK) { + mclk.apbdmask.modify(|_, w| w.adc1_().set_bit()); + } + #[inline] + fn enable_peripheral_clock(clocks: &mut GenericClockController, gclock: &GClock) { + clocks.adc1(gclock).expect("adc clock setup failed"); + } +} + /// An ADC where results are accessible via interrupt servicing. pub struct InterruptAdc where + ADC: AdcPeripheral, C: ConversionMode, { adc: Adc, @@ -28,13 +68,13 @@ where } /// `Adc` encapsulates the device ADC -pub struct Adc { +pub struct Adc { adc: ADC, } /// Describes how an interrupt-driven ADC should finalize the peripheral /// upon the completion of a conversion. -pub trait ConversionMode { +pub trait ConversionMode { fn on_start(adc: &mut Adc); fn on_complete(adc: &mut Adc); fn on_stop(adc: &mut Adc); @@ -43,28 +83,53 @@ pub trait ConversionMode { pub struct SingleConversion; pub struct FreeRunning; -macro_rules! adc_hal { - ($($ADC:ident: ($init:ident, $mclk:ident, $apmask:ident, $compcal:ident, $refcal:ident, $r2rcal:ident),)+) => { - $( -impl Adc<$ADC> { - pub fn $init(adc: $ADC, mclk: &mut MCLK, clocks: &mut GenericClockController, gclk:GEN_A) -> Self { - mclk.$mclk.modify(|_, w| w.$apmask().set_bit()); +impl Adc { + pub fn adc0( + adc: ADC0, + mclk: &mut MCLK, + clocks: &mut GenericClockController, + gclk: GEN_A, + ) -> Self { + Adc::new(adc, mclk, clocks, gclk) + } +} + +impl Adc { + pub fn adc1( + adc: ADC1, + mclk: &mut MCLK, + clocks: &mut GenericClockController, + gclk: GEN_A, + ) -> Self { + Adc::new(adc, mclk, clocks, gclk) + } +} + +impl Adc { + pub fn new( + adc: ADC, + mclk: &mut MCLK, + clocks: &mut GenericClockController, + gclk: GEN_A, + ) -> Self { + ADC::enable_apb_clock(mclk); // set to 1/(1/(48000000/32) * 6) = 250000 SPS - let adc_clock = clocks.configure_gclk_divider_and_source(gclk, 1, DFLL, false) + let adc_clock = clocks + .configure_gclk_divider_and_source(gclk, 1, DFLL, false) .expect("adc clock setup failed"); - clocks.$init(&adc_clock).expect("adc clock setup failed"); + ADC::enable_peripheral_clock(clocks, &adc_clock); adc.ctrla.modify(|_, w| w.prescaler().div32()); adc.ctrlb.modify(|_, w| w.ressel()._12bit()); while adc.syncbusy.read().ctrlb().bit_is_set() {} - adc.sampctrl.modify(|_, w| unsafe {w.samplen().bits(5)}); // sample length + adc.sampctrl.modify(|_, w| unsafe { w.samplen().bits(5) }); // sample length while adc.syncbusy.read().sampctrl().bit_is_set() {} adc.inputctrl.modify(|_, w| w.muxneg().gnd()); // No negative input (internal gnd) while adc.syncbusy.read().inputctrl().bit_is_set() {} adc.calib.write(|w| unsafe { - w.biascomp().bits(calibration::$compcal()); - w.biasrefbuf().bits(calibration::$refcal()); - w.biasr2r().bits(calibration::$r2rcal()) + w.biascomp().bits(calibration::biascomp_scale_cal::()); + w.biasrefbuf().bits(calibration::biasref_scale_cal::()); + w.biasr2r().bits(calibration::biasr2r_scale_cal::()) }); let mut newadc = Self { adc }; @@ -112,9 +177,7 @@ impl Adc<$ADC> { /// Set the input resolution pub fn resolution(&mut self, resolution: Resolution) { - self.adc - .ctrlb - .modify(|_, w| w.ressel().variant(resolution)); + self.adc.ctrlb.modify(|_, w| w.ressel().variant(resolution)); while self.adc.syncbusy.read().ctrlb().bit_is_set() {} } @@ -178,39 +241,38 @@ impl Adc<$ADC> { /// Sets the mux to a particular pin. The pin mux is enabled-protected, /// so must be called while the peripheral is disabled. - fn mux>(&mut self, _pin: &mut PIN) { + fn mux>(&mut self, _pin: &mut PIN) { let chan = PIN::channel(); while self.adc.syncbusy.read().inputctrl().bit_is_set() {} self.adc.inputctrl.modify(|_, w| w.muxpos().bits(chan)); } } -impl ConversionMode<$ADC> for SingleConversion { - fn on_start(_adc: &mut Adc<$ADC>) { - } - fn on_complete(adc: &mut Adc<$ADC>) { +impl ConversionMode for SingleConversion { + fn on_start(_adc: &mut Adc) {} + fn on_complete(adc: &mut Adc) { adc.disable_interrupts(); adc.power_down(); } - fn on_stop(_adc: &mut Adc<$ADC>) { - } + fn on_stop(_adc: &mut Adc) {} } -impl ConversionMode<$ADC> for FreeRunning { - fn on_start(adc: &mut Adc<$ADC>) { +impl ConversionMode for FreeRunning { + fn on_start(adc: &mut Adc) { adc.enable_freerunning(); } - fn on_complete(_adc: &mut Adc<$ADC>) { - } - fn on_stop(adc: &mut Adc<$ADC>) { + fn on_complete(_adc: &mut Adc) {} + fn on_stop(adc: &mut Adc) { adc.disable_interrupts(); adc.power_down(); adc.disable_freerunning(); } } -impl InterruptAdc<$ADC, C> - where C: ConversionMode<$ADC> +impl InterruptAdc +where + ADC: AdcPeripheral, + C: ConversionMode, { pub fn service_interrupt_ready(&mut self) -> Option { if let Some(res) = self.adc.service_interrupt_ready() { @@ -222,7 +284,7 @@ impl InterruptAdc<$ADC, C> } /// Starts a conversion sampling the specified pin. - pub fn start_conversion>(&mut self, pin: &mut PIN) { + pub fn start_conversion>(&mut self, pin: &mut PIN) { self.adc.mux(pin); self.adc.power_up(); C::on_start(&mut self.adc); @@ -235,100 +297,126 @@ impl InterruptAdc<$ADC, C> } } -impl From> for InterruptAdc<$ADC, C> - where C: ConversionMode<$ADC> +impl From> for InterruptAdc +where + ADC: AdcPeripheral, + C: ConversionMode, { - fn from(adc: Adc<$ADC>) -> Self { + fn from(adc: Adc) -> Self { Self { adc, - m: core::marker::PhantomData{}, + m: core::marker::PhantomData {}, } } } -impl OneShot<$ADC, WORD, PIN> for Adc<$ADC> +impl OneShot for Adc where - WORD: From, - PIN: Channel<$ADC, ID=u8>, + ADC: AdcPeripheral, + WORD: From, + PIN: Channel, { - type Error = (); + type Error = (); - fn read(&mut self, pin: &mut PIN) -> nb::Result { + fn read(&mut self, pin: &mut PIN) -> nb::Result { self.mux(pin); self.power_up(); let result = self.synchronous_convert(); self.power_down(); Ok(result.into()) - } + } } - )+ + +pub trait AdcChannel: PinId { + const CHANNEL: u8; +} + +impl Channel for Pin +where + ADC: AdcPeripheral, + I: AdcChannel, +{ + type ID = u8; + fn channel() -> u8 { + I::CHANNEL } } -adc_hal! { - ADC0: (adc0, apbdmask, adc0_, adc0_biascomp_scale_cal, adc0_biasref_scale_cal, adc0_biasr2r_scale_cal), - ADC1: (adc1, apbdmask, adc1_, adc1_biascomp_scale_cal, adc1_biasref_scale_cal, adc1_biasr2r_scale_cal), +pub trait IsAdcPin +where + Self: AnyPin, + Self::Id: AdcChannel, + Self: Channel, +{ +} + +impl IsAdcPin for Pin +where + ADC: AdcPeripheral, + I: AdcChannel, +{ } macro_rules! adc_pins { ( $( - $PinId:ident: ($ADC:ident, $CHAN:literal), + $PinId:ident: [ $( ($ADC:ident, $CHAN:literal) ),+ ], )+ ) => { $( - impl Channel<$ADC> for Pin<$PinId, AlternateB> { - type ID = u8; - fn channel() -> u8 { $CHAN } - } + $( + impl AdcChannel<$ADC> for $PinId { + const CHANNEL: u8 = $CHAN; + } + )+ )+ } } adc_pins! { - PA02: (ADC0, 0), - PA03: (ADC0, 1), - PB08: (ADC0, 2), - PB09: (ADC0, 3), - PA04: (ADC0, 4), - PA05: (ADC0, 5), - PA06: (ADC0, 6), - PA07: (ADC0, 7), - PA08: (ADC0, 8), - PA09: (ADC0, 9), - PA10: (ADC0, 10), - PA11: (ADC0, 11), - PB02: (ADC0, 14), - PB03: (ADC0, 15), - - PB08: (ADC1, 0), - PB09: (ADC1, 1), - PA08: (ADC1, 2), - PA09: (ADC1, 3), + PA02: [(ADC0, 0)], + PA03: [(ADC0, 1)], + PB08: [(ADC0, 2)], + PB09: [(ADC0, 3)], + PA04: [(ADC0, 4)], + PA05: [(ADC0, 5)], + PA06: [(ADC0, 6)], + PA07: [(ADC0, 7)], + PA08: [(ADC0, 8)], + PA09: [(ADC0, 9)], + PA10: [(ADC0, 10)], + PA11: [(ADC0, 11)], + PB02: [(ADC0, 14)], + PB03: [(ADC0, 15)], + + PB08: [(ADC1, 0)], + PB09: [(ADC1, 1)], + PA08: [(ADC1, 2)], + PA09: [(ADC1, 3)], } #[cfg(feature = "min-samd51j")] adc_pins! { - PB00: (ADC0, 12), - PB01: (ADC0, 13), - PB04: (ADC1, 6), - PB05: (ADC1, 7), - PB06: (ADC1, 8), - PB07: (ADC1, 9), + PB00: [(ADC0, 12)], + PB01: [(ADC0, 13)], + PB04: [(ADC1, 6)], + PB05: [(ADC1, 7)], + PB06: [(ADC1, 8)], + PB07: [(ADC1, 9)], } #[cfg(feature = "min-samd51n")] adc_pins! { - PC02: (ADC1, 4), - PC03: (ADC1, 5), - PC00: (ADC1, 10), - PC01: (ADC1, 11), + PC02: [(ADC1, 4)], + PC03: [(ADC1, 5)], + PC00: [(ADC1, 10)], + PC01: [(ADC1, 11)], } #[cfg(feature = "min-samd51p")] adc_pins! { - PC30: (ADC1, 12), - PC31: (ADC1, 13), - PD00: (ADC1, 14), - PD01: (ADC1, 15), + PC30: [(ADC1, 12)], + PC31: [(ADC1, 13)], + PD00: [(ADC1, 14)], + PD01: [(ADC1, 15)], } diff --git a/hal/src/thumbv7em/calibration.rs b/hal/src/thumbv7em/calibration.rs index 340e2415ad3b..7635034df5a9 100644 --- a/hal/src/thumbv7em/calibration.rs +++ b/hal/src/thumbv7em/calibration.rs @@ -3,6 +3,8 @@ use core::ptr; +use crate::adc::AdcPeripheral; + // "The NVM Software Calibration Area can be read at address 0x00800080." const ADDR: u32 = 0x00800080; @@ -59,3 +61,24 @@ pub fn adc1_biasref_scale_cal() -> u8 { pub fn adc1_biasr2r_scale_cal() -> u8 { cal(3, 0, 0b111) as u8 } + +pub fn biascomp_scale_cal() -> u8 { + let offset = 2 * A::NUM as u32; + let shift = 2; + let mask = 0b111; + cal(offset, shift, mask) as u8 +} + +pub fn biasref_scale_cal() -> u8 { + let offset = 2 * A::NUM as u32; + let shift = 5; + let mask = 0b111; + cal(offset, shift, mask) as u8 +} + +pub fn biasr2r_scale_cal() -> u8 { + let offset = 2 * A::NUM as u32 + 1; + let shift = 0; + let mask = 0b111; + cal(offset, shift, mask) as u8 +}