diff --git a/ci.sh b/ci.sh
index cd82af2f1..d17f4e13e 100755
--- a/ci.sh
+++ b/ci.sh
@@ -124,6 +124,7 @@ cargo batch \
     --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,time \
     --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h725re,defmt,exti,time-driver-any,time \
     --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7b3ai,defmt,exti,time-driver-any,time \
+    --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7b3ai,defmt,exti,time-driver-tim1,time \
     --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l431cb,defmt,exti,time-driver-any,time \
     --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,time \
     --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l422cb,defmt,exti,time-driver-any,time \
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index e78f81dca..c961fc14b 100644
--- a/embassy-stm32/src/time_driver.rs
+++ b/embassy-stm32/src/time_driver.rs
@@ -8,7 +8,7 @@ use critical_section::CriticalSection;
 use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
 use embassy_sync::blocking_mutex::Mutex;
 use embassy_time_driver::{AlarmHandle, Driver, TICK_HZ};
-use stm32_metapac::timer::regs;
+use stm32_metapac::timer::{regs, TimGp16};
 
 use crate::interrupt::typelevel::Interrupt;
 use crate::pac::timer::vals;
@@ -16,8 +16,8 @@ use crate::rcc::sealed::RccPeripheral;
 #[cfg(feature = "low-power")]
 use crate::rtc::Rtc;
 #[cfg(any(time_driver_tim1, time_driver_tim8, time_driver_tim20))]
-use crate::timer::sealed::AdvancedControlInstance;
-use crate::timer::sealed::{CoreInstance, GeneralPurpose16bitInstance as Instance};
+use crate::timer::AdvancedInstance1Channel;
+use crate::timer::CoreInstance;
 use crate::{interrupt, peripherals};
 
 // NOTE regarding ALARM_COUNT:
@@ -207,6 +207,10 @@ foreach_interrupt! {
     };
 }
 
+fn regs_gp16() -> TimGp16 {
+    unsafe { TimGp16::from_ptr(T::regs()) }
+}
+
 // Clock timekeeping works with something we call "periods", which are time intervals
 // of 2^15 ticks. The Clock counter value is 16 bits, so one "overflow cycle" is 2 periods.
 //
