aboutsummaryrefslogtreecommitdiff
path: root/embassy-nrf/src/saadc.rs
diff options
context:
space:
mode:
authorhuntc <[email protected]>2021-10-12 11:24:26 +1100
committerhuntc <[email protected]>2021-10-18 10:26:11 +1100
commit103a3305e2a7bb9ef87d72a1237cbc5237aa8156 (patch)
tree5523c43673d7a885b4f059fff0c34872878fd137 /embassy-nrf/src/saadc.rs
parent90f6b56cba8123d51631aec2ceea7262eee149c5 (diff)
Implements continuous sampling for the nRF SAADC
Implements continuous sampling for the nRF SAADC and also renames `OneShot` to `Saadc`. The one-shot behaviour is retained with the `sample` method and a new `run_sampler` method is provided for efficiently (i.e. zero copying) sampler processing. A double buffer is used for continuously sampling, which wlll be swapped once sampling has taken place. A sample frequency is provided and will set the internal timer of the SAADC when there is just the one channel being sampled. Otherwise, PPI will be used to hook up the TIMER peripheral to drive the sampling task.
Diffstat (limited to 'embassy-nrf/src/saadc.rs')
-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());