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