@@ -271,7 +275,7 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
 
 impl RtcDriver {
     fn init(&'static self, cs: critical_section::CriticalSection) {
-        let r = T::regs_gp16();
+        let r = regs_gp16();
 
         <T as RccPeripheral>::enable_and_reset_with_cs(cs);
 
@@ -308,9 +312,9 @@ impl RtcDriver {
 
         #[cfg(any(time_driver_tim1, time_driver_tim8, time_driver_tim20))]
         {
-            <T as AdvancedControlInstance>::CaptureCompareInterrupt::unpend();
+            <T as AdvancedInstance1Channel>::CaptureCompareInterrupt::unpend();
             unsafe {
-                <T as AdvancedControlInstance>::CaptureCompareInterrupt::enable();
+                <T as AdvancedInstance1Channel>::CaptureCompareInterrupt::enable();
             }
         }
 
@@ -318,7 +322,7 @@ impl RtcDriver {
     }
 
     fn on_interrupt(&self) {
-        let r = T::regs_gp16();
+        let r = regs_gp16();
 
         // XXX: reduce the size of this critical section ?
         critical_section::with(|cs| {
@@ -349,7 +353,7 @@ impl RtcDriver {
     }
 
     fn next_period(&self) {
-        let r = T::regs_gp16();
+        let r = regs_gp16();
 
         // We only modify the period from the timer interrupt, so we know this can't race.
         let period = self.period.load(Ordering::Relaxed) + 1;
@@ -413,7 +417,7 @@ impl RtcDriver {
     /// Add the given offset to the current time
     fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) {
         let offset = offset.as_ticks();
-        let cnt = T::regs_gp16().cnt().read().cnt() as u32;
+        let cnt = regs_gp16().cnt().read().cnt() as u32;
         let period = self.period.load(Ordering::SeqCst);
 
         // Correct the race, if it exists
@@ -439,7 +443,7 @@ impl RtcDriver {
         let period = if cnt > u16::MAX as u32 / 2 { period + 1 } else { period };
 
         self.period.store(period, Ordering::SeqCst);
-        T::regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16));
+        regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16));
 
         // Now, recompute all alarms
         for i in 0..ALARM_COUNT {
@@ -496,7 +500,7 @@ impl RtcDriver {
                     .unwrap()
                     .start_wakeup_alarm(time_until_next_alarm, cs);
 
-                T::regs_gp16().cr1().modify(|w| w.set_cen(false));
+                regs_gp16().cr1().modify(|w| w.set_cen(false));
 
                 Ok(())
             }
@@ -506,7 +510,7 @@ impl RtcDriver {
     #[cfg(feature = "low-power")]
     /// Resume the timer with the given offset
     pub(crate) fn resume_time(&self) {
-        if T::regs_gp16().cr1().read().cen() {
+        if regs_gp16().cr1().read().cen() {
             // Time isn't currently stopped
 
             return;
@@ -515,14 +519,14 @@ impl RtcDriver {
         critical_section::with(|cs| {
             self.stop_wakeup_alarm(cs);
 
-            T::regs_gp16().cr1().modify(|w| w.set_cen(true));
+            regs_gp16().cr1().modify(|w| w.set_cen(true));
         })
     }
 }
 
 impl Driver for RtcDriver {
     fn now(&self) -> u64 {
-        let r = T::regs_gp16();
+        let r = regs_gp16();
 
         let period = self.period.load(Ordering::Relaxed);
         compiler_fence(Ordering::Acquire);
@@ -553,7 +557,7 @@ impl Driver for RtcDriver {
 
     fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
         critical_section::with(|cs| {
-            let r = T::regs_gp16();
+            let r = regs_gp16();
 
             let n = alarm.id() as usize;
             let alarm = self.get_alarm(cs, alarm);
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs
index 723e8506b..a892646cf 100644
--- a/embassy-stm32/src/timer/complementary_pwm.rs
+++ b/embassy-stm32/src/timer/complementary_pwm.rs
@@ -5,11 +5,15 @@ use core::marker::PhantomData;
 use embassy_hal_internal::{into_ref, PeripheralRef};
 use stm32_metapac::timer::vals::Ckd;
 
-use super::simple_pwm::*;
-use super::*;
-#[allow(unused_imports)]
-use crate::gpio::sealed::{AFType, Pin};
+use super::low_level::{CountingMode, OutputPolarity, Timer};
+use super::simple_pwm::{Ch1, Ch2, Ch3, Ch4, PwmPin};
+use super::{
+    AdvancedInstance4Channel, Channel, Channel1ComplementaryPin, Channel2ComplementaryPin, Channel3ComplementaryPin,
+    Channel4ComplementaryPin,
+};
 use crate::gpio::{AnyPin, OutputType};
+use crate::time::Hertz;
+use crate::timer::low_level::OutputCompareMode;
 use crate::Peripheral;
 
 /// Complementary PWM pin wrapper.
@@ -22,7 +26,7 @@ pub struct ComplementaryPwmPin<'d, T, C> {
 
 macro_rules! complementary_channel_impl {
     ($new_chx:ident, $channel:ident, $pin_trait:ident) => {
-        impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwmPin<'d, T, $channel> {
+        impl<'d, T: AdvancedInstance4Channel> ComplementaryPwmPin<'d, T, $channel> {
             #[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")]
             pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, output_type: OutputType) -> Self {
                 into_ref!(pin);
@@ -47,11 +51,11 @@ complementary_channel_impl!(new_ch3, Ch3, Channel3ComplementaryPin);
 complementary_channel_impl!(new_ch4, Ch4, Channel4ComplementaryPin);
 
 /// PWM driver with support for standard and complementary outputs.
-pub struct ComplementaryPwm<'d, T> {
-    inner: PeripheralRef<'d, T>,
+pub struct ComplementaryPwm<'d, T: AdvancedInstance4Channel> {
+    inner: Timer<'d, T>,
 }
 
-impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
+impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
     /// Create a new complementary PWM driver.
     #[allow(clippy::too_many_arguments)]
     pub fn new(
@@ -71,11 +75,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
     }
 
     fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self {
-        into_ref!(tim);
-
-        T::enable_and_reset();
-
-        let mut this = Self { inner: tim };
+        let mut this = Self { inner: Timer::new(tim) };
 
         this.inner.set_counting_mode(counting_mode);
         this.set_frequency(freq);
@@ -122,7 +122,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
     ///
     /// This value depends on the configured frequency and the timer's clock rate from RCC.
     pub fn get_max_duty(&self) -> u16 {
-        self.inner.get_max_compare_value() + 1
+        self.inner.get_max_compare_value() as u16 + 1
     }
 
     /// Set the duty for a given channel.
@@ -130,7 +130,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
     /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.
     pub fn set_duty(&mut self, channel: Channel, duty: u16) {
         assert!(duty <= self.get_max_duty());
-        self.inner.set_compare_value(channel, duty)
+        self.inner.set_compare_value(channel, duty as _)
     }
 
     /// Set the output polarity for a given channel.
@@ -148,7 +148,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
     }
 }
 
-impl<'d, T: ComplementaryCaptureCompare16bitInstance> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> {
+impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> {
     type Channel = Channel;
     type Time = Hertz;
     type Duty = u16;
@@ -168,16 +168,16 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> embedded_hal_02::Pwm for C
     }
 
     fn get_duty(&self, channel: Self::Channel) -> Self::Duty {
-        self.inner.get_compare_value(channel)
+        self.inner.get_compare_value(channel) as u16
     }
 
     fn get_max_duty(&self) -> Self::Duty {
-        self.inner.get_max_compare_value() + 1
+        self.inner.get_max_compare_value() as u16 + 1
     }
 
     fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) {
         assert!(duty <= self.get_max_duty());
-        self.inner.set_compare_value(channel, duty)
+        self.inner.set_compare_value(channel, duty as u32)
     }
 
     fn set_period<P>(&mut self, period: P)
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs
new file mode 100644
index 000000000..a5d942314
--- /dev/null
+++ b/embassy-stm32/src/timer/low_level.rs
@@ -0,0 +1,638 @@
+//! Low-level timer driver.
+//!
+//! This is an unopinionated, very low-level driver for all STM32 timers. It allows direct register
+//! manipulation with the `regs_*()` methods, and has utility functions that are thin wrappers
+//! over the registers.
+//!
+//! The available functionality depends on the timer type.
+
+use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
+
+use super::*;
+use crate::pac::timer::vals;
+use crate::time::Hertz;
+
+/// Input capture mode.
+#[derive(Clone, Copy)]
+pub enum InputCaptureMode {
+    /// Rising edge only.
+    Rising,
+    /// Falling edge only.
+    Falling,
+    /// Both rising or falling edges.
+    BothEdges,
+}
+
+/// Input TI selection.
+#[derive(Clone, Copy)]
+pub enum InputTISelection {
+    /// Normal
+    Normal,
+    /// Alternate
+    Alternate,
+    /// TRC
+    TRC,
+}
+
+impl From<InputTISelection> for stm32_metapac::timer::vals::CcmrInputCcs {
+    fn from(tisel: InputTISelection) -> Self {
+        match tisel {
+            InputTISelection::Normal => stm32_metapac::timer::vals::CcmrInputCcs::TI4,
+            InputTISelection::Alternate => stm32_metapac::timer::vals::CcmrInputCcs::TI3,
+            InputTISelection::TRC => stm32_metapac::timer::vals::CcmrInputCcs::TRC,
+        }
+    }
+}
+
+/// Timer counting mode.
+#[repr(u8)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
+pub enum CountingMode {
+    #[default]
+    /// The timer counts up to the reload value and then resets back to 0.
+    EdgeAlignedUp,
+    /// The timer counts down to 0 and then resets back to the reload value.
+    EdgeAlignedDown,
+    /// The timer counts up to the reload value and then counts back to 0.
+    ///
+    /// The output compare interrupt flags of channels configured in output are
+    /// set when the counter is counting down.
+    CenterAlignedDownInterrupts,
+    /// The timer counts up to the reload value and then counts back to 0.
+    ///
+    /// The output compare interrupt flags of channels configured in output are
+    /// set when the counter is counting up.
+    CenterAlignedUpInterrupts,
+    /// The timer counts up to the reload value and then counts back to 0.
+    ///
+    /// The output compare interrupt flags of channels configured in output are
+    /// set when the counter is counting both up or down.
+    CenterAlignedBothInterrupts,
+}
+
+impl CountingMode {
+    /// Return whether this mode is edge-aligned (up or down).
+    pub fn is_edge_aligned(&self) -> bool {
+        matches!(self, CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown)
+    }
+
+    /// Return whether this mode is center-aligned.
+    pub fn is_center_aligned(&self) -> bool {
+        matches!(
+            self,
+            CountingMode::CenterAlignedDownInterrupts
+                | CountingMode::CenterAlignedUpInterrupts
+                | CountingMode::CenterAlignedBothInterrupts
+        )
+    }
+}
+
+impl From<CountingMode> for (vals::Cms, vals::Dir) {
+    fn from(value: CountingMode) -> Self {
+        match value {
+            CountingMode::EdgeAlignedUp => (vals::Cms::EDGEALIGNED, vals::Dir::UP),
+            CountingMode::EdgeAlignedDown => (vals::Cms::EDGEALIGNED, vals::Dir::DOWN),
+            CountingMode::CenterAlignedDownInterrupts => (vals::Cms::CENTERALIGNED1, vals::Dir::UP),
+            CountingMode::CenterAlignedUpInterrupts => (vals::Cms::CENTERALIGNED2, vals::Dir::UP),
+            CountingMode::CenterAlignedBothInterrupts => (vals::Cms::CENTERALIGNED3, vals::Dir::UP),
+        }
+    }
+}
+
+impl From<(vals::Cms, vals::Dir)> for CountingMode {
+    fn from(value: (vals::Cms, vals::Dir)) -> Self {
+        match value {
+            (vals::Cms::EDGEALIGNED, vals::Dir::UP) => CountingMode::EdgeAlignedUp,
+            (vals::Cms::EDGEALIGNED, vals::Dir::DOWN) => CountingMode::EdgeAlignedDown,
+            (vals::Cms::CENTERALIGNED1, _) => CountingMode::CenterAlignedDownInterrupts,
+            (vals::Cms::CENTERALIGNED2, _) => CountingMode::CenterAlignedUpInterrupts,
+            (vals::Cms::CENTERALIGNED3, _) => CountingMode::CenterAlignedBothInterrupts,
+        }
+    }
+}
+
+/// Output compare mode.
+#[derive(Clone, Copy)]
+pub enum OutputCompareMode {
+    /// The comparison between the output compare register TIMx_CCRx and
+    /// the counter TIMx_CNT has no effect on the outputs.
+    /// (this mode is used to generate a timing base).
+    Frozen,
+    /// Set channel to active level on match. OCxREF signal is forced high when the
+    /// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx).
+    ActiveOnMatch,
+    /// Set channel to inactive level on match. OCxREF signal is forced low when the
+    /// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx).
+    InactiveOnMatch,
+    /// Toggle - OCxREF toggles when TIMx_CNT=TIMx_CCRx.
+    Toggle,
+    /// Force inactive level - OCxREF is forced low.
+    ForceInactive,
+    /// Force active level - OCxREF is forced high.
+    ForceActive,
+    /// PWM mode 1 - In upcounting, channel is active as long as TIMx_CNT<TIMx_CCRx
+    /// else inactive. In downcounting, channel is inactive (OCxREF=0) as long as
+    /// TIMx_CNT>TIMx_CCRx else active (OCxREF=1).
+    PwmMode1,
+    /// PWM mode 2 - In upcounting, channel is inactive as long as
+    /// TIMx_CNT<TIMx_CCRx else active. In downcounting, channel is active as long as
+    /// TIMx_CNT>TIMx_CCRx else inactive.
+    PwmMode2,
+    // TODO: there's more modes here depending on the chip family.
+}
+
+impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm {
+    fn from(mode: OutputCompareMode) -> Self {
+        match mode {
+            OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN,
+            OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVEONMATCH,
+            OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVEONMATCH,
+            OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE,
+            OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCEINACTIVE,
+            OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCEACTIVE,
+            OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWMMODE1,
+            OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWMMODE2,
+        }
+    }
+}
+
+/// Timer output pin polarity.
+#[derive(Clone, Copy)]
+pub enum OutputPolarity {
+    /// Active high (higher duty value makes the pin spend more time high).
+    ActiveHigh,
+    /// Active low (higher duty value makes the pin spend more time low).
+    ActiveLow,
+}
+
+impl From<OutputPolarity> for bool {
+    fn from(mode: OutputPolarity) -> Self {
+        match mode {
+            OutputPolarity::ActiveHigh => false,
+            OutputPolarity::ActiveLow => true,
+        }
+    }
+}
+
+/// Low-level timer driver.
+pub struct Timer<'d, T: CoreInstance> {
+    tim: PeripheralRef<'d, T>,
+}
+
+impl<'d, T: CoreInstance> Drop for Timer<'d, T> {
+    fn drop(&mut self) {
+        T::disable()
+    }
+}
+
+impl<'d, T: CoreInstance> Timer<'d, T> {
+    /// Create a new timer driver.
+    pub fn new(tim: impl Peripheral<P = T> + 'd) -> Self {
+        into_ref!(tim);
+
+        T::enable_and_reset();
+
+        Self { tim }
+    }
+
+    /// Get access to the virutal core 16bit timer registers.
+    ///
+    /// Note: This works even if the timer is more capable, because registers
+    /// for the less capable timers are a subset. This allows writing a driver
+    /// for a given set of capabilities, and having it transparently work with
+    /// more capable timers.
+    pub fn regs_core(&self) -> crate::pac::timer::TimCore {
+        unsafe { crate::pac::timer::TimCore::from_ptr(T::regs()) }
+    }
+
+    #[cfg(not(stm32l0))]
+    fn regs_gp32_unchecked(&self) -> crate::pac::timer::TimGp32 {
+        unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) }
+    }
+
+    /// Start the timer.
+    pub fn start(&self) {
+        self.regs_core().cr1().modify(|r| r.set_cen(true));
+    }
+
+    /// Stop the timer.
+    pub fn stop(&self) {
+        self.regs_core().cr1().modify(|r| r.set_cen(false));
+    }
+
+    /// Reset the counter value to 0
+    pub fn reset(&self) {
+        self.regs_core().cnt().write(|r| r.set_cnt(0));
+    }
+
+    /// Set the frequency of how many times per second the timer counts up to the max value or down to 0.
+    ///
+    /// This means that in the default edge-aligned mode,
+    /// the timer counter will wrap around at the same frequency as is being set.
+    /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved
+    /// because it needs to count up and down.
+    pub fn set_frequency(&self, frequency: Hertz) {
+        let f = frequency.0;
+        assert!(f > 0);
+        let timer_f = T::frequency().0;
+
+        match T::BITS {
+            TimerBits::Bits16 => {
+                let pclk_ticks_per_timer_period = timer_f / f;
+                let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 16)).try_into());
+                let divide_by = pclk_ticks_per_timer_period / (u32::from(psc) + 1);
+
+                // the timer counts `0..=arr`, we want it to count `0..divide_by`
+                let arr = unwrap!(u16::try_from(divide_by - 1));
+
+                let regs = self.regs_core();
+                regs.psc().write_value(psc);
+                regs.arr().write(|r| r.set_arr(arr));
+
+                regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY));
+                regs.egr().write(|r| r.set_ug(true));
+                regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
+            }
+            #[cfg(not(stm32l0))]
+            TimerBits::Bits32 => {
+                let pclk_ticks_per_timer_period = (timer_f / f) as u64;
+                let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 32)).try_into());
+                let arr: u32 = unwrap!((pclk_ticks_per_timer_period / (psc as u64 + 1)).try_into());
+
+                let regs = self.regs_gp32_unchecked();
+                regs.psc().write_value(psc);
+                regs.arr().write_value(arr);
+
+                regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY));
+                regs.egr().write(|r| r.set_ug(true));
+                regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
+            }
+        }
+    }
+
+    /// Clear update interrupt.
+    ///
+    /// Returns whether the update interrupt flag was set.
+    pub fn clear_update_interrupt(&self) -> bool {
+        let regs = self.regs_core();
+        let sr = regs.sr().read();
+        if sr.uif() {
+            regs.sr().modify(|r| {
+                r.set_uif(false);
+            });
+            true
+        } else {
+            false
+        }
+    }
+
+    /// Enable/disable the update interrupt.
+    pub fn enable_update_interrupt(&self, enable: bool) {
+        self.regs_core().dier().modify(|r| r.set_uie(enable));
+    }
+
+    /// Enable/disable autoreload preload.
+    pub fn set_autoreload_preload(&self, enable: bool) {
+        self.regs_core().cr1().modify(|r| r.set_arpe(enable));
+    }
+
+    /// Get the timer frequency.
+    pub fn get_frequency(&self) -> Hertz {
+        let timer_f = T::frequency();
+
+        match T::BITS {
+            TimerBits::Bits16 => {
+                let regs = self.regs_core();
+                let arr = regs.arr().read().arr();
+                let psc = regs.psc().read();
+
+                timer_f / arr / (psc + 1)
+            }
+            #[cfg(not(stm32l0))]
+            TimerBits::Bits32 => {
+                let regs = self.regs_gp32_unchecked();
+                let arr = regs.arr().read();
+                let psc = regs.psc().read();
+
+                timer_f / arr / (psc + 1)
+            }
+        }
+    }
+}
+
+impl<'d, T: BasicNoCr2Instance> Timer<'d, T> {
+    /// Get access to the Baisc 16bit timer registers.
+    ///
+    /// Note: This works even if the timer is more capable, because registers
+    /// for the less capable timers are a subset. This allows writing a driver
+    /// for a given set of capabilities, and having it transparently work with
+    /// more capable timers.
+    pub fn regs_basic_no_cr2(&self) -> crate::pac::timer::TimBasicNoCr2 {
+        unsafe { crate::pac::timer::TimBasicNoCr2::from_ptr(T::regs()) }
+    }
+
+    /// Enable/disable the update dma.
+    pub fn enable_update_dma(&self, enable: bool) {
+        self.regs_basic_no_cr2().dier().modify(|r| r.set_ude(enable));
+    }
+
+    /// Get the update dma enable/disable state.
+    pub fn get_update_dma_state(&self) -> bool {
+        self.regs_basic_no_cr2().dier().read().ude()
+    }
+}
+
+impl<'d, T: BasicInstance> Timer<'d, T> {
+    /// Get access to the Baisc 16bit timer registers.
+    ///
+    /// Note: This works even if the timer is more capable, because registers
+    /// for the less capable timers are a subset. This allows writing a driver
+    /// for a given set of capabilities, and having it transparently work with
+    /// more capable timers.
+    pub fn regs_basic(&self) -> crate::pac::timer::TimBasic {
+        unsafe { crate::pac::timer::TimBasic::from_ptr(T::regs()) }
+    }
+}
+
+impl<'d, T: GeneralInstance1Channel> Timer<'d, T> {
+    /// Get access to the general purpose 1 channel 16bit timer registers.
+    ///
+    /// Note: This works even if the timer is more capable, because registers
+    /// for the less capable timers are a subset. This allows writing a driver
+    /// for a given set of capabilities, and having it transparently work with
+    /// more capable timers.
+    pub fn regs_1ch(&self) -> crate::pac::timer::Tim1ch {
+        unsafe { crate::pac::timer::Tim1ch::from_ptr(T::regs()) }
+    }
+
+    /// Set clock divider.
+    pub fn set_clock_division(&self, ckd: vals::Ckd) {
+        self.regs_1ch().cr1().modify(|r| r.set_ckd(ckd));
+    }
+
+    /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC.
+    pub fn get_max_compare_value(&self) -> u32 {
+        match T::BITS {
+            TimerBits::Bits16 => self.regs_1ch().arr().read().arr() as u32,
+            #[cfg(not(stm32l0))]
+            TimerBits::Bits32 => self.regs_gp32_unchecked().arr().read(),
+        }
+    }
+}
+
+impl<'d, T: GeneralInstance2Channel> Timer<'d, T> {
+    /// Get access to the general purpose 2 channel 16bit timer registers.
+    ///
+    /// Note: This works even if the timer is more capable, because registers
+    /// for the less capable timers are a subset. This allows writing a driver
+    /// for a given set of capabilities, and having it transparently work with
+    /// more capable timers.
+    pub fn regs_2ch(&self) -> crate::pac::timer::Tim2ch {
+        unsafe { crate::pac::timer::Tim2ch::from_ptr(T::regs()) }
+    }
+}
+
+impl<'d, T: GeneralInstance4Channel> Timer<'d, T> {
+    /// Get access to the general purpose 16bit timer registers.
+    ///
+    /// Note: This works even if the timer is more capable, because registers
+    /// for the less capable timers are a subset. This allows writing a driver
+    /// for a given set of capabilities, and having it transparently work with
+    /// more capable timers.
+    pub fn regs_gp16(&self) -> crate::pac::timer::TimGp16 {
+        unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }
+    }
+
+    /// Enable timer outputs.
+    pub fn enable_outputs(&self) {
+        self.tim.enable_outputs()
+    }
+
+    /// Set counting mode.
+    pub fn set_counting_mode(&self, mode: CountingMode) {
+        let (cms, dir) = mode.into();
+
+        let timer_enabled = self.regs_core().cr1().read().cen();
+        // Changing from edge aligned to center aligned (and vice versa) is not allowed while the timer is running.
+        // Changing direction is discouraged while the timer is running.
+        assert!(!timer_enabled);
+
+        self.regs_gp16().cr1().modify(|r| r.set_dir(dir));
+        self.regs_gp16().cr1().modify(|r| r.set_cms(cms))
+    }
+
+    /// Get counting mode.
+    pub fn get_counting_mode(&self) -> CountingMode {
+        let cr1 = self.regs_gp16().cr1().read();
+        (cr1.cms(), cr1.dir()).into()
+    }
+
+    /// Set input capture filter.
+    pub fn set_input_capture_filter(&self, channel: Channel, icf: vals::FilterValue) {
+        let raw_channel = channel.index();
+        self.regs_gp16()
+            .ccmr_input(raw_channel / 2)
+            .modify(|r| r.set_icf(raw_channel % 2, icf));
+    }
+
+    /// Clear input interrupt.
+    pub fn clear_input_interrupt(&self, channel: Channel) {
+        self.regs_gp16().sr().modify(|r| r.set_ccif(channel.index(), false));
+    }
+
+    /// Enable input interrupt.
+    pub fn enable_input_interrupt(&self, channel: Channel, enable: bool) {
+        self.regs_gp16().dier().modify(|r| r.set_ccie(channel.index(), enable));
+    }
+
+    /// Set input capture prescaler.
+    pub fn set_input_capture_prescaler(&self, channel: Channel, factor: u8) {
+        let raw_channel = channel.index();
+        self.regs_gp16()
+            .ccmr_input(raw_channel / 2)
+            .modify(|r| r.set_icpsc(raw_channel % 2, factor));
+    }
+
+    /// Set input TI selection.
+    pub fn set_input_ti_selection(&self, channel: Channel, tisel: InputTISelection) {
+        let raw_channel = channel.index();
+        self.regs_gp16()
+            .ccmr_input(raw_channel / 2)
+            .modify(|r| r.set_ccs(raw_channel % 2, tisel.into()));
+    }
+
+    /// Set input capture mode.
+    pub fn set_input_capture_mode(&self, channel: Channel, mode: InputCaptureMode) {
+        self.regs_gp16().ccer().modify(|r| match mode {
+            InputCaptureMode::Rising => {
+                r.set_ccnp(channel.index(), false);
+                r.set_ccp(channel.index(), false);
+            }
+            InputCaptureMode::Falling => {
+                r.set_ccnp(channel.index(), false);
+                r.set_ccp(channel.index(), true);
+            }
+            InputCaptureMode::BothEdges => {
+                r.set_ccnp(channel.index(), true);
+                r.set_ccp(channel.index(), true);
+            }
+        });
+    }
+
+    /// Set output compare mode.
+    pub fn set_output_compare_mode(&self, channel: Channel, mode: OutputCompareMode) {
+        let raw_channel: usize = channel.index();
+        self.regs_gp16()
+            .ccmr_output(raw_channel / 2)
+            .modify(|w| w.set_ocm(raw_channel % 2, mode.into()));
+    }
+
+    /// Set output polarity.
+    pub fn set_output_polarity(&self, channel: Channel, polarity: OutputPolarity) {
+        self.regs_gp16()
+            .ccer()
+            .modify(|w| w.set_ccp(channel.index(), polarity.into()));
+    }
+
+    /// Enable/disable a channel.
+    pub fn enable_channel(&self, channel: Channel, enable: bool) {
+        self.regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable));
+    }
+
+    /// Get enable/disable state of a channel
+    pub fn get_channel_enable_state(&self, channel: Channel) -> bool {
+        self.regs_gp16().ccer().read().cce(channel.index())
+    }
+
+    /// Set compare value for a channel.
+    pub fn set_compare_value(&self, channel: Channel, value: u32) {
+        match T::BITS {
+            TimerBits::Bits16 => {
+                let value = unwrap!(u16::try_from(value));
+                self.regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value));
+            }
+            #[cfg(not(stm32l0))]
+            TimerBits::Bits32 => {
+                self.regs_gp32_unchecked().ccr(channel.index()).write_value(value);
+            }
+        }
+    }
+
+    /// Get compare value for a channel.
+    pub fn get_compare_value(&self, channel: Channel) -> u32 {
+        match T::BITS {
+            TimerBits::Bits16 => self.regs_gp16().ccr(channel.index()).read().ccr() as u32,
+            #[cfg(not(stm32l0))]
+            TimerBits::Bits32 => self.regs_gp32_unchecked().ccr(channel.index()).read(),
+        }
+    }
+
+    /// Get capture value for a channel.
+    pub fn get_capture_value(&self, channel: Channel) -> u32 {
+        self.get_compare_value(channel)
+    }
+
+    /// Set output compare preload.
+    pub fn set_output_compare_preload(&self, channel: Channel, preload: bool) {
+        let channel_index = channel.index();
+        self.regs_gp16()
+            .ccmr_output(channel_index / 2)
+            .modify(|w| w.set_ocpe(channel_index % 2, preload));
+    }
+
+    /// Get capture compare DMA selection
+    pub fn get_cc_dma_selection(&self) -> vals::Ccds {
+        self.regs_gp16().cr2().read().ccds()
+    }
+
+    /// Set capture compare DMA selection
+    pub fn set_cc_dma_selection(&self, ccds: vals::Ccds) {
+        self.regs_gp16().cr2().modify(|w| w.set_ccds(ccds))
+    }
+
+    /// Get capture compare DMA enable state
+    pub fn get_cc_dma_enable_state(&self, channel: Channel) -> bool {
+        self.regs_gp16().dier().read().ccde(channel.index())
+    }
+
+    /// Set capture compare DMA enable state
+    pub fn set_cc_dma_enable_state(&self, channel: Channel, ccde: bool) {
+        self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde))
+    }
+}
+
+#[cfg(not(stm32l0))]
+impl<'d, T: GeneralInstance32bit4Channel> Timer<'d, T> {
+    /// Get access to the general purpose 32bit timer registers.
+    ///
+    /// Note: This works even if the timer is more capable, because registers
+    /// for the less capable timers are a subset. This allows writing a driver
+    /// for a given set of capabilities, and having it transparently work with
+    /// more capable timers.
+    pub fn regs_gp32(&self) -> crate::pac::timer::TimGp32 {
+        unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) }
+    }
+}
+
+#[cfg(not(stm32l0))]
+impl<'d, T: AdvancedInstance1Channel> Timer<'d, T> {
+    /// Get access to the general purpose 1 channel with one complementary 16bit timer registers.
+    ///
+    /// Note: This works even if the timer is more capable, because registers
+    /// for the less capable timers are a subset. This allows writing a driver
+    /// for a given set of capabilities, and having it transparently work with
+    /// more capable timers.
+    pub fn regs_1ch_cmp(&self) -> crate::pac::timer::Tim1chCmp {
+        unsafe { crate::pac::timer::Tim1chCmp::from_ptr(T::regs()) }
+    }
+
+    /// Set clock divider for the dead time.
+    pub fn set_dead_time_clock_division(&self, value: vals::Ckd) {
+        self.regs_1ch_cmp().cr1().modify(|w| w.set_ckd(value));
+    }
+
+    /// Set dead time, as a fraction of the max duty value.
+    pub fn set_dead_time_value(&self, value: u8) {
+        self.regs_1ch_cmp().bdtr().modify(|w| w.set_dtg(value));
+    }
+
+    /// Set state of MOE-bit in BDTR register to en-/disable output
+    pub fn set_moe(&self, enable: bool) {
+        self.regs_1ch_cmp().bdtr().modify(|w| w.set_moe(enable));
+    }
+}
+
+#[cfg(not(stm32l0))]
+impl<'d, T: AdvancedInstance2Channel> Timer<'d, T> {
+    /// Get access to the general purpose 2 channel with one complementary 16bit timer registers.
+    ///
+    /// Note: This works even if the timer is more capable, because registers
+    /// for the less capable timers are a subset. This allows writing a driver
+    /// for a given set of capabilities, and having it transparently work with
+    /// more capable timers.
+    pub fn regs_2ch_cmp(&self) -> crate::pac::timer::Tim2chCmp {
+        unsafe { crate::pac::timer::Tim2chCmp::from_ptr(T::regs()) }
+    }
+}
+
+#[cfg(not(stm32l0))]
+impl<'d, T: AdvancedInstance4Channel> Timer<'d, T> {
+    /// Get access to the advanced timer registers.
+    pub fn regs_advanced(&self) -> crate::pac::timer::TimAdv {
+        unsafe { crate::pac::timer::TimAdv::from_ptr(T::regs()) }
+    }
+
+    /// Set complementary output polarity.
+    pub fn set_complementary_output_polarity(&self, channel: Channel, polarity: OutputPolarity) {
+        self.regs_advanced()
+            .ccer()
+            .modify(|w| w.set_ccnp(channel.index(), polarity.into()));
+    }
+
+    /// Enable/disable a complementary channel.
+    pub fn enable_complementary_channel(&self, channel: Channel, enable: bool) {
+        self.regs_advanced()
+            .ccer()
+            .modify(|w| w.set_ccne(channel.index(), enable));
+    }
+}
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs
index e5e84c255..2ba6b3f11 100644
--- a/embassy-stm32/src/timer/mod.rs
+++ b/embassy-stm32/src/timer/mod.rs
@@ -1,490 +1,13 @@
 //! Timers, PWM, quadrature decoder.
