From a83560c6b166e3f354471a2e1cf1c52a6d2be59b Mon Sep 17 00:00:00 2001
From: Mathias <mk@blackbird.online>
Date: Thu, 29 Sep 2022 07:49:32 +0200
Subject: [PATCH 01/12] Implement RTC peripheral for all stm32 families with
 rtc

---
 embassy-stm32/Cargo.toml                  |   1 +
 embassy-stm32/src/lib.rs                  |   2 +
 embassy-stm32/src/rtc/datetime_chrono.rs  |  85 ++++++++
 embassy-stm32/src/rtc/datetime_no_deps.rs | 146 +++++++++++++
 embassy-stm32/src/rtc/mod.rs              | 238 ++++++++++++++++++++++
 embassy-stm32/src/rtc/v2/mod.rs           | 171 ++++++++++++++++
 embassy-stm32/src/rtc/v2/v2f0.rs          |  41 ++++
 embassy-stm32/src/rtc/v2/v2f2.rs          |  31 +++
 embassy-stm32/src/rtc/v2/v2f3.rs          |  31 +++
 embassy-stm32/src/rtc/v2/v2f4.rs          |  31 +++
 embassy-stm32/src/rtc/v2/v2f7.rs          |  41 ++++
 embassy-stm32/src/rtc/v2/v2h7.rs          |  33 +++
 embassy-stm32/src/rtc/v2/v2l0.rs          |  40 ++++
 embassy-stm32/src/rtc/v2/v2l1.rs          |  40 ++++
 embassy-stm32/src/rtc/v2/v2l4.rs          |  41 ++++
 embassy-stm32/src/rtc/v2/v2wb.rs          |  39 ++++
 embassy-stm32/src/rtc/v3.rs               | 212 +++++++++++++++++++
 17 files changed, 1223 insertions(+)
 create mode 100644 embassy-stm32/src/rtc/datetime_chrono.rs
 create mode 100644 embassy-stm32/src/rtc/datetime_no_deps.rs
 create mode 100644 embassy-stm32/src/rtc/mod.rs
 create mode 100644 embassy-stm32/src/rtc/v2/mod.rs
 create mode 100644 embassy-stm32/src/rtc/v2/v2f0.rs
 create mode 100644 embassy-stm32/src/rtc/v2/v2f2.rs
 create mode 100644 embassy-stm32/src/rtc/v2/v2f3.rs
 create mode 100644 embassy-stm32/src/rtc/v2/v2f4.rs
 create mode 100644 embassy-stm32/src/rtc/v2/v2f7.rs
 create mode 100644 embassy-stm32/src/rtc/v2/v2h7.rs
 create mode 100644 embassy-stm32/src/rtc/v2/v2l0.rs
 create mode 100644 embassy-stm32/src/rtc/v2/v2l1.rs
 create mode 100644 embassy-stm32/src/rtc/v2/v2l4.rs
 create mode 100644 embassy-stm32/src/rtc/v2/v2wb.rs
 create mode 100644 embassy-stm32/src/rtc/v3.rs

diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index a4a232f51..a3ce9d996 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -66,6 +66,7 @@ stm32-fmc = "0.2.4"
 seq-macro = "0.3.0"
 cfg-if = "1.0.0"
 embedded-io = { version = "0.3.0", features = ["async"], optional = true }
+chrono = { version = "^0.4", default-features = false, optional = true}
 
 [build-dependencies]
 proc-macro2 = "1.0.36"
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 0392e8086..906980ac4 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -46,6 +46,8 @@ pub mod flash;
 pub mod pwm;
 #[cfg(rng)]
 pub mod rng;
+#[cfg(all(rtc, not(rtc_v1)))]
+pub mod rtc;
 #[cfg(sdmmc)]
 pub mod sdmmc;
 #[cfg(spi)]
