nrf/saadc: initial implementation
This commit is contained in:
parent
a08d781442
commit
53645d9d38
@ -100,6 +100,7 @@ pub mod interrupt;
|
||||
#[cfg(feature = "52840")]
|
||||
pub mod qspi;
|
||||
pub mod rtc;
|
||||
pub mod saadc;
|
||||
pub mod spim;
|
||||
pub mod uarte;
|
||||
|
||||
@ -130,6 +131,9 @@ embassy_extras::peripherals! {
|
||||
#[cfg(any(feature = "52833", feature = "52840"))]
|
||||
spim3: SPIM3,
|
||||
|
||||
// SAADC
|
||||
saadc: SAADC,
|
||||
|
||||
// GPIOTE
|
||||
gpiote: GPIOTE,
|
||||
gpiote_ch_0: GPIOTE_CH0,
|
||||
|
242
embassy-nrf/src/saadc.rs
Normal file
242
embassy-nrf/src/saadc.rs
Normal file
@ -0,0 +1,242 @@
|
||||
use core::future::Future;
|
||||
use core::marker::PhantomData;
|
||||
use core::pin::Pin;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::task::Poll;
|
||||
use embassy::traits;
|
||||
use embassy::util::{wake_on_interrupt, PeripheralBorrow};
|
||||
use embassy_extras::unborrow;
|
||||
use futures::future::poll_fn;
|
||||
use traits::spi::FullDuplex;
|
||||
|
||||
use crate::gpio::Pin as GpioPin;
|
||||
use crate::interrupt::{self, Interrupt};
|
||||
use crate::{pac, peripherals, slice_in_ram_or};
|
||||
|
||||
#[cfg(feature = "9160")]
|
||||
use pac::{saadc_ns as saadc, SAADC_NS as SAADC};
|
||||
|
||||
#[cfg(not(feature = "9160"))]
|
||||
use pac::{saadc, SAADC};
|
||||
|
||||
pub use saadc::{
|
||||
ch::{
|
||||
config::{GAIN_A as Gain, REFSEL_A as Reference, RESP_A as Resistor, TACQ_A as Time},
|
||||
pselp::PSELP_A as PositiveChannel,
|
||||
},
|
||||
oversample::OVERSAMPLE_A as Oversample,
|
||||
resolution::VAL_A as Resolution,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[non_exhaustive]
|
||||
pub enum Error {}
|
||||
|
||||
/// One-shot saadc. Continuous sample mode TODO.
|
||||
pub struct OneShot<'d, T: PositivePin> {
|
||||
peri: peripherals::SAADC,
|
||||
positive_pin: T,
|
||||
irq: interrupt::SAADC,
|
||||
phantom: PhantomData<(&'d mut peripherals::SAADC, &'d mut T)>,
|
||||
}
|
||||
|
||||
/// Used to configure the SAADC peripheral.
|
||||
///
|
||||
/// See the `Default` impl for suitable default values.
|
||||
pub struct Config {
|
||||
/// Output resolution in bits.
|
||||
pub resolution: Resolution,
|
||||
/// Average 2^`oversample` input samples before transferring the result into memory.
|
||||
pub oversample: Oversample,
|
||||
/// Reference voltage of the SAADC input.
|
||||
pub reference: Reference,
|
||||
/// Gain used to control the effective input range of the SAADC.
|
||||
pub gain: Gain,
|
||||
/// Positive channel resistor control.
|
||||
pub resistor: Resistor,
|
||||
/// Acquisition time in microseconds.
|
||||
pub time: Time,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
resolution: Resolution::_14BIT,
|
||||
oversample: Oversample::OVER8X,
|
||||
reference: Reference::VDD1_4,
|
||||
gain: Gain::GAIN1_4,
|
||||
resistor: Resistor::BYPASS,
|
||||
time: Time::_20US,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: PositivePin> OneShot<'d, T> {
|
||||
pub fn new(
|
||||
saadc: impl PeripheralBorrow<Target = peripherals::SAADC> + 'd,
|
||||
irq: impl PeripheralBorrow<Target = interrupt::SAADC> + 'd,
|
||||
positive_pin: impl PeripheralBorrow<Target = T> + 'd,
|
||||
config: Config,
|
||||
) -> Self {
|
||||
unborrow!(saadc, irq, positive_pin);
|
||||
|
||||
let r = unsafe { &*SAADC::ptr() };
|
||||
|
||||
let Config {
|
||||
resolution,
|
||||
oversample,
|
||||
reference,
|
||||
gain,
|
||||
resistor,
|
||||
time,
|
||||
} = config;
|
||||
|
||||
// Configure pins
|
||||
r.enable.write(|w| w.enable().enabled());
|
||||
r.resolution.write(|w| w.val().variant(resolution));
|
||||
r.oversample.write(|w| w.oversample().variant(oversample));
|
||||
|
||||
r.ch[0].config.write(|w| {
|
||||
w.refsel().variant(reference);
|
||||
w.gain().variant(gain);
|
||||
w.tacq().variant(time);
|
||||
w.mode().se();
|
||||
w.resp().variant(resistor);
|
||||
w.resn().bypass();
|
||||
if !matches!(oversample, Oversample::BYPASS) {
|
||||
w.burst().enabled();
|
||||
} else {
|
||||
w.burst().disabled();
|
||||
}
|
||||
w
|
||||
});
|
||||
|
||||
// Set positive channel
|
||||
r.ch[0]
|
||||
.pselp
|
||||
.write(|w| w.pselp().variant(positive_pin.channel()));
|
||||
|
||||
// Disable all events interrupts
|
||||
r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) });
|
||||
|
||||
Self {
|
||||
peri: saadc,
|
||||
positive_pin,
|
||||
irq,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn regs(&self) -> &saadc::RegisterBlock {
|
||||
unsafe { &*SAADC::ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: PositivePin> Drop for OneShot<'d, T> {
|
||||
fn drop(&mut self) {
|
||||
let r = self.regs();
|
||||
r.enable.write(|w| w.enable().disabled());
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Sample {
|
||||
type SampleFuture<'a>: Future<Output = i16> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
fn sample<'a>(self: Pin<&'a mut Self>) -> Self::SampleFuture<'a>;
|
||||
}
|
||||
|
||||
impl<'d, T: PositivePin> Sample for OneShot<'d, T> {
|
||||
#[rustfmt::skip]
|
||||
type SampleFuture<'a> where Self: 'a = impl Future<Output = i16> + 'a;
|
||||
|
||||
fn sample<'a>(self: Pin<&'a mut Self>) -> Self::SampleFuture<'a> {
|
||||
async move {
|
||||
let this = unsafe { self.get_unchecked_mut() };
|
||||
let r = this.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 this
|
||||
// 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 = this.regs();
|
||||
|
||||
if r.events_end.read().bits() != 0 {
|
||||
r.events_end.reset();
|
||||
return Poll::Ready(());
|
||||
}
|
||||
|
||||
wake_on_interrupt(&mut this.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.
|
||||
///
|
||||
/// Currently negative is always shorted to ground (0V).
|
||||
pub trait PositivePin {
|
||||
fn channel(&self) -> PositiveChannel;
|
||||
}
|
||||
|
||||
macro_rules! positive_pin_mappings {
|
||||
( $($ch:ident => $pin:ident,)*) => {
|
||||
$(
|
||||
impl PositivePin for crate::peripherals::$pin {
|
||||
fn channel(&self) -> PositiveChannel {
|
||||
PositiveChannel::$ch
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
// TODO the variant names are unchecked
|
||||
// the pins are copied from nrf hal
|
||||
#[cfg(feature = "9160")]
|
||||
positive_pin_mappings! {
|
||||
ANALOGINPUT0 => P0_13,
|
||||
ANALOGINPUT1 => P0_14,
|
||||
ANALOGINPUT2 => P0_15,
|
||||
ANALOGINPUT3 => P0_16,
|
||||
ANALOGINPUT4 => P0_17,
|
||||
ANALOGINPUT5 => P0_18,
|
||||
ANALOGINPUT6 => P0_19,
|
||||
ANALOGINPUT7 => P0_20,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "9160"))]
|
||||
positive_pin_mappings! {
|
||||
ANALOGINPUT0 => P0_02,
|
||||
ANALOGINPUT1 => P0_03,
|
||||
ANALOGINPUT2 => P0_04,
|
||||
ANALOGINPUT3 => P0_05,
|
||||
ANALOGINPUT4 => P0_28,
|
||||
ANALOGINPUT5 => P0_29,
|
||||
ANALOGINPUT6 => P0_30,
|
||||
ANALOGINPUT7 => P0_31,
|
||||
}
|
Loading…
Reference in New Issue
Block a user