-//!
-
-//! Timer inheritance
-//!
-
-// sealed:
-//
-// Core -------------------------> 1CH -------------------------> 1CH_CMP
-//   |                              |                              ^   |
-//   +--> Basic_NoCr2 --> Basic     +--> 2CH --> GP16 --> GP32     |   +--> 2CH_CMP --> ADV
-//            |             |             |      ^  |              |           ^         ^
-//            |             |             +------|--|--------------|-----------+         |
-//            |             +--------------------+  +--------------|-----------|---------+
-//            |             |                                      |           |
-//            |             +--------------------------------------|-----------+
-//            +----------------------------------------------------+
-
-//! ```text
-//! BasicInstance --> CaptureCompare16bitInstance --+--> ComplementaryCaptureCompare16bitInstance
-//!                                                 |
-//!                                                 +--> CaptureCompare32bitInstance
-//! ```
-//!
-//! Mapping:
-//!
-//! |                   trait                    | timer                                                                                             |
-//! | :----------------------------------------: | ------------------------------------------------------------------------------------------------- |
-//! |              [BasicInstance]               | Basic Timer                                                                                       |
-//! |       [CaptureCompare16bitInstance]        | 1-channel Timer, 2-channel Timer, General Purpose 16-bit Timer                                    |
-//! |       [CaptureCompare32bitInstance]        | General Purpose 32-bit Timer                                                                      |
-//! | [ComplementaryCaptureCompare16bitInstance] | 1-channel with one complentary Timer, 2-channel with one complentary Timer, Advance Control Timer |
 
 #[cfg(not(stm32l0))]
 pub mod complementary_pwm;
+pub mod low_level;
 pub mod qei;
 pub mod simple_pwm;
 
-use stm32_metapac::timer::vals;
-
 use crate::interrupt;
 use crate::rcc::RccPeripheral;