diff --git a/embassy-stm32/src/rtc/datetime_chrono.rs b/embassy-stm32/src/rtc/datetime_chrono.rs
new file mode 100644
index 000000000..b46316cc9
--- /dev/null
+++ b/embassy-stm32/src/rtc/datetime_chrono.rs
@@ -0,0 +1,85 @@
+use chrono::{Datelike, Timelike};
+
+use super::byte_to_bcd2;
+use crate::pac::rtc::Rtc;
+
+/// Alias for [`chrono::NaiveDateTime`]
+pub type DateTime = chrono::NaiveDateTime;
+/// Alias for [`chrono::Weekday`]
+pub type DayOfWeek = chrono::Weekday;
+
+/// Errors regarding the [`DateTime`] and [`DateTimeFilter`] structs.
+///
+/// [`DateTimeFilter`]: struct.DateTimeFilter.html
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum Error {
+    /// The [DateTime] has an invalid year. The year must be between 0 and 4095.
+    InvalidYear,
+    /// The [DateTime] contains an invalid date.
+    InvalidDate,
+    /// The [DateTime] contains an invalid time.
+    InvalidTime,
+}
+
+pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
+    dotw.num_days_from_monday() as u8
+}
+
+pub(crate) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
+    if dt.year() < 0 || dt.year() > 4095 {
+        // rp2040 can't hold these years
+        Err(Error::InvalidYear)
+    } else {
+        // The rest of the chrono date is assumed to be valid
+        Ok(())
+    }
+}
+
+pub(super) fn write_date_time(rtc: &Rtc, t: DateTime) {
+    let (ht, hu) = byte_to_bcd2(t.hour() as u8);
+    let (mnt, mnu) = byte_to_bcd2(t.minute() as u8);
+    let (st, su) = byte_to_bcd2(t.second() as u8);
+
+    let (dt, du) = byte_to_bcd2(t.day() as u8);
+    let (mt, mu) = byte_to_bcd2(t.month() as u8);
+    let yr = t.year() as u16;
+    let yr_offset = (yr - 1970_u16) as u8;
+    let (yt, yu) = byte_to_bcd2(yr_offset);
+
+    unsafe {
+        rtc.tr().write(|w| {
+            w.set_ht(ht);
+            w.set_hu(hu);
+            w.set_mnt(mnt);
+            w.set_mnu(mnu);
+            w.set_st(st);
+            w.set_su(su);
+            w.set_pm(stm32_metapac::rtc::vals::Ampm::AM);
+        });
+
+        rtc.dr().write(|w| {
+            w.set_dt(dt);
+            w.set_du(du);
+            w.set_mt(mt > 0);
+            w.set_mu(mu);
+            w.set_yt(yt);
+            w.set_yu(yu);
+            w.set_wdu(day_of_week_to_u8(t.weekday()));
+        });
+    }
+}
+
+pub(super) fn datetime(
+    year: u16,
+    month: u8,
+    day: u8,
+    _day_of_week: u8,
+    hour: u8,
+    minute: u8,
+    second: u8,
+) -> Result<DateTime, Error> {
+    let date = chrono::NaiveDate::from_ymd_opt(year.into(), month.try_into().unwrap(), day.into())
+        .ok_or(Error::InvalidDate)?;
+    let time = chrono::NaiveTime::from_hms_opt(hour.into(), minute.into(), second.into()).ok_or(Error::InvalidTime)?;
+    Ok(DateTime::new(date, time))
+}
diff --git a/embassy-stm32/src/rtc/datetime_no_deps.rs b/embassy-stm32/src/rtc/datetime_no_deps.rs
new file mode 100644
index 000000000..173f38377
--- /dev/null
+++ b/embassy-stm32/src/rtc/datetime_no_deps.rs
@@ -0,0 +1,146 @@
+use super::byte_to_bcd2;
+use crate::pac::rtc::Rtc;
+
+/// Errors regarding the [`DateTime`] struct.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum Error {
+    /// The [DateTime] contains an invalid year value. Must be between `0..=4095`.
+    InvalidYear,
+    /// The [DateTime] contains an invalid month value. Must be between `1..=12`.
+    InvalidMonth,
+    /// The [DateTime] contains an invalid day value. Must be between `1..=31`.
+    InvalidDay,
+    /// The [DateTime] contains an invalid day of week. Must be between `0..=6` where 0 is Sunday.
+    InvalidDayOfWeek(
+        /// The value of the DayOfWeek that was given.
+        u8,
+    ),
+    /// The [DateTime] contains an invalid hour value. Must be between `0..=23`.
+    InvalidHour,
+    /// The [DateTime] contains an invalid minute value. Must be between `0..=59`.
+    InvalidMinute,
+    /// The [DateTime] contains an invalid second value. Must be between `0..=59`.
+    InvalidSecond,
+}
+
+/// Structure containing date and time information
+pub struct DateTime {
+    /// 0..4095
+    pub year: u16,
+    /// 1..12, 1 is January
+    pub month: u8,
+    /// 1..28,29,30,31 depending on month
+    pub day: u8,
+    ///
+    pub day_of_week: DayOfWeek,
+    /// 0..23
+    pub hour: u8,
+    /// 0..59
+    pub minute: u8,
+    /// 0..59
+    pub second: u8,
+}
+
+/// A day of the week
+#[repr(u8)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
+#[allow(missing_docs)]
+pub enum DayOfWeek {
+    Monday = 0,
+    Tuesday = 1,
+    Wednesday = 2,
+    Thursday = 3,
+    Friday = 4,
+    Saturday = 5,
+    Sunday = 6,
+}
+
+fn day_of_week_from_u8(v: u8) -> Result<DayOfWeek, Error> {
+    Ok(match v {
+        0 => DayOfWeek::Monday,
+        1 => DayOfWeek::Tuesday,
+        2 => DayOfWeek::Wednesday,
+        3 => DayOfWeek::Thursday,
+        4 => DayOfWeek::Friday,
+        5 => DayOfWeek::Saturday,
+        6 => DayOfWeek::Sunday,
+        x => return Err(Error::InvalidDayOfWeek(x)),
+    })
+}
+
+pub(super) fn day_of_week_to_u8(dotw: DayOfWeek) -> u8 {
+    dotw as u8
+}
+
+pub(super) fn validate_datetime(dt: &DateTime) -> Result<(), Error> {
+    if dt.year > 4095 {
+        Err(Error::InvalidYear)
+    } else if dt.month < 1 || dt.month > 12 {
+        Err(Error::InvalidMonth)
+    } else if dt.day < 1 || dt.day > 31 {
+        Err(Error::InvalidDay)
+    } else if dt.hour > 23 {
+        Err(Error::InvalidHour)
+    } else if dt.minute > 59 {
+        Err(Error::InvalidMinute)
+    } else if dt.second > 59 {
+        Err(Error::InvalidSecond)
+    } else {
+        Ok(())
+    }
+}
+
+pub(super) fn write_date_time(rtc: &Rtc, t: DateTime) {
+    let (ht, hu) = byte_to_bcd2(t.hour as u8);
+    let (mnt, mnu) = byte_to_bcd2(t.minute as u8);
+    let (st, su) = byte_to_bcd2(t.second as u8);
+
+    let (dt, du) = byte_to_bcd2(t.day as u8);
+    let (mt, mu) = byte_to_bcd2(t.month as u8);
+    let yr = t.year as u16;
+    let yr_offset = (yr - 1970_u16) as u8;
+    let (yt, yu) = byte_to_bcd2(yr_offset);
+
+    unsafe {
+        rtc.tr().write(|w| {
+            w.set_ht(ht);
+            w.set_hu(hu);
+            w.set_mnt(mnt);
+            w.set_mnu(mnu);
+            w.set_st(st);
+            w.set_su(su);
+            w.set_pm(stm32_metapac::rtc::vals::Ampm::AM);
+        });
+
+        rtc.dr().write(|w| {
+            w.set_dt(dt);
+            w.set_du(du);
+            w.set_mt(mt > 0);
+            w.set_mu(mu);
+            w.set_yt(yt);
+            w.set_yu(yu);
+            w.set_wdu(day_of_week_to_u8(t.day_of_week));
+        });
+    }
+}
+
+pub(super) fn datetime(
+    year: u16,
+    month: u8,
+    day: u8,
+    day_of_week: u8,
+    hour: u8,
+    minute: u8,
+    second: u8,
+) -> Result<DateTime, Error> {
+    let day_of_week = day_of_week_from_u8(day_of_week)?;
+    Ok(DateTime {
+        year,
+        month,
+        day,
+        day_of_week,
+        hour,
+        minute,
+        second,
+    })
+}
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs
new file mode 100644
index 000000000..ee3349b27
--- /dev/null
+++ b/embassy-stm32/src/rtc/mod.rs
@@ -0,0 +1,238 @@
+//! RTC peripheral abstraction
+use core::marker::PhantomData;
+
+#[cfg_attr(feature = "chrono", path = "datetime_chrono.rs")]
+#[cfg_attr(not(feature = "chrono"), path = "datetime_no_deps.rs")]
+mod datetime;
+
+pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
+
+/// refer to AN4759 to compare features of RTC2 and RTC3
+#[cfg_attr(any(rtc_v1), path = "v1.rs")]
+#[cfg_attr(
+    any(
+        rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb
+    ),
+    path = "v2/mod.rs"
+)]
+#[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")]
+mod versions;
+use embassy_hal_common::Peripheral;
+pub use versions::*;
+
+/// Errors that can occur on methods on [RtcClock]
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum RtcError {
+    /// An invalid DateTime was given or stored on the hardware.
+    InvalidDateTime(DateTimeError),
+
+    /// The RTC clock is not running
+    NotRunning,
+}
+
+/// RTC Abstraction
+pub struct Rtc<'d, T: Instance> {
+    phantom: PhantomData<&'d mut T>,
+    rtc_config: RtcConfig,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+#[repr(u8)]
+pub enum RtcClockSource {
+    /// 00: No clock
+    NoClock = 0b00,
+    /// 01: LSE oscillator clock used as RTC clock
+    LSE = 0b01,
+    /// 10: LSI oscillator clock used as RTC clock
+    LSI = 0b10,
+    /// 11: HSE oscillator clock divided by 32 used as RTC clock
+    HSE = 0b11,
+}
+
+#[derive(Copy, Clone, PartialEq)]
+pub struct RtcConfig {
+    /// RTC clock source
+    clock_config: RtcClockSource,
+    /// Asynchronous prescaler factor
+    /// This is the asynchronous division factor:
+    /// ck_apre frequency = RTCCLK frequency/(PREDIV_A+1)
+    /// ck_apre drives the subsecond register
+    async_prescaler: u8,
+    /// Synchronous prescaler factor
+    /// This is the synchronous division factor:
+    /// ck_spre frequency = ck_apre frequency/(PREDIV_S+1)
+    /// ck_spre must be 1Hz
+    sync_prescaler: u16,
+}
+
+impl Default for RtcConfig {
+    /// LSI with prescalers assuming 32.768 kHz.
+    /// Raw sub-seconds in 1/256.
+    fn default() -> Self {
+        RtcConfig {
+            clock_config: RtcClockSource::LSI,
+            async_prescaler: 127,
+            sync_prescaler: 255,
+        }
+    }
+}
+
+impl RtcConfig {
+    /// Sets the clock source of RTC config
+    pub fn clock_config(mut self, cfg: RtcClockSource) -> Self {
+        self.clock_config = cfg;
+        self
+    }
+
+    /// Set the asynchronous prescaler of RTC config
+    pub fn async_prescaler(mut self, prescaler: u8) -> Self {
+        self.async_prescaler = prescaler;
+        self
+    }
+
+    /// Set the synchronous prescaler of RTC config
+    pub fn sync_prescaler(mut self, prescaler: u16) -> Self {
+        self.sync_prescaler = prescaler;
+        self
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+#[repr(u8)]
+pub enum RtcCalibrationCyclePeriod {
+    /// 8-second calibration period
+    Seconds8,
+    /// 16-second calibration period
+    Seconds16,
+    /// 32-second calibration period
+    Seconds32,
+}
+
+impl Default for RtcCalibrationCyclePeriod {
+    fn default() -> Self {
+        RtcCalibrationCyclePeriod::Seconds32
+    }
+}
+
+impl<'d, T: Instance> Rtc<'d, T> {
+    pub fn new(_rtc: impl Peripheral<P = T> + 'd, rtc_config: RtcConfig) -> Self {
+        unsafe { enable_peripheral_clk() };
+
+        let mut rtc_struct = Self {
+            phantom: PhantomData,
+            rtc_config,
+        };
+
+        rtc_struct.apply_config(rtc_config);
+
+        rtc_struct
+    }
+
+    /// Set the datetime to a new value.
+    ///
+    /// # Errors
+    ///
+    /// Will return `RtcError::InvalidDateTime` if the datetime is not a valid range.
+    pub fn set_datetime(&mut self, t: DateTime) -> Result<(), RtcError> {
+        self::datetime::validate_datetime(&t).map_err(RtcError::InvalidDateTime)?;
+        self.write(true, |rtc| self::datetime::write_date_time(rtc, t));
+
+        Ok(())
+    }
+
+    /// Return the current datetime.
+    ///
+    /// # Errors
+    ///
+    /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`].
+    pub fn now(&self) -> Result<DateTime, RtcError> {
+        let r = T::regs();
+        unsafe {
+            let tr = r.tr().read();
+            let second = bcd2_to_byte((tr.st(), tr.su()));
+            let minute = bcd2_to_byte((tr.mnt(), tr.mnu()));
+            let hour = bcd2_to_byte((tr.ht(), tr.hu()));
+            // Reading either RTC_SSR or RTC_TR locks the values in the higher-order
+            // calendar shadow registers until RTC_DR is read.
+            let dr = r.dr().read();
+
+            let weekday = dr.wdu();
+            let day = bcd2_to_byte((dr.dt(), dr.du()));
+            let month = bcd2_to_byte((dr.mt() as u8, dr.mu()));
+            let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16;
+
+            self::datetime::datetime(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime)
+        }
+    }
+
+    /// Check if daylight savings time is active.
+    pub fn get_daylight_savings(&self) -> bool {
+        let cr = unsafe { T::regs().cr().read() };
+        cr.bkp()
+    }
+
+    /// Enable/disable daylight savings time.
+    pub fn set_daylight_savings(&mut self, daylight_savings: bool) {
+        self.write(true, |rtc| {
+            unsafe { rtc.cr().modify(|w| w.set_bkp(daylight_savings)) };
+        })
+    }
+
+    pub fn get_config(&self) -> RtcConfig {
+        self.rtc_config
+    }
+
+    pub const BACKUP_REGISTER_COUNT: usize = BACKUP_REGISTER_COUNT;
+
+    /// Read content of the backup register.
+    ///
+    /// The registers retain their values during wakes from standby mode or system resets. They also
+    /// retain their value when Vdd is switched off as long as V_BAT is powered.
+    pub fn read_backup_register(&self, register: usize) -> Option<u32> {
+        read_backup_register(&T::regs(), register)
+    }
+
+    /// Set content of the backup register.
+    ///
+    /// The registers retain their values during wakes from standby mode or system resets. They also
+    /// retain their value when Vdd is switched off as long as V_BAT is powered.
+    pub fn write_backup_register(&self, register: usize, value: u32) {
+        write_backup_register(&T::regs(), register, value)
+    }
+}
+
+pub(crate) fn byte_to_bcd2(byte: u8) -> (u8, u8) {
+    let mut bcd_high: u8 = 0;
+    let mut value = byte;
+
+    while value >= 10 {
+        bcd_high += 1;
+        value -= 10;
+    }
+
+    (bcd_high, ((bcd_high << 4) | value) as u8)
+}
+
+pub(crate) fn bcd2_to_byte(bcd: (u8, u8)) -> u8 {
+    let value = bcd.1 | bcd.0 << 4;
+
+    let tmp = ((value & 0xF0) >> 0x4) * 10;
+
+    tmp + (value & 0x0F)
+}
+
+pub(crate) mod sealed {
+    pub trait Instance {
+        fn regs() -> crate::pac::rtc::Rtc;
+    }
+}
+
+pub trait Instance: sealed::Instance + 'static {}
+
+impl sealed::Instance for crate::peripherals::RTC {
+    fn regs() -> crate::pac::rtc::Rtc {
+        crate::pac::RTC
+    }
+}
+
+impl Instance for crate::peripherals::RTC {}
diff --git a/embassy-stm32/src/rtc/v2/mod.rs b/embassy-stm32/src/rtc/v2/mod.rs
new file mode 100644
index 000000000..296adae89
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/mod.rs
@@ -0,0 +1,171 @@
+use stm32_metapac::rtc::vals::{Init, Osel, Pol};
+
+use super::{Instance, RtcConfig};
+use crate::pac::rtc::Rtc;
+
+#[cfg_attr(rtc_v2f0, path = "v2f0.rs")]
+#[cfg_attr(rtc_v2f2, path = "v2f2.rs")]
+#[cfg_attr(rtc_v2f3, path = "v2f3.rs")]
+#[cfg_attr(rtc_v2f4, path = "v2f4.rs")]
+#[cfg_attr(rtc_v2f7, path = "v2f7.rs")]
+#[cfg_attr(rtc_v2h7, path = "v2h7.rs")]
+#[cfg_attr(rtc_v2l0, path = "v2l0.rs")]
+#[cfg_attr(rtc_v2l1, path = "v2l1.rs")]
+#[cfg_attr(rtc_v2l4, path = "v2l4.rs")]
+#[cfg_attr(rtc_v2wb, path = "v2wb.rs")]
+mod family;
+
+pub use family::*;
+
+impl<'d, T: Instance> super::Rtc<'d, T> {
+    /// Applies the RTC config
+    /// It this changes the RTC clock source the time will be reset
+    pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) {
+        // Unlock the backup domain
+        unsafe {
+            unlock_backup_domain(rtc_config.clock_config as u8);
+        }
+
+        self.write(true, |rtc| unsafe {
+            rtc.cr().modify(|w| {
+                #[cfg(rtc_v2f2)]
+                w.set_fmt(false);
+                #[cfg(not(rtc_v2f2))]
+                w.set_fmt(stm32_metapac::rtc::vals::Fmt::TWENTY_FOUR_HOUR);
+                w.set_osel(Osel::DISABLED);
+                w.set_pol(Pol::HIGH);
+            });
+
+            rtc.prer().modify(|w| {
+                w.set_prediv_s(rtc_config.sync_prescaler);
+                w.set_prediv_a(rtc_config.async_prescaler);
+            });
+        });
+
+        self.rtc_config = rtc_config;
+    }
+
+    /// Calibrate the clock drift.
+    ///
+    /// `clock_drift` can be adjusted from -487.1 ppm to 488.5 ppm and is clamped to this range.
+    ///
+    /// ### Note
+    ///
+    /// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler`
+    /// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12).
+    #[cfg(not(rtc_v2f2))]
+    pub fn calibrate(&mut self, mut clock_drift: f32, period: super::RtcCalibrationCyclePeriod) {
+        const RTC_CALR_MIN_PPM: f32 = -487.1;
+        const RTC_CALR_MAX_PPM: f32 = 488.5;
+        const RTC_CALR_RESOLUTION_PPM: f32 = 0.9537;
+
+        if clock_drift < RTC_CALR_MIN_PPM {
+            clock_drift = RTC_CALR_MIN_PPM;
+        } else if clock_drift > RTC_CALR_MAX_PPM {
+            clock_drift = RTC_CALR_MAX_PPM;
+        }
+
+        clock_drift = clock_drift / RTC_CALR_RESOLUTION_PPM;
+
+        self.write(false, |rtc| {
+            unsafe {
+                rtc.calr().write(|w| {
+                    match period {
+                        super::RtcCalibrationCyclePeriod::Seconds8 => {
+                            w.set_calw8(stm32_metapac::rtc::vals::Calw8::EIGHT_SECOND);
+                        }
+                        super::RtcCalibrationCyclePeriod::Seconds16 => {
+                            w.set_calw16(stm32_metapac::rtc::vals::Calw16::SIXTEEN_SECOND);
+                        }
+                        super::RtcCalibrationCyclePeriod::Seconds32 => {
+                            // Set neither `calw8` nor `calw16` to use 32 seconds
+                        }
+                    }
+
+                    // Extra pulses during calibration cycle period: CALP * 512 - CALM
+                    //
+                    // CALP sets whether pulses are added or omitted.
+                    //
+                    // CALM contains how many pulses (out of 512) are masked in a
+                    // given calibration cycle period.
+                    if clock_drift > 0.0 {
+                        // Maximum (about 512.2) rounds to 512.
+                        clock_drift += 0.5;
+
+                        // When the offset is positive (0 to 512), the opposite of
+                        // the offset (512 - offset) is masked, i.e. for the
+                        // maximum offset (512), 0 pulses are masked.
+                        w.set_calp(stm32_metapac::rtc::vals::Calp::INCREASEFREQ);
+                        w.set_calm(512 - clock_drift as u16);
+                    } else {
+                        // Minimum (about -510.7) rounds to -511.
+                        clock_drift -= 0.5;
+
+                        // When the offset is negative or zero (-511 to 0),
+                        // the absolute offset is masked, i.e. for the minimum
+                        // offset (-511), 511 pulses are masked.
+                        w.set_calp(stm32_metapac::rtc::vals::Calp::NOCHANGE);
+                        w.set_calm((clock_drift * -1.0) as u16);
+                    }
+                });
+            }
+        })
+    }
+
+    pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R
+    where
+        F: FnOnce(&crate::pac::rtc::Rtc) -> R,
+    {
+        let r = T::regs();
+        // Disable write protection.
+        // This is safe, as we're only writin the correct and expected values.
+        unsafe {
+            r.wpr().write(|w| w.set_key(0xca));
+            r.wpr().write(|w| w.set_key(0x53));
+
+            // true if initf bit indicates RTC peripheral is in init mode
+            if init_mode && !r.isr().read().initf() {
+                // to update calendar date/time, time format, and prescaler configuration, RTC must be in init mode
+                r.isr().modify(|w| w.set_init(Init::INITMODE));
+                // wait till init state entered
+                // ~2 RTCCLK cycles
+                while !r.isr().read().initf() {}
+            }
+        }
+
+        let result = f(&r);
+
+        unsafe {
+            if init_mode {
+                r.isr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode
+            }
+
+            // Re-enable write protection.
+            // This is safe, as the field accepts the full range of 8-bit values.
+            r.wpr().write(|w| w.set_key(0xff));
+        }
+        result
+    }
+}
+
+/// Read content of the backup register.
+///
+/// The registers retain their values during wakes from standby mode or system resets. They also
+/// retain their value when Vdd is switched off as long as V_BAT is powered.
+pub fn read_backup_register(rtc: &Rtc, register: usize) -> Option<u32> {
+    if register < BACKUP_REGISTER_COUNT {
+        Some(unsafe { rtc.bkpr(register).read().bkp() })
+    } else {
+        None
+    }
+}
+
+/// Set content of the backup register.
+///
+/// The registers retain their values during wakes from standby mode or system resets. They also
+/// retain their value when Vdd is switched off as long as V_BAT is powered.
+pub fn write_backup_register(rtc: &Rtc, register: usize, value: u32) {
+    if register < BACKUP_REGISTER_COUNT {
+        unsafe { rtc.bkpr(register).write(|w| w.set_bkp(value)) }
+    }
+}
diff --git a/embassy-stm32/src/rtc/v2/v2f0.rs b/embassy-stm32/src/rtc/v2/v2f0.rs
new file mode 100644
index 000000000..d6871d91e
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2f0.rs
@@ -0,0 +1,41 @@
+use stm32_metapac::rcc::vals::Rtcsel;
+
+pub const BACKUP_REGISTER_COUNT: usize = 20;
+
+/// Unlock the backup domain
+pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
+    crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
+    while !crate::pac::PWR.cr1().read().dbp() {}
+
+    let reg = crate::pac::RCC.bdcr().read();
+    assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
+
+    if !reg.rtcen() || reg.rtcsel().0 != clock_config {
+        crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
+
+        crate::pac::RCC.bdcr().modify(|w| {
+            // Reset
+            w.set_bdrst(false);
+
+            // Select RTC source
+            w.set_rtcsel(Rtcsel(clock_config));
+            w.set_rtcen(true);
+
+            // Restore bcdr
+            w.set_lscosel(reg.lscosel());
+            w.set_lscoen(reg.lscoen());
+
+            w.set_lseon(reg.lseon());
+            w.set_lsedrv(reg.lsedrv());
+            w.set_lsebyp(reg.lsebyp());
+        });
+    }
+}
+
+pub(crate) unsafe fn enable_peripheral_clk() {
+    // enable peripheral clock for communication
+    crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
+
+    // read to allow the pwr clock to enable
+    crate::pac::PWR.cr1().read();
+}
diff --git a/embassy-stm32/src/rtc/v2/v2f2.rs b/embassy-stm32/src/rtc/v2/v2f2.rs
new file mode 100644
index 000000000..e041f3f4e
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2f2.rs
@@ -0,0 +1,31 @@
+use stm32_metapac::rcc::vals::Rtcsel;
+
+pub const BACKUP_REGISTER_COUNT: usize = 20;
+
+/// Unlock the backup domain
+pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
+    crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
+    while !crate::pac::PWR.cr().read().dbp() {}
+
+    let reg = crate::pac::RCC.bdcr().read();
+
+    if !reg.rtcen() || reg.rtcsel().0 != clock_config {
+        crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
+
+        crate::pac::RCC.bdcr().modify(|w| {
+            // Reset
+            w.set_bdrst(false);
+
+            // Select RTC source
+            w.set_rtcsel(Rtcsel(clock_config));
+            w.set_rtcen(true);
+
+            w.set_lseon(reg.lseon());
+            w.set_lsebyp(reg.lsebyp());
+        });
+    }
+}
+
+pub(crate) unsafe fn enable_peripheral_clk() {
+    // Nothing to do
+}
diff --git a/embassy-stm32/src/rtc/v2/v2f3.rs b/embassy-stm32/src/rtc/v2/v2f3.rs
new file mode 100644
index 000000000..e041f3f4e
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2f3.rs
@@ -0,0 +1,31 @@
+use stm32_metapac::rcc::vals::Rtcsel;
+
+pub const BACKUP_REGISTER_COUNT: usize = 20;
+
+/// Unlock the backup domain
+pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
+    crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
+    while !crate::pac::PWR.cr().read().dbp() {}
+
+    let reg = crate::pac::RCC.bdcr().read();
+
+    if !reg.rtcen() || reg.rtcsel().0 != clock_config {
+        crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
+
+        crate::pac::RCC.bdcr().modify(|w| {
+            // Reset
+            w.set_bdrst(false);
+
+            // Select RTC source
+            w.set_rtcsel(Rtcsel(clock_config));
+            w.set_rtcen(true);
+
+            w.set_lseon(reg.lseon());
+            w.set_lsebyp(reg.lsebyp());
+        });
+    }
+}
+
+pub(crate) unsafe fn enable_peripheral_clk() {
+    // Nothing to do
+}
diff --git a/embassy-stm32/src/rtc/v2/v2f4.rs b/embassy-stm32/src/rtc/v2/v2f4.rs
new file mode 100644
index 000000000..4dd21cae4
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2f4.rs
@@ -0,0 +1,31 @@
+use stm32_metapac::rcc::vals::Rtcsel;
+
+pub const BACKUP_REGISTER_COUNT: usize = 20;
+
+/// Unlock the backup domain
+pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
+    crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
+    while !crate::pac::PWR.cr1().read().dbp() {}
+
+    let reg = crate::pac::RCC.bdcr().read();
+
+    if !reg.rtcen() || reg.rtcsel().0 != clock_config {
+        crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
+
+        crate::pac::RCC.bdcr().modify(|w| {
+            // Reset
+            w.set_bdrst(false);
+
+            // Select RTC source
+            w.set_rtcsel(Rtcsel(clock_config));
+            w.set_rtcen(true);
+
+            w.set_lseon(reg.lseon());
+            w.set_lsebyp(reg.lsebyp());
+        });
+    }
+}
+
+pub(crate) unsafe fn enable_peripheral_clk() {
+    // Nothing to do
+}
diff --git a/embassy-stm32/src/rtc/v2/v2f7.rs b/embassy-stm32/src/rtc/v2/v2f7.rs
new file mode 100644
index 000000000..d6871d91e
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2f7.rs
@@ -0,0 +1,41 @@
+use stm32_metapac::rcc::vals::Rtcsel;
+
+pub const BACKUP_REGISTER_COUNT: usize = 20;
+
+/// Unlock the backup domain
+pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
+    crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
+    while !crate::pac::PWR.cr1().read().dbp() {}
+
+    let reg = crate::pac::RCC.bdcr().read();
+    assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
+
+    if !reg.rtcen() || reg.rtcsel().0 != clock_config {
+        crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
+
+        crate::pac::RCC.bdcr().modify(|w| {
+            // Reset
+            w.set_bdrst(false);
+
+            // Select RTC source
+            w.set_rtcsel(Rtcsel(clock_config));
+            w.set_rtcen(true);
+
+            // Restore bcdr
+            w.set_lscosel(reg.lscosel());
+            w.set_lscoen(reg.lscoen());
+
+            w.set_lseon(reg.lseon());
+            w.set_lsedrv(reg.lsedrv());
+            w.set_lsebyp(reg.lsebyp());
+        });
+    }
+}
+
+pub(crate) unsafe fn enable_peripheral_clk() {
+    // enable peripheral clock for communication
+    crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
+
+    // read to allow the pwr clock to enable
+    crate::pac::PWR.cr1().read();
+}
diff --git a/embassy-stm32/src/rtc/v2/v2h7.rs b/embassy-stm32/src/rtc/v2/v2h7.rs
new file mode 100644
index 000000000..f3b180683
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2h7.rs
@@ -0,0 +1,33 @@
+use stm32_metapac::rcc::vals::Rtcsel;
+
+pub const BACKUP_REGISTER_COUNT: usize = 20;
+
+/// Unlock the backup domain
+pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
+    crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
+    while !crate::pac::PWR.cr1().read().dbp() {}
+
+    let reg = crate::pac::RCC.bdcr().read();
+    assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
+
+    if !reg.rtcen() || reg.rtcsel().0 != clock_config {
+        crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
+
+        crate::pac::RCC.bdcr().modify(|w| {
+            // Reset
+            w.set_bdrst(false);
+
+            // Select RTC source
+            w.set_rtcsel(Rtcsel(clock_config));
+            w.set_rtcen(true);
+
+            w.set_lseon(reg.lseon());
+            w.set_lsedrv(reg.lsedrv());
+            w.set_lsebyp(reg.lsebyp());
+        });
+    }
+}
+
+pub(crate) unsafe fn enable_peripheral_clk() {
+    // Nothing to do
+}
diff --git a/embassy-stm32/src/rtc/v2/v2l0.rs b/embassy-stm32/src/rtc/v2/v2l0.rs
new file mode 100644
index 000000000..8d8005887
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2l0.rs
@@ -0,0 +1,40 @@
+pub const BACKUP_REGISTER_COUNT: usize = 20;
+
+/// Unlock the backup domain
+pub(super) unsafe fn unlock_backup_domain(_clock_config: u8) {
+    // FIXME:
+    // crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
+    // while !crate::pac::PWR.cr1().read().dbp() {}
+
+    // let reg = crate::pac::RCC.bdcr().read();
+    // assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
+
+    // if !reg.rtcen() || reg.rtcsel().0 != clock_config {
+    //     crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
+
+    //     crate::pac::RCC.bdcr().modify(|w| {
+    //         // Reset
+    //         w.set_bdrst(false);
+
+    //         // Select RTC source
+    //         w.set_rtcsel(Rtcsel(clock_config));
+    //         w.set_rtcen(true);
+
+    //         // Restore bcdr
+    //         w.set_lscosel(reg.lscosel());
+    //         w.set_lscoen(reg.lscoen());
+
+    //         w.set_lseon(reg.lseon());
+    //         w.set_lsedrv(reg.lsedrv());
+    //         w.set_lsebyp(reg.lsebyp());
+    //     });
+    // }
+}
+
+pub(crate) unsafe fn enable_peripheral_clk() {
+    // // enable peripheral clock for communication
+    // crate::pac::rcc.apb1enr1().modify(|w| w.set_rtcapben(true));
+
+    // // read to allow the pwr clock to enable
+    // crate::pac::PWR.cr1().read();
+}
diff --git a/embassy-stm32/src/rtc/v2/v2l1.rs b/embassy-stm32/src/rtc/v2/v2l1.rs
new file mode 100644
index 000000000..8d8005887
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2l1.rs
@@ -0,0 +1,40 @@
+pub const BACKUP_REGISTER_COUNT: usize = 20;
+
+/// Unlock the backup domain
+pub(super) unsafe fn unlock_backup_domain(_clock_config: u8) {
+    // FIXME:
+    // crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
+    // while !crate::pac::PWR.cr1().read().dbp() {}
+
+    // let reg = crate::pac::RCC.bdcr().read();
+    // assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
+
+    // if !reg.rtcen() || reg.rtcsel().0 != clock_config {
+    //     crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
+
+    //     crate::pac::RCC.bdcr().modify(|w| {
+    //         // Reset
+    //         w.set_bdrst(false);
+
+    //         // Select RTC source
+    //         w.set_rtcsel(Rtcsel(clock_config));
+    //         w.set_rtcen(true);
+
+    //         // Restore bcdr
+    //         w.set_lscosel(reg.lscosel());
+    //         w.set_lscoen(reg.lscoen());
+
+    //         w.set_lseon(reg.lseon());
+    //         w.set_lsedrv(reg.lsedrv());
+    //         w.set_lsebyp(reg.lsebyp());
+    //     });
+    // }
+}
+
+pub(crate) unsafe fn enable_peripheral_clk() {
+    // // enable peripheral clock for communication
+    // crate::pac::rcc.apb1enr1().modify(|w| w.set_rtcapben(true));
+
+    // // read to allow the pwr clock to enable
+    // crate::pac::PWR.cr1().read();
+}
diff --git a/embassy-stm32/src/rtc/v2/v2l4.rs b/embassy-stm32/src/rtc/v2/v2l4.rs
new file mode 100644
index 000000000..d6871d91e
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2l4.rs
@@ -0,0 +1,41 @@
+use stm32_metapac::rcc::vals::Rtcsel;
+
+pub const BACKUP_REGISTER_COUNT: usize = 20;
+
+/// Unlock the backup domain
+pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
+    crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
+    while !crate::pac::PWR.cr1().read().dbp() {}
+
+    let reg = crate::pac::RCC.bdcr().read();
+    assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
+
+    if !reg.rtcen() || reg.rtcsel().0 != clock_config {
+        crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
+
+        crate::pac::RCC.bdcr().modify(|w| {
+            // Reset
+            w.set_bdrst(false);
+
+            // Select RTC source
+            w.set_rtcsel(Rtcsel(clock_config));
+            w.set_rtcen(true);
+
+            // Restore bcdr
+            w.set_lscosel(reg.lscosel());
+            w.set_lscoen(reg.lscoen());
+
+            w.set_lseon(reg.lseon());
+            w.set_lsedrv(reg.lsedrv());
+            w.set_lsebyp(reg.lsebyp());
+        });
+    }
+}
+
+pub(crate) unsafe fn enable_peripheral_clk() {
+    // enable peripheral clock for communication
+    crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
+
+    // read to allow the pwr clock to enable
+    crate::pac::PWR.cr1().read();
+}
diff --git a/embassy-stm32/src/rtc/v2/v2wb.rs b/embassy-stm32/src/rtc/v2/v2wb.rs
new file mode 100644
index 000000000..98761fa60
--- /dev/null
+++ b/embassy-stm32/src/rtc/v2/v2wb.rs
@@ -0,0 +1,39 @@
+pub const BACKUP_REGISTER_COUNT: usize = 20;
+
+/// Unlock the backup domain
+pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
+    crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
+    while !crate::pac::PWR.cr1().read().dbp() {}
+
+    let reg = crate::pac::RCC.bdcr().read();
+    assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
+
+    if !reg.rtcen() || reg.rtcsel() != clock_config {
+        crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
+
+        crate::pac::RCC.bdcr().modify(|w| {
+            // Reset
+            w.set_bdrst(false);
+
+            // Select RTC source
+            w.set_rtcsel(clock_config);
+            w.set_rtcen(true);
+
+            // Restore bcdr
+            w.set_lscosel(reg.lscosel());
+            w.set_lscoen(reg.lscoen());
+
+            w.set_lseon(reg.lseon());
+            w.set_lsedrv(reg.lsedrv());
+            w.set_lsebyp(reg.lsebyp());
+        });
+    }
+}
+
+pub(crate) unsafe fn enable_peripheral_clk() {
+    // enable peripheral clock for communication
+    crate::pac::RCC.apb1enr1().modify(|w| w.set_rtcapben(true));
+
+    // read to allow the pwr clock to enable
+    crate::pac::PWR.cr1().read();
+}
diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs
new file mode 100644
index 000000000..7255e97eb
--- /dev/null
+++ b/embassy-stm32/src/rtc/v3.rs
@@ -0,0 +1,212 @@
+use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Init, Key, Osel, Pol, TampalrmPu, TampalrmType};
+
+use super::{Instance, RtcCalibrationCyclePeriod, RtcConfig};
+use crate::pac::rtc::Rtc;
+
+impl<'d, T: Instance> super::Rtc<'d, T> {
+    /// Applies the RTC config
+    /// It this changes the RTC clock source the time will be reset
+    pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) {
+        // Unlock the backup domain
+        unsafe {
+            #[cfg(feature = "stm32g0c1ve")]
+            {
+                crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
+                while !crate::pac::PWR.cr1().read().dbp() {}
+            }
+
+            #[cfg(not(feature = "stm32g0c1ve"))]
+            {
+                crate::pac::PWR
+                    .cr1()
+                    .modify(|w| w.set_dbp(stm32_metapac::pwr::vals::Dbp::ENABLED));
+                while crate::pac::PWR.cr1().read().dbp() != stm32_metapac::pwr::vals::Dbp::DISABLED {}
+            }
+
+            let reg = crate::pac::RCC.bdcr().read();
+            assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
+
+            let config_rtcsel = rtc_config.clock_config as u8;
+            #[cfg(rtc_v3)]
+            #[cfg(not(any(feature = "stm32wl54jc-cm0p", feature = "stm32wle5ub", feature = "stm32g0c1ve")))]
+            let config_rtcsel = stm32_metapac::rtc::vals::Rtcsel(config_rtcsel);
+            #[cfg(feature = "stm32g0c1ve")]
+            let config_rtcsel = stm32_metapac::rcc::vals::Rtcsel(config_rtcsel);
+
+            if !reg.rtcen() || reg.rtcsel() != config_rtcsel {
+                crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
+
+                crate::pac::RCC.bdcr().modify(|w| {
+                    // Reset
+                    w.set_bdrst(false);
+
+                    // Select RTC source
+                    w.set_rtcsel(config_rtcsel);
+
+                    w.set_rtcen(true);
+
+                    // Restore bcdr
+                    w.set_lscosel(reg.lscosel());
+                    w.set_lscoen(reg.lscoen());
+
+                    w.set_lseon(reg.lseon());
+                    w.set_lsedrv(reg.lsedrv());
+                    w.set_lsebyp(reg.lsebyp());
+                });
+            }
+        }
+
+        self.write(true, |rtc| {
+            unsafe {
+                rtc.cr().modify(|w| {
+                    w.set_fmt(Fmt::TWENTYFOURHOUR);
+                    w.set_osel(Osel::DISABLED);
+                    w.set_pol(Pol::HIGH);
+                });
+
+                rtc.prer().modify(|w| {
+                    w.set_prediv_s(rtc_config.sync_prescaler);
+                    w.set_prediv_a(rtc_config.async_prescaler);
+                });
+
+                // TODO: configuration for output pins
+                rtc.cr().modify(|w| {
+                    w.set_out2en(false);
+                    w.set_tampalrm_type(TampalrmType::PUSHPULL);
+                    w.set_tampalrm_pu(TampalrmPu::NOPULLUP);
+                });
+            }
+        });
+
+        self.rtc_config = rtc_config;
+    }
+
+    const RTC_CALR_MIN_PPM: f32 = -487.1;
+    const RTC_CALR_MAX_PPM: f32 = 488.5;
+    const RTC_CALR_RESOLUTION_PPM: f32 = 0.9537;
+
+    /// Calibrate the clock drift.
+    ///
+    /// `clock_drift` can be adjusted from -487.1 ppm to 488.5 ppm and is clamped to this range.
+    ///
+    /// ### Note
+    ///
+    /// To perform a calibration when `async_prescaler` is less then 3, `sync_prescaler`
+    /// has to be reduced accordingly (see RM0351 Rev 9, sec 38.3.12).
+    pub fn calibrate(&mut self, mut clock_drift: f32, period: RtcCalibrationCyclePeriod) {
+        if clock_drift < Self::RTC_CALR_MIN_PPM {
+            clock_drift = Self::RTC_CALR_MIN_PPM;
+        } else if clock_drift > Self::RTC_CALR_MAX_PPM {
+            clock_drift = Self::RTC_CALR_MAX_PPM;
+        }
+
+        clock_drift = clock_drift / Self::RTC_CALR_RESOLUTION_PPM;
+
+        self.write(false, |rtc| {
+            unsafe {
+                rtc.calr().write(|w| {
+                    match period {
+                        RtcCalibrationCyclePeriod::Seconds8 => {
+                            w.set_calw8(Calw8::EIGHTSECONDS);
+                        }
+                        RtcCalibrationCyclePeriod::Seconds16 => {
+                            w.set_calw16(Calw16::SIXTEENSECONDS);
+                        }
+                        RtcCalibrationCyclePeriod::Seconds32 => {
+                            // Set neither `calw8` nor `calw16` to use 32 seconds
+                        }
+                    }
+
+                    // Extra pulses during calibration cycle period: CALP * 512 - CALM
+                    //
+                    // CALP sets whether pulses are added or omitted.
+                    //
+                    // CALM contains how many pulses (out of 512) are masked in a
+                    // given calibration cycle period.
+                    if clock_drift > 0.0 {
+                        // Maximum (about 512.2) rounds to 512.
+                        clock_drift += 0.5;
+
+                        // When the offset is positive (0 to 512), the opposite of
+                        // the offset (512 - offset) is masked, i.e. for the
+                        // maximum offset (512), 0 pulses are masked.
+                        w.set_calp(Calp::INCREASEFREQ);
+                        w.set_calm(512 - clock_drift as u16);
+                    } else {
+                        // Minimum (about -510.7) rounds to -511.
+                        clock_drift -= 0.5;
+
+                        // When the offset is negative or zero (-511 to 0),
+                        // the absolute offset is masked, i.e. for the minimum
+                        // offset (-511), 511 pulses are masked.
+                        w.set_calp(Calp::NOCHANGE);
+                        w.set_calm((clock_drift * -1.0) as u16);
+                    }
+                });
+            }
+        })
+    }
+
+    pub(super) fn write<F, R>(&mut self, init_mode: bool, f: F) -> R
+    where
+        F: FnOnce(&crate::pac::rtc::Rtc) -> R,
+    {
+        let r = T::regs();
+        // Disable write protection.
+        // This is safe, as we're only writin the correct and expected values.
+        unsafe {
+            r.wpr().write(|w| w.set_key(Key::DEACTIVATE1));
+            r.wpr().write(|w| w.set_key(Key::DEACTIVATE2));
+
+            if init_mode && !r.icsr().read().initf() {
+                r.icsr().modify(|w| w.set_init(Init::INITMODE));
+                // wait till init state entered
+                // ~2 RTCCLK cycles
+                while !r.icsr().read().initf() {}
+            }
+        }
+
+        let result = f(&r);
+
+        unsafe {
+            if init_mode {
+                r.icsr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode
+            }
+
+            // Re-enable write protection.
+            // This is safe, as the field accepts the full range of 8-bit values.
+            r.wpr().write(|w| w.set_key(Key::ACTIVATE));
+        }
+        result
+    }
+}
+
+pub(super) unsafe fn enable_peripheral_clk() {
+    // Nothing to do
+}
+
+pub const BACKUP_REGISTER_COUNT: usize = 32;
+
+/// Read content of the backup register.
+///
+/// The registers retain their values during wakes from standby mode or system resets. They also
+/// retain their value when Vdd is switched off as long as V_BAT is powered.
+pub fn read_backup_register(_rtc: &Rtc, register: usize) -> Option<u32> {
+    if register < BACKUP_REGISTER_COUNT {
+        //Some(rtc.bkpr()[register].read().bits())
+        None // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC
+    } else {
+        None
+    }
+}
+
+/// Set content of the backup register.
+///
+/// The registers retain their values during wakes from standby mode or system resets. They also
+/// retain their value when Vdd is switched off as long as V_BAT is powered.
+pub fn write_backup_register(_rtc: &Rtc, register: usize, _value: u32) {
+    if register < BACKUP_REGISTER_COUNT {
+        // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC
+        //unsafe { self.rtc.bkpr()[register].write(|w| w.bits(value)) }
+    }
+}

