diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 229f902fab..df4f9f371e 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -73,7 +73,7 @@ rand_core = "0.6.3"
 sdio-host = "0.5.0"
 critical-section = "1.1"
 #stm32-metapac = { version = "15" }
-stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-567fd0b1b7dfd9c1aa9e54d365547afe1ceb1241" }
+stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-274eeb0ed4477768d026276c4e9873586c1b9a05" }
 
 vcell = "0.1.3"
 nb = "1.0.0"
@@ -102,7 +102,7 @@ proc-macro2 = "1.0.36"
 quote = "1.0.15"
 
 #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
-stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-567fd0b1b7dfd9c1aa9e54d365547afe1ceb1241", default-features = false, features = ["metadata"] }
+stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-274eeb0ed4477768d026276c4e9873586c1b9a05", default-features = false, features = ["metadata"] }
 
 [features]
 default = ["rt"]
diff --git a/embassy-stm32/src/dts/mod.rs b/embassy-stm32/src/dts/mod.rs
new file mode 100644
index 0000000000..58d7cf8412
--- /dev/null
+++ b/embassy-stm32/src/dts/mod.rs
@@ -0,0 +1,239 @@
+//! Digital Temperature Sensor (DTS)
+
+use core::future::poll_fn;
+use core::sync::atomic::{compiler_fence, Ordering};
+use core::task::Poll;
+
+use embassy_hal_internal::{into_ref, PeripheralRef};
+use embassy_sync::waitqueue::AtomicWaker;
+
+use crate::interrupt::InterruptExt;
+use crate::peripherals::DTS;
+use crate::time::Hertz;
+use crate::{interrupt, pac, rcc, Peripheral};
+
+mod tsel;
+pub use tsel::TriggerSel;
+
+#[allow(missing_docs)]
+#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum SampleTime {
+    ClockCycles1 = 1,
+    ClockCycles2 = 2,
+    ClockCycles3 = 3,
+    ClockCycles4 = 4,
+    ClockCycles5 = 5,
+    ClockCycles6 = 6,
+    ClockCycles7 = 7,
+    ClockCycles8 = 8,
+    ClockCycles9 = 9,
+    ClockCycles10 = 10,
+    ClockCycles11 = 11,
+    ClockCycles12 = 12,
+    ClockCycles13 = 13,
+    ClockCycles14 = 14,
+    ClockCycles15 = 15,
+}
+
+#[non_exhaustive]
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+/// Config
+pub struct Config {
+    /// Sample time
+    pub sample_time: SampleTime,
+    /// Trigger selection
+    pub trigger: TriggerSel,
+}
+
+impl Default for Config {
+    fn default() -> Self {
+        Self {
+            sample_time: SampleTime::ClockCycles1,
+            trigger: TriggerSel::Software,
+        }
+    }
+}
+
+/// The read-only factory calibration values used for converting a
+/// measurement to a temperature.
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub struct FactoryCalibration {
+    /// The calibration temperature in degrees Celsius.
+    pub t0: u8,
+    /// The frequency at the calibration temperature.
+    pub fmt0: Hertz,
+    /// The ramp coefficient in Hertz per degree Celsius.
+    pub ramp_coeff: u16,
+}
+
+const MAX_DTS_CLK_FREQ: Hertz = Hertz::mhz(1);
+
+/// Digital temperature sensor driver.
+pub struct Dts<'d> {
+    _peri: PeripheralRef<'d, DTS>,
+}
+
+static WAKER: AtomicWaker = AtomicWaker::new();
+
+impl<'d> Dts<'d> {
+    /// Create a new temperature sensor driver.
+    pub fn new(
+        _peri: impl Peripheral<P = DTS> + 'd,
+        _irq: impl interrupt::typelevel::Binding<interrupt::typelevel::DTS, InterruptHandler> + 'd,
+        config: Config,
+    ) -> Self {
+        into_ref!(_peri);
+        rcc::enable_and_reset::<DTS>();
+
+        let prescaler = rcc::frequency::<DTS>() / MAX_DTS_CLK_FREQ;
+
+        if prescaler > 127 {
+            panic!("DTS PCLK frequency must be less than 127 MHz.");
+        }
+
+        Self::regs().cfgr1().modify(|w| {
+            w.set_refclk_sel(false);
+            w.set_hsref_clk_div(prescaler as u8);
+            w.set_q_meas_opt(false);
+            // Software trigger
+            w.set_intrig_sel(0);
+            w.set_smp_time(config.sample_time as u8);
+            w.set_intrig_sel(config.trigger as u8);
+            w.set_start(true);
+            w.set_en(true);
+        });
+
+        interrupt::DTS.unpend();
+        unsafe { interrupt::DTS.enable() };
+
+        Self { _peri }
+    }
+
+    /// Reconfigure the driver.
+    pub fn set_config(&mut self, config: &Config) {
+        Self::regs().cfgr1().modify(|w| {
+            w.set_smp_time(config.sample_time as u8);
+            w.set_intrig_sel(config.trigger as u8);
+        });
+    }
+
+    /// Get the read-only factory calibration values used for converting a
+    /// measurement to a temperature.
+    pub fn factory_calibration() -> FactoryCalibration {
+        let t0valr1 = Self::regs().t0valr1().read();
+        let t0 = match t0valr1.t0() {
+            0 => 30,
+            1 => 130,
+            _ => unimplemented!(),
+        };
+        let fmt0 = Hertz::hz(t0valr1.fmt0() as u32 * 100);
+
+        let ramp_coeff = Self::regs().rampvalr().read().ramp_coeff();
+
+        FactoryCalibration { t0, fmt0, ramp_coeff }
+    }
+
+    /// Perform an asynchronous temperature measurement. The returned future can
+    /// be awaited to obtain the measurement.
+    ///
+    /// The future returned waits for the next measurement to complete.
+    ///
+    /// # Example
+    ///
+    /// ```no_run
+    /// use embassy_stm32::{bind_interrupts, dts};
+    /// use embassy_stm32::dts::Dts;
+    ///
+    /// bind_interrupts!(struct Irqs {
+    ///     DTS => temp::InterruptHandler;
+    /// });
+    ///
+    /// # async {
+    /// # let p: embassy_stm32::Peripherals = todo!();
+    /// let mut dts = Dts::new(p.DTS, Irqs, Default::default());
+    /// let v: u16 = dts.read().await;
+    /// # };
+    /// ```
+    pub async fn read(&mut self) -> u16 {
+        let r = Self::regs();
+
+        r.itenr().modify(|w| w.set_iteen(true));
+
+        poll_fn(|cx| {
+            WAKER.register(cx.waker());
+            if r.itenr().read().iteen() {
+                Poll::Pending
+            } else {
+                Poll::Ready(r.dr().read().mfreq())
+            }
+        })
+        .await
+    }
+
+    /// Returns the last measurement made, if any.
+    ///
+    /// There is no guarantee that the measurement is recent or that a
+    /// measurement has ever completed.
+    pub fn read_immediate(&mut self) -> u16 {
+        Self::regs().dr().read().mfreq()
+    }
+
+    fn regs() -> pac::dts::Dts {
+        pac::DTS
+    }
+}
+
+impl<'d> Drop for Dts<'d> {
+    fn drop(&mut self) {
+        Self::regs().cfgr1().modify(|w| w.set_en(false));
+        rcc::disable::<DTS>();
+    }
+}
+
+/// Interrupt handler.
+pub struct InterruptHandler {
+    _private: (),
+}
+
+impl interrupt::typelevel::Handler<interrupt::typelevel::DTS> for InterruptHandler {
+    unsafe fn on_interrupt() {
+        let r = pac::DTS;
+        let (sr, itenr) = (r.sr().read(), r.itenr().read());
+
+        if (itenr.iteen() && sr.itef()) || (itenr.aiteen() && sr.aitef()) {
+            r.itenr().modify(|w| {
+                w.set_iteen(false);
+                w.set_aiteen(false);
+            });
+            r.icifr().modify(|w| {
+                w.set_citef(true);
+                w.set_caitef(true);
+            });
+        } else if (itenr.itlen() && sr.itlf()) || (itenr.aitlen() && sr.aitlf()) {
+            r.itenr().modify(|w| {
+                w.set_itlen(false);
+                w.set_aitlen(false);
+            });
+            r.icifr().modify(|w| {
+                w.set_citlf(true);
+                w.set_caitlf(true);
+            });
+        } else if (itenr.ithen() && sr.ithf()) || (itenr.aithen() && sr.aithf()) {
+            r.itenr().modify(|w| {
+                w.set_ithen(false);
+                w.set_aithen(false);
+            });
+            r.icifr().modify(|w| {
+                w.set_cithf(true);
+                w.set_caithf(true);
+            });
+        } else {
+            return;
+        }
+
+        compiler_fence(Ordering::SeqCst);
+        WAKER.wake();
+    }
+}
diff --git a/embassy-stm32/src/dts/tsel.rs b/embassy-stm32/src/dts/tsel.rs
new file mode 100644
index 0000000000..99eab6dd8d
--- /dev/null
+++ b/embassy-stm32/src/dts/tsel.rs
@@ -0,0 +1,51 @@
+/// Trigger selection for H5
+#[cfg(stm32h5)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum TriggerSel {
+    /// Software triggering. Performs continuous measurements.
+    Software = 0,
+    /// LPTIM1 CH1
+    Lptim1 = 1,
+    /// LPTIM2 CH1
+    Lptim2 = 2,
+    /// LPTIM3 CH1
+    #[cfg(not(stm32h503))]
+    Lptim3 = 3,
+    /// EXTI13
+    Exti13 = 4,
+}
+
+/// Trigger selection for H7, except for H7R and H7S
+#[cfg(stm32h7)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum TriggerSel {
+    /// Software triggering. Performs continuous measurements.
+    Software = 0,
+    /// LPTIM1 OUT
+    Lptim1 = 1,
+    /// LPTIM2 OUT
+    Lptim2 = 2,
+    /// LPTIM3 OUT
+    Lptim3 = 3,
+    /// EXTI13
+    Exti13 = 4,
+}
+
+/// Trigger selection for H7R and H7S
+#[cfg(stm32h7rs)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum TriggerSel {
+    /// Software triggering. Performs continuous measurements.
+    Software = 0,
+    /// LPTIM4 OUT
+    Lptim4 = 1,
+    /// LPTIM2 CH1
+    Lptim2 = 2,
+    /// LPTIM3 CH1
+    Lptim3 = 3,
+    /// EXTI13
+    Exti13 = 4,
+}
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index d04199d05d..61da754c30 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -68,6 +68,8 @@ pub mod dac;
 pub mod dcmi;
 #[cfg(dsihost)]
 pub mod dsihost;