-use crate::time::Hertz;
-
-/// Low-level timer access.
-#[cfg(feature = "unstable-pac")]
-pub mod low_level {
-    pub use super::sealed::*;
-}
-
-pub(crate) mod sealed {
-    use super::*;
-
-    /// Virtual Core 16-bit timer instance.  
-    pub trait CoreInstance: RccPeripheral {
-        /// Interrupt for this timer.
-        type Interrupt: interrupt::typelevel::Interrupt;
-
-        /// Get access to the virutal core 16bit timer registers.
-        ///
-        /// Note: This works even if the timer is more capable, because registers
-        /// for the less capable timers are a subset. This allows writing a driver
-        /// for a given set of capabilities, and having it transparently work with
-        /// more capable timers.
-        fn regs_core() -> crate::pac::timer::TimCore;
-
-        /// Start the timer.
-        fn start(&self) {
-            Self::regs_core().cr1().modify(|r| r.set_cen(true));
-        }
-
-        /// Stop the timer.
-        fn stop(&self) {
-            Self::regs_core().cr1().modify(|r| r.set_cen(false));
-        }
-
-        /// Reset the counter value to 0
-        fn reset(&self) {
-            Self::regs_core().cnt().write(|r| r.set_cnt(0));
-        }
-
-        /// Set the frequency of how many times per second the timer counts up to the max value or down to 0.
-        ///
-        /// This means that in the default edge-aligned mode,
-        /// the timer counter will wrap around at the same frequency as is being set.
-        /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved
-        /// because it needs to count up and down.
-        fn set_frequency(&self, frequency: Hertz) {
-            let f = frequency.0;
-            let timer_f = Self::frequency().0;
-            assert!(f > 0);
-            let pclk_ticks_per_timer_period = timer_f / f;
-            let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 16)).try_into());
-            let divide_by = pclk_ticks_per_timer_period / (u32::from(psc) + 1);
-
-            // the timer counts `0..=arr`, we want it to count `0..divide_by`
-            let arr = unwrap!(u16::try_from(divide_by - 1));
-
-            let regs = Self::regs_core();
-            regs.psc().write_value(psc);
-            regs.arr().write(|r| r.set_arr(arr));
-
-            regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY));
-            regs.egr().write(|r| r.set_ug(true));
-            regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
-        }
-
-        /// Clear update interrupt.
-        ///
-        /// Returns whether the update interrupt flag was set.
-        fn clear_update_interrupt(&self) -> bool {
-            let regs = Self::regs_core();
-            let sr = regs.sr().read();
-            if sr.uif() {
-                regs.sr().modify(|r| {
-                    r.set_uif(false);
-                });
-                true
-            } else {
-                false
-            }
-        }
-
-        /// Enable/disable the update interrupt.
-        fn enable_update_interrupt(&self, enable: bool) {
-            Self::regs_core().dier().modify(|r| r.set_uie(enable));
-        }
-
-        /// Enable/disable autoreload preload.
-        fn set_autoreload_preload(&self, enable: bool) {
-            Self::regs_core().cr1().modify(|r| r.set_arpe(enable));
-        }
-
-        /// Get the timer frequency.
-        fn get_frequency(&self) -> Hertz {
-            let timer_f = Self::frequency();
-
-            let regs = Self::regs_core();
-            let arr = regs.arr().read().arr();
-            let psc = regs.psc().read();
-
-            timer_f / arr / (psc + 1)
-        }
-    }
-
-    /// Virtual Basic without CR2 16-bit timer instance.
-    pub trait BasicNoCr2Instance: CoreInstance {
-        /// Get access to the Baisc 16bit timer registers.
-        ///
-        /// Note: This works even if the timer is more capable, because registers
-        /// for the less capable timers are a subset. This allows writing a driver
-        /// for a given set of capabilities, and having it transparently work with
-        /// more capable timers.
-        fn regs_basic_no_cr2() -> crate::pac::timer::TimBasicNoCr2;
-
-        /// Enable/disable the update dma.
-        fn enable_update_dma(&self, enable: bool) {
-            Self::regs_basic_no_cr2().dier().modify(|r| r.set_ude(enable));
-        }
-
-        /// Get the update dma enable/disable state.
-        fn get_update_dma_state(&self) -> bool {
-            Self::regs_basic_no_cr2().dier().read().ude()
-        }
-    }
-
-    /// Basic 16-bit timer instance.
-    pub trait BasicInstance: BasicNoCr2Instance {
-        /// Get access to the Baisc 16bit timer registers.
-        ///
-        /// Note: This works even if the timer is more capable, because registers
-        /// for the less capable timers are a subset. This allows writing a driver
-        /// for a given set of capabilities, and having it transparently work with
-        /// more capable timers.
-        fn regs_basic() -> crate::pac::timer::TimBasic;
-    }
-
-    /// Gneral-purpose 1 channel 16-bit timer instance.
-    pub trait GeneralPurpose1ChannelInstance: CoreInstance {
-        /// Get access to the general purpose 1 channel 16bit timer registers.
-        ///
-        /// Note: This works even if the timer is more capable, because registers
-        /// for the less capable timers are a subset. This allows writing a driver
-        /// for a given set of capabilities, and having it transparently work with
-        /// more capable timers.
-        fn regs_1ch() -> crate::pac::timer::Tim1ch;
-
-        /// Set clock divider.
-        fn set_clock_division(&self, ckd: vals::Ckd) {
-            Self::regs_1ch().cr1().modify(|r| r.set_ckd(ckd));
-        }
-
-        /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC.
-        fn get_max_compare_value(&self) -> u16 {
-            Self::regs_1ch().arr().read().arr()
-        }
-    }
-
-    /// Gneral-purpose 1 channel 16-bit  timer instance.
-    pub trait GeneralPurpose2ChannelInstance: GeneralPurpose1ChannelInstance {
-        /// Get access to the general purpose 2 channel 16bit timer registers.
-        ///
-        /// Note: This works even if the timer is more capable, because registers
-        /// for the less capable timers are a subset. This allows writing a driver
-        /// for a given set of capabilities, and having it transparently work with
-        /// more capable timers.
-        fn regs_2ch() -> crate::pac::timer::Tim2ch;
-    }
-
-    /// Gneral-purpose 16-bit timer instance.
-    pub trait GeneralPurpose16bitInstance: BasicInstance + GeneralPurpose2ChannelInstance {
-        /// Get access to the general purpose 16bit timer registers.
-        ///
-        /// Note: This works even if the timer is more capable, because registers
-        /// for the less capable timers are a subset. This allows writing a driver
-        /// for a given set of capabilities, and having it transparently work with
-        /// more capable timers.
-        fn regs_gp16() -> crate::pac::timer::TimGp16;
-
-        /// Set counting mode.
-        fn set_counting_mode(&self, mode: CountingMode) {
-            let (cms, dir) = mode.into();
-
-            let timer_enabled = Self::regs_core().cr1().read().cen();
-            // Changing from edge aligned to center aligned (and vice versa) is not allowed while the timer is running.
-            // Changing direction is discouraged while the timer is running.
-            assert!(!timer_enabled);
-
-            Self::regs_gp16().cr1().modify(|r| r.set_dir(dir));
-            Self::regs_gp16().cr1().modify(|r| r.set_cms(cms))
-        }
-
-        /// Get counting mode.
-        fn get_counting_mode(&self) -> CountingMode {
-            let cr1 = Self::regs_gp16().cr1().read();
-            (cr1.cms(), cr1.dir()).into()
-        }
-
-        /// Set input capture filter.
-        fn set_input_capture_filter(&self, channel: Channel, icf: vals::FilterValue) {
-            let raw_channel = channel.index();
-            Self::regs_gp16()
-                .ccmr_input(raw_channel / 2)
-                .modify(|r| r.set_icf(raw_channel % 2, icf));
-        }
-
-        /// Clear input interrupt.
-        fn clear_input_interrupt(&self, channel: Channel) {
-            Self::regs_gp16().sr().modify(|r| r.set_ccif(channel.index(), false));
-        }
-
-        /// Enable input interrupt.
-        fn enable_input_interrupt(&self, channel: Channel, enable: bool) {
-            Self::regs_gp16().dier().modify(|r| r.set_ccie(channel.index(), enable));
-        }
-
-        /// Set input capture prescaler.
-        fn set_input_capture_prescaler(&self, channel: Channel, factor: u8) {
-            let raw_channel = channel.index();
-            Self::regs_gp16()
-                .ccmr_input(raw_channel / 2)
-                .modify(|r| r.set_icpsc(raw_channel % 2, factor));
-        }
-
-        /// Set input TI selection.
-        fn set_input_ti_selection(&self, channel: Channel, tisel: InputTISelection) {
-            let raw_channel = channel.index();
-            Self::regs_gp16()
-                .ccmr_input(raw_channel / 2)
-                .modify(|r| r.set_ccs(raw_channel % 2, tisel.into()));
-        }
-
-        /// Set input capture mode.
-        fn set_input_capture_mode(&self, channel: Channel, mode: InputCaptureMode) {
-            Self::regs_gp16().ccer().modify(|r| match mode {
-                InputCaptureMode::Rising => {
-                    r.set_ccnp(channel.index(), false);
-                    r.set_ccp(channel.index(), false);
-                }
-                InputCaptureMode::Falling => {
-                    r.set_ccnp(channel.index(), false);
-                    r.set_ccp(channel.index(), true);
-                }
-                InputCaptureMode::BothEdges => {
-                    r.set_ccnp(channel.index(), true);
-                    r.set_ccp(channel.index(), true);
-                }
-            });
-        }
-
-        /// Set output compare mode.
-        fn set_output_compare_mode(&self, channel: Channel, mode: OutputCompareMode) {
-            let raw_channel: usize = channel.index();
-            Self::regs_gp16()
-                .ccmr_output(raw_channel / 2)
-                .modify(|w| w.set_ocm(raw_channel % 2, mode.into()));
-        }
-
-        /// Set output polarity.
-        fn set_output_polarity(&self, channel: Channel, polarity: OutputPolarity) {
-            Self::regs_gp16()
-                .ccer()
-                .modify(|w| w.set_ccp(channel.index(), polarity.into()));
-        }
-
-        /// Enable/disable a channel.
-        fn enable_channel(&self, channel: Channel, enable: bool) {
-            Self::regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable));
-        }
-
-        /// Get enable/disable state of a channel
-        fn get_channel_enable_state(&self, channel: Channel) -> bool {
-            Self::regs_gp16().ccer().read().cce(channel.index())
-        }
-
-        /// Set compare value for a channel.
-        fn set_compare_value(&self, channel: Channel, value: u16) {
-            Self::regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value));
-        }
-
-        /// Get capture value for a channel.
-        fn get_capture_value(&self, channel: Channel) -> u16 {
-            Self::regs_gp16().ccr(channel.index()).read().ccr()
-        }
-
-        /// Get compare value for a channel.
-        fn get_compare_value(&self, channel: Channel) -> u16 {
-            Self::regs_gp16().ccr(channel.index()).read().ccr()
-        }
-
-        /// Set output compare preload.
-        fn set_output_compare_preload(&self, channel: Channel, preload: bool) {
-            let channel_index = channel.index();
-            Self::regs_gp16()
-                .ccmr_output(channel_index / 2)
-                .modify(|w| w.set_ocpe(channel_index % 2, preload));
-        }
-
-        /// Get capture compare DMA selection
-        fn get_cc_dma_selection(&self) -> super::vals::Ccds {
-            Self::regs_gp16().cr2().read().ccds()
-        }
-
-        /// Set capture compare DMA selection
-        fn set_cc_dma_selection(&self, ccds: super::vals::Ccds) {
-            Self::regs_gp16().cr2().modify(|w| w.set_ccds(ccds))
-        }
-
-        /// Get capture compare DMA enable state
-        fn get_cc_dma_enable_state(&self, channel: Channel) -> bool {
-            Self::regs_gp16().dier().read().ccde(channel.index())
-        }
-
-        /// Set capture compare DMA enable state
-        fn set_cc_dma_enable_state(&self, channel: Channel, ccde: bool) {
-            Self::regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde))
-        }
-    }
-
-    #[cfg(not(stm32l0))]
-    /// Gneral-purpose 32-bit timer instance.
-    pub trait GeneralPurpose32bitInstance: GeneralPurpose16bitInstance {
-        /// Get access to the general purpose 32bit timer registers.
-        ///
-        /// Note: This works even if the timer is more capable, because registers
-        /// for the less capable timers are a subset. This allows writing a driver
-        /// for a given set of capabilities, and having it transparently work with
-        /// more capable timers.
-        fn regs_gp32() -> crate::pac::timer::TimGp32;
-
-        /// Set timer frequency.
-        fn set_frequency(&self, frequency: Hertz) {
-            let f = frequency.0;
-            assert!(f > 0);
-            let timer_f = Self::frequency().0;
-            let pclk_ticks_per_timer_period = (timer_f / f) as u64;
-            let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 32)).try_into());
-            let arr: u32 = unwrap!((pclk_ticks_per_timer_period / (psc as u64 + 1)).try_into());
-
-            let regs = Self::regs_gp32();
-            regs.psc().write_value(psc);
-            regs.arr().write_value(arr);
-
-            regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY));
-            regs.egr().write(|r| r.set_ug(true));
-            regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
-        }
-
-        /// Get timer frequency.
-        fn get_frequency(&self) -> Hertz {
-            let timer_f = Self::frequency();
-
-            let regs = Self::regs_gp32();
-            let arr = regs.arr().read();
-            let psc = regs.psc().read();
-
-            timer_f / arr / (psc + 1)
-        }
-
-        /// Set comapre value for a channel.
-        fn set_compare_value(&self, channel: Channel, value: u32) {
-            Self::regs_gp32().ccr(channel.index()).write_value(value);
-        }
-
-        /// Get capture value for a channel.
-        fn get_capture_value(&self, channel: Channel) -> u32 {
-            Self::regs_gp32().ccr(channel.index()).read()
-        }
-
-        /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC.
-        fn get_max_compare_value(&self) -> u32 {
-            Self::regs_gp32().arr().read()
-        }
-
-        /// Get compare value for a channel.
-        fn get_compare_value(&self, channel: Channel) -> u32 {
-            Self::regs_gp32().ccr(channel.index()).read()
-        }
-    }
-
-    #[cfg(not(stm32l0))]
-    /// Gneral-purpose 1 channel with one complementary 16-bit timer instance.
-    pub trait GeneralPurpose1ChannelComplementaryInstance: BasicNoCr2Instance + GeneralPurpose1ChannelInstance {
-        /// Get access to the general purpose 1 channel with one complementary 16bit timer registers.
-        ///
-        /// Note: This works even if the timer is more capable, because registers
-        /// for the less capable timers are a subset. This allows writing a driver
-        /// for a given set of capabilities, and having it transparently work with
-        /// more capable timers.
-        fn regs_1ch_cmp() -> crate::pac::timer::Tim1chCmp;
-
-        /// Set clock divider for the dead time.
-        fn set_dead_time_clock_division(&self, value: vals::Ckd) {
-            Self::regs_1ch_cmp().cr1().modify(|w| w.set_ckd(value));
-        }
-
-        /// Set dead time, as a fraction of the max duty value.
-        fn set_dead_time_value(&self, value: u8) {
-            Self::regs_1ch_cmp().bdtr().modify(|w| w.set_dtg(value));
-        }
-
-        /// Set state of MOE-bit in BDTR register to en-/disable output
-        fn set_moe(&self, enable: bool) {
-            Self::regs_1ch_cmp().bdtr().modify(|w| w.set_moe(enable));
-        }
-    }
-
-    #[cfg(not(stm32l0))]
-    /// Gneral-purpose 2 channel with one complementary 16-bit timer instance.
-    pub trait GeneralPurpose2ChannelComplementaryInstance:
-        BasicInstance + GeneralPurpose2ChannelInstance + GeneralPurpose1ChannelComplementaryInstance
-    {
-        /// Get access to the general purpose 2 channel with one complementary 16bit timer registers.
-        ///
-        /// Note: This works even if the timer is more capable, because registers
-        /// for the less capable timers are a subset. This allows writing a driver
-        /// for a given set of capabilities, and having it transparently work with
-        /// more capable timers.
-        fn regs_2ch_cmp() -> crate::pac::timer::Tim2chCmp;
-    }
-
-    #[cfg(not(stm32l0))]
-    /// Advanced control timer instance.
-    pub trait AdvancedControlInstance:
-        GeneralPurpose2ChannelComplementaryInstance + GeneralPurpose16bitInstance
-    {
-        /// Capture compare interrupt for this timer.
-        type CaptureCompareInterrupt: interrupt::typelevel::Interrupt;
-
-        /// Get access to the advanced timer registers.
-        fn regs_advanced() -> crate::pac::timer::TimAdv;
-
-        /// Set complementary output polarity.
-        fn set_complementary_output_polarity(&self, channel: Channel, polarity: OutputPolarity) {
-            Self::regs_advanced()
-                .ccer()
-                .modify(|w| w.set_ccnp(channel.index(), polarity.into()));
-        }
-
-        /// Enable/disable a complementary channel.
-        fn enable_complementary_channel(&self, channel: Channel, enable: bool) {
-            Self::regs_advanced()
-                .ccer()
-                .modify(|w| w.set_ccne(channel.index(), enable));
-        }
-    }
-}
 
 /// Timer channel.
 #[derive(Clone, Copy)]