From 79cee7415110817b529a68848247a86708bcd6af Mon Sep 17 00:00:00 2001
From: Mathias <mk@blackbird.online>
Date: Tue, 11 Oct 2022 09:19:47 +0200
Subject: [PATCH 02/12] Fix stm32wl55jc-cm4 RTC

---
 embassy-stm32/src/rtc/v3.rs | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs
index 7255e97eb..dfe27ed6e 100644
--- a/embassy-stm32/src/rtc/v3.rs
+++ b/embassy-stm32/src/rtc/v3.rs
@@ -28,7 +28,12 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
 
             let config_rtcsel = rtc_config.clock_config as u8;
             #[cfg(rtc_v3)]
-            #[cfg(not(any(feature = "stm32wl54jc-cm0p", feature = "stm32wle5ub", feature = "stm32g0c1ve")))]
+            #[cfg(not(any(
+                feature = "stm32wl54jc-cm0p",
+                feature = "stm32wle5ub",
+                feature = "stm32g0c1ve",
+                feature = "stm32wl55jc-cm4"
+            )))]
             let config_rtcsel = stm32_metapac::rtc::vals::Rtcsel(config_rtcsel);
             #[cfg(feature = "stm32g0c1ve")]
             let config_rtcsel = stm32_metapac::rcc::vals::Rtcsel(config_rtcsel);

