aboutsummaryrefslogtreecommitdiff
path: root/embassy-nrf/src
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-nrf/src')
-rw-r--r--embassy-nrf/src/saadc.rs145
1 files changed, 141 insertions, 4 deletions
diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs
index 94744c444..b14e2156d 100644
--- a/embassy-nrf/src/saadc.rs
+++ b/embassy-nrf/src/saadc.rs
@@ -10,6 +10,7 @@ use embassy_hal_common::unborrow;
10use futures::future::poll_fn; 10use futures::future::poll_fn;
11 11
12use crate::interrupt; 12use crate::interrupt;
13use crate::ppi::Task;
13use crate::{pac, peripherals}; 14use crate::{pac, peripherals};
14 15
15use pac::{saadc, SAADC}; 16use pac::{saadc, SAADC};
@@ -29,7 +30,7 @@ pub use saadc::{
29pub enum Error {} 30pub enum Error {}
30 31
31/// One-shot saadc. Continuous sample mode TODO. 32/// One-shot saadc. Continuous sample mode TODO.
32pub struct OneShot<'d, const N: usize> { 33pub struct Saadc<'d, const N: usize> {
33 phantom: PhantomData<&'d mut peripherals::SAADC>, 34 phantom: PhantomData<&'d mut peripherals::SAADC>,
34} 35}
35 36
@@ -50,7 +51,7 @@ impl Default for Config {
50 /// Default configuration for single channel sampling. 51 /// Default configuration for single channel sampling.
51 fn default() -> Self { 52 fn default() -> Self {
52 Self { 53 Self {
53 resolution: Resolution::_14BIT, 54 resolution: Resolution::_12BIT,
54 oversample: Oversample::BYPASS, 55 oversample: Oversample::BYPASS,
55 } 56 }
56 } 57 }
@@ -109,7 +110,31 @@ impl<'d> ChannelConfig<'d> {
109 } 110 }
110} 111}
111 112
112impl<'d, const N: usize> OneShot<'d, N> { 113/// A sample rate can be provided for the internal clock of the SAADC when
114/// one channel is to be continuously sampled. When there are multiple channels
115/// to be continuously sampled then an external source is used to generate
116/// TASK_SAMPLE tasks.
117pub enum Mode {
118 /// The internal clock is to be used with a sample rate expressed as a divisor of
119 /// 16MHz, ranging from 80..2047. For example, 1600 represnts a sample rate of 10KHz
120 /// given 16_000_000 / 10_000_000 = 1600.
121 Timers(u16),
122 /// TASK_SAMPLE tasks are generated outside of the SAADC e.g. via PPI on a
123 /// timer.
124 Task,
125}
126
127/// The state of a continuously running sampler. While it reflects
128/// the progress of a sampler, it also signals what should be done
129/// next. For example, if the sampler has stopped then the Saadc implementation
130/// can then tear down its infrastructure.
131#[derive(PartialEq)]
132pub enum SamplerState {
133 Sampled,
134 Stopped,
135}
136
137impl<'d, const N: usize> Saadc<'d, N> {
113 pub fn new( 138 pub fn new(
114 _saadc: impl Unborrow<Target = peripherals::SAADC> + 'd, 139 _saadc: impl Unborrow<Target = peripherals::SAADC> + 'd,
115 irq: impl Unborrow<Target = interrupt::SAADC> + 'd, 140 irq: impl Unborrow<Target = interrupt::SAADC> + 'd,
@@ -176,12 +201,18 @@ impl<'d, const N: usize> OneShot<'d, N> {
176 r.intenclr.write(|w| w.end().clear()); 201 r.intenclr.write(|w| w.end().clear());
177 WAKER.wake(); 202 WAKER.wake();
178 } 203 }
204
205 if r.events_started.read().bits() != 0 {
206 r.intenclr.write(|w| w.started().clear());
207 WAKER.wake();
208 }
179 } 209 }
180 210
181 fn regs() -> &'static saadc::RegisterBlock { 211 fn regs() -> &'static saadc::RegisterBlock {
182 unsafe { &*SAADC::ptr() } 212 unsafe { &*SAADC::ptr() }
183 } 213 }
184 214
215 /// One shot sampling. The buffer must be the same size as the number of channels configured.
185 pub async fn sample(&mut self, buf: &mut [i16; N]) { 216 pub async fn sample(&mut self, buf: &mut [i16; N]) {
186 let r = Self::regs(); 217 let r = Self::regs();
187 218
@@ -219,9 +250,115 @@ impl<'d, const N: usize> OneShot<'d, N> {
219 }) 250 })
220 .await; 251 .await;
221 } 252 }
253
254 /// Continuous sampling with double buffers. The sample buffers generally
255 /// should be a multiple of the number of channels configured.
256 ///
257 /// A sampler closure is provided that receives the buffer of samples, noting
258 /// that the size of this buffer can be less than the original buffer's size.
259 /// A command is return from the closure that indicates whether the sampling
260 /// should continue or stop.
261 pub async fn run_sampler<S, const N0: usize>(
262 &mut self,
263 bufs: &mut [[i16; N0]; 2],
264 mode: Mode,
265 sampler: S,
266 ) where
267 S: Fn(&[i16]) -> SamplerState,
268 {
269 let r = Self::regs();
270
271 // Establish mode and sample rate
272 match mode {
273 Mode::Timers(sample_rate) => r.samplerate.write(|w| {
274 unsafe {
275 w.cc().bits(sample_rate);
276 w.mode().timers();
277 }
278 w
279 }),
280 Mode::Task => r.samplerate.write(|w| {
281 unsafe {
282 w.cc().bits(0);
283 w.mode().task();
284 }
285 w
286 }),
287 }
288
289 // Set up the initial DMA
290 r.result
291 .ptr
292 .write(|w| unsafe { w.ptr().bits(bufs[0].as_mut_ptr() as u32) });
293 r.result
294 .maxcnt
295 .write(|w| unsafe { w.maxcnt().bits(N0 as _) });
296
297 // Reset and enable the events
298 r.events_end.reset();
299 r.intenset.write(|w| w.end().set());
300 r.events_started.reset();
301 r.intenset.write(|w| w.started().set());
302
303 // Don't reorder the ADC start event before the previous writes. Hopefully self
304 // wouldn't happen anyway.
305 compiler_fence(Ordering::SeqCst);
306
307 r.tasks_start.write(|w| unsafe { w.bits(1) });
308
309 let mut current_buffer = 0;
310
311 // Wait for events and complete when the sampler indicates it has had enough.
312 poll_fn(|cx| {
313 let r = Self::regs();
314
315 WAKER.register(cx.waker());
316
317 if r.events_end.read().bits() != 0 {
318 r.events_end.reset();
319 r.intenset.write(|w| w.end().set());
320 info!("Ended");
321
322 if sampler(&bufs[current_buffer][0..r.result.amount.read().bits() as usize])
323 == SamplerState::Sampled
324 {
325 let next_buffer = 1 - current_buffer;
326 current_buffer = next_buffer;
327 r.tasks_start.write(|w| unsafe { w.bits(1) });
328 } else {
329 r.tasks_stop.write(|w| unsafe { w.bits(1) });
330 return Poll::Ready(());
331 };
332 }
333
334 if r.events_started.read().bits() != 0 {
335 r.events_started.reset();
336 r.intenset.write(|w| w.started().set());
337 info!("Started");
338
339 if let Mode::Timers(_) = mode {
340 r.tasks_sample.write(|w| unsafe { w.bits(1) });
341 }
342
343 let next_buffer = 1 - current_buffer;
344 r.result
345 .ptr
346 .write(|w| unsafe { w.ptr().bits(bufs[next_buffer].as_mut_ptr() as u32) });
347 }
348
349 Poll::Pending
350 })
351 .await;
352 }
353
354 /// Return the sample task for use with PPI
355 pub fn task_sample(&self) -> Task {
356 let r = Self::regs();
357 Task::from_reg(&r.tasks_sample)
358 }
222} 359}
223 360
224impl<'d, const N: usize> Drop for OneShot<'d, N> { 361impl<'d, const N: usize> Drop for Saadc<'d, N> {
225 fn drop(&mut self) { 362 fn drop(&mut self) {
226 let r = Self::regs(); 363 let r = Self::regs();
227 r.enable.write(|w| w.enable().disabled()); 364 r.enable.write(|w| w.enable().disabled());