embassy_nrf/
pdm.rs

1//! Pulse Density Modulation (PDM) microphone driver
2
3#![macro_use]
4
5use core::future::poll_fn;
6use core::marker::PhantomData;
7use core::sync::atomic::{compiler_fence, Ordering};
8use core::task::Poll;
9
10use embassy_hal_internal::drop::OnDrop;
11use embassy_hal_internal::{Peri, PeripheralType};
12use embassy_sync::waitqueue::AtomicWaker;
13use fixed::types::I7F1;
14
15use crate::chip::EASY_DMA_SIZE;
16use crate::gpio::{AnyPin, Pin as GpioPin, SealedPin, DISCONNECTED};
17use crate::interrupt::typelevel::Interrupt;
18use crate::pac::gpio::vals as gpiovals;
19use crate::pac::pdm::vals;
20pub use crate::pac::pdm::vals::Freq as Frequency;
21#[cfg(any(
22    feature = "nrf52840",
23    feature = "nrf52833",
24    feature = "_nrf5340-app",
25    feature = "_nrf91",
26))]
27pub use crate::pac::pdm::vals::Ratio;
28use crate::{interrupt, pac};
29
30/// Interrupt handler
31pub struct InterruptHandler<T: Instance> {
32    _phantom: PhantomData<T>,
33}
34
35impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
36    unsafe fn on_interrupt() {
37        let r = T::regs();
38
39        if r.events_end().read() != 0 {
40            r.intenclr().write(|w| w.set_end(true));
41        }
42
43        if r.events_started().read() != 0 {
44            r.intenclr().write(|w| w.set_started(true));
45        }
46
47        if r.events_stopped().read() != 0 {
48            r.intenclr().write(|w| w.set_stopped(true));
49        }
50
51        T::state().waker.wake();
52    }
53}
54
55/// PDM microphone interface
56pub struct Pdm<'d, T: Instance> {
57    _peri: Peri<'d, T>,
58}
59
60/// PDM error
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62#[cfg_attr(feature = "defmt", derive(defmt::Format))]
63#[non_exhaustive]
64pub enum Error {
65    /// Buffer is too long
66    BufferTooLong,
67    /// Buffer is empty
68    BufferZeroLength,
69    /// PDM is not running
70    NotRunning,
71    /// PDM is already running
72    AlreadyRunning,
73}
74
75static DUMMY_BUFFER: [i16; 1] = [0; 1];
76
77/// The state of a continuously running sampler. While it reflects
78/// the progress of a sampler, it also signals what should be done
79/// next. For example, if the sampler has stopped then the PDM implementation
80/// can then tear down its infrastructure
81#[derive(PartialEq)]
82pub enum SamplerState {
83    /// The sampler processed the samples and is ready for more
84    Sampled,
85    /// The sampler is done processing samples
86    Stopped,
87}
88
89impl<'d, T: Instance> Pdm<'d, T> {
90    /// Create PDM driver
91    pub fn new(
92        pdm: Peri<'d, T>,
93        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
94        clk: Peri<'d, impl GpioPin>,
95        din: Peri<'d, impl GpioPin>,
96        config: Config,
97    ) -> Self {
98        Self::new_inner(pdm, clk.into(), din.into(), config)
99    }
100
101    fn new_inner(pdm: Peri<'d, T>, clk: Peri<'d, AnyPin>, din: Peri<'d, AnyPin>, config: Config) -> Self {
102        let r = T::regs();
103
104        // setup gpio pins
105        din.conf().write(|w| w.set_input(gpiovals::Input::CONNECT));
106        r.psel().din().write_value(din.psel_bits());
107        clk.set_low();
108        clk.conf().write(|w| w.set_dir(gpiovals::Dir::OUTPUT));
109        r.psel().clk().write_value(clk.psel_bits());
110
111        // configure
112        r.pdmclkctrl().write(|w| w.set_freq(config.frequency));
113        #[cfg(any(
114            feature = "nrf52840",
115            feature = "nrf52833",
116            feature = "_nrf5340-app",
117            feature = "_nrf91",
118        ))]
119        r.ratio().write(|w| w.set_ratio(config.ratio));
120        r.mode().write(|w| {
121            w.set_operation(config.operation_mode.into());
122            w.set_edge(config.edge.into());
123        });
124
125        Self::_set_gain(r, config.gain_left, config.gain_right);
126
127        // Disable all events interrupts
128        r.intenclr().write(|w| w.0 = 0x003F_FFFF);
129
130        // IRQ
131        T::Interrupt::unpend();
132        unsafe { T::Interrupt::enable() };
133
134        r.enable().write(|w| w.set_enable(true));
135
136        Self { _peri: pdm }
137    }
138
139    fn _set_gain(r: pac::pdm::Pdm, gain_left: I7F1, gain_right: I7F1) {
140        let gain_to_bits = |gain: I7F1| -> vals::Gain {
141            let gain: i8 = gain.saturating_add(I7F1::from_bits(0x28)).to_bits().clamp(0, 0x50);
142            vals::Gain::from_bits(gain as u8)
143        };
144        r.gainl().write(|w| w.set_gainl(gain_to_bits(gain_left)));
145        r.gainr().write(|w| w.set_gainr(gain_to_bits(gain_right)));
146    }
147
148    /// Adjust the gain of the PDM microphone on the fly
149    pub fn set_gain(&mut self, gain_left: I7F1, gain_right: I7F1) {
150        Self::_set_gain(T::regs(), gain_left, gain_right)
151    }
152
153    /// Start sampling microphone data into a dummy buffer.
154    /// Useful to start the microphone and keep it active between recording samples.
155    pub async fn start(&mut self) {
156        let r = T::regs();
157
158        // start dummy sampling because microphone needs some setup time
159        r.sample().ptr().write_value(DUMMY_BUFFER.as_ptr() as u32);
160        r.sample().maxcnt().write(|w| w.set_buffsize(DUMMY_BUFFER.len() as _));
161
162        r.tasks_start().write_value(1);
163    }
164
165    /// Stop sampling microphone data inta a dummy buffer
166    pub async fn stop(&mut self) {
167        let r = T::regs();
168        r.tasks_stop().write_value(1);
169        r.events_started().write_value(0);
170    }
171
172    /// Sample data into the given buffer
173    pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
174        if buffer.is_empty() {
175            return Err(Error::BufferZeroLength);
176        }
177        if buffer.len() > EASY_DMA_SIZE {
178            return Err(Error::BufferTooLong);
179        }
180
181        let r = T::regs();
182
183        if r.events_started().read() == 0 {
184            return Err(Error::NotRunning);
185        }
186
187        let drop = OnDrop::new(move || {
188            r.intenclr().write(|w| w.set_end(true));
189            r.events_stopped().write_value(0);
190
191            // reset to dummy buffer
192            r.sample().ptr().write_value(DUMMY_BUFFER.as_ptr() as u32);
193            r.sample().maxcnt().write(|w| w.set_buffsize(DUMMY_BUFFER.len() as _));
194
195            while r.events_stopped().read() == 0 {}
196        });
197
198        // setup user buffer
199        let ptr = buffer.as_ptr();
200        let len = buffer.len();
201        r.sample().ptr().write_value(ptr as u32);
202        r.sample().maxcnt().write(|w| w.set_buffsize(len as _));
203
204        // wait till the current sample is finished and the user buffer sample is started
205        Self::wait_for_sample().await;
206
207        // reset the buffer back to the dummy buffer
208        r.sample().ptr().write_value(DUMMY_BUFFER.as_ptr() as u32);
209        r.sample().maxcnt().write(|w| w.set_buffsize(DUMMY_BUFFER.len() as _));
210
211        // wait till the user buffer is sampled
212        Self::wait_for_sample().await;
213
214        drop.defuse();
215
216        Ok(())
217    }
218
219    async fn wait_for_sample() {
220        let r = T::regs();
221
222        r.events_end().write_value(0);
223        r.intenset().write(|w| w.set_end(true));
224
225        compiler_fence(Ordering::SeqCst);
226
227        poll_fn(|cx| {
228            T::state().waker.register(cx.waker());
229            if r.events_end().read() != 0 {
230                return Poll::Ready(());
231            }
232            Poll::Pending
233        })
234        .await;
235
236        compiler_fence(Ordering::SeqCst);
237    }
238
239    /// Continuous sampling with double buffers.
240    ///
241    /// A sampler closure is provided that receives the buffer of samples, noting
242    /// that the size of this buffer can be less than the original buffer's size.
243    /// A command is return from the closure that indicates whether the sampling
244    /// should continue or stop.
245    ///
246    /// NOTE: The time spent within the callback supplied should not exceed the time
247    /// taken to acquire the samples into a single buffer. You should measure the
248    /// time taken by the callback and set the sample buffer size accordingly.
249    /// Exceeding this time can lead to samples becoming dropped.
250    pub async fn run_task_sampler<S, const N: usize>(
251        &mut self,
252        bufs: &mut [[i16; N]; 2],
253        mut sampler: S,
254    ) -> Result<(), Error>
255    where
256        S: FnMut(&[i16; N]) -> SamplerState,
257    {
258        let r = T::regs();
259
260        if r.events_started().read() != 0 {
261            return Err(Error::AlreadyRunning);
262        }
263
264        r.sample().ptr().write_value(bufs[0].as_mut_ptr() as u32);
265        r.sample().maxcnt().write(|w| w.set_buffsize(N as _));
266
267        // Reset and enable the events
268        r.events_end().write_value(0);
269        r.events_started().write_value(0);
270        r.events_stopped().write_value(0);
271        r.intenset().write(|w| {
272            w.set_end(true);
273            w.set_started(true);
274            w.set_stopped(true);
275        });
276
277        // Don't reorder the start event before the previous writes. Hopefully self
278        // wouldn't happen anyway
279        compiler_fence(Ordering::SeqCst);
280
281        r.tasks_start().write_value(1);
282
283        let mut current_buffer = 0;
284
285        let mut done = false;
286
287        let drop = OnDrop::new(|| {
288            r.tasks_stop().write_value(1);
289            // N.B. It would be better if this were async, but Drop only support sync code
290            while r.events_stopped().read() != 0 {}
291        });
292
293        // Wait for events and complete when the sampler indicates it has had enough
294        poll_fn(|cx| {
295            let r = T::regs();
296
297            T::state().waker.register(cx.waker());
298
299            if r.events_end().read() != 0 {
300                compiler_fence(Ordering::SeqCst);
301
302                r.events_end().write_value(0);
303                r.intenset().write(|w| w.set_end(true));
304
305                if !done {
306                    // Discard the last buffer after the user requested a stop
307                    if sampler(&bufs[current_buffer]) == SamplerState::Sampled {
308                        let next_buffer = 1 - current_buffer;
309                        current_buffer = next_buffer;
310                    } else {
311                        r.tasks_stop().write_value(1);
312                        done = true;
313                    };
314                };
315            }
316
317            if r.events_started().read() != 0 {
318                r.events_started().write_value(0);
319                r.intenset().write(|w| w.set_started(true));
320
321                let next_buffer = 1 - current_buffer;
322                r.sample().ptr().write_value(bufs[next_buffer].as_mut_ptr() as u32);
323            }
324
325            if r.events_stopped().read() != 0 {
326                return Poll::Ready(());
327            }
328
329            Poll::Pending
330        })
331        .await;
332        drop.defuse();
333        Ok(())
334    }
335}
336
337/// PDM microphone driver Config
338pub struct Config {
339    /// Use stero or mono operation
340    pub operation_mode: OperationMode,
341    /// On which edge the left channel should be samples
342    pub edge: Edge,
343    /// Clock frequency
344    pub frequency: Frequency,
345    /// Clock ratio
346    #[cfg(any(
347        feature = "nrf52840",
348        feature = "nrf52833",
349        feature = "_nrf5340-app",
350        feature = "_nrf91",
351    ))]
352    pub ratio: Ratio,
353    /// Gain left in dB
354    pub gain_left: I7F1,
355    /// Gain right in dB
356    pub gain_right: I7F1,
357}
358
359impl Default for Config {
360    fn default() -> Self {
361        Self {
362            operation_mode: OperationMode::Mono,
363            edge: Edge::LeftFalling,
364            frequency: Frequency::DEFAULT,
365            #[cfg(any(
366                feature = "nrf52840",
367                feature = "nrf52833",
368                feature = "_nrf5340-app",
369                feature = "_nrf91",
370            ))]
371            ratio: Ratio::RATIO80,
372            gain_left: I7F1::ZERO,
373            gain_right: I7F1::ZERO,
374        }
375    }
376}
377
378/// PDM operation mode
379#[derive(PartialEq)]
380pub enum OperationMode {
381    /// Mono (1 channel)
382    Mono,
383    /// Stereo (2 channels)
384    Stereo,
385}
386
387impl From<OperationMode> for vals::Operation {
388    fn from(mode: OperationMode) -> Self {
389        match mode {
390            OperationMode::Mono => vals::Operation::MONO,
391            OperationMode::Stereo => vals::Operation::STEREO,
392        }
393    }
394}
395
396/// PDM edge polarity
397#[derive(PartialEq)]
398pub enum Edge {
399    /// Left edge is rising
400    LeftRising,
401    /// Left edge is falling
402    LeftFalling,
403}
404
405impl From<Edge> for vals::Edge {
406    fn from(edge: Edge) -> Self {
407        match edge {
408            Edge::LeftRising => vals::Edge::LEFT_RISING,
409            Edge::LeftFalling => vals::Edge::LEFT_FALLING,
410        }
411    }
412}
413
414impl<'d, T: Instance> Drop for Pdm<'d, T> {
415    fn drop(&mut self) {
416        let r = T::regs();
417
418        r.tasks_stop().write_value(1);
419
420        r.enable().write(|w| w.set_enable(false));
421
422        r.psel().din().write_value(DISCONNECTED);
423        r.psel().clk().write_value(DISCONNECTED);
424    }
425}
426
427/// Peripheral static state
428pub(crate) struct State {
429    waker: AtomicWaker,
430}
431
432impl State {
433    pub(crate) const fn new() -> Self {
434        Self {
435            waker: AtomicWaker::new(),
436        }
437    }
438}
439
440pub(crate) trait SealedInstance {
441    fn regs() -> crate::pac::pdm::Pdm;
442    fn state() -> &'static State;
443}
444
445/// PDM peripheral instance
446#[allow(private_bounds)]
447pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
448    /// Interrupt for this peripheral
449    type Interrupt: interrupt::typelevel::Interrupt;
450}
451
452macro_rules! impl_pdm {
453    ($type:ident, $pac_type:ident, $irq:ident) => {
454        impl crate::pdm::SealedInstance for peripherals::$type {
455            fn regs() -> crate::pac::pdm::Pdm {
456                pac::$pac_type
457            }
458            fn state() -> &'static crate::pdm::State {
459                static STATE: crate::pdm::State = crate::pdm::State::new();
460                &STATE
461            }
462        }
463        impl crate::pdm::Instance for peripherals::$type {
464            type Interrupt = crate::interrupt::typelevel::$irq;
465        }
466    };
467}