From 9223b67306fcdf41b03d2a9e5d833e5b71b44954 Mon Sep 17 00:00:00 2001
From: Mathias <mk@blackbird.online>
Date: Tue, 11 Oct 2022 10:28:28 +0200
Subject: [PATCH 03/12] Fix RTC for v2l0 & v2l1

---
 embassy-stm32/src/rtc/v2/v2l0.rs | 46 +++++++++++---------------------
 embassy-stm32/src/rtc/v2/v2l1.rs | 44 ++++++++++--------------------
 embassy-stm32/src/rtc/v3.rs      |  3 ++-
 3 files changed, 32 insertions(+), 61 deletions(-)

diff --git a/embassy-stm32/src/rtc/v2/v2l0.rs b/embassy-stm32/src/rtc/v2/v2l0.rs
index 8d8005887..dbd3b0882 100644
--- a/embassy-stm32/src/rtc/v2/v2l0.rs
+++ b/embassy-stm32/src/rtc/v2/v2l0.rs
@@ -1,40 +1,26 @@
 pub const BACKUP_REGISTER_COUNT: usize = 20;
 
 /// Unlock the backup domain
-pub(super) unsafe fn unlock_backup_domain(_clock_config: u8) {
-    // FIXME:
-    // crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
-    // while !crate::pac::PWR.cr1().read().dbp() {}
+pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
+    // TODO: Missing from PAC?
+    // crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
+    // while !crate::pac::PWR.cr().read().dbp() {}
 
-    // let reg = crate::pac::RCC.bdcr().read();
-    // assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
+    let reg = crate::pac::RCC.csr().read();
 
-    // if !reg.rtcen() || reg.rtcsel().0 != clock_config {
-    //     crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
+    if !reg.rtcen() || reg.rtcsel().0 != clock_config {
+        crate::pac::RCC.csr().modify(|w| {
+            // Select RTC source
+            w.set_rtcsel(crate::pac::rcc::vals::Rtcsel(clock_config));
+            w.set_rtcen(true);
 
-    //     crate::pac::RCC.bdcr().modify(|w| {
-    //         // Reset
-    //         w.set_bdrst(false);
-
-    //         // Select RTC source
-    //         w.set_rtcsel(Rtcsel(clock_config));
-    //         w.set_rtcen(true);
-
-    //         // Restore bcdr
-    //         w.set_lscosel(reg.lscosel());
-    //         w.set_lscoen(reg.lscoen());
-
-    //         w.set_lseon(reg.lseon());
-    //         w.set_lsedrv(reg.lsedrv());
-    //         w.set_lsebyp(reg.lsebyp());
-    //     });
-    // }
+            w.set_lseon(reg.lseon());
+            w.set_lsedrv(reg.lsedrv());
+            w.set_lsebyp(reg.lsebyp());
+        });
+    }
 }
 
 pub(crate) unsafe fn enable_peripheral_clk() {
-    // // enable peripheral clock for communication
-    // crate::pac::rcc.apb1enr1().modify(|w| w.set_rtcapben(true));
-
-    // // read to allow the pwr clock to enable
-    // crate::pac::PWR.cr1().read();
+    // Nothing to do
 }