@@ -511,181 +34,44 @@ impl Channel {
     }
 }
 
-/// Input capture mode.
-#[derive(Clone, Copy)]
-pub enum InputCaptureMode {
-    /// Rising edge only.
-    Rising,
-    /// Falling edge only.
-    Falling,
-    /// Both rising or falling edges.
-    BothEdges,
+/// Amount of bits of a timer.
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum TimerBits {
+    /// 16 bits.
+    Bits16,
+    /// 32 bits.
+    #[cfg(not(stm32l0))]
+    Bits32,
 }
 
-/// Input TI selection.
-#[derive(Clone, Copy)]
-pub enum InputTISelection {
-    /// Normal
-    Normal,
-    /// Alternate
-    Alternate,
-    /// TRC
-    TRC,
-}
+/// Core timer instance.
+pub trait CoreInstance: RccPeripheral + 'static {
+    /// Interrupt for this timer.
+    type Interrupt: interrupt::typelevel::Interrupt;
 
-impl From<InputTISelection> for stm32_metapac::timer::vals::CcmrInputCcs {
-    fn from(tisel: InputTISelection) -> Self {
-        match tisel {
-            InputTISelection::Normal => stm32_metapac::timer::vals::CcmrInputCcs::TI4,
-            InputTISelection::Alternate => stm32_metapac::timer::vals::CcmrInputCcs::TI3,
-            InputTISelection::TRC => stm32_metapac::timer::vals::CcmrInputCcs::TRC,
-        }
-    }
-}
+    /// Amount of bits this timer has.
+    const BITS: TimerBits;
 
-/// Timer counting mode.
-#[repr(u8)]
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
-pub enum CountingMode {
-    #[default]
-    /// The timer counts up to the reload value and then resets back to 0.
-    EdgeAlignedUp,
-    /// The timer counts down to 0 and then resets back to the reload value.
-    EdgeAlignedDown,
-    /// The timer counts up to the reload value and then counts back to 0.
+    /// Registers for this timer.
     ///
-    /// The output compare interrupt flags of channels configured in output are
-    /// set when the counter is counting down.
-    CenterAlignedDownInterrupts,
-    /// The timer counts up to the reload value and then counts back to 0.
-    ///
-    /// The output compare interrupt flags of channels configured in output are
-    /// set when the counter is counting up.
-    CenterAlignedUpInterrupts,
-    /// The timer counts up to the reload value and then counts back to 0.
-    ///
-    /// The output compare interrupt flags of channels configured in output are
-    /// set when the counter is counting both up or down.
-    CenterAlignedBothInterrupts,
+    /// This is a raw pointer to the register block. The actual register block layout varies depending on the timer type.
+    fn regs() -> *mut ();
 }
+/// Cut-down basic timer instance.
+pub trait BasicNoCr2Instance: CoreInstance {}
+/// Basic timer instance.
+pub trait BasicInstance: BasicNoCr2Instance {}
 
-impl CountingMode {
-    /// Return whether this mode is edge-aligned (up or down).
-    pub fn is_edge_aligned(&self) -> bool {
-        matches!(self, CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown)
-    }
+/// General-purpose 16-bit timer with 1 channel instance.
+pub trait GeneralInstance1Channel: CoreInstance {}
 
-    /// Return whether this mode is center-aligned.
-    pub fn is_center_aligned(&self) -> bool {
-        matches!(
-            self,
-            CountingMode::CenterAlignedDownInterrupts
-                | CountingMode::CenterAlignedUpInterrupts
-                | CountingMode::CenterAlignedBothInterrupts
-        )
-    }
-}
+/// General-purpose 16-bit timer with 2 channels instance.
+pub trait GeneralInstance2Channel: GeneralInstance1Channel {}
 
-impl From<CountingMode> for (vals::Cms, vals::Dir) {
-    fn from(value: CountingMode) -> Self {
-        match value {
-            CountingMode::EdgeAlignedUp => (vals::Cms::EDGEALIGNED, vals::Dir::UP),
-            CountingMode::EdgeAlignedDown => (vals::Cms::EDGEALIGNED, vals::Dir::DOWN),
-            CountingMode::CenterAlignedDownInterrupts => (vals::Cms::CENTERALIGNED1, vals::Dir::UP),
-            CountingMode::CenterAlignedUpInterrupts => (vals::Cms::CENTERALIGNED2, vals::Dir::UP),
-            CountingMode::CenterAlignedBothInterrupts => (vals::Cms::CENTERALIGNED3, vals::Dir::UP),
-        }
-    }
-}
-
-impl From<(vals::Cms, vals::Dir)> for CountingMode {
-    fn from(value: (vals::Cms, vals::Dir)) -> Self {
-        match value {
-            (vals::Cms::EDGEALIGNED, vals::Dir::UP) => CountingMode::EdgeAlignedUp,
-            (vals::Cms::EDGEALIGNED, vals::Dir::DOWN) => CountingMode::EdgeAlignedDown,
-            (vals::Cms::CENTERALIGNED1, _) => CountingMode::CenterAlignedDownInterrupts,
-            (vals::Cms::CENTERALIGNED2, _) => CountingMode::CenterAlignedUpInterrupts,
-            (vals::Cms::CENTERALIGNED3, _) => CountingMode::CenterAlignedBothInterrupts,
-        }
-    }
-}
-
-/// Output compare mode.
-#[derive(Clone, Copy)]
-pub enum OutputCompareMode {
-    /// The comparison between the output compare register TIMx_CCRx and
-    /// the counter TIMx_CNT has no effect on the outputs.
-    /// (this mode is used to generate a timing base).
-    Frozen,
-    /// Set channel to active level on match. OCxREF signal is forced high when the
-    /// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx).
-    ActiveOnMatch,
-    /// Set channel to inactive level on match. OCxREF signal is forced low when the
-    /// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx).
-    InactiveOnMatch,
-    /// Toggle - OCxREF toggles when TIMx_CNT=TIMx_CCRx.
-    Toggle,
-    /// Force inactive level - OCxREF is forced low.
-    ForceInactive,
-    /// Force active level - OCxREF is forced high.
-    ForceActive,
-    /// PWM mode 1 - In upcounting, channel is active as long as TIMx_CNT<TIMx_CCRx
-    /// else inactive. In downcounting, channel is inactive (OCxREF=0) as long as
-    /// TIMx_CNT>TIMx_CCRx else active (OCxREF=1).
-    PwmMode1,
-    /// PWM mode 2 - In upcounting, channel is inactive as long as
-    /// TIMx_CNT<TIMx_CCRx else active. In downcounting, channel is active as long as
-    /// TIMx_CNT>TIMx_CCRx else inactive.
-    PwmMode2,
-    // TODO: there's more modes here depending on the chip family.
-}
-
-impl From<OutputCompareMode> for stm32_metapac::timer::vals::Ocm {
-    fn from(mode: OutputCompareMode) -> Self {
-        match mode {
-            OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN,
-            OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVEONMATCH,
-            OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVEONMATCH,
-            OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE,
-            OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCEINACTIVE,
-            OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCEACTIVE,
-            OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWMMODE1,
-            OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWMMODE2,
-        }
-    }
-}
-
-/// Timer output pin polarity.
-#[derive(Clone, Copy)]
-pub enum OutputPolarity {
-    /// Active high (higher duty value makes the pin spend more time high).
-    ActiveHigh,
-    /// Active low (higher duty value makes the pin spend more time low).
-    ActiveLow,
-}
-
-impl From<OutputPolarity> for bool {
-    fn from(mode: OutputPolarity) -> Self {
-        match mode {
-            OutputPolarity::ActiveHigh => false,
-            OutputPolarity::ActiveLow => true,
-        }
-    }
-}
-
-/// Basic 16-bit timer instance.
-pub trait BasicInstance: sealed::BasicInstance + sealed::BasicNoCr2Instance + sealed::CoreInstance + 'static {}
-
-// It's just a General-purpose 16-bit timer instance.
-/// Capture Compare timer instance.
-pub trait CaptureCompare16bitInstance:
-    BasicInstance
-    + sealed::GeneralPurpose2ChannelInstance
-    + sealed::GeneralPurpose1ChannelInstance
-    + sealed::GeneralPurpose16bitInstance
-    + 'static
-{
-    // SimplePwm<'d, T> is implemented for T: CaptureCompare16bitInstance
+/// General-purpose 16-bit timer with 4 channels instance.
+pub trait GeneralInstance4Channel: BasicInstance + GeneralInstance2Channel {
+    // SimplePwm<'d, T> is implemented for T: GeneralInstance4Channel
     // Advanced timers implement this trait, but the output needs to be
     // enabled explicitly.
     // To support general-purpose and advanced timers, this function is added
@@ -694,296 +80,149 @@ pub trait CaptureCompare16bitInstance:
     fn enable_outputs(&self) {}
 }
 
-#[cfg(not(stm32l0))]
-// It's just a General-purpose 32-bit timer instance.
-/// Capture Compare 32-bit timer instance.
-pub trait CaptureCompare32bitInstance:
-    CaptureCompare16bitInstance + sealed::GeneralPurpose32bitInstance + 'static
-{
+/// General-purpose 32-bit timer with 4 channels instance.
+pub trait GeneralInstance32bit4Channel: GeneralInstance4Channel {}
+
+/// Advanced 16-bit timer with 1 channel instance.
+pub trait AdvancedInstance1Channel: BasicNoCr2Instance + GeneralInstance1Channel {
+    /// Capture compare interrupt for this timer.
+    type CaptureCompareInterrupt: interrupt::typelevel::Interrupt;
 }
+/// Advanced 16-bit timer with 2 channels instance.
 
-#[cfg(not(stm32l0))]
-// It's just a Advanced Control timer instance.
-/// Complementary Capture Compare 32-bit timer instance.
-pub trait ComplementaryCaptureCompare16bitInstance:
-    CaptureCompare16bitInstance
-    + sealed::GeneralPurpose1ChannelComplementaryInstance
-    + sealed::GeneralPurpose2ChannelComplementaryInstance
-    + sealed::AdvancedControlInstance
-    + 'static
-{
-}
+pub trait AdvancedInstance2Channel: BasicInstance + GeneralInstance2Channel + AdvancedInstance1Channel {}
 
-pin_trait!(Channel1Pin, CaptureCompare16bitInstance);
-pin_trait!(Channel2Pin, CaptureCompare16bitInstance);
-pin_trait!(Channel3Pin, CaptureCompare16bitInstance);
-pin_trait!(Channel4Pin, CaptureCompare16bitInstance);
-pin_trait!(ExternalTriggerPin, CaptureCompare16bitInstance);
+/// Advanced 16-bit timer with 4 channels instance.
+pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel {}
 
-cfg_if::cfg_if! {
-    if #[cfg(not(stm32l0))] {
-        pin_trait!(Channel1ComplementaryPin, ComplementaryCaptureCompare16bitInstance);
-        pin_trait!(Channel2ComplementaryPin, ComplementaryCaptureCompare16bitInstance);
-        pin_trait!(Channel3ComplementaryPin, ComplementaryCaptureCompare16bitInstance);
-        pin_trait!(Channel4ComplementaryPin, ComplementaryCaptureCompare16bitInstance);
+pin_trait!(Channel1Pin, GeneralInstance4Channel);
+pin_trait!(Channel2Pin, GeneralInstance4Channel);
+pin_trait!(Channel3Pin, GeneralInstance4Channel);
+pin_trait!(Channel4Pin, GeneralInstance4Channel);
+pin_trait!(ExternalTriggerPin, GeneralInstance4Channel);
 
-        pin_trait!(BreakInputPin, ComplementaryCaptureCompare16bitInstance);
-        pin_trait!(BreakInput2Pin, ComplementaryCaptureCompare16bitInstance);
+pin_trait!(Channel1ComplementaryPin, AdvancedInstance4Channel);
+pin_trait!(Channel2ComplementaryPin, AdvancedInstance4Channel);
+pin_trait!(Channel3ComplementaryPin, AdvancedInstance4Channel);
+pin_trait!(Channel4ComplementaryPin, AdvancedInstance4Channel);
 
-        pin_trait!(BreakInputComparator1Pin, ComplementaryCaptureCompare16bitInstance);
-        pin_trait!(BreakInputComparator2Pin, ComplementaryCaptureCompare16bitInstance);
+pin_trait!(BreakInputPin, AdvancedInstance4Channel);
+pin_trait!(BreakInput2Pin, AdvancedInstance4Channel);
 
-        pin_trait!(BreakInput2Comparator1Pin, ComplementaryCaptureCompare16bitInstance);
-        pin_trait!(BreakInput2Comparator2Pin, ComplementaryCaptureCompare16bitInstance);
-    }
-}
+pin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel);
+pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel);
+
+pin_trait!(BreakInput2Comparator1Pin, AdvancedInstance4Channel);
+pin_trait!(BreakInput2Comparator2Pin, AdvancedInstance4Channel);
+
+// Update Event trigger DMA for every timer
+dma_trait!(UpDma, BasicInstance);
+
+dma_trait!(Ch1Dma, GeneralInstance4Channel);
+dma_trait!(Ch2Dma, GeneralInstance4Channel);
+dma_trait!(Ch3Dma, GeneralInstance4Channel);
+dma_trait!(Ch4Dma, GeneralInstance4Channel);
 
 #[allow(unused)]
 macro_rules! impl_core_timer {
-    ($inst:ident, $irq:ident) => {
-        impl sealed::CoreInstance for crate::peripherals::$inst {
-            type Interrupt = crate::interrupt::typelevel::$irq;
+    ($inst:ident, $bits:expr) => {
+        impl CoreInstance for crate::peripherals::$inst {
+            type Interrupt = crate::_generated::peripheral_interrupts::$inst::UP;
 
-            fn regs_core() -> crate::pac::timer::TimCore {
-                unsafe { crate::pac::timer::TimCore::from_ptr(crate::pac::$inst.as_ptr()) }
-            }
-        }
-    };
-}
+            const BITS: TimerBits = $bits;
 
-#[allow(unused)]
-macro_rules! impl_basic_no_cr2_timer {
-    ($inst:ident) => {
-        impl sealed::BasicNoCr2Instance for crate::peripherals::$inst {
-            fn regs_basic_no_cr2() -> crate::pac::timer::TimBasicNoCr2 {
-                unsafe { crate::pac::timer::TimBasicNoCr2::from_ptr(crate::pac::$inst.as_ptr()) }
-            }
-        }
-    };
-}
-
-#[allow(unused)]
-macro_rules! impl_basic_timer {
-    ($inst:ident) => {
-        impl sealed::BasicInstance for crate::peripherals::$inst {
-            fn regs_basic() -> crate::pac::timer::TimBasic {
-                unsafe { crate::pac::timer::TimBasic::from_ptr(crate::pac::$inst.as_ptr()) }
-            }
-        }
-    };
-}
-
-#[allow(unused)]
-macro_rules! impl_1ch_timer {
-    ($inst:ident) => {
-        impl sealed::GeneralPurpose1ChannelInstance for crate::peripherals::$inst {
-            fn regs_1ch() -> crate::pac::timer::Tim1ch {
-                unsafe { crate::pac::timer::Tim1ch::from_ptr(crate::pac::$inst.as_ptr()) }
-            }
-        }
-    };
-}
-
-#[allow(unused)]
-macro_rules! impl_2ch_timer {
-    ($inst:ident) => {
-        impl sealed::GeneralPurpose2ChannelInstance for crate::peripherals::$inst {
-            fn regs_2ch() -> crate::pac::timer::Tim2ch {
-                unsafe { crate::pac::timer::Tim2ch::from_ptr(crate::pac::$inst.as_ptr()) }
-            }
-        }
-    };
-}
-
-#[allow(unused)]
-macro_rules! impl_gp16_timer {
-    ($inst:ident) => {
-        impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst {
-            fn regs_gp16() -> crate::pac::timer::TimGp16 {
-                unsafe { crate::pac::timer::TimGp16::from_ptr(crate::pac::$inst.as_ptr()) }
-            }
-        }
-    };
-}
-
-#[allow(unused)]
-macro_rules! impl_gp32_timer {
-    ($inst:ident) => {
-        impl sealed::GeneralPurpose32bitInstance for crate::peripherals::$inst {
-            fn regs_gp32() -> crate::pac::timer::TimGp32 {
-                crate::pac::$inst
-            }
-        }
-    };
-}
-
-#[allow(unused)]
-macro_rules! impl_1ch_cmp_timer {
-    ($inst:ident) => {
-        impl sealed::GeneralPurpose1ChannelComplementaryInstance for crate::peripherals::$inst {
-            fn regs_1ch_cmp() -> crate::pac::timer::Tim1chCmp {
-                unsafe { crate::pac::timer::Tim1chCmp::from_ptr(crate::pac::$inst.as_ptr()) }
-            }
-        }
-    };
-}
-
-#[allow(unused)]
-macro_rules! impl_2ch_cmp_timer {
-    ($inst:ident) => {
-        impl sealed::GeneralPurpose2ChannelComplementaryInstance for crate::peripherals::$inst {
-            fn regs_2ch_cmp() -> crate::pac::timer::Tim2chCmp {
-                unsafe { crate::pac::timer::Tim2chCmp::from_ptr(crate::pac::$inst.as_ptr()) }
-            }
-        }
-    };
-}
-
-#[allow(unused)]
-macro_rules! impl_adv_timer {
-    ($inst:ident, $irq:ident) => {
-        impl sealed::AdvancedControlInstance for crate::peripherals::$inst {
-            type CaptureCompareInterrupt = crate::interrupt::typelevel::$irq;
-
-            fn regs_advanced() -> crate::pac::timer::TimAdv {
-                unsafe { crate::pac::timer::TimAdv::from_ptr(crate::pac::$inst.as_ptr()) }
+            fn regs() -> *mut () {
+                crate::pac::$inst.as_ptr()
             }
         }
     };
 }
 
 foreach_interrupt! {
-
     ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => {
-        impl_core_timer!($inst, $irq);
-        impl_basic_no_cr2_timer!($inst);
-        impl_basic_timer!($inst);
+        impl_core_timer!($inst, TimerBits::Bits16);
+        impl BasicNoCr2Instance for crate::peripherals::$inst {}
         impl BasicInstance for crate::peripherals::$inst {}
     };
 
     ($inst:ident, timer, TIM_1CH, UP, $irq:ident) => {
-        impl_core_timer!($inst, $irq);
-        impl_basic_no_cr2_timer!($inst);
-        impl_basic_timer!($inst);
-        impl_1ch_timer!($inst);
-        impl_2ch_timer!($inst);
-        impl_gp16_timer!($inst);
+        impl_core_timer!($inst, TimerBits::Bits16);
+        impl BasicNoCr2Instance for crate::peripherals::$inst {}
         impl BasicInstance for crate::peripherals::$inst {}
-        impl CaptureCompare16bitInstance for crate::peripherals::$inst {}
+        impl GeneralInstance1Channel for crate::peripherals::$inst {}
+        impl GeneralInstance2Channel for crate::peripherals::$inst {}
+        impl GeneralInstance4Channel for crate::peripherals::$inst {}
     };
 
-
     ($inst:ident, timer, TIM_2CH, UP, $irq:ident) => {
-        impl_core_timer!($inst, $irq);
-        impl_basic_no_cr2_timer!($inst);
-        impl_basic_timer!($inst);
-        impl_1ch_timer!($inst);
-        impl_2ch_timer!($inst);
-        impl_gp16_timer!($inst);
+        impl_core_timer!($inst, TimerBits::Bits16);
+        impl BasicNoCr2Instance for crate::peripherals::$inst {}
         impl BasicInstance for crate::peripherals::$inst {}
-        impl CaptureCompare16bitInstance for crate::peripherals::$inst {}
+        impl GeneralInstance1Channel for crate::peripherals::$inst {}
+        impl GeneralInstance2Channel for crate::peripherals::$inst {}
+        impl GeneralInstance4Channel for crate::peripherals::$inst {}
     };
 
     ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => {
-        impl_core_timer!($inst, $irq);
-        impl_basic_no_cr2_timer!($inst);
-        impl_basic_timer!($inst);
-        impl_1ch_timer!($inst);
-        impl_2ch_timer!($inst);
-        impl_gp16_timer!($inst);
+        impl_core_timer!($inst, TimerBits::Bits16);
+        impl BasicNoCr2Instance for crate::peripherals::$inst {}
         impl BasicInstance for crate::peripherals::$inst {}
-        impl CaptureCompare16bitInstance for crate::peripherals::$inst {}
+        impl GeneralInstance1Channel for crate::peripherals::$inst {}
+        impl GeneralInstance2Channel for crate::peripherals::$inst {}
+        impl GeneralInstance4Channel for crate::peripherals::$inst {}
     };
 
     ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => {
-        impl_core_timer!($inst, $irq);
-        impl_basic_no_cr2_timer!($inst);
-        impl_basic_timer!($inst);
-        impl_1ch_timer!($inst);
-        impl_2ch_timer!($inst);
-        impl_gp16_timer!($inst);
-        impl_gp32_timer!($inst);
+        impl_core_timer!($inst, TimerBits::Bits32);
+        impl BasicNoCr2Instance for crate::peripherals::$inst {}
         impl BasicInstance for crate::peripherals::$inst {}
-        impl CaptureCompare16bitInstance for crate::peripherals::$inst {}
-        impl CaptureCompare32bitInstance for crate::peripherals::$inst {}
+        impl GeneralInstance1Channel for crate::peripherals::$inst {}
+        impl GeneralInstance2Channel for crate::peripherals::$inst {}
+        impl GeneralInstance4Channel for crate::peripherals::$inst {}
+        impl GeneralInstance32bit4Channel for crate::peripherals::$inst {}
     };
 
     ($inst:ident, timer, TIM_1CH_CMP, UP, $irq:ident) => {
-        impl_core_timer!($inst, $irq);
-        impl_basic_no_cr2_timer!($inst);
-        impl_basic_timer!($inst);
-        impl_1ch_timer!($inst);
-        impl_2ch_timer!($inst);
-        impl_gp16_timer!($inst);
-        impl_1ch_cmp_timer!($inst);
-        impl_2ch_cmp_timer!($inst);
+        impl_core_timer!($inst, TimerBits::Bits16);
+        impl BasicNoCr2Instance for crate::peripherals::$inst {}
         impl BasicInstance for crate::peripherals::$inst {}
-        impl CaptureCompare16bitInstance for crate::peripherals::$inst {
-            /// Enable timer outputs.
-            fn enable_outputs(&self) {
-                use crate::timer::sealed::GeneralPurpose1ChannelComplementaryInstance;
-                self.set_moe(true);
-            }
-        }
-        impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {}
+        impl GeneralInstance1Channel for crate::peripherals::$inst {}
+        impl GeneralInstance2Channel for crate::peripherals::$inst {}
+        impl GeneralInstance4Channel for crate::peripherals::$inst { fn enable_outputs(&self) { set_moe::<Self>() }}
+        impl AdvancedInstance1Channel for crate::peripherals::$inst { type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC; }
+        impl AdvancedInstance2Channel for crate::peripherals::$inst {}
+        impl AdvancedInstance4Channel for crate::peripherals::$inst {}
     };
-    ($inst:ident, timer, TIM_1CH_CMP, CC, $irq:ident) => {
-        impl_adv_timer!($inst, $irq);
-    };
-
 
     ($inst:ident, timer, TIM_2CH_CMP, UP, $irq:ident) => {
-        impl_core_timer!($inst, $irq);
-        impl_basic_no_cr2_timer!($inst);
-        impl_basic_timer!($inst);
-        impl_1ch_timer!($inst);
-        impl_2ch_timer!($inst);
-        impl_gp16_timer!($inst);
-        impl_1ch_cmp_timer!($inst);
-        impl_2ch_cmp_timer!($inst);
+        impl_core_timer!($inst, TimerBits::Bits16);
+        impl BasicNoCr2Instance for crate::peripherals::$inst {}
         impl BasicInstance for crate::peripherals::$inst {}
-        impl CaptureCompare16bitInstance for crate::peripherals::$inst {
-            /// Enable timer outputs.
-            fn enable_outputs(&self) {
-                use crate::timer::sealed::GeneralPurpose1ChannelComplementaryInstance;
-                self.set_moe(true);
-            }
-        }
-        impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {}
+        impl GeneralInstance1Channel for crate::peripherals::$inst {}
+        impl GeneralInstance2Channel for crate::peripherals::$inst {}
+        impl GeneralInstance4Channel for crate::peripherals::$inst { fn enable_outputs(&self) { set_moe::<Self>() }}
+        impl AdvancedInstance1Channel for crate::peripherals::$inst { type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC; }
+        impl AdvancedInstance2Channel for crate::peripherals::$inst {}
+        impl AdvancedInstance4Channel for crate::peripherals::$inst {}
     };
-    ($inst:ident, timer, TIM_2CH_CMP, CC, $irq:ident) => {
-        impl_adv_timer!($inst, $irq);
-    };
-
 
     ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => {
-        impl_core_timer!($inst, $irq);
-        impl_basic_no_cr2_timer!($inst);
-        impl_basic_timer!($inst);
-        impl_1ch_timer!($inst);
-        impl_2ch_timer!($inst);
-        impl_gp16_timer!($inst);
-        impl_1ch_cmp_timer!($inst);
-        impl_2ch_cmp_timer!($inst);
+        impl_core_timer!($inst, TimerBits::Bits16);
+        impl BasicNoCr2Instance for crate::peripherals::$inst {}
         impl BasicInstance for crate::peripherals::$inst {}
-        impl CaptureCompare16bitInstance for crate::peripherals::$inst {
-            /// Enable timer outputs.
-            fn enable_outputs(&self) {
-                use crate::timer::sealed::GeneralPurpose1ChannelComplementaryInstance;
-                self.set_moe(true);
-            }
-        }
-        impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {}
-    };
-    ($inst:ident, timer, TIM_ADV, CC, $irq:ident) => {
-        impl_adv_timer!($inst, $irq);
+        impl GeneralInstance1Channel for crate::peripherals::$inst {}
+        impl GeneralInstance2Channel for crate::peripherals::$inst {}
+        impl GeneralInstance4Channel for crate::peripherals::$inst { fn enable_outputs(&self) { set_moe::<Self>() }}
+        impl AdvancedInstance1Channel for crate::peripherals::$inst { type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC; }
+        impl AdvancedInstance2Channel for crate::peripherals::$inst {}
+        impl AdvancedInstance4Channel for crate::peripherals::$inst {}
     };
 }
 
-// Update Event trigger DMA for every timer
-dma_trait!(UpDma, BasicInstance);
-
-dma_trait!(Ch1Dma, CaptureCompare16bitInstance);
-dma_trait!(Ch2Dma, CaptureCompare16bitInstance);
-dma_trait!(Ch3Dma, CaptureCompare16bitInstance);
-dma_trait!(Ch4Dma, CaptureCompare16bitInstance);
+#[cfg(not(stm32l0))]
+#[allow(unused)]
+fn set_moe<T: GeneralInstance4Channel>() {
+    unsafe { crate::pac::timer::Tim1chCmp::from_ptr(T::regs()) }
+        .bdtr()
+        .modify(|w| w.set_moe(true));
+}
diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs
index 59efb72ba..b6ab939cc 100644
--- a/embassy-stm32/src/timer/qei.rs
+++ b/embassy-stm32/src/timer/qei.rs
@@ -3,8 +3,10 @@
 use core::marker::PhantomData;
 
 use embassy_hal_internal::{into_ref, PeripheralRef};
+use stm32_metapac::timer::vals;
 
-use super::*;
+use super::low_level::Timer;
+use super::{Channel1Pin, Channel2Pin, GeneralInstance4Channel};
 use crate::gpio::sealed::AFType;
 use crate::gpio::AnyPin;
 use crate::Peripheral;
@@ -30,7 +32,7 @@ pub struct QeiPin<'d, T, Channel> {
 
 macro_rules! channel_impl {
     ($new_chx:ident, $channel:ident, $pin_trait:ident) => {
-        impl<'d, T: CaptureCompare16bitInstance> QeiPin<'d, T, $channel> {
+        impl<'d, T: GeneralInstance4Channel> QeiPin<'d, T, $channel> {
             #[doc = concat!("Create a new ", stringify!($channel), " QEI pin instance.")]
             pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd) -> Self {
                 into_ref!(pin);
@@ -53,29 +55,28 @@ channel_impl!(new_ch1, Ch1, Channel1Pin);
 channel_impl!(new_ch2, Ch2, Channel2Pin);
 
 /// Quadrature decoder driver.
-pub struct Qei<'d, T> {
-    _inner: PeripheralRef<'d, T>,
+pub struct Qei<'d, T: GeneralInstance4Channel> {
+    inner: Timer<'d, T>,
 }
 
-impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> {
+impl<'d, T: GeneralInstance4Channel> Qei<'d, T> {
     /// Create a new quadrature decoder driver.
     pub fn new(tim: impl Peripheral<P = T> + 'd, _ch1: QeiPin<'d, T, Ch1>, _ch2: QeiPin<'d, T, Ch2>) -> Self {
         Self::new_inner(tim)
     }
 
     fn new_inner(tim: impl Peripheral<P = T> + 'd) -> Self {
-        into_ref!(tim);
-
-        T::enable_and_reset();
+        let inner = Timer::new(tim);
+        let r = inner.regs_gp16();
 
         // Configure TxC1 and TxC2 as captures
-        T::regs_gp16().ccmr_input(0).modify(|w| {
+        r.ccmr_input(0).modify(|w| {
             w.set_ccs(0, vals::CcmrInputCcs::TI4);
             w.set_ccs(1, vals::CcmrInputCcs::TI4);
         });
 
         // enable and configure to capture on rising edge
-        T::regs_gp16().ccer().modify(|w| {
+        r.ccer().modify(|w| {
             w.set_cce(0, true);
             w.set_cce(1, true);
 
@@ -83,19 +84,19 @@ impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> {
             w.set_ccp(1, false);
         });
 
-        T::regs_gp16().smcr().modify(|w| {
+        r.smcr().modify(|w| {
             w.set_sms(vals::Sms::ENCODER_MODE_3);
         });
 
-        T::regs_gp16().arr().modify(|w| w.set_arr(u16::MAX));
-        T::regs_gp16().cr1().modify(|w| w.set_cen(true));
+        r.arr().modify(|w| w.set_arr(u16::MAX));
+        r.cr1().modify(|w| w.set_cen(true));
 
-        Self { _inner: tim }
+        Self { inner }
     }
 
     /// Get direction.
     pub fn read_direction(&self) -> Direction {
-        match T::regs_gp16().cr1().read().dir() {
+        match self.inner.regs_gp16().cr1().read().dir() {
             vals::Dir::DOWN => Direction::Downcounting,
             vals::Dir::UP => Direction::Upcounting,
         }
@@ -103,6 +104,6 @@ impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> {
 
     /// Get count.
     pub fn count(&self) -> u16 {
-        T::regs_gp16().cnt().read().cnt()
+        self.inner.regs_gp16().cnt().read().cnt()
     }
 }
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs
index 4669fc6cc..b54e9a0d6 100644
--- a/embassy-stm32/src/timer/simple_pwm.rs
+++ b/embassy-stm32/src/timer/simple_pwm.rs
@@ -4,10 +4,10 @@ use core::marker::PhantomData;
 
 use embassy_hal_internal::{into_ref, PeripheralRef};
 
-use super::*;
-#[allow(unused_imports)]
-use crate::gpio::sealed::{AFType, Pin};
+use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer};
+use super::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel};
 use crate::gpio::{AnyPin, OutputType};
+use crate::time::Hertz;
 use crate::Peripheral;
 
 /// Channel 1 marker type.
@@ -29,7 +29,7 @@ pub struct PwmPin<'d, T, C> {
 
 macro_rules! channel_impl {
     ($new_chx:ident, $channel:ident, $pin_trait:ident) => {
-        impl<'d, T: CaptureCompare16bitInstance> PwmPin<'d, T, $channel> {
+        impl<'d, T: GeneralInstance4Channel> PwmPin<'d, T, $channel> {
             #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")]
             pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, output_type: OutputType) -> Self {
                 into_ref!(pin);
@@ -54,11 +54,11 @@ channel_impl!(new_ch3, Ch3, Channel3Pin);
 channel_impl!(new_ch4, Ch4, Channel4Pin);
 
 /// Simple PWM driver.
-pub struct SimplePwm<'d, T> {
-    inner: PeripheralRef<'d, T>,
+pub struct SimplePwm<'d, T: GeneralInstance4Channel> {
+    inner: Timer<'d, T>,
 }
 
-impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
+impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
     /// Create a new simple PWM driver.
     pub fn new(
         tim: impl Peripheral<P = T> + 'd,
@@ -73,15 +73,11 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
     }
 
     fn new_inner(tim: impl Peripheral<P = T> + 'd, freq: Hertz, counting_mode: CountingMode) -> Self {
-        into_ref!(tim);
-
-        T::enable_and_reset();
-
-        let mut this = Self { inner: tim };
+        let mut this = Self { inner: Timer::new(tim) };
 
         this.inner.set_counting_mode(counting_mode);
         this.set_frequency(freq);
-        this.inner.enable_outputs(); // Required for advanced timers, see CaptureCompare16bitInstance for details
+        this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details
         this.inner.start();
 
         [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4]
@@ -126,14 +122,14 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
     /// Get max duty value.
     ///
     /// This value depends on the configured frequency and the timer's clock rate from RCC.
-    pub fn get_max_duty(&self) -> u16 {
+    pub fn get_max_duty(&self) -> u32 {
         self.inner.get_max_compare_value() + 1
     }
 
     /// Set the duty for a given channel.
     ///
     /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.
-    pub fn set_duty(&mut self, channel: Channel, duty: u16) {
+    pub fn set_duty(&mut self, channel: Channel, duty: u32) {
         assert!(duty <= self.get_max_duty());
         self.inner.set_compare_value(channel, duty)
     }
@@ -141,7 +137,7 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
     /// Get the duty for a given channel.
     ///
     /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included.
-    pub fn get_duty(&self, channel: Channel) -> u16 {
+    pub fn get_duty(&self, channel: Channel) -> u32 {
         self.inner.get_compare_value(channel)
     }
 
@@ -165,8 +161,6 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
         channel: Channel,
         duty: &[u16],
     ) {
-        assert!(duty.iter().all(|v| *v <= self.get_max_duty()));
-
         into_ref!(dma);
 
         #[allow(clippy::let_unit_value)] // eg. stm32f334
@@ -201,7 +195,7 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
                 &mut dma,
                 req,
                 duty,
-                T::regs_1ch().ccr(channel.index()).as_ptr() as *mut _,
+                self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut _,
                 dma_transfer_option,
             )
             .await
@@ -227,22 +221,20 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
 
 macro_rules! impl_waveform_chx {
     ($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => {
-        impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
+        impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> {
             /// Generate a sequence of PWM waveform
             ///
             /// Note:
             /// you will need to provide corresponding TIMx_CHy DMA channel to use this method.
             pub async fn $fn_name(&mut self, dma: impl Peripheral<P = impl super::$dma_ch<T>>, duty: &[u16]) {
-                use super::vals::Ccds;
-
-                assert!(duty.iter().all(|v| *v <= self.get_max_duty()));
+                use crate::pac::timer::vals::Ccds;
 
                 into_ref!(dma);
 
                 #[allow(clippy::let_unit_value)] // eg. stm32f334
                 let req = dma.request();
 
-                let cc_channel = super::Channel::$cc_ch;
+                let cc_channel = Channel::$cc_ch;
 
                 let original_duty_state = self.get_duty(cc_channel);
                 let original_enable_state = self.is_enabled(cc_channel);
@@ -279,7 +271,7 @@ macro_rules! impl_waveform_chx {
                         &mut dma,
                         req,
                         duty,
-                        T::regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut _,
+                        self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut _,
                         dma_transfer_option,
                     )
                     .await
@@ -314,10 +306,10 @@ impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2);
 impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3);
 impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4);
 
-impl<'d, T: CaptureCompare16bitInstance> embedded_hal_02::Pwm for SimplePwm<'d, T> {
+impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> {
     type Channel = Channel;
     type Time = Hertz;
-    type Duty = u16;
+    type Duty = u32;
 
     fn disable(&mut self, channel: Self::Channel) {
         self.inner.enable_channel(channel, false);
diff --git a/examples/stm32f4/src/bin/ws2812_pwm.rs b/examples/stm32f4/src/bin/ws2812_pwm.rs
index 6122cea2d..cbaff75fc 100644
--- a/examples/stm32f4/src/bin/ws2812_pwm.rs
+++ b/examples/stm32f4/src/bin/ws2812_pwm.rs
@@ -15,8 +15,9 @@
 use embassy_executor::Spawner;
 use embassy_stm32::gpio::OutputType;
 use embassy_stm32::time::khz;
+use embassy_stm32::timer::low_level::CountingMode;
 use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
-use embassy_stm32::timer::{Channel, CountingMode};
+use embassy_stm32::timer::Channel;
 use embassy_time::{Duration, Ticker, Timer};
 use {defmt_rtt as _, panic_probe as _};
 
@@ -60,7 +61,7 @@ async fn main(_spawner: Spawner) {
     // construct ws2812 non-return-to-zero (NRZ) code bit by bit
     // ws2812 only need 24 bits for each LED, but we add one bit more to keep PWM output low
 
-    let max_duty = ws2812_pwm.get_max_duty();
+    let max_duty = ws2812_pwm.get_max_duty() as u16;
     let n0 = 8 * max_duty / 25; // ws2812 Bit 0 high level timing
     let n1 = 2 * n0; // ws2812 Bit 1 high level timing
 
diff --git a/examples/stm32h7/src/bin/dac_dma.rs b/examples/stm32h7/src/bin/dac_dma.rs
index feec28993..c45747f35 100644
--- a/examples/stm32h7/src/bin/dac_dma.rs
+++ b/examples/stm32h7/src/bin/dac_dma.rs
@@ -8,7 +8,7 @@ use embassy_stm32::pac::timer::vals::Mms;
 use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7};
 use embassy_stm32::rcc::low_level::RccPeripheral;
 use embassy_stm32::time::Hertz;
-use embassy_stm32::timer::low_level::BasicInstance;
+use embassy_stm32::timer::low_level::Timer;
 use micromath::F32Ext;
 use {defmt_rtt as _, panic_probe as _};
 
@@ -51,12 +51,12 @@ async fn main(spawner: Spawner) {
     // Obtain two independent channels (p.DAC1 can only be consumed once, though!)
     let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split();
 
-    spawner.spawn(dac_task1(dac_ch1)).ok();
-    spawner.spawn(dac_task2(dac_ch2)).ok();
+    spawner.spawn(dac_task1(p.TIM6, dac_ch1)).ok();
+    spawner.spawn(dac_task2(p.TIM7, dac_ch2)).ok();
 }
 
 #[embassy_executor::task]
-async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
+async fn dac_task1(tim: TIM6, mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
     let data: &[u8; 256] = &calculate_array::<256>();
 
     info!("TIM6 frequency is {}", TIM6::frequency());
@@ -74,10 +74,10 @@ async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
     dac.set_triggering(true);
     dac.enable();
 
-    TIM6::enable_and_reset();
-    TIM6::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
-    TIM6::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
-    TIM6::regs_basic().cr1().modify(|w| {
+    let tim = Timer::new(tim);
+    tim.regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
+    tim.regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
+    tim.regs_basic().cr1().modify(|w| {
         w.set_opm(false);
         w.set_cen(true);
     });
@@ -99,7 +99,7 @@ async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
 }
 
 #[embassy_executor::task]
-async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
+async fn dac_task2(tim: TIM7, mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
     let data: &[u8; 256] = &calculate_array::<256>();
 
     info!("TIM7 frequency is {}", TIM7::frequency());
@@ -111,10 +111,10 @@ async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
         error!("Reload value {} below threshold!", reload);
     }
 
-    TIM7::enable_and_reset();
-    TIM7::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
-    TIM7::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
-    TIM7::regs_basic().cr1().modify(|w| {
+    let tim = Timer::new(tim);
+    tim.regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
+    tim.regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
+    tim.regs_basic().cr1().modify(|w| {
         w.set_opm(false);
         w.set_cen(true);
     });
diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs
index 049d9967d..780fbc6f0 100644
--- a/examples/stm32h7/src/bin/low_level_timer_api.rs
+++ b/examples/stm32h7/src/bin/low_level_timer_api.rs
@@ -6,8 +6,9 @@ use embassy_executor::Spawner;
 use embassy_stm32::gpio::low_level::AFType;
 use embassy_stm32::gpio::Speed;
 use embassy_stm32::time::{khz, Hertz};
-use embassy_stm32::timer::*;
-use embassy_stm32::{into_ref, Config, Peripheral, PeripheralRef};
+use embassy_stm32::timer::low_level::{OutputCompareMode, Timer as LLTimer};
+use embassy_stm32::timer::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance32bit4Channel};
+use embassy_stm32::{into_ref, Config, Peripheral};
 use embassy_time::Timer;
 use {defmt_rtt as _, panic_probe as _};
 
@@ -56,11 +57,11 @@ async fn main(_spawner: Spawner) {
         Timer::after_millis(300).await;
     }
 }
-pub struct SimplePwm32<'d, T: CaptureCompare32bitInstance> {
-    inner: PeripheralRef<'d, T>,
+pub struct SimplePwm32<'d, T: GeneralInstance32bit4Channel> {
+    tim: LLTimer<'d, T>,
 }
 
-impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> {
+impl<'d, T: GeneralInstance32bit4Channel> SimplePwm32<'d, T> {
     pub fn new(
         tim: impl Peripheral<P = T> + 'd,
         ch1: impl Peripheral<P = impl Channel1Pin<T>> + 'd,
@@ -69,9 +70,7 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> {
         ch4: impl Peripheral<P = impl Channel4Pin<T>> + 'd,
         freq: Hertz,
     ) -> Self {
-        into_ref!(tim, ch1, ch2, ch3, ch4);
-
-        T::enable_and_reset();
+        into_ref!(ch1, ch2, ch3, ch4);
 
         ch1.set_speed(Speed::VeryHigh);
         ch1.set_as_af(ch1.af_num(), AFType::OutputPushPull);
@@ -82,12 +81,12 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> {
         ch4.set_speed(Speed::VeryHigh);
         ch4.set_as_af(ch1.af_num(), AFType::OutputPushPull);
 
-        let mut this = Self { inner: tim };
+        let mut this = Self { tim: LLTimer::new(tim) };
 
         this.set_frequency(freq);
-        this.inner.start();
+        this.tim.start();
 
-        let r = T::regs_gp32();
+        let r = this.tim.regs_gp32();
         r.ccmr_output(0)
             .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into()));
         r.ccmr_output(0)
@@ -101,23 +100,26 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> {
     }
 
     pub fn enable(&mut self, channel: Channel) {
-        T::regs_gp32().ccer().modify(|w| w.set_cce(channel.index(), true));
+        self.tim.regs_gp32().ccer().modify(|w| w.set_cce(channel.index(), true));
     }
 
     pub fn disable(&mut self, channel: Channel) {
-        T::regs_gp32().ccer().modify(|w| w.set_cce(channel.index(), false));
+        self.tim
+            .regs_gp32()
+            .ccer()
+            .modify(|w| w.set_cce(channel.index(), false));
     }
 
     pub fn set_frequency(&mut self, freq: Hertz) {
-        <T as embassy_stm32::timer::low_level::GeneralPurpose32bitInstance>::set_frequency(&mut self.inner, freq);
+        self.tim.set_frequency(freq);
     }
 
     pub fn get_max_duty(&self) -> u32 {
-        T::regs_gp32().arr().read()
+        self.tim.regs_gp32().arr().read()
     }
 
     pub fn set_duty(&mut self, channel: Channel, duty: u32) {
         defmt::assert!(duty < self.get_max_duty());
-        T::regs_gp32().ccr(channel.index()).write_value(duty)
+        self.tim.regs_gp32().ccr(channel.index()).write_value(duty)
     }
 }
diff --git a/examples/stm32l4/src/bin/dac_dma.rs b/examples/stm32l4/src/bin/dac_dma.rs
index f227812cd..98edd39c0 100644
--- a/examples/stm32l4/src/bin/dac_dma.rs
+++ b/examples/stm32l4/src/bin/dac_dma.rs
@@ -8,7 +8,7 @@ use embassy_stm32::pac::timer::vals::Mms;
 use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7};
 use embassy_stm32::rcc::low_level::RccPeripheral;
 use embassy_stm32::time::Hertz;
-use embassy_stm32::timer::low_level::BasicInstance;
+use embassy_stm32::timer::low_level::Timer;
 use micromath::F32Ext;
 use {defmt_rtt as _, panic_probe as _};
 
@@ -22,12 +22,12 @@ async fn main(spawner: Spawner) {
     // Obtain two independent channels (p.DAC1 can only be consumed once, though!)
     let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split();
 
-    spawner.spawn(dac_task1(dac_ch1)).ok();
-    spawner.spawn(dac_task2(dac_ch2)).ok();
+    spawner.spawn(dac_task1(p.TIM6, dac_ch1)).ok();
+    spawner.spawn(dac_task2(p.TIM7, dac_ch2)).ok();
 }
 
 #[embassy_executor::task]
-async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
+async fn dac_task1(tim: TIM6, mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
     let data: &[u8; 256] = &calculate_array::<256>();
 
     info!("TIM6 frequency is {}", TIM6::frequency());
@@ -45,10 +45,10 @@ async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
     dac.set_triggering(true);
     dac.enable();
 
-    TIM6::enable_and_reset();
-    TIM6::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
-    TIM6::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
-    TIM6::regs_basic().cr1().modify(|w| {
+    let tim = Timer::new(tim);
+    tim.regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
+    tim.regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
+    tim.regs_basic().cr1().modify(|w| {
         w.set_opm(false);
         w.set_cen(true);
     });
@@ -70,7 +70,7 @@ async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) {
 }
 
 #[embassy_executor::task]
-async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
+async fn dac_task2(tim: TIM7, mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
     let data: &[u8; 256] = &calculate_array::<256>();
 
     info!("TIM7 frequency is {}", TIM7::frequency());
@@ -82,10 +82,10 @@ async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) {
         error!("Reload value {} below threshold!", reload);
     }
 
-    TIM7::enable_and_reset();
-    TIM7::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
-    TIM7::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
-    TIM7::regs_basic().cr1().modify(|w| {
+    let tim = Timer::new(tim);
+    tim.regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1));
+    tim.regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE));
+    tim.regs_basic().cr1().modify(|w| {
         w.set_opm(false);
         w.set_cen(true);
     });