diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 6b5c134b8..c6ccb6a0e 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -148,6 +148,8 @@ impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0); impl_pwm!(PWM0, PWM0, PWM0); +impl_pdm!(PDM, PDM, PDM); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index c5de9a447..7d1bce1c0 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -150,6 +150,8 @@ impl_twis!(TWISPI0, TWIS0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); impl_pwm!(PWM0, PWM0, PWM0); +impl_pdm!(PDM, PDM, PDM); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index c2b23fc5b..ce19a18e8 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -146,6 +146,9 @@ embassy_hal_common::peripherals! { // I2S I2S, + + // PDM + PDM, } impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); @@ -168,6 +171,8 @@ impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); +impl_pdm!(PDM, PDM, PDM); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 95f71ade7..08b82021d 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -197,6 +197,8 @@ impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); impl_pwm!(PWM3, PWM3, PWM3); +impl_pdm!(PDM, PDM, PDM); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 5e7479e88..4e8b6d9ee 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -208,6 +208,8 @@ impl_timer!(TIMER4, TIMER4, TIMER4, extended); impl_qspi!(QSPI, QSPI, QSPI); +impl_pdm!(PDM, PDM, PDM); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); impl_pin!(P0_02, 0, 2); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 9c7b738e6..050612b1c 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -34,7 +34,7 @@ pub mod pac { nvmc_ns as nvmc, oscillators_ns as oscillators, p0_ns as p0, - pdm0_ns as pdm0, + pdm0_ns as pdm, power_ns as power, pwm0_ns as pwm0, qdec0_ns as qdec0, @@ -253,6 +253,9 @@ embassy_hal_common::peripherals! { // QSPI QSPI, + // PDM + PDM0, + // GPIOTE GPIOTE_CH0, GPIOTE_CH1, @@ -398,6 +401,8 @@ impl_timer!(TIMER2, TIMER2, TIMER2); impl_qspi!(QSPI, QSPI, QSPI); +impl_pdm!(PDM0, PDM0, PDM0); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); #[cfg(feature = "nfc-pins-as-gpio")] diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index 385bd133d..d2b45114f 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -301,6 +301,8 @@ impl_pwm!(PWM1, PWM1, PWM1); impl_pwm!(PWM2, PWM2, PWM2); impl_pwm!(PWM3, PWM3, PWM3); +impl_pdm!(PDM, PDM, PDM); + impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 446b1f41a..17aa5ad15 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -47,8 +47,10 @@ pub mod nvmc; #[cfg(any( feature = "nrf52810", feature = "nrf52811", + feature = "nrf52832", feature = "nrf52833", feature = "nrf52840", + feature = "_nrf5340-app", feature = "_nrf9160" ))] pub mod pdm; diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 54feca4c1..8815bb316 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -1,25 +1,37 @@ //! Pulse Density Modulation (PDM) mirophone driver. +#![macro_use] + use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; +use embassy_cortex_m::interrupt::Interrupt; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; -use embassy_sync::waitqueue::AtomicWaker; use futures::future::poll_fn; use crate::chip::EASY_DMA_SIZE; use crate::gpio::sealed::Pin; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::{self, InterruptExt}; -use crate::peripherals::PDM; -use crate::{pac, Peripheral}; +use crate::Peripheral; + +/// Interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::Handler for InterruptHandler { + unsafe fn on_interrupt() { + T::regs().intenclr.write(|w| w.end().clear()); + T::state().waker.wake(); + } +} /// PDM microphone interface -pub struct Pdm<'d> { - irq: PeripheralRef<'d, interrupt::PDM>, - phantom: PhantomData<&'d PDM>, +pub struct Pdm<'d, T: Instance> { + _peri: PeripheralRef<'d, T>, } /// PDM error. @@ -35,32 +47,30 @@ pub enum Error { NotRunning, } -static WAKER: AtomicWaker = AtomicWaker::new(); static DUMMY_BUFFER: [i16; 1] = [0; 1]; -impl<'d> Pdm<'d> { +impl<'d, T: Instance> Pdm<'d, T> { /// Create PDM driver pub fn new( - pdm: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + pdm: impl Peripheral

+ 'd, + _irq: impl interrupt::Binding> + 'd, clk: impl Peripheral

+ 'd, din: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(clk, din); - Self::new_inner(pdm, irq, clk.map_into(), din.map_into(), config) + into_ref!(pdm, clk, din); + Self::new_inner(pdm, clk.map_into(), din.map_into(), config) } fn new_inner( - _pdm: impl Peripheral

+ 'd, - irq: impl Peripheral

+ 'd, + pdm: PeripheralRef<'d, T>, clk: PeripheralRef<'d, AnyPin>, din: PeripheralRef<'d, AnyPin>, config: Config, ) -> Self { - into_ref!(irq); + into_ref!(pdm); - let r = Self::regs(); + let r = T::regs(); // setup gpio pins din.conf().write(|w| w.input().set_bit()); @@ -84,26 +94,18 @@ impl<'d> Pdm<'d> { r.gainr.write(|w| w.gainr().default_gain()); // IRQ - irq.disable(); - irq.set_handler(|_| { - let r = Self::regs(); - r.intenclr.write(|w| w.end().clear()); - WAKER.wake(); - }); - irq.enable(); + unsafe { T::Interrupt::steal() }.unpend(); + unsafe { T::Interrupt::steal() }.enable(); r.enable.write(|w| w.enable().set_bit()); - Self { - phantom: PhantomData, - irq, - } + Self { _peri: pdm } } /// Start sampling microphon data into a dummy buffer /// Usefull to start the microphon and keep it active between recording samples pub async fn start(&mut self) { - let r = Self::regs(); + let r = T::regs(); // start dummy sampling because microphon needs some setup time r.sample @@ -113,13 +115,13 @@ impl<'d> Pdm<'d> { .maxcnt .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); - r.tasks_start.write(|w| w.tasks_start().set_bit()); + r.tasks_start.write(|w| unsafe { w.bits(1) }); } /// Stop sampling microphon data inta a dummy buffer pub async fn stop(&mut self) { - let r = Self::regs(); - r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + let r = T::regs(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); r.events_started.reset(); } @@ -132,9 +134,9 @@ impl<'d> Pdm<'d> { return Err(Error::BufferTooLong); } - let r = Self::regs(); + let r = T::regs(); - if r.events_started.read().events_started().bit_is_clear() { + if r.events_started.read().bits() == 0 { return Err(Error::NotRunning); } @@ -179,7 +181,7 @@ impl<'d> Pdm<'d> { } async fn wait_for_sample() { - let r = Self::regs(); + let r = T::regs(); r.events_end.reset(); r.intenset.write(|w| w.end().set()); @@ -187,8 +189,8 @@ impl<'d> Pdm<'d> { compiler_fence(Ordering::SeqCst); poll_fn(|cx| { - WAKER.register(cx.waker()); - if r.events_end.read().events_end().bit_is_set() { + T::state().waker.register(cx.waker()); + if r.events_end.read().bits() != 0 { return Poll::Ready(()); } Poll::Pending @@ -197,10 +199,6 @@ impl<'d> Pdm<'d> { compiler_fence(Ordering::SeqCst); } - - fn regs() -> &'static pac::pdm::RegisterBlock { - unsafe { &*pac::PDM::ptr() } - } } /// PDM microphone driver Config @@ -238,13 +236,11 @@ pub enum Edge { LeftFalling, } -impl<'d> Drop for Pdm<'d> { +impl<'d, T: Instance> Drop for Pdm<'d, T> { fn drop(&mut self) { - let r = Self::regs(); + let r = T::regs(); - r.tasks_stop.write(|w| w.tasks_stop().set_bit()); - - self.irq.disable(); + r.tasks_stop.write(|w| unsafe { w.bits(1) }); r.enable.write(|w| w.enable().disabled()); @@ -252,3 +248,48 @@ impl<'d> Drop for Pdm<'d> { r.psel.clk.reset(); } } + +pub(crate) mod sealed { + use embassy_sync::waitqueue::AtomicWaker; + + /// Peripheral static state + pub struct State { + pub waker: AtomicWaker, + } + + impl State { + pub const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + } + } + } + + pub trait Instance { + fn regs() -> &'static crate::pac::pdm::RegisterBlock; + fn state() -> &'static State; + } +} + +/// PDM peripheral instance. +pub trait Instance: Peripheral

+ sealed::Instance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: Interrupt; +} + +macro_rules! impl_pdm { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::pdm::sealed::Instance for peripherals::$type { + fn regs() -> &'static crate::pac::pdm::RegisterBlock { + unsafe { &*pac::$pac_type::ptr() } + } + fn state() -> &'static crate::pdm::sealed::State { + static STATE: crate::pdm::sealed::State = crate::pdm::sealed::State::new(); + &STATE + } + } + impl crate::pdm::Instance for peripherals::$type { + type Interrupt = crate::interrupt::$irq; + } + }; +} diff --git a/examples/nrf52840/src/bin/pdm.rs b/examples/nrf52840/src/bin/pdm.rs index 7388580fb..6b41320ca 100644 --- a/examples/nrf52840/src/bin/pdm.rs +++ b/examples/nrf52840/src/bin/pdm.rs @@ -4,16 +4,20 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_nrf::interrupt; -use embassy_nrf::pdm::{Config, Pdm}; +use embassy_nrf::pdm::{self, Config, Pdm}; +use embassy_nrf::{bind_interrupts, peripherals}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + PDM => pdm::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_p: Spawner) { let p = embassy_nrf::init(Default::default()); let config = Config::default(); - let mut pdm = Pdm::new(p.PDM, interrupt::take!(PDM), p.P0_01, p.P0_00, config); + let mut pdm = Pdm::new(p.PDM, Irqs, p.P0_01, p.P0_00, config); loop { pdm.start().await;