diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs
index faa52d8fb..3e500098c 100644
--- a/embassy-nrf/src/chips/nrf52810.rs
+++ b/embassy-nrf/src/chips/nrf52810.rs
@@ -128,6 +128,9 @@ embassy_hal_common::peripherals! {
 
     // QDEC
     QDEC,
+
+    // PDM
+    PDM,
 }
 
 impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs
index bbdf1cbe5..25c7c0d91 100644
--- a/embassy-nrf/src/chips/nrf52811.rs
+++ b/embassy-nrf/src/chips/nrf52811.rs
@@ -128,6 +128,9 @@ embassy_hal_common::peripherals! {
 
     // QDEC
     QDEC,
+
+    // PDM
+    PDM,
 }
 
 impl_uarte!(UARTE0, UARTE0, UARTE0_UART0);
diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs
index 39a0f93f9..3b33907d2 100644
--- a/embassy-nrf/src/chips/nrf52833.rs
+++ b/embassy-nrf/src/chips/nrf52833.rs
@@ -158,6 +158,9 @@ embassy_hal_common::peripherals! {
 
     // QDEC
     QDEC,
+
+    // PDM
+    PDM,
 }
 
 #[cfg(feature = "nightly")]
diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs
index e3d8f34a1..ae59f8b25 100644
--- a/embassy-nrf/src/chips/nrf52840.rs
+++ b/embassy-nrf/src/chips/nrf52840.rs
@@ -161,6 +161,9 @@ embassy_hal_common::peripherals! {
 
     // TEMP
     TEMP,
+
+    // PDM
+    PDM,
 }
 
 #[cfg(feature = "nightly")]
diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs
index a4be8564e..f8ed11e03 100644
--- a/embassy-nrf/src/chips/nrf9160.rs
+++ b/embassy-nrf/src/chips/nrf9160.rs
@@ -260,6 +260,9 @@ embassy_hal_common::peripherals! {
     P0_29,
     P0_30,
     P0_31,
+
+    // PDM
+    PDM,
 }
 
 impl_uarte!(UARTETWISPI0, UARTE0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0);
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs
index d7bd21702..bc70fc2f6 100644
--- a/embassy-nrf/src/lib.rs
+++ b/embassy-nrf/src/lib.rs
@@ -76,6 +76,14 @@ pub mod gpio;
 pub mod gpiote;
 #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))]
 pub mod nvmc;
+#[cfg(any(
+    feature = "nrf52810",
+    feature = "nrf52811",
+    feature = "nrf52833",
+    feature = "nrf52840",
+    feature = "_nrf9160"
+))]
+pub mod pdm;
 pub mod ppi;
 #[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))]
 pub mod pwm;
diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs
new file mode 100644
index 000000000..b7c7022cf
--- /dev/null
+++ b/embassy-nrf/src/pdm.rs
@@ -0,0 +1,242 @@
+//! PDM mirophone interface
+
+use core::marker::PhantomData;
+use core::sync::atomic::{compiler_fence, Ordering};
+use core::task::Poll;
+
+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};
+
+/// PDM microphone interface
+pub struct Pdm<'d> {
+    irq: PeripheralRef<'d, interrupt::PDM>,
+    phantom: PhantomData<&'d PDM>,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+#[non_exhaustive]
+pub enum Error {
+    BufferTooLong,
+    BufferZeroLength,
+    NotRunning,
+}
+
+static WAKER: AtomicWaker = AtomicWaker::new();
+static DUMMY_BUFFER: [i16; 1] = [0; 1];
+
+impl<'d> Pdm<'d> {
+    /// Create PDM driver
+    pub fn new(
+        pdm: impl Peripheral<P = PDM> + 'd,
+        irq: impl Peripheral<P = interrupt::PDM> + 'd,
+        clk: impl Peripheral<P = impl GpioPin> + 'd,
+        din: impl Peripheral<P = impl GpioPin> + 'd,
+        config: Config,
+    ) -> Self {
+        into_ref!(clk, din);
+        Self::new_inner(pdm, irq, clk.map_into(), din.map_into(), config)
+    }
+
+    fn new_inner(
+        _pdm: impl Peripheral<P = PDM> + 'd,
+        irq: impl Peripheral<P = interrupt::PDM> + 'd,
+        clk: PeripheralRef<'d, AnyPin>,
+        din: PeripheralRef<'d, AnyPin>,
+        config: Config,
+    ) -> Self {
+        into_ref!(irq);
+
+        let r = Self::regs();
+
+        // setup gpio pins
+        din.conf().write(|w| w.input().set_bit());
+        r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) });
+        clk.set_low();
+        clk.conf().write(|w| w.dir().output());
+        r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) });
+
+        // configure
+        // use default for
+        // - gain right
+        // - gain left
+        // - clk
+        // - ratio
+        r.mode.write(|w| {
+            w.edge().bit(config.edge == Edge::LeftRising);
+            w.operation().bit(config.operation_mode == OperationMode::Mono);
+            w
+        });
+        r.gainl.write(|w| w.gainl().default_gain());
+        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();
+
+        r.enable.write(|w| w.enable().set_bit());
+
+        Self {
+            phantom: PhantomData,
+            irq,
+        }
+    }
+
+    /// 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();
+
+        // start dummy sampling because microphon needs some setup time
+        r.sample
+            .ptr
+            .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
+        r.sample
+            .maxcnt
+            .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
+
+        r.tasks_start.write(|w| w.tasks_start().set_bit());
+    }
+
+    /// 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());
+        r.events_started.reset();
+    }
+
+    pub async fn sample(&mut self, buffer: &mut [i16]) -> Result<(), Error> {
+        if buffer.len() == 0 {
+            return Err(Error::BufferZeroLength);
+        }
+        if buffer.len() > EASY_DMA_SIZE {
+            return Err(Error::BufferTooLong);
+        }
+
+        let r = Self::regs();
+
+        if r.events_started.read().events_started().bit_is_clear() {
+            return Err(Error::NotRunning);
+        }
+
+        let drop = OnDrop::new(move || {
+            r.intenclr.write(|w| w.end().clear());
+            r.events_stopped.reset();
+
+            // reset to dummy buffer
+            r.sample
+                .ptr
+                .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
+            r.sample
+                .maxcnt
+                .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
+
+            while r.events_stopped.read().bits() == 0 {}
+        });
+
+        // setup user buffer
+        let ptr = buffer.as_ptr();
+        let len = buffer.len();
+        r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) });
+        r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) });
+
+        // wait till the current sample is finished and the user buffer sample is started
+        Self::wait_for_sample().await;
+
+        // reset the buffer back to the dummy buffer
+        r.sample
+            .ptr
+            .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) });
+        r.sample
+            .maxcnt
+            .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) });
+
+        // wait till the user buffer is sampled
+        Self::wait_for_sample().await;
+
+        drop.defuse();
+
+        Ok(())
+    }
+
+    async fn wait_for_sample() {
+        let r = Self::regs();
+
+        r.events_end.reset();
+        r.intenset.write(|w| w.end().set());
+
+        compiler_fence(Ordering::SeqCst);
+
+        poll_fn(|cx| {
+            WAKER.register(cx.waker());
+            if r.events_end.read().events_end().bit_is_set() {
+                return Poll::Ready(());
+            }
+            Poll::Pending
+        })
+        .await;
+
+        compiler_fence(Ordering::SeqCst);
+    }
+
+    fn regs() -> &'static pac::pdm::RegisterBlock {
+        unsafe { &*pac::PDM::ptr() }
+    }
+}
+
+/// PDM microphone driver Config
+pub struct Config {
+    /// Use stero or mono operation
+    pub operation_mode: OperationMode,
+    /// On which edge the left channel should be samples
+    pub edge: Edge,
+}
+
+impl Default for Config {
+    fn default() -> Self {
+        Self {
+            operation_mode: OperationMode::Mono,
+            edge: Edge::LeftFalling,
+        }
+    }
+}
+
+#[derive(PartialEq)]
+pub enum OperationMode {
+    Mono,
+    Stereo,
+}
+#[derive(PartialEq)]
+pub enum Edge {
+    LeftRising,
+    LeftFalling,
+}
+
+impl<'d> Drop for Pdm<'d> {
+    fn drop(&mut self) {
+        let r = Self::regs();
+
+        r.tasks_stop.write(|w| w.tasks_stop().set_bit());
+
+        self.irq.disable();
+
+        r.enable.write(|w| w.enable().disabled());
+
+        r.psel.din.reset();
+        r.psel.clk.reset();
+    }
+}
diff --git a/examples/nrf/src/bin/pdm.rs b/examples/nrf/src/bin/pdm.rs
new file mode 100644
index 000000000..7388580fb
--- /dev/null
+++ b/examples/nrf/src/bin/pdm.rs
@@ -0,0 +1,33 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+use defmt::info;
+use embassy_executor::Spawner;
+use embassy_nrf::interrupt;
+use embassy_nrf::pdm::{Config, Pdm};
+use embassy_time::{Duration, Timer};
+use {defmt_rtt as _, panic_probe as _};
+
+#[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);
+
+    loop {
+        pdm.start().await;
+
+        // wait some time till the microphon settled
+        Timer::after(Duration::from_millis(1000)).await;
+
+        const SAMPLES: usize = 2048;
+        let mut buf = [0i16; SAMPLES];
+        pdm.sample(&mut buf).await.unwrap();
+
+        info!("samples: {:?}", &buf);
+
+        pdm.stop().await;
+        Timer::after(Duration::from_millis(100)).await;
+    }
+}