diff options
| -rw-r--r-- | embassy-nrf/src/saadc.rs | 83 | ||||
| -rw-r--r-- | examples/nrf/src/bin/saadc_continuous.rs | 57 |
2 files changed, 116 insertions, 24 deletions
diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 617c9e041..dcc7e86a2 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs | |||
| @@ -10,7 +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::ppi::{Event, Task}; |
| 14 | use crate::{pac, peripherals}; | 14 | use crate::{pac, peripherals}; |
| 15 | 15 | ||
| 16 | use pac::{saadc, SAADC}; | 16 | use pac::{saadc, SAADC}; |
| @@ -207,6 +207,11 @@ impl<'d, const N: usize> Saadc<'d, N> { | |||
| 207 | fn on_interrupt(_ctx: *mut ()) { | 207 | fn on_interrupt(_ctx: *mut ()) { |
| 208 | let r = Self::regs(); | 208 | let r = Self::regs(); |
| 209 | 209 | ||
| 210 | if r.events_calibratedone.read().bits() != 0 { | ||
| 211 | r.intenclr.write(|w| w.calibratedone().clear()); | ||
| 212 | WAKER.wake(); | ||
| 213 | } | ||
| 214 | |||
| 210 | if r.events_end.read().bits() != 0 { | 215 | if r.events_end.read().bits() != 0 { |
| 211 | r.intenclr.write(|w| w.end().clear()); | 216 | r.intenclr.write(|w| w.end().clear()); |
| 212 | WAKER.wake(); | 217 | WAKER.wake(); |
| @@ -222,6 +227,35 @@ impl<'d, const N: usize> Saadc<'d, N> { | |||
| 222 | unsafe { &*SAADC::ptr() } | 227 | unsafe { &*SAADC::ptr() } |
| 223 | } | 228 | } |
| 224 | 229 | ||
| 230 | /// Perform SAADC calibration. Completes when done. | ||
| 231 | pub async fn calibrate(&self) { | ||
| 232 | let r = Self::regs(); | ||
| 233 | |||
| 234 | // Reset and enable the end event | ||
| 235 | r.events_calibratedone.reset(); | ||
| 236 | r.intenset.write(|w| w.calibratedone().set()); | ||
| 237 | |||
| 238 | // Order is important | ||
| 239 | compiler_fence(Ordering::SeqCst); | ||
| 240 | |||
| 241 | r.tasks_calibrateoffset.write(|w| unsafe { w.bits(1) }); | ||
| 242 | |||
| 243 | // Wait for 'calibratedone' event. | ||
| 244 | poll_fn(|cx| { | ||
| 245 | let r = Self::regs(); | ||
| 246 | |||
| 247 | WAKER.register(cx.waker()); | ||
| 248 | |||
| 249 | if r.events_calibratedone.read().bits() != 0 { | ||
| 250 | r.events_calibratedone.reset(); | ||
| 251 | return Poll::Ready(()); | ||
| 252 | } | ||
| 253 | |||
| 254 | Poll::Pending | ||
| 255 | }) | ||
| 256 | .await; | ||
| 257 | } | ||
| 258 | |||
| 225 | /// One shot sampling. The buffer must be the same size as the number of channels configured. | 259 | /// One shot sampling. The buffer must be the same size as the number of channels configured. |
| 226 | pub async fn sample(&mut self, buf: &mut [i16; N]) { | 260 | pub async fn sample(&mut self, buf: &mut [i16; N]) { |
| 227 | let r = Self::regs(); | 261 | let r = Self::regs(); |
| @@ -263,29 +297,46 @@ impl<'d, const N: usize> Saadc<'d, N> { | |||
| 263 | 297 | ||
| 264 | /// Continuous sampling with double buffers. | 298 | /// Continuous sampling with double buffers. |
| 265 | /// | 299 | /// |
| 300 | /// NOTE: It is important that the time spent within the callback supplied | ||
| 301 | /// does not exceed the time taken to acquire the samples into a single buffer. | ||
| 302 | /// You should measure the time taken by the callback and set the sample buffer | ||
| 303 | /// size accordingly. Exceeding this time can lead to the peripheral re-writing | ||
| 304 | /// the other buffer. | ||
| 305 | /// | ||
| 266 | /// A task-driven approach to driving TASK_SAMPLE is expected. With a task | 306 | /// A task-driven approach to driving TASK_SAMPLE is expected. With a task |
| 267 | /// driven approach, multiple channels can be used. | 307 | /// driven approach, multiple channels can be used. |
| 268 | /// | 308 | /// |
| 309 | /// In addition, the caller is responsible for triggering TASK_START in | ||
| 310 | /// relation to the previous one having ended (EVENTS_END). The the initial | ||
| 311 | /// TASKS_START is triggered by this method. | ||
| 312 | /// | ||
| 313 | /// A closure is provided so that any required initialization such as starting | ||
| 314 | /// the sampling task can occur once the peripheral has been started. | ||
| 315 | /// | ||
| 269 | /// A sampler closure is provided that receives the buffer of samples, noting | 316 | /// A sampler closure is provided that receives the buffer of samples, noting |
| 270 | /// that the size of this buffer can be less than the original buffer's size. | 317 | /// that the size of this buffer can be less than the original buffer's size. |
| 271 | /// A command is return from the closure that indicates whether the sampling | 318 | /// A command is return from the closure that indicates whether the sampling |
| 272 | /// should continue or stop. | 319 | /// should continue or stop. |
| 273 | pub async fn run_task_sampler<S, const N0: usize>( | 320 | pub async fn run_task_sampler<I, S, const N0: usize>( |
| 274 | &mut self, | 321 | &mut self, |
| 275 | bufs: &mut [[[i16; N]; N0]; 2], | 322 | bufs: &mut [[[i16; N]; N0]; 2], |
| 323 | init: I, | ||
| 276 | sampler: S, | 324 | sampler: S, |
| 277 | ) where | 325 | ) where |
| 326 | I: FnMut(), | ||
| 278 | S: FnMut(&[[i16; N]]) -> SamplerState, | 327 | S: FnMut(&[[i16; N]]) -> SamplerState, |
| 279 | { | 328 | { |
| 280 | self.run_sampler(bufs, None, sampler).await; | 329 | self.run_sampler(bufs, None, init, sampler).await; |
| 281 | } | 330 | } |
| 282 | 331 | ||
| 283 | async fn run_sampler<S, const N0: usize>( | 332 | async fn run_sampler<I, S, const N0: usize>( |
| 284 | &mut self, | 333 | &mut self, |
| 285 | bufs: &mut [[[i16; N]; N0]; 2], | 334 | bufs: &mut [[[i16; N]; N0]; 2], |
| 286 | sample_rate_divisor: Option<u16>, | 335 | sample_rate_divisor: Option<u16>, |
| 336 | mut init: I, | ||
| 287 | mut sampler: S, | 337 | mut sampler: S, |
| 288 | ) where | 338 | ) where |
| 339 | I: FnMut(), | ||
| 289 | S: FnMut(&[[i16; N]]) -> SamplerState, | 340 | S: FnMut(&[[i16; N]]) -> SamplerState, |
| 290 | { | 341 | { |
| 291 | let r = Self::regs(); | 342 | let r = Self::regs(); |
| @@ -330,6 +381,8 @@ impl<'d, const N: usize> Saadc<'d, N> { | |||
| 330 | 381 | ||
| 331 | r.tasks_start.write(|w| unsafe { w.bits(1) }); | 382 | r.tasks_start.write(|w| unsafe { w.bits(1) }); |
| 332 | 383 | ||
| 384 | let mut inited = false; | ||
| 385 | |||
| 333 | let mut current_buffer = 0; | 386 | let mut current_buffer = 0; |
| 334 | 387 | ||
| 335 | // Wait for events and complete when the sampler indicates it has had enough. | 388 | // Wait for events and complete when the sampler indicates it has had enough. |
| @@ -347,7 +400,6 @@ impl<'d, const N: usize> Saadc<'d, N> { | |||
| 347 | if sampler(&bufs[current_buffer]) == SamplerState::Sampled { | 400 | if sampler(&bufs[current_buffer]) == SamplerState::Sampled { |
| 348 | let next_buffer = 1 - current_buffer; | 401 | let next_buffer = 1 - current_buffer; |
| 349 | current_buffer = next_buffer; | 402 | current_buffer = next_buffer; |
| 350 | r.tasks_start.write(|w| unsafe { w.bits(1) }); | ||
| 351 | } else { | 403 | } else { |
| 352 | return Poll::Ready(()); | 404 | return Poll::Ready(()); |
| 353 | }; | 405 | }; |
| @@ -357,6 +409,11 @@ impl<'d, const N: usize> Saadc<'d, N> { | |||
| 357 | r.events_started.reset(); | 409 | r.events_started.reset(); |
| 358 | r.intenset.write(|w| w.started().set()); | 410 | r.intenset.write(|w| w.started().set()); |
| 359 | 411 | ||
| 412 | if !inited { | ||
| 413 | init(); | ||
| 414 | inited = true; | ||
| 415 | } | ||
| 416 | |||
| 360 | let next_buffer = 1 - current_buffer; | 417 | let next_buffer = 1 - current_buffer; |
| 361 | r.result | 418 | r.result |
| 362 | .ptr | 419 | .ptr |
| @@ -368,11 +425,23 @@ impl<'d, const N: usize> Saadc<'d, N> { | |||
| 368 | .await; | 425 | .await; |
| 369 | } | 426 | } |
| 370 | 427 | ||
| 428 | /// Return the end event for use with PPI | ||
| 429 | pub fn event_end(&self) -> Event { | ||
| 430 | let r = Self::regs(); | ||
| 431 | Event::from_reg(&r.events_end) | ||
| 432 | } | ||
| 433 | |||
| 371 | /// Return the sample task for use with PPI | 434 | /// Return the sample task for use with PPI |
| 372 | pub fn task_sample(&self) -> Task { | 435 | pub fn task_sample(&self) -> Task { |
| 373 | let r = Self::regs(); | 436 | let r = Self::regs(); |
| 374 | Task::from_reg(&r.tasks_sample) | 437 | Task::from_reg(&r.tasks_sample) |
| 375 | } | 438 | } |
| 439 | |||
| 440 | /// Return the start task for use with PPI | ||
| 441 | pub fn task_start(&self) -> Task { | ||
| 442 | let r = Self::regs(); | ||
| 443 | Task::from_reg(&r.tasks_start) | ||
| 444 | } | ||
| 376 | } | 445 | } |
| 377 | 446 | ||
| 378 | impl<'d> Saadc<'d, 1> { | 447 | impl<'d> Saadc<'d, 1> { |
| @@ -386,7 +455,7 @@ impl<'d> Saadc<'d, 1> { | |||
| 386 | /// that the size of this buffer can be less than the original buffer's size. | 455 | /// that the size of this buffer can be less than the original buffer's size. |
| 387 | /// A command is return from the closure that indicates whether the sampling | 456 | /// A command is return from the closure that indicates whether the sampling |
| 388 | /// should continue or stop. | 457 | /// should continue or stop. |
| 389 | pub async fn run_timer_sampler<S, const N0: usize>( | 458 | pub async fn run_timer_sampler<I, S, const N0: usize>( |
| 390 | &mut self, | 459 | &mut self, |
| 391 | bufs: &mut [[[i16; 1]; N0]; 2], | 460 | bufs: &mut [[[i16; 1]; N0]; 2], |
| 392 | sample_rate_divisor: u16, | 461 | sample_rate_divisor: u16, |
| @@ -394,7 +463,7 @@ impl<'d> Saadc<'d, 1> { | |||
| 394 | ) where | 463 | ) where |
| 395 | S: FnMut(&[[i16; 1]]) -> SamplerState, | 464 | S: FnMut(&[[i16; 1]]) -> SamplerState, |
| 396 | { | 465 | { |
| 397 | self.run_sampler(bufs, Some(sample_rate_divisor), sampler) | 466 | self.run_sampler(bufs, Some(sample_rate_divisor), || {}, sampler) |
| 398 | .await; | 467 | .await; |
| 399 | } | 468 | } |
| 400 | } | 469 | } |
diff --git a/examples/nrf/src/bin/saadc_continuous.rs b/examples/nrf/src/bin/saadc_continuous.rs index 81559237b..991adabad 100644 --- a/examples/nrf/src/bin/saadc_continuous.rs +++ b/examples/nrf/src/bin/saadc_continuous.rs | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #[path = "../example_common.rs"] | 5 | #[path = "../example_common.rs"] |
| 6 | mod example_common; | 6 | mod example_common; |
| 7 | use embassy::executor::Spawner; | 7 | use embassy::executor::Spawner; |
| 8 | use embassy::time::Duration; | ||
| 8 | use embassy_nrf::ppi::Ppi; | 9 | use embassy_nrf::ppi::Ppi; |
| 9 | use embassy_nrf::saadc::{ChannelConfig, Config, Saadc, SamplerState}; | 10 | use embassy_nrf::saadc::{ChannelConfig, Config, Saadc, SamplerState}; |
| 10 | use embassy_nrf::timer::{Frequency, Timer}; | 11 | use embassy_nrf::timer::{Frequency, Timer}; |
| @@ -26,34 +27,56 @@ async fn main(_spawner: Spawner, mut p: Peripherals) { | |||
| 26 | [channel_1_config, channel_2_config, channel_3_config], | 27 | [channel_1_config, channel_2_config, channel_3_config], |
| 27 | ); | 28 | ); |
| 28 | 29 | ||
| 30 | // We want the task start to effectively short with the last one ending so | ||
| 31 | // we don't miss any samples. The Saadc will trigger the initial TASKS_START. | ||
| 32 | let mut start_ppi = Ppi::new_one_to_one(p.PPI_CH0, saadc.event_end(), saadc.task_start()); | ||
| 33 | start_ppi.enable(); | ||
| 34 | |||
| 29 | let mut timer = Timer::new(p.TIMER0); | 35 | let mut timer = Timer::new(p.TIMER0); |
| 30 | timer.set_frequency(Frequency::F1MHz); | 36 | timer.set_frequency(Frequency::F1MHz); |
| 31 | timer.cc(0).write(100); // We want to sample at 10KHz | 37 | timer.cc(0).write(1000); // We want to sample at 1KHz |
| 32 | timer.cc(0).short_compare_clear(); | 38 | timer.cc(0).short_compare_clear(); |
| 33 | 39 | ||
| 34 | let mut ppi = Ppi::new_one_to_one(p.PPI_CH0, timer.cc(0).event_compare(), saadc.task_sample()); | 40 | let mut sample_ppi = |
| 35 | ppi.enable(); | 41 | Ppi::new_one_to_one(p.PPI_CH1, timer.cc(0).event_compare(), saadc.task_sample()); |
| 36 | 42 | ||
| 37 | timer.start(); | 43 | timer.start(); |
| 38 | 44 | ||
| 39 | let mut bufs = [[[0; 3]; 50]; 2]; | 45 | // This delay demonstrates that starting the timer prior to running |
| 46 | // the task sampler is benign given the calibration that follows. | ||
| 47 | embassy::time::Timer::after(Duration::from_millis(500)).await; | ||
| 48 | saadc.calibrate().await; | ||
| 49 | |||
| 50 | let mut bufs = [[[0; 3]; 500]; 2]; | ||
| 40 | 51 | ||
| 41 | let mut c = 0; | 52 | let mut c = 0; |
| 42 | let mut a: i32 = 0; | 53 | let mut a: i32 = 0; |
| 43 | 54 | ||
| 44 | saadc | 55 | saadc |
| 45 | .run_task_sampler(&mut bufs, move |buf| { | 56 | .run_task_sampler( |
| 46 | for b in buf { | 57 | &mut bufs, |
| 47 | a += b[0] as i32; | 58 | || { |
| 48 | } | 59 | sample_ppi.enable(); |
| 49 | c += buf.len(); | 60 | }, |
| 50 | if c > 10000 { | 61 | move |buf| { |
| 51 | a = a / c as i32; | 62 | // NOTE: It is important that the time spent within this callback |
| 52 | info!("channel 1: {=i32}", a); | 63 | // does not exceed the time taken to acquire the 1500 samples we |
| 53 | c = 0; | 64 | // have in this example, which would be 10us + 2us per |
| 54 | a = 0; | 65 | // sample * 1500 = 18ms. You need to measure the time taken here |
| 55 | } | 66 | // and set the sample buffer size accordingly. Exceeding this |
| 56 | SamplerState::Sampled | 67 | // time can lead to the peripheral re-writing the other buffer. |
| 57 | }) | 68 | for b in buf { |
| 69 | a += b[0] as i32; | ||
| 70 | } | ||
| 71 | c += buf.len(); | ||
| 72 | if c > 1000 { | ||
| 73 | a = a / c as i32; | ||
| 74 | info!("channel 1: {=i32}", a); | ||
| 75 | c = 0; | ||
| 76 | a = 0; | ||
| 77 | } | ||
| 78 | SamplerState::Sampled | ||
| 79 | }, | ||
| 80 | ) | ||
| 58 | .await; | 81 | .await; |
| 59 | } | 82 | } |