diff --git a/embassy-stm32/src/rtc/v2/v2l1.rs b/embassy-stm32/src/rtc/v2/v2l1.rs
index 8d8005887..1ac78b31a 100644
--- a/embassy-stm32/src/rtc/v2/v2l1.rs
+++ b/embassy-stm32/src/rtc/v2/v2l1.rs
@@ -1,40 +1,24 @@
 pub const BACKUP_REGISTER_COUNT: usize = 20;
 
 /// Unlock the backup domain
-pub(super) unsafe fn unlock_backup_domain(_clock_config: u8) {
-    // FIXME:
-    // crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
-    // while !crate::pac::PWR.cr1().read().dbp() {}
+pub(super) unsafe fn unlock_backup_domain(clock_config: u8) {
+    crate::pac::PWR.cr().modify(|w| w.set_dbp(true));
+    while !crate::pac::PWR.cr().read().dbp() {}
 
-    // let reg = crate::pac::RCC.bdcr().read();
-    // assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
+    let reg = crate::pac::RCC.csr().read();
 
-    // if !reg.rtcen() || reg.rtcsel().0 != clock_config {
-    //     crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
+    if !reg.rtcen() || reg.rtcsel().0 != clock_config {
+        crate::pac::RCC.csr().modify(|w| {
+            // Select RTC source
+            w.set_rtcsel(crate::pac::rcc::vals::Rtcsel(clock_config));
+            w.set_rtcen(true);
 
-    //     crate::pac::RCC.bdcr().modify(|w| {
-    //         // Reset
-    //         w.set_bdrst(false);
-
-    //         // Select RTC source
-    //         w.set_rtcsel(Rtcsel(clock_config));
-    //         w.set_rtcen(true);
-
-    //         // Restore bcdr
-    //         w.set_lscosel(reg.lscosel());
-    //         w.set_lscoen(reg.lscoen());
-
-    //         w.set_lseon(reg.lseon());
-    //         w.set_lsedrv(reg.lsedrv());
-    //         w.set_lsebyp(reg.lsebyp());
-    //     });
-    // }
+            w.set_lseon(reg.lseon());
+            w.set_lsebyp(reg.lsebyp());
+        });
+    }
 }
 
 pub(crate) unsafe fn enable_peripheral_clk() {
-    // // enable peripheral clock for communication
-    // crate::pac::rcc.apb1enr1().modify(|w| w.set_rtcapben(true));
-
-    // // read to allow the pwr clock to enable
-    // crate::pac::PWR.cr1().read();
+    // Nothing to do
 }
diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs
index dfe27ed6e..c9a794c3a 100644
--- a/embassy-stm32/src/rtc/v3.rs
+++ b/embassy-stm32/src/rtc/v3.rs
@@ -32,7 +32,8 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
                 feature = "stm32wl54jc-cm0p",
                 feature = "stm32wle5ub",
                 feature = "stm32g0c1ve",
-                feature = "stm32wl55jc-cm4"
+                feature = "stm32wl55jc-cm4",
+                feature = "stm32wl55uc-cm4"
             )))]
             let config_rtcsel = stm32_metapac::rtc::vals::Rtcsel(config_rtcsel);
             #[cfg(feature = "stm32g0c1ve")]

