2021-10-10 23:22:01 +00:00
|
|
|
#![macro_use]
|
|
|
|
|
2021-03-24 17:33:17 +00:00
|
|
|
use core::marker::PhantomData;
|
|
|
|
use core::sync::atomic::{compiler_fence, Ordering};
|
|
|
|
use core::task::Poll;
|
2021-09-01 21:54:26 +00:00
|
|
|
use embassy::interrupt::InterruptExt;
|
2021-09-10 23:53:53 +00:00
|
|
|
use embassy::util::Unborrow;
|
|
|
|
use embassy::waitqueue::AtomicWaker;
|
2021-07-29 11:44:51 +00:00
|
|
|
use embassy_hal_common::unborrow;
|
2021-03-24 17:33:17 +00:00
|
|
|
use futures::future::poll_fn;
|
|
|
|
|
2021-04-14 17:59:52 +00:00
|
|
|
use crate::interrupt;
|
2021-10-12 00:24:26 +00:00
|
|
|
use crate::ppi::Task;
|
2021-04-14 17:59:52 +00:00
|
|
|
use crate::{pac, peripherals};
|
2021-03-24 17:33:17 +00:00
|
|
|
|
|
|
|
use pac::{saadc, SAADC};
|
|
|
|
|
2021-10-13 20:01:49 +00:00
|
|
|
// We treat the positive and negative channels with the same enum values to keep our type tidy and given they are the same
|
|
|
|
pub(crate) use saadc::ch::pselp::PSELP_A as InputChannel;
|
|
|
|
|
2021-03-24 17:33:17 +00:00
|
|
|
pub use saadc::{
|
2021-10-13 20:01:49 +00:00
|
|
|
ch::config::{GAIN_A as Gain, REFSEL_A as Reference, RESP_A as Resistor, TACQ_A as Time},
|
2021-03-24 17:33:17 +00:00
|
|
|
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.
|
2021-10-12 00:24:26 +00:00
|
|
|
pub struct Saadc<'d, const N: usize> {
|
2021-05-22 13:42:14 +00:00
|
|
|
phantom: PhantomData<&'d mut peripherals::SAADC>,
|
2021-03-24 17:33:17 +00:00
|
|
|
}
|
|
|
|
|
2021-09-01 21:54:26 +00:00
|
|
|
static WAKER: AtomicWaker = AtomicWaker::new();
|
|
|
|
|
2021-03-24 17:33:17 +00:00
|
|
|
/// Used to configure the SAADC peripheral.
|
|
|
|
///
|
|
|
|
/// See the `Default` impl for suitable default values.
|
2021-10-07 07:00:03 +00:00
|
|
|
#[non_exhaustive]
|
2021-03-24 17:33:17 +00:00
|
|
|
pub struct Config {
|
|
|
|
/// Output resolution in bits.
|
|
|
|
pub resolution: Resolution,
|
|
|
|
/// Average 2^`oversample` input samples before transferring the result into memory.
|
|
|
|
pub oversample: Oversample,
|
2021-10-07 07:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Config {
|
|
|
|
/// Default configuration for single channel sampling.
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
2021-10-12 00:24:26 +00:00
|
|
|
resolution: Resolution::_12BIT,
|
2021-10-07 07:00:03 +00:00
|
|
|
oversample: Oversample::BYPASS,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Used to configure an individual SAADC peripheral channel.
|
|
|
|
///
|
|
|
|
/// See the `Default` impl for suitable default values.
|
|
|
|
#[non_exhaustive]
|
2021-10-10 21:52:45 +00:00
|
|
|
pub struct ChannelConfig<'d> {
|
2021-03-24 17:33:17 +00:00
|
|
|
/// 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,
|
2021-10-07 07:00:03 +00:00
|
|
|
/// Positive channel to sample
|
2021-10-10 22:38:35 +00:00
|
|
|
p_channel: InputChannel,
|
2021-10-07 07:00:03 +00:00
|
|
|
/// An optional negative channel to sample
|
2021-10-10 22:38:35 +00:00
|
|
|
n_channel: Option<InputChannel>,
|
2021-10-10 21:52:45 +00:00
|
|
|
|
|
|
|
phantom: PhantomData<&'d ()>,
|
2021-03-24 17:33:17 +00:00
|
|
|
}
|
|
|
|
|
2021-10-10 21:52:45 +00:00
|
|
|
impl<'d> ChannelConfig<'d> {
|
2021-10-07 07:00:03 +00:00
|
|
|
/// Default configuration for single ended channel sampling.
|
2021-10-10 22:38:35 +00:00
|
|
|
pub fn single_ended(input: impl Unborrow<Target = impl Input> + 'd) -> Self {
|
|
|
|
unborrow!(input);
|
2021-10-07 07:00:03 +00:00
|
|
|
Self {
|
|
|
|
reference: Reference::INTERNAL,
|
|
|
|
gain: Gain::GAIN1_6,
|
|
|
|
resistor: Resistor::BYPASS,
|
|
|
|
time: Time::_10US,
|
2021-10-10 22:38:35 +00:00
|
|
|
p_channel: input.channel(),
|
2021-10-07 07:00:03 +00:00
|
|
|
n_channel: None,
|
2021-10-10 21:52:45 +00:00
|
|
|
phantom: PhantomData,
|
2021-10-07 07:00:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/// Default configuration for differential channel sampling.
|
|
|
|
pub fn differential(
|
2021-10-10 22:38:35 +00:00
|
|
|
p_input: impl Unborrow<Target = impl Input> + 'd,
|
|
|
|
n_input: impl Unborrow<Target = impl Input> + 'd,
|
2021-10-07 07:00:03 +00:00
|
|
|
) -> Self {
|
2021-10-10 22:38:35 +00:00
|
|
|
unborrow!(p_input, n_input);
|
2021-03-24 17:33:17 +00:00
|
|
|
Self {
|
2021-10-15 07:45:53 +00:00
|
|
|
reference: Reference::INTERNAL,
|
2021-10-07 07:00:03 +00:00
|
|
|
gain: Gain::GAIN1_6,
|
2021-03-24 17:33:17 +00:00
|
|
|
resistor: Resistor::BYPASS,
|
2021-10-07 07:00:03 +00:00
|
|
|
time: Time::_10US,
|
2021-10-10 22:38:35 +00:00
|
|
|
p_channel: p_input.channel(),
|
|
|
|
n_channel: Some(n_input.channel()),
|
2021-10-10 21:52:45 +00:00
|
|
|
phantom: PhantomData,
|
2021-03-24 17:33:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-12 00:24:26 +00:00
|
|
|
/// A sample rate can be provided for the internal clock of the SAADC when
|
|
|
|
/// one channel is to be continuously sampled. When there are multiple channels
|
|
|
|
/// to be continuously sampled then an external source is used to generate
|
|
|
|
/// TASK_SAMPLE tasks.
|
|
|
|
pub enum Mode {
|
|
|
|
/// The internal clock is to be used with a sample rate expressed as a divisor of
|
|
|
|
/// 16MHz, ranging from 80..2047. For example, 1600 represnts a sample rate of 10KHz
|
|
|
|
/// given 16_000_000 / 10_000_000 = 1600.
|
|
|
|
Timers(u16),
|
|
|
|
/// TASK_SAMPLE tasks are generated outside of the SAADC e.g. via PPI on a
|
|
|
|
/// timer.
|
|
|
|
Task,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The state of a continuously running sampler. While it reflects
|
|
|
|
/// the progress of a sampler, it also signals what should be done
|
|
|
|
/// next. For example, if the sampler has stopped then the Saadc implementation
|
|
|
|
/// can then tear down its infrastructure.
|
|
|
|
#[derive(PartialEq)]
|
|
|
|
pub enum SamplerState {
|
|
|
|
Sampled,
|
|
|
|
Stopped,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'d, const N: usize> Saadc<'d, N> {
|
2021-03-24 17:33:17 +00:00
|
|
|
pub fn new(
|
2021-05-17 10:23:04 +00:00
|
|
|
_saadc: impl Unborrow<Target = peripherals::SAADC> + 'd,
|
2021-04-14 17:59:52 +00:00
|
|
|
irq: impl Unborrow<Target = interrupt::SAADC> + 'd,
|
2021-03-24 17:33:17 +00:00
|
|
|
config: Config,
|
2021-10-07 07:00:03 +00:00
|
|
|
channel_configs: [ChannelConfig; N],
|
2021-03-24 17:33:17 +00:00
|
|
|
) -> Self {
|
2021-05-21 11:06:28 +00:00
|
|
|
unborrow!(irq);
|
2021-03-24 17:33:17 +00:00
|
|
|
|
|
|
|
let r = unsafe { &*SAADC::ptr() };
|
|
|
|
|
|
|
|
let Config {
|
|
|
|
resolution,
|
|
|
|
oversample,
|
|
|
|
} = config;
|
|
|
|
|
2021-10-07 07:00:03 +00:00
|
|
|
// Configure channels
|
2021-03-24 17:33:17 +00:00
|
|
|
r.enable.write(|w| w.enable().enabled());
|
|
|
|
r.resolution.write(|w| w.val().variant(resolution));
|
|
|
|
r.oversample.write(|w| w.oversample().variant(oversample));
|
|
|
|
|
2021-10-07 07:00:03 +00:00
|
|
|
for (i, cc) in channel_configs.iter().enumerate() {
|
|
|
|
r.ch[i].pselp.write(|w| w.pselp().variant(cc.p_channel));
|
2021-10-10 22:38:35 +00:00
|
|
|
if let Some(n_channel) = cc.n_channel {
|
|
|
|
r.ch[i]
|
|
|
|
.pseln
|
|
|
|
.write(|w| unsafe { w.pseln().bits(n_channel as u8) });
|
2021-03-24 17:33:17 +00:00
|
|
|
}
|
2021-10-07 07:00:03 +00:00
|
|
|
r.ch[i].config.write(|w| {
|
|
|
|
w.refsel().variant(cc.reference);
|
|
|
|
w.gain().variant(cc.gain);
|
|
|
|
w.tacq().variant(cc.time);
|
|
|
|
if cc.n_channel.is_none() {
|
|
|
|
w.mode().se();
|
|
|
|
} else {
|
|
|
|
w.mode().diff();
|
|
|
|
}
|
|
|
|
w.resp().variant(cc.resistor);
|
|
|
|
w.resn().bypass();
|
|
|
|
if !matches!(oversample, Oversample::BYPASS) {
|
|
|
|
w.burst().enabled();
|
|
|
|
} else {
|
|
|
|
w.burst().disabled();
|
|
|
|
}
|
|
|
|
w
|
|
|
|
});
|
|
|
|
}
|
2021-03-24 17:33:17 +00:00
|
|
|
|
|
|
|
// Disable all events interrupts
|
|
|
|
r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) });
|
|
|
|
|
2021-09-01 21:54:26 +00:00
|
|
|
irq.set_handler(Self::on_interrupt);
|
|
|
|
irq.unpend();
|
|
|
|
irq.enable();
|
|
|
|
|
2021-03-24 17:33:17 +00:00
|
|
|
Self {
|
|
|
|
phantom: PhantomData,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-01 21:54:26 +00:00
|
|
|
fn on_interrupt(_ctx: *mut ()) {
|
|
|
|
let r = Self::regs();
|
|
|
|
|
|
|
|
if r.events_end.read().bits() != 0 {
|
|
|
|
r.intenclr.write(|w| w.end().clear());
|
|
|
|
WAKER.wake();
|
|
|
|
}
|
2021-10-12 00:24:26 +00:00
|
|
|
|
|
|
|
if r.events_started.read().bits() != 0 {
|
|
|
|
r.intenclr.write(|w| w.started().clear());
|
|
|
|
WAKER.wake();
|
|
|
|
}
|
2021-09-01 21:54:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn regs() -> &'static saadc::RegisterBlock {
|
2021-03-24 17:33:17 +00:00
|
|
|
unsafe { &*SAADC::ptr() }
|
|
|
|
}
|
2021-05-22 13:42:14 +00:00
|
|
|
|
2021-10-12 00:24:26 +00:00
|
|
|
/// One shot sampling. The buffer must be the same size as the number of channels configured.
|
2021-10-07 07:00:03 +00:00
|
|
|
pub async fn sample(&mut self, buf: &mut [i16; N]) {
|
2021-09-01 21:54:26 +00:00
|
|
|
let r = Self::regs();
|
2021-05-22 13:42:14 +00:00
|
|
|
|
|
|
|
// Set up the DMA
|
|
|
|
r.result
|
|
|
|
.ptr
|
2021-10-07 07:00:03 +00:00
|
|
|
.write(|w| unsafe { w.ptr().bits(buf.as_mut_ptr() as u32) });
|
|
|
|
r.result
|
|
|
|
.maxcnt
|
2021-10-10 21:54:24 +00:00
|
|
|
.write(|w| unsafe { w.maxcnt().bits(N as _) });
|
2021-05-22 13:42:14 +00:00
|
|
|
|
|
|
|
// 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| {
|
2021-09-01 21:54:26 +00:00
|
|
|
let r = Self::regs();
|
|
|
|
|
|
|
|
WAKER.register(cx.waker());
|
2021-05-22 13:42:14 +00:00
|
|
|
|
|
|
|
if r.events_end.read().bits() != 0 {
|
|
|
|
r.events_end.reset();
|
|
|
|
return Poll::Ready(());
|
|
|
|
}
|
|
|
|
|
|
|
|
Poll::Pending
|
|
|
|
})
|
|
|
|
.await;
|
|
|
|
}
|
2021-10-12 00:24:26 +00:00
|
|
|
|
|
|
|
/// Continuous sampling with double buffers. The sample buffers generally
|
|
|
|
/// should be a multiple of the number of channels configured.
|
|
|
|
///
|
|
|
|
/// A sampler closure is provided that receives the buffer of samples, noting
|
|
|
|
/// that the size of this buffer can be less than the original buffer's size.
|
|
|
|
/// A command is return from the closure that indicates whether the sampling
|
|
|
|
/// should continue or stop.
|
|
|
|
pub async fn run_sampler<S, const N0: usize>(
|
|
|
|
&mut self,
|
|
|
|
bufs: &mut [[i16; N0]; 2],
|
|
|
|
mode: Mode,
|
2021-10-15 06:44:23 +00:00
|
|
|
mut sampler: S,
|
2021-10-12 00:24:26 +00:00
|
|
|
) where
|
2021-10-15 06:44:23 +00:00
|
|
|
S: FnMut(&[i16]) -> SamplerState,
|
2021-10-12 00:24:26 +00:00
|
|
|
{
|
|
|
|
let r = Self::regs();
|
|
|
|
|
|
|
|
// Establish mode and sample rate
|
|
|
|
match mode {
|
2021-10-14 21:12:13 +00:00
|
|
|
Mode::Timers(sample_rate) => {
|
2021-10-16 19:26:06 +00:00
|
|
|
r.samplerate.write(|w| unsafe {
|
|
|
|
w.cc().bits(sample_rate);
|
|
|
|
w.mode().timers();
|
2021-10-14 21:12:13 +00:00
|
|
|
w
|
|
|
|
});
|
|
|
|
r.tasks_sample.write(|w| unsafe { w.bits(1) }); // Need to kick-start the internal timer
|
|
|
|
}
|
2021-10-16 19:26:06 +00:00
|
|
|
Mode::Task => r.samplerate.write(|w| unsafe {
|
|
|
|
w.cc().bits(0);
|
|
|
|
w.mode().task();
|
2021-10-12 00:24:26 +00:00
|
|
|
w
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up the initial DMA
|
|
|
|
r.result
|
|
|
|
.ptr
|
|
|
|
.write(|w| unsafe { w.ptr().bits(bufs[0].as_mut_ptr() as u32) });
|
|
|
|
r.result
|
|
|
|
.maxcnt
|
|
|
|
.write(|w| unsafe { w.maxcnt().bits(N0 as _) });
|
|
|
|
|
|
|
|
// Reset and enable the events
|
|
|
|
r.events_end.reset();
|
|
|
|
r.events_started.reset();
|
2021-10-16 19:28:19 +00:00
|
|
|
r.intenset.write(|w| {
|
|
|
|
w.end().set();
|
|
|
|
w.started().set();
|
|
|
|
w
|
|
|
|
});
|
2021-10-12 00:24:26 +00:00
|
|
|
|
|
|
|
// 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) });
|
|
|
|
|
|
|
|
let mut current_buffer = 0;
|
|
|
|
|
|
|
|
// Wait for events and complete when the sampler indicates it has had enough.
|
|
|
|
poll_fn(|cx| {
|
|
|
|
let r = Self::regs();
|
|
|
|
|
|
|
|
WAKER.register(cx.waker());
|
|
|
|
|
|
|
|
if r.events_end.read().bits() != 0 {
|
|
|
|
r.events_end.reset();
|
|
|
|
r.intenset.write(|w| w.end().set());
|
|
|
|
|
|
|
|
if sampler(&bufs[current_buffer][0..r.result.amount.read().bits() as usize])
|
|
|
|
== SamplerState::Sampled
|
|
|
|
{
|
|
|
|
let next_buffer = 1 - current_buffer;
|
|
|
|
current_buffer = next_buffer;
|
|
|
|
r.tasks_start.write(|w| unsafe { w.bits(1) });
|
|
|
|
} else {
|
|
|
|
r.tasks_stop.write(|w| unsafe { w.bits(1) });
|
|
|
|
return Poll::Ready(());
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.events_started.read().bits() != 0 {
|
|
|
|
r.events_started.reset();
|
|
|
|
r.intenset.write(|w| w.started().set());
|
|
|
|
|
|
|
|
let next_buffer = 1 - current_buffer;
|
|
|
|
r.result
|
|
|
|
.ptr
|
|
|
|
.write(|w| unsafe { w.ptr().bits(bufs[next_buffer].as_mut_ptr() as u32) });
|
|
|
|
}
|
|
|
|
|
|
|
|
Poll::Pending
|
|
|
|
})
|
|
|
|
.await;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the sample task for use with PPI
|
|
|
|
pub fn task_sample(&self) -> Task {
|
|
|
|
let r = Self::regs();
|
|
|
|
Task::from_reg(&r.tasks_sample)
|
|
|
|
}
|
2021-03-24 17:33:17 +00:00
|
|
|
}
|
|
|
|
|
2021-10-12 00:24:26 +00:00
|
|
|
impl<'d, const N: usize> Drop for Saadc<'d, N> {
|
2021-03-24 17:33:17 +00:00
|
|
|
fn drop(&mut self) {
|
2021-09-01 21:54:26 +00:00
|
|
|
let r = Self::regs();
|
2021-03-24 17:33:17 +00:00
|
|
|
r.enable.write(|w| w.enable().disabled());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-13 20:01:39 +00:00
|
|
|
pub(crate) mod sealed {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
pub trait Input {
|
|
|
|
fn channel(&self) -> InputChannel;
|
|
|
|
}
|
2021-03-24 17:33:17 +00:00
|
|
|
}
|
|
|
|
|
2021-10-13 20:01:39 +00:00
|
|
|
/// An input that can be used as either or negative end of a ADC differential in the SAADC periperhal.
|
|
|
|
pub trait Input: sealed::Input + Unborrow<Target = Self> {}
|
|
|
|
|
2021-10-10 23:22:01 +00:00
|
|
|
macro_rules! impl_saadc_input {
|
|
|
|
($pin:ident, $ch:ident) => {
|
2021-10-13 20:01:39 +00:00
|
|
|
impl crate::saadc::sealed::Input for crate::peripherals::$pin {
|
2021-10-10 23:22:01 +00:00
|
|
|
fn channel(&self) -> crate::saadc::InputChannel {
|
|
|
|
crate::saadc::InputChannel::$ch
|
2021-10-07 07:00:03 +00:00
|
|
|
}
|
2021-10-10 23:22:01 +00:00
|
|
|
}
|
2021-10-13 20:01:39 +00:00
|
|
|
impl crate::saadc::Input for crate::peripherals::$pin {}
|
2021-10-07 07:00:03 +00:00
|
|
|
};
|
|
|
|
}
|