+#[cfg(dts)]
+pub mod dts;
 #[cfg(eth)]
 pub mod eth;
 #[cfg(feature = "exti")]
diff --git a/examples/stm32h5/src/bin/dts.rs b/examples/stm32h5/src/bin/dts.rs
new file mode 100644
index 0000000000..8c18fafea1
--- /dev/null
+++ b/examples/stm32h5/src/bin/dts.rs
@@ -0,0 +1,75 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_stm32::dts::{Dts, InterruptHandler, SampleTime};
+use embassy_stm32::peripherals::DTS;
+use embassy_stm32::rcc::frequency;
+use embassy_stm32::{bind_interrupts, dts, Config};
+use embassy_time::Timer;
+use {defmt_rtt as _, panic_probe as _};
+
+bind_interrupts!(struct Irqs {
+    DTS => InterruptHandler;
+});
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) {
+    let mut config = Config::default();
+    {
+        use embassy_stm32::rcc::*;
+        config.rcc.hsi = Some(HSIPrescaler::DIV1);
+        config.rcc.csi = true;
+        config.rcc.pll1 = Some(Pll {
+            source: PllSource::HSI,
+            prediv: PllPreDiv::DIV4,
+            mul: PllMul::MUL25,
+            divp: Some(PllDiv::DIV2),
+            divq: Some(PllDiv::DIV4), // SPI1 cksel defaults to pll1_q
+            divr: None,
+        });
+        config.rcc.pll2 = Some(Pll {
+            source: PllSource::HSI,
+            prediv: PllPreDiv::DIV4,
+            mul: PllMul::MUL25,
+            divp: None,
+            divq: None,
+            divr: Some(PllDiv::DIV4), // 100mhz
+        });
+        config.rcc.sys = Sysclk::PLL1_P; // 200 Mhz
+        config.rcc.ahb_pre = AHBPrescaler::DIV1; // 200 Mhz
+        config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz
+        config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz
+        config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz
+        config.rcc.voltage_scale = VoltageScale::Scale1;
+        config.rcc.mux.adcdacsel = mux::Adcdacsel::PLL2_R;
+    }
+    let p = embassy_stm32::init(config);
+
+    info!("Hello World!");
+
+    let mut config = dts::Config::default();
+    config.sample_time = SampleTime::ClockCycles15;
+    let mut dts = Dts::new(p.DTS, Irqs, config);
+
+    let cal = Dts::factory_calibration();
+    let convert_to_celsius = |raw_temp: u16| {
+        let raw_temp = raw_temp as f32;
+        let sample_time = (config.sample_time as u8) as f32;
+
+        let f = frequency::<DTS>().0 as f32;
+
+        let t0 = cal.t0 as f32;
+        let fmt0 = cal.fmt0.0 as f32;
+        let ramp_coeff = cal.ramp_coeff as f32;
+
+        ((f * sample_time / raw_temp) - fmt0) / ramp_coeff + t0
+    };
+
+    loop {
+        let temp = dts.read().await;
+        info!("Temp: {} degrees", convert_to_celsius(temp));
+        Timer::after_millis(500).await;
+    }
+}