From 86113e199f37fe0888979608a08bfdaf21bff19a Mon Sep 17 00:00:00 2001
From: Mathias <mk@blackbird.online>
Date: Tue, 11 Oct 2022 10:35:43 +0200
Subject: [PATCH 04/12] Remove unused feature gate

---
 embassy-stm32/src/rtc/v3.rs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs
index c9a794c3a..eba67c28f 100644
--- a/embassy-stm32/src/rtc/v3.rs
+++ b/embassy-stm32/src/rtc/v3.rs
@@ -27,7 +27,6 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
             assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
 
             let config_rtcsel = rtc_config.clock_config as u8;
-            #[cfg(rtc_v3)]
             #[cfg(not(any(
                 feature = "stm32wl54jc-cm0p",
                 feature = "stm32wle5ub",

From 5e74926907b48b7f778de2ff4dfd6f1fd6e24dcb Mon Sep 17 00:00:00 2001
From: Mathias <mk@blackbird.online>
Date: Mon, 13 Feb 2023 15:12:33 +0100
Subject: [PATCH 05/12] feature-gate variants without vals defined

---
 embassy-stm32/src/rtc/v3.rs | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs
index eba67c28f..6998c48c2 100644
--- a/embassy-stm32/src/rtc/v3.rs
+++ b/embassy-stm32/src/rtc/v3.rs
@@ -15,7 +15,12 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
                 while !crate::pac::PWR.cr1().read().dbp() {}
             }
 
