diff options
Diffstat (limited to 'embassy-nrf/src')
| -rw-r--r-- | embassy-nrf/src/saadc.rs | 145 |
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; | |||
| 10 | use futures::future::poll_fn; | 10 | use futures::future::poll_fn; |
| 11 | 11 | ||
| 12 | use crate::interrupt; | 12 | use crate::interrupt; |
| 13 | use crate::ppi::Task; | ||
| 13 | use crate::{pac, peripherals}; | 14 | use crate::{pac, peripherals}; |
| 14 | 15 | ||
| 15 | use pac::{saadc, SAADC}; | 16 | use pac::{saadc, SAADC}; |
| @@ -29,7 +30,7 @@ pub use saadc::{ | |||
| 29 | pub enum Error {} | 30 | pub enum Error {} |
| 30 | 31 | ||
| 31 | /// One-shot saadc. Continuous sample mode TODO. | 32 | /// One-shot saadc. Continuous sample mode TODO. |
| 32 | pub struct OneShot<'d, const N: usize> { | 33 | pub 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 | ||
| 112 | impl<'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. | ||
| 117 | pub 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)] | ||
| 132 | pub enum SamplerState { | ||
| 133 | Sampled, | ||
| 134 | Stopped, | ||
| 135 | } | ||
| 136 | |||
| 137 | impl<'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 | ||
| 224 | impl<'d, const N: usize> Drop for OneShot<'d, N> { | 361 | impl<'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()); |
