Merge pull request #198 from lulf/saadc-multiple-pins

Makes it possible to use the ADC with different analog pins
This commit is contained in:
Dario Nieuwenhuis 2021-05-22 15:50:41 +02:00 committed by GitHub
commit 62ad8ebab0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -30,9 +30,9 @@ pub use saadc::{
pub enum Error {} pub enum Error {}
/// One-shot saadc. Continuous sample mode TODO. /// One-shot saadc. Continuous sample mode TODO.
pub struct OneShot<'d, T: PositivePin> { pub struct OneShot<'d> {
irq: interrupt::SAADC, irq: interrupt::SAADC,
phantom: PhantomData<(&'d mut peripherals::SAADC, &'d mut T)>, phantom: PhantomData<&'d mut peripherals::SAADC>,
} }
/// Used to configure the SAADC peripheral. /// Used to configure the SAADC peripheral.
@ -66,14 +66,13 @@ impl Default for Config {
} }
} }
impl<'d, T: PositivePin> OneShot<'d, T> { impl<'d> OneShot<'d> {
pub fn new( pub fn new(
_saadc: impl Unborrow<Target = peripherals::SAADC> + 'd, _saadc: impl Unborrow<Target = peripherals::SAADC> + 'd,
irq: impl Unborrow<Target = interrupt::SAADC> + 'd, irq: impl Unborrow<Target = interrupt::SAADC> + 'd,
positive_pin: impl Unborrow<Target = T> + 'd,
config: Config, config: Config,
) -> Self { ) -> Self {
unborrow!(irq, positive_pin); unborrow!(irq);
let r = unsafe { &*SAADC::ptr() }; let r = unsafe { &*SAADC::ptr() };
@ -106,11 +105,6 @@ impl<'d, T: PositivePin> OneShot<'d, T> {
w w
}); });
// Set positive channel
r.ch[0]
.pselp
.write(|w| w.pselp().variant(positive_pin.channel()));
// Disable all events interrupts // Disable all events interrupts
r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) }); r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) });
@ -123,9 +117,52 @@ impl<'d, T: PositivePin> OneShot<'d, T> {
fn regs(&self) -> &saadc::RegisterBlock { fn regs(&self) -> &saadc::RegisterBlock {
unsafe { &*SAADC::ptr() } unsafe { &*SAADC::ptr() }
} }
async fn sample_inner(&mut self, pin: PositiveChannel) -> i16 {
let r = self.regs();
// Set positive channel
r.ch[0].pselp.write(|w| w.pselp().variant(pin));
// Set up the DMA
let mut val: i16 = 0;
r.result
.ptr
.write(|w| unsafe { w.ptr().bits(((&mut val) as *mut _) as u32) });
r.result.maxcnt.write(|w| unsafe { w.maxcnt().bits(1) });
// Reset and enable the end event
r.events_end.reset();
r.intenset.write(|w| w.end().set());
// Don't reorder the ADC start event before the previous writes. Hopefully self
// wouldn't happen anyway.
compiler_fence(Ordering::SeqCst);
r.tasks_start.write(|w| unsafe { w.bits(1) });
r.tasks_sample.write(|w| unsafe { w.bits(1) });
// Wait for 'end' event.
poll_fn(|cx| {
let r = self.regs();
if r.events_end.read().bits() != 0 {
r.events_end.reset();
return Poll::Ready(());
}
wake_on_interrupt(&mut self.irq, cx.waker());
Poll::Pending
})
.await;
// The DMA wrote the sampled value to `val`.
val
}
} }
impl<'d, T: PositivePin> Drop for OneShot<'d, T> { impl<'d> Drop for OneShot<'d> {
fn drop(&mut self) { fn drop(&mut self) {
let r = self.regs(); let r = self.regs();
r.enable.write(|w| w.enable().disabled()); r.enable.write(|w| w.enable().disabled());
@ -137,60 +174,22 @@ pub trait Sample {
where where
Self: 'a; Self: 'a;
fn sample<'a>(&'a mut self) -> Self::SampleFuture<'a>; fn sample<'a, T: PositivePin>(&'a mut self, pin: &mut T) -> Self::SampleFuture<'a>;
} }
impl<'d, T: PositivePin> Sample for OneShot<'d, T> { impl<'d> Sample for OneShot<'d> {
#[rustfmt::skip] #[rustfmt::skip]
type SampleFuture<'a> where Self: 'a = impl Future<Output = i16> + 'a; type SampleFuture<'a> where Self: 'a = impl Future<Output = i16> + 'a;
fn sample<'a>(&'a mut self) -> Self::SampleFuture<'a> { fn sample<'a, T: PositivePin>(&'a mut self, pin: &mut T) -> Self::SampleFuture<'a> {
async move { self.sample_inner(pin.channel())
let r = self.regs();
// Set up the DMA
let mut val: i16 = 0;
r.result
.ptr
.write(|w| unsafe { w.ptr().bits(((&mut val) as *mut _) as u32) });
r.result.maxcnt.write(|w| unsafe { w.maxcnt().bits(1) });
// Reset and enable the end event
r.events_end.reset();
r.intenset.write(|w| w.end().set());
// Don't reorder the ADC start event before the previous writes. Hopefully self
// wouldn't happen anyway.
compiler_fence(Ordering::SeqCst);
r.tasks_start.write(|w| unsafe { w.bits(1) });
r.tasks_sample.write(|w| unsafe { w.bits(1) });
// Wait for 'end' event.
poll_fn(|cx| {
let r = self.regs();
if r.events_end.read().bits() != 0 {
r.events_end.reset();
return Poll::Ready(());
}
wake_on_interrupt(&mut self.irq, cx.waker());
Poll::Pending
})
.await;
// The DMA wrote the sampled value to `val`.
val
}
} }
} }
/// A pin that can be used as the positive end of a ADC differential in the SAADC periperhal. /// A pin that can be used as the positive end of a ADC differential in the SAADC periperhal.
/// ///
/// Currently negative is always shorted to ground (0V). /// Currently negative is always shorted to ground (0V).
pub trait PositivePin: Unborrow<Target = Self> { pub trait PositivePin {
fn channel(&self) -> PositiveChannel; fn channel(&self) -> PositiveChannel;
} }