-            #[cfg(not(feature = "stm32g0c1ve"))]
+            #[cfg(not(any(
+                feature = "stm32g0c1ve",
+                feature = "stm32g491re",
+                feature = "stm32u585zi",
+                feature = "stm32g473cc"
+            )))]
             {
                 crate::pac::PWR
                     .cr1()
@@ -32,7 +37,11 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
                 feature = "stm32wle5ub",
                 feature = "stm32g0c1ve",
                 feature = "stm32wl55jc-cm4",
-                feature = "stm32wl55uc-cm4"
+                feature = "stm32wl55uc-cm4",
+                feature = "stm32g491re",
+                feature = "stm32g473cc",
+                feature = "stm32u585zi",
+                feature = "stm32wle5jb"
             )))]
             let config_rtcsel = stm32_metapac::rtc::vals::Rtcsel(config_rtcsel);
             #[cfg(feature = "stm32g0c1ve")]

From 8da9c07a656f375b62f76cdb601b6eba75b6d9d0 Mon Sep 17 00:00:00 2001
From: xoviat <xoviat@users.noreply.github.com>
Date: Sun, 16 Apr 2023 11:06:05 -0500
Subject: [PATCH 06/12] stm32/rtc: disable nonworking versions

---
 embassy-stm32/src/lib.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 70e6aa2bf..675fd1ec1 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -49,7 +49,7 @@ pub mod pwm;
 pub mod qspi;
 #[cfg(rng)]
 pub mod rng;
-#[cfg(all(rtc, not(rtc_v1)))]
+#[cfg(all(rtc, not(any(rtc_v1, rtc_v2f0, rtc_v2f7, rtc_v3))))]
 pub mod rtc;
 #[cfg(sdmmc)]
 pub mod sdmmc;

From bc550cbfda773483dd1a90e87c2300f627695cea Mon Sep 17 00:00:00 2001
From: xoviat <xoviat@users.noreply.github.com>
Date: Sun, 16 Apr 2023 11:06:24 -0500
Subject: [PATCH 07/12] adjust .vscode file

---
 .vscode/settings.json | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.vscode/settings.json b/.vscode/settings.json
index 700804dca..9ef7fe1ce 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -16,10 +16,11 @@
     // "embassy-executor/Cargo.toml",
     // "embassy-sync/Cargo.toml",
     "examples/nrf52840/Cargo.toml",
-    //"examples/nrf5340/Cargo.toml",
+    // "examples/nrf5340/Cargo.toml",
     // "examples/nrf-rtos-trace/Cargo.toml",
     // "examples/rp/Cargo.toml",
     // "examples/std/Cargo.toml",
+    // "examples/stm32c0/Cargo.toml",
     // "examples/stm32f0/Cargo.toml",
     // "examples/stm32f1/Cargo.toml",
     // "examples/stm32f2/Cargo.toml",
@@ -28,6 +29,7 @@
     // "examples/stm32f7/Cargo.toml",
     // "examples/stm32g0/Cargo.toml",
     // "examples/stm32g4/Cargo.toml",
+    // "examples/stm32h5/Cargo.toml",
     // "examples/stm32h7/Cargo.toml",
     // "examples/stm32l0/Cargo.toml",
     // "examples/stm32l1/Cargo.toml",
@@ -35,9 +37,7 @@
     // "examples/stm32l5/Cargo.toml",
     // "examples/stm32u5/Cargo.toml",
     // "examples/stm32wb/Cargo.toml",
-    // "examples/stm32wb55/Cargo.toml",
     // "examples/stm32wl/Cargo.toml",
-    // "examples/stm32wl55/Cargo.toml",
     // "examples/wasm/Cargo.toml",
   ],
 }
\ No newline at end of file

From e9ede443bca345f8651a7d8229b319432650dfce Mon Sep 17 00:00:00 2001
From: xoviat <xoviat@users.noreply.github.com>
Date: Sun, 16 Apr 2023 11:14:17 -0500
Subject: [PATCH 08/12] stm32/rtc: disable nonworking versions

---
 embassy-stm32/src/lib.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 675fd1ec1..0dbc9e5c8 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -49,7 +49,7 @@ pub mod pwm;
 pub mod qspi;
 #[cfg(rng)]
 pub mod rng;
-#[cfg(all(rtc, not(any(rtc_v1, rtc_v2f0, rtc_v2f7, rtc_v3))))]
+#[cfg(all(rtc, not(any(rtc_v1, rtc_v2f0, rtc_v2f7, rtc_v3, rtc_v3u5))))]
 pub mod rtc;
 #[cfg(sdmmc)]
 pub mod sdmmc;

From 776e001b5befc50ff409b5cd8252bbb61fbb5acc Mon Sep 17 00:00:00 2001
From: xoviat <xoviat@users.noreply.github.com>
Date: Sun, 16 Apr 2023 17:47:25 -0500
Subject: [PATCH 09/12] stm32: remove TIMX singleton when used on timer driver

fixes #1316.
---
 embassy-hal-common/src/macros.rs |  25 ++++++-
 embassy-stm32/build.rs           | 114 ++++++++++++++++++-------------
 2 files changed, 92 insertions(+), 47 deletions(-)

diff --git a/embassy-hal-common/src/macros.rs b/embassy-hal-common/src/macros.rs
index 5e62e048a..f06b46002 100644
--- a/embassy-hal-common/src/macros.rs
+++ b/embassy-hal-common/src/macros.rs
@@ -1,5 +1,5 @@
 #[macro_export]
-macro_rules! peripherals {
+macro_rules! peripherals_definition {
     ($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
         /// Types for the peripheral singletons.
         pub mod peripherals {
@@ -26,7 +26,12 @@ macro_rules! peripherals {
                 $crate::impl_peripheral!($name);
             )*
         }
+    };
+}
 
+#[macro_export]
+macro_rules! peripherals_struct {
+    ($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
         /// Struct containing all the peripheral singletons.
         ///
         /// To obtain the peripherals, you must initialize the HAL, by calling [`crate::init`].
@@ -76,6 +81,24 @@ macro_rules! peripherals {
     };
 }
 
+#[macro_export]
+macro_rules! peripherals {
+    ($($(#[$cfg:meta])? $name:ident),*$(,)?) => {
+        $crate::peripherals_definition!(
+            $(
+                $(#[$cfg])?
+                $name,
+            )*
+        );
+        $crate::peripherals_struct!(
+            $(
+                $(#[$cfg])?
+                $name,
+            )*
+        );
+    };
+}
+
 #[macro_export]
 macro_rules! into_ref {
     ($($name:ident),*) => {
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 73bd29fcf..bb6c99963 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -81,11 +81,74 @@ fn main() {
         singletons.push(c.name.to_string());
     }
 
+    // ========
+    // Handle time-driver-XXXX features.
+
+    let time_driver = match env::vars()
+        .map(|(a, _)| a)
+        .filter(|x| x.starts_with("CARGO_FEATURE_TIME_DRIVER_"))
+        .get_one()
+    {
+        Ok(x) => Some(
+            x.strip_prefix("CARGO_FEATURE_TIME_DRIVER_")
+                .unwrap()
+                .to_ascii_lowercase(),
+        ),
+        Err(GetOneError::None) => None,
+        Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"),
+    };
+
+    let time_driver_singleton = match time_driver.as_ref().map(|x| x.as_ref()) {
+        None => "",
+        Some("tim2") => "TIM2",
+        Some("tim3") => "TIM3",
+        Some("tim4") => "TIM4",
+        Some("tim5") => "TIM5",
+        Some("tim12") => "TIM12",
+        Some("tim15") => "TIM15",
+        Some("any") => {
+            if singletons.contains(&"TIM2".to_string()) {
+                "TIM2"
+            } else if singletons.contains(&"TIM3".to_string()) {
+                "TIM3"
+            } else if singletons.contains(&"TIM4".to_string()) {
+                "TIM4"
+            } else if singletons.contains(&"TIM5".to_string()) {
+                "TIM5"
+            } else if singletons.contains(&"TIM12".to_string()) {
+                "TIM12"
+            } else if singletons.contains(&"TIM15".to_string()) {
+                "TIM15"
+            } else {
+                panic!("time-driver-any requested, but the chip doesn't have TIM2, TIM3, TIM4, TIM5, TIM12 or TIM15.")
+            }
+        }
+        _ => panic!("unknown time_driver {:?}", time_driver),
+    };
+
+    if time_driver_singleton != "" {
+        println!("cargo:rustc-cfg=time_driver_{}", time_driver_singleton.to_lowercase());
+    }
+
+    // ========
+    // Write singletons
+
     let mut g = TokenStream::new();
 
     let singleton_tokens: Vec<_> = singletons.iter().map(|s| format_ident!("{}", s)).collect();
+
     g.extend(quote! {
-        embassy_hal_common::peripherals!(#(#singleton_tokens),*);
+        embassy_hal_common::peripherals_definition!(#(#singleton_tokens),*);
+    });
+
+    let singleton_tokens: Vec<_> = singletons
+        .iter()
+        .filter(|s| *s != &time_driver_singleton.to_string())
+        .map(|s| format_ident!("{}", s))
+        .collect();
+
+    g.extend(quote! {
+        embassy_hal_common::peripherals_struct!(#(#singleton_tokens),*);
     });
 
     // ========
@@ -536,6 +599,10 @@ fn main() {
                     let pin_name = format_ident!("{}", pin.pin);
                     let af = pin.af.unwrap_or(0);
 
+                    if peri == time_driver_singleton {
+                        continue;
+                    }
+
                     // MCO is special
                     if pin.signal.starts_with("MCO_") {
                         // Supported in H7 only for now
@@ -838,51 +905,6 @@ fn main() {
     println!("cargo:rustc-cfg={}x", &chip_name[..8]); // stm32f42x
     println!("cargo:rustc-cfg={}x{}", &chip_name[..7], &chip_name[8..9]); // stm32f4x9
 
-    // ========
-    // Handle time-driver-XXXX features.
-
-    let time_driver = match env::vars()
-        .map(|(a, _)| a)
-        .filter(|x| x.starts_with("CARGO_FEATURE_TIME_DRIVER_"))
-        .get_one()
-    {
-        Ok(x) => Some(
-            x.strip_prefix("CARGO_FEATURE_TIME_DRIVER_")
-                .unwrap()
-                .to_ascii_lowercase(),
-        ),
-        Err(GetOneError::None) => None,
-        Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"),
-    };
-
-    match time_driver.as_ref().map(|x| x.as_ref()) {
-        None => {}
-        Some("tim2") => println!("cargo:rustc-cfg=time_driver_tim2"),
-        Some("tim3") => println!("cargo:rustc-cfg=time_driver_tim3"),
-        Some("tim4") => println!("cargo:rustc-cfg=time_driver_tim4"),
-        Some("tim5") => println!("cargo:rustc-cfg=time_driver_tim5"),
-        Some("tim12") => println!("cargo:rustc-cfg=time_driver_tim12"),
-        Some("tim15") => println!("cargo:rustc-cfg=time_driver_tim15"),
-        Some("any") => {
-            if singletons.contains(&"TIM2".to_string()) {
-                println!("cargo:rustc-cfg=time_driver_tim2");
-            } else if singletons.contains(&"TIM3".to_string()) {
-                println!("cargo:rustc-cfg=time_driver_tim3");
-            } else if singletons.contains(&"TIM4".to_string()) {
-                println!("cargo:rustc-cfg=time_driver_tim4");
-            } else if singletons.contains(&"TIM5".to_string()) {
-                println!("cargo:rustc-cfg=time_driver_tim5");
-            } else if singletons.contains(&"TIM12".to_string()) {
-                println!("cargo:rustc-cfg=time_driver_tim12");
-            } else if singletons.contains(&"TIM15".to_string()) {
-                println!("cargo:rustc-cfg=time_driver_tim15");
-            } else {
-                panic!("time-driver-any requested, but the chip doesn't have TIM2, TIM3, TIM4, TIM5, TIM12 or TIM15.")
-            }
-        }
-        _ => panic!("unknown time_driver {:?}", time_driver),
-    }
-
     // Handle time-driver-XXXX features.
     if env::var("CARGO_FEATURE_TIME_DRIVER_ANY").is_ok() {}
     println!("cargo:rustc-cfg={}", &chip_name[..chip_name.len() - 2]);

From 9e1ddeac8604f5df85df56159b97004de890aeb3 Mon Sep 17 00:00:00 2001
From: xoviat <xoviat@users.noreply.github.com>
Date: Sun, 16 Apr 2023 18:32:55 -0500
Subject: [PATCH 10/12] stm32: fix defective example

---
 embassy-stm32/build.rs          | 4 ----
 examples/stm32g4/src/bin/pwm.rs | 4 ++--
 2 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index bb6c99963..c7d12e13a 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -599,10 +599,6 @@ fn main() {
                     let pin_name = format_ident!("{}", pin.pin);
                     let af = pin.af.unwrap_or(0);
 
-                    if peri == time_driver_singleton {
-                        continue;
-                    }
-
                     // MCO is special
                     if pin.signal.starts_with("MCO_") {
                         // Supported in H7 only for now
diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs
index 017e89e41..8f7842ed7 100644
--- a/examples/stm32g4/src/bin/pwm.rs
+++ b/examples/stm32g4/src/bin/pwm.rs
@@ -15,8 +15,8 @@ async fn main(_spawner: Spawner) {
     let p = embassy_stm32::init(Default::default());
     info!("Hello World!");
 
-    let ch1 = PwmPin::new_ch1(p.PA5);
-    let mut pwm = SimplePwm::new(p.TIM2, Some(ch1), None, None, None, khz(10));
+    let ch1 = PwmPin::new_ch1(p.PC0);
+    let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10));
     let max = pwm.get_max_duty();
     pwm.enable(Channel::Ch1);
 

From 90c1422381f4a798c83146a2125529bb2f761598 Mon Sep 17 00:00:00 2001
From: xoviat <xoviat@users.noreply.github.com>
Date: Sun, 16 Apr 2023 19:30:42 -0500
Subject: [PATCH 11/12] stm32/rtc: remove chrono datetime and add converters

---
 .../rtc/{datetime_no_deps.rs => datetime.rs}  | 56 +++++++++++++++++++
 embassy-stm32/src/rtc/mod.rs                  |  3 -
 examples/stm32f4/Cargo.toml                   |  2 +-
 3 files changed, 57 insertions(+), 4 deletions(-)
 rename embassy-stm32/src/rtc/{datetime_no_deps.rs => datetime.rs} (69%)

diff --git a/embassy-stm32/src/rtc/datetime_no_deps.rs b/embassy-stm32/src/rtc/datetime.rs
similarity index 69%
rename from embassy-stm32/src/rtc/datetime_no_deps.rs
rename to embassy-stm32/src/rtc/datetime.rs
index 173f38377..5be68b89b 100644
--- a/embassy-stm32/src/rtc/datetime_no_deps.rs
+++ b/embassy-stm32/src/rtc/datetime.rs
@@ -1,3 +1,8 @@
+use core::convert::From;
+
+#[cfg(feature = "chrono")]
+use chrono::{self, Datelike, NaiveDate, Timelike, Weekday};
+
 use super::byte_to_bcd2;
 use crate::pac::rtc::Rtc;
 
@@ -41,6 +46,35 @@ pub struct DateTime {
     pub second: u8,
 }
 
+#[cfg(feature = "chrono")]
+impl From<chrono::NaiveDateTime> for DateTime {
+    fn from(date_time: chrono::NaiveDateTime) -> Self {
+        Self {
+            year: (date_time.year() - 1970) as u16,
+            month: date_time.month() as u8,
+            day: date_time.day() as u8,
+            day_of_week: date_time.weekday().into(),
+            hour: date_time.hour() as u8,
+            minute: date_time.minute() as u8,
+            second: date_time.second() as u8,
+        }
+    }
+}
+
+#[cfg(feature = "chrono")]
+impl From<DateTime> for chrono::NaiveDateTime {
+    fn from(date_time: DateTime) -> Self {
+        NaiveDate::from_ymd_opt(
+            (date_time.year + 1970) as i32,
+            date_time.month as u32,
+            date_time.day as u32,
+        )
+        .unwrap()
+        .and_hms_opt(date_time.hour as u32, date_time.minute as u32, date_time.second as u32)
+        .unwrap()
+    }
+}
+
 /// A day of the week
 #[repr(u8)]
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
@@ -55,6 +89,28 @@ pub enum DayOfWeek {
     Sunday = 6,
 }
 
+#[cfg(feature = "chrono")]
+impl From<chrono::Weekday> for DayOfWeek {
+    fn from(weekday: Weekday) -> Self {
+        day_of_week_from_u8(weekday.number_from_monday() as u8).unwrap()
+    }
+}
+
+#[cfg(feature = "chrono")]
+impl From<DayOfWeek> for chrono::Weekday {
+    fn from(weekday: DayOfWeek) -> Self {
+        match weekday {
+            DayOfWeek::Monday => Weekday::Mon,
+            DayOfWeek::Tuesday => Weekday::Tue,
+            DayOfWeek::Wednesday => Weekday::Wed,
+            DayOfWeek::Thursday => Weekday::Thu,
+            DayOfWeek::Friday => Weekday::Fri,
+            DayOfWeek::Saturday => Weekday::Sat,
+            DayOfWeek::Sunday => Weekday::Sun,
+        }
+    }
+}
+
 fn day_of_week_from_u8(v: u8) -> Result<DayOfWeek, Error> {
     Ok(match v {
         0 => DayOfWeek::Monday,
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs
index ee3349b27..170783b2d 100644
--- a/embassy-stm32/src/rtc/mod.rs
+++ b/embassy-stm32/src/rtc/mod.rs
@@ -1,8 +1,5 @@
 //! RTC peripheral abstraction
 use core::marker::PhantomData;
-
-#[cfg_attr(feature = "chrono", path = "datetime_chrono.rs")]
-#[cfg_attr(not(feature = "chrono"), path = "datetime_no_deps.rs")]
 mod datetime;
 
 pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml
index 1736769ef..69dcab64c 100644
--- a/examples/stm32f4/Cargo.toml
+++ b/examples/stm32f4/Cargo.toml
@@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
 embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] }
 embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers", "arch-cortex-m", "executor-thread", "executor-interrupt"] }
 embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
-embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc"]  }
+embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "embedded-sdmmc", "chrono"]  }
 embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
 embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] }
 

From 27ec29e2c53e4bc618ec97f0071befe52c8520b9 Mon Sep 17 00:00:00 2001
From: xoviat <xoviat@users.noreply.github.com>
Date: Sun, 16 Apr 2023 19:32:15 -0500
Subject: [PATCH 12/12] stm32/rtc: remove unused import

---
 embassy-stm32/src/rtc/datetime.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/embassy-stm32/src/rtc/datetime.rs b/embassy-stm32/src/rtc/datetime.rs
index 5be68b89b..6274c1e05 100644
--- a/embassy-stm32/src/rtc/datetime.rs
+++ b/embassy-stm32/src/rtc/datetime.rs
@@ -1,3 +1,4 @@
+#[cfg(feature = "chrono")]
 use core::convert::From;
 
 #[cfg(feature = "chrono")]