From f01609036ff757ef3f04e568c646a467289d5440 Mon Sep 17 00:00:00 2001
From: Matt Ickstadt <mattico8@gmail.com>
Date: Tue, 3 Oct 2023 16:45:05 -0500
Subject: [PATCH] h7: implement RTC and LSE clock configuration

---
 embassy-stm32/Cargo.toml        |  4 +--
 embassy-stm32/src/dac/mod.rs    |  4 +--
 embassy-stm32/src/rcc/bd.rs     | 47 +++++++++++++++++--------
 embassy-stm32/src/rcc/f4.rs     |  2 +-
 embassy-stm32/src/rcc/h.rs      | 57 ++++++++++++++++++++++++++++--
 embassy-stm32/src/rcc/l4.rs     |  2 +-
 embassy-stm32/src/rcc/mod.rs    | 59 +++++++++++++++++++++++++------
 embassy-stm32/src/rcc/wl.rs     |  2 +-
 embassy-stm32/src/rtc/mod.rs    | 61 ++++++++++++++++++++++++---------
 embassy-stm32/src/rtc/v2.rs     |  2 ++
 examples/stm32h7/Cargo.toml     |  3 +-
 examples/stm32h7/src/bin/rtc.rs | 39 +++++++++++++++++++++
 12 files changed, 231 insertions(+), 51 deletions(-)
 create mode 100644 examples/stm32h7/src/bin/rtc.rs

diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 1b688eca9..5258e4c7c 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -59,7 +59,7 @@ sdio-host = "0.5.0"
 embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
 critical-section = "1.1"
 atomic-polyfill = "1.0.1"
-stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-172c5ea18824d7cd38decb210e4af441fa3816cb" }
+stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f0f06b4c95bd9e185e4aa5f2e1d4b76ba84f1594" }
 vcell = "0.1.3"
 bxcan = "0.7.0"
 nb = "1.0.0"
@@ -78,7 +78,7 @@ critical-section = { version = "1.1", features = ["std"] }
 [build-dependencies]
 proc-macro2 = "1.0.36"
 quote = "1.0.15"
-stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-172c5ea18824d7cd38decb210e4af441fa3816cb", default-features = false, features = ["metadata"]}
+stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f0f06b4c95bd9e185e4aa5f2e1d4b76ba84f1594", default-features = false, features = ["metadata"]}
 
 
 [features]
diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs
index a2040b857..976aa3eea 100644
--- a/embassy-stm32/src/dac/mod.rs
+++ b/embassy-stm32/src/dac/mod.rs
@@ -564,7 +564,7 @@ pub trait DacPin<T: Instance, const C: u8>: crate::gpio::Pin + 'static {}
 foreach_peripheral!(
     (dac, $inst:ident) => {
         // H7 uses single bit for both DAC1 and DAC2, this is a hack until a proper fix is implemented
-        #[cfg(rcc_h7)]
+        #[cfg(any(rcc_h7, rcc_h7rm0433))]
         impl crate::rcc::sealed::RccPeripheral for peripherals::$inst {
             fn frequency() -> crate::time::Hertz {
                 critical_section::with(|_| unsafe { crate::rcc::get_freqs().apb1 })
@@ -590,7 +590,7 @@ foreach_peripheral!(
             }
         }
 
-        #[cfg(rcc_h7)]
+        #[cfg(any(rcc_h7, rcc_h7rm0433))]
         impl crate::rcc::RccPeripheral for peripherals::$inst {}
 
         impl crate::dac::sealed::Instance for peripherals::$inst {
diff --git a/embassy-stm32/src/rcc/bd.rs b/embassy-stm32/src/rcc/bd.rs
index cec2ea010..9c784c3a3 100644
--- a/embassy-stm32/src/rcc/bd.rs
+++ b/embassy-stm32/src/rcc/bd.rs
@@ -1,26 +1,36 @@
+#[allow(dead_code)]
+#[derive(Clone, Copy)]
+pub enum LseCfg {
+    Oscillator(LseDrive),
+    Bypass,
+}
+
+impl Default for LseCfg {
+    fn default() -> Self {
+        Self::Oscillator(Default::default())
+    }
+}
+
 #[allow(dead_code)]
 #[derive(Default, Clone, Copy)]
 pub enum LseDrive {
-    #[cfg(any(rtc_v2f7, rtc_v2l4))]
     Low = 0,
     MediumLow = 0x01,
     #[default]
     MediumHigh = 0x02,
-    #[cfg(any(rtc_v2f7, rtc_v2l4))]
     High = 0x03,
 }
 
-#[cfg(any(rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l4))]
+// All families but these have the LSEDRV register
+#[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f400, rcc_f410, rcc_l1)))]
 impl From<LseDrive> for crate::pac::rcc::vals::Lsedrv {
     fn from(value: LseDrive) -> Self {
         use crate::pac::rcc::vals::Lsedrv;
 
         match value {
-            #[cfg(any(rtc_v2f7, rtc_v2l4))]
             LseDrive::Low => Lsedrv::LOW,
             LseDrive::MediumLow => Lsedrv::MEDIUMLOW,
             LseDrive::MediumHigh => Lsedrv::MEDIUMHIGH,
-            #[cfg(any(rtc_v2f7, rtc_v2l4))]
             LseDrive::High => Lsedrv::HIGH,
         }
     }
@@ -87,14 +97,19 @@ impl BackupDomain {
         rtc_v3u5
     ))]
     #[allow(dead_code, unused_variables)]
-    pub fn configure_ls(clock_source: RtcClockSource, lsi: bool, lse: Option<LseDrive>) {
+    pub fn configure_ls(clock_source: RtcClockSource, lsi: bool, lse: Option<LseCfg>) {
         use atomic_polyfill::{compiler_fence, Ordering};
 
         match clock_source {
             RtcClockSource::LSI => assert!(lsi),
-            RtcClockSource::LSE => assert!(&lse.is_some()),
+            RtcClockSource::LSE => assert!(lse.is_some()),
             _ => {}
         };
+        let (lse_en, lse_byp, lse_drv) = match lse {
+            Some(LseCfg::Oscillator(lse_drv)) => (true, false, Some(lse_drv)),
+            Some(LseCfg::Bypass) => (true, true, None),
+            None => (false, false, None),
+        };
 
         if lsi {
             #[cfg(rtc_v3u5)]
@@ -131,10 +146,11 @@ impl BackupDomain {
         {
             ok &= reg.rtcen() == (clock_source != RtcClockSource::NOCLOCK);
         }
-        ok &= reg.lseon() == lse.is_some();
-        #[cfg(any(rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l4))]
-        if let Some(lse_drive) = lse {
-            ok &= reg.lsedrv() == lse_drive.into();
+        ok &= reg.lseon() == lse_en;
+        ok &= reg.lsebyp() == lse_byp;
+        #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f400, rcc_f410, rcc_l1)))]
+        if let Some(lse_drv) = lse_drv {
+            ok &= reg.lsedrv() == lse_drv.into();
         }
 
         // if configuration is OK, we're done.
@@ -153,10 +169,13 @@ impl BackupDomain {
             Self::modify(|w| w.set_bdrst(false));
         }
 
-        if let Some(lse_drive) = lse {
+        if lse_en {
             Self::modify(|w| {
-                #[cfg(any(rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l4))]
-                w.set_lsedrv(lse_drive.into());
+                #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f400, rcc_f410, rcc_l1)))]
+                if let Some(lse_drv) = lse_drv {
+                    w.set_lsedrv(lse_drv.into());
+                }
+                w.set_lsebyp(lse_byp);
                 w.set_lseon(true);
             });
 
diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs
index edc5ff52e..5ba958a02 100644
--- a/embassy-stm32/src/rcc/f4.rs
+++ b/embassy-stm32/src/rcc/f4.rs
@@ -540,7 +540,7 @@ pub(crate) unsafe fn init(config: Config) {
         #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
         pllsai: plls.pllsaiclk.map(Hertz),
 
-        rtc: rtc,
+        rtc,
         rtc_hse: None,
     });
 }
diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs
index 43e8db22e..5f9cc1c8b 100644
--- a/embassy-stm32/src/rcc/h.rs
+++ b/embassy-stm32/src/rcc/h.rs
@@ -9,6 +9,8 @@ pub use crate::pac::rcc::vals::Adcsel as AdcClockSource;
 pub use crate::pac::rcc::vals::Ckpersel as PerClockSource;
 use crate::pac::rcc::vals::{Ckpersel, Hsidiv, Pllrge, Pllsrc, Pllvcosel, Sw, Timpre};
 use crate::pac::{FLASH, PWR, RCC};
+#[cfg(stm32h7)]
+use crate::rcc::bd::{BackupDomain, LseCfg, RtcClockSource};
 use crate::rcc::{set_freqs, Clocks};
 use crate::time::Hertz;
 
@@ -46,9 +48,9 @@ pub enum VoltageScale {
 pub enum HseMode {
     /// crystal/ceramic oscillator (HSEBYP=0)
     Oscillator,
-    ///  external analog clock (low swing) (HSEBYP=1, HSEEXT=0)
+    /// external analog clock (low swing) (HSEBYP=1, HSEEXT=0)
     Bypass,
-    ///  external digital clock (full swing) (HSEBYP=1, HSEEXT=1)
+    /// external digital clock (full swing) (HSEBYP=1, HSEEXT=1)
     #[cfg(any(rcc_h5, rcc_h50))]
     BypassDigital,
 }
@@ -61,6 +63,15 @@ pub struct Hse {
     pub mode: HseMode,
 }
 
+#[cfg(stm32h7)]
+#[derive(Clone, Copy, Eq, PartialEq)]
+pub enum Lse {
+    /// 32.768 kHz crystal/ceramic oscillator (LSEBYP=0)
+    Oscillator,
+    /// external clock input up to 1MHz (LSEBYP=1)
+    Bypass(Hertz),
+}
+
 #[derive(Clone, Copy, Eq, PartialEq)]
 pub enum Hsi {
     /// 64Mhz
@@ -157,6 +168,10 @@ impl From<TimerPrescaler> for Timpre {
 pub struct Config {
     pub hsi: Option<Hsi>,
     pub hse: Option<Hse>,
+    #[cfg(stm32h7)]
+    pub lse: Option<Lse>,
+    #[cfg(stm32h7)]
+    pub lsi: bool,
     pub csi: bool,
     pub hsi48: bool,
     pub sys: Sysclk,
@@ -181,6 +196,8 @@ pub struct Config {
     pub adc_clock_source: AdcClockSource,
     pub timer_prescaler: TimerPrescaler,
     pub voltage_scale: VoltageScale,
+    #[cfg(stm32h7)]
+    pub rtc_mux: Option<RtcClockSource>,
 }
 
 impl Default for Config {
@@ -188,6 +205,10 @@ impl Default for Config {
         Self {
             hsi: Some(Hsi::Mhz64),
             hse: None,
+            #[cfg(stm32h7)]
+            lse: None,
+            #[cfg(stm32h7)]
+            lsi: false,
             csi: false,
             hsi48: false,
             sys: Sysclk::HSI,
@@ -210,6 +231,8 @@ impl Default for Config {
             adc_clock_source: AdcClockSource::from_bits(0), // PLL2_P on H7, HCLK on H5
             timer_prescaler: TimerPrescaler::DefaultX2,
             voltage_scale: VoltageScale::Scale0,
+            #[cfg(stm32h7)]
+            rtc_mux: None,
         }
     }
 }
@@ -448,6 +471,19 @@ pub(crate) unsafe fn init(config: Config) {
 
     flash_setup(hclk, config.voltage_scale);
 
+    #[cfg(stm32h7)]
+    {
+        let lsecfg = config.lse.map(|lse| match lse {
+            Lse::Bypass(freq) => {
+                assert!(freq <= Hertz(1_000_000));
+                LseCfg::Bypass
+            }
+            Lse::Oscillator => LseCfg::Oscillator(Default::default()),
+        });
+
+        BackupDomain::configure_ls(config.rtc_mux.unwrap_or(RtcClockSource::NOCLOCK), config.lsi, lsecfg);
+    }
+
     #[cfg(stm32h7)]
     {
         RCC.d1cfgr().modify(|w| {
@@ -512,6 +548,17 @@ pub(crate) unsafe fn init(config: Config) {
         while !pac::SYSCFG.cccsr().read().ready() {}
     }
 
+    #[cfg(stm32h7)]
+    let rtc_clk = match config.rtc_mux {
+        Some(RtcClockSource::LSI) => Some(LSI_FREQ),
+        Some(RtcClockSource::LSE) => Some(match config.lse {
+            Some(Lse::Oscillator) => Hertz(32768),
+            Some(Lse::Bypass(freq)) => freq,
+            None => panic!("LSE not configured"),
+        }),
+        _ => None,
+    };
+
     set_freqs(Clocks {
         sys,
         ahb1: hclk,
@@ -525,7 +572,11 @@ pub(crate) unsafe fn init(config: Config) {
         apb4,
         apb1_tim,
         apb2_tim,
-        adc: adc,
+        adc,
+        #[cfg(stm32h7)]
+        rtc: rtc_clk,
+        #[cfg(stm32h7)]
+        rtc_hse: None,
     });
 }
 
diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs
index 6f1f7458c..3a24eca82 100644
--- a/embassy-stm32/src/rcc/l4.rs
+++ b/embassy-stm32/src/rcc/l4.rs
@@ -420,7 +420,7 @@ pub(crate) unsafe fn init(config: Config) {
                 w.set_msirgsel(true);
                 w.set_msion(true);
 
-                if let RtcClockSource::LSE = config.rtc_mux {
+                if config.rtc_mux == RtcClockSource::LSE {
                     // If LSE is enabled, enable calibration of MSI
                     w.set_msipllen(true);
                 } else {
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index ac9673833..bf497ca12 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -21,7 +21,7 @@ pub use mco::*;
 #[cfg_attr(rcc_c0, path = "c0.rs")]
 #[cfg_attr(rcc_g0, path = "g0.rs")]
 #[cfg_attr(rcc_g4, path = "g4.rs")]
-#[cfg_attr(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab), path = "h.rs")]
+#[cfg_attr(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7rm0433, rcc_h7ab), path = "h.rs")]
 #[cfg_attr(rcc_l0, path = "l0.rs")]
 #[cfg_attr(rcc_l1, path = "l1.rs")]
 #[cfg_attr(rcc_l4, path = "l4.rs")]
@@ -57,9 +57,9 @@ pub struct Clocks {
     pub apb2: Hertz,
     #[cfg(not(any(rcc_c0, rcc_g0)))]
     pub apb2_tim: Hertz,
-    #[cfg(any(rcc_wl5, rcc_wle, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_u5))]
+    #[cfg(any(rcc_wl5, rcc_wle, rcc_h5, rcc_h50, rcc_h7, rcc_h7rm0433, rcc_h7ab, rcc_u5))]
     pub apb3: Hertz,
-    #[cfg(any(rcc_h7, rcc_h7ab))]
+    #[cfg(any(rcc_h7, rcc_h7rm0433, rcc_h7ab))]
     pub apb4: Hertz,
     #[cfg(any(rcc_wba))]
     pub apb7: Hertz,
@@ -67,16 +67,44 @@ pub struct Clocks {
     // AHB
     pub ahb1: Hertz,
     #[cfg(any(
-        rcc_l4, rcc_l5, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_g4, rcc_u5, rcc_wb,
-        rcc_wba, rcc_wl5, rcc_wle
+        rcc_l4,
+        rcc_l5,
+        rcc_f2,
+        rcc_f4,
+        rcc_f410,
+        rcc_f7,
+        rcc_h5,
+        rcc_h50,
+        rcc_h7,
+        rcc_h7rm0433,
+        rcc_h7ab,
+        rcc_g4,
+        rcc_u5,
+        rcc_wb,
+        rcc_wba,
+        rcc_wl5,
+        rcc_wle
     ))]
     pub ahb2: Hertz,
     #[cfg(any(
-        rcc_l4, rcc_l5, rcc_f2, rcc_f4, rcc_f410, rcc_f7, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_u5, rcc_wb, rcc_wl5,
+        rcc_l4,
+        rcc_l5,
+        rcc_f2,
+        rcc_f4,
+        rcc_f410,
+        rcc_f7,
+        rcc_h5,
+        rcc_h50,
+        rcc_h7,
+        rcc_h7rm0433,
+        rcc_h7ab,
+        rcc_u5,
+        rcc_wb,
+        rcc_wl5,
         rcc_wle
     ))]
     pub ahb3: Hertz,
-    #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_wba))]
+    #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7rm0433, rcc_h7ab, rcc_wba))]
     pub ahb4: Hertz,
 
     #[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))]
@@ -88,7 +116,18 @@ pub struct Clocks {
     #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f446, stm32f469, stm32f479))]
     pub pllsai: Option<Hertz>,
 
-    #[cfg(any(rcc_f1, rcc_f100, rcc_f1cl, rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_f3, rcc_g4))]
+    #[cfg(any(
+        rcc_f1,
+        rcc_f100,
+        rcc_f1cl,
+        rcc_h5,
+        rcc_h50,
+        rcc_h7,
+        rcc_h7rm0433,
+        rcc_h7ab,
+        rcc_f3,
+        rcc_g4
+    ))]
     pub adc: Option<Hertz>,
 
     #[cfg(any(rcc_f3, rcc_g4))]
@@ -97,11 +136,11 @@ pub struct Clocks {
     #[cfg(stm32f334)]
     pub hrtim: Option<Hertz>,
 
-    #[cfg(any(rcc_wb, rcc_f4, rcc_f410, rcc_f7))]
+    #[cfg(any(rcc_wb, rcc_f4, rcc_f410, rcc_f7, rcc_h7, rcc_h7rm0433, rcc_h7ab))]
     /// Set only if the lsi or lse is configured, indicates stop is supported
     pub rtc: Option<Hertz>,
 
-    #[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
+    #[cfg(any(rcc_wb, rcc_f4, rcc_f410, rcc_h7, rcc_h7rm0433, rcc_h7ab))]
     /// Set if the hse is configured, indicates stop is not supported
     pub rtc_hse: Option<Hertz>,
 }
diff --git a/embassy-stm32/src/rcc/wl.rs b/embassy-stm32/src/rcc/wl.rs
index 6643d278a..937f55503 100644
--- a/embassy-stm32/src/rcc/wl.rs
+++ b/embassy-stm32/src/rcc/wl.rs
@@ -261,7 +261,7 @@ pub(crate) unsafe fn init(config: Config) {
                 w.set_msirange(range.into());
                 w.set_msion(true);
 
-                if let RtcClockSource::LSE = config.rtc_mux {
+                if config.rtc_mux == RtcClockSource::LSE {
                     // If LSE is enabled, enable calibration of MSI
                     w.set_msipllen(true);
                 } else {
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs
index 73b78f253..28dde2eb1 100644
--- a/embassy-stm32/src/rtc/mod.rs
+++ b/embassy-stm32/src/rtc/mod.rs
@@ -93,21 +93,50 @@ impl RtcTimeProvider {
     ///
     /// 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 = RTC::regs();
-        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();
+        // For RM0433 we use BYPSHAD=1 to work around errata ES0392 2.19.1
+        #[cfg(rcc_h7rm0433)]
+        loop {
+            let r = RTC::regs();
+            let ss = r.ssr().read().ss();
+            let dr = r.dr().read();
+            let tr = r.tr().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;
+            // If an RTCCLK edge occurs during read we may see inconsistent values
+            // so read ssr again and see if it has changed. (see RM0433 Rev 7 46.3.9)
+            let ss_after = r.ssr().read().ss();
+            if ss == ss_after {
+                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()));
 
-        self::datetime::datetime(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime)
+                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;
+
+                return self::datetime::datetime(year, month, day, weekday, hour, minute, second)
+                    .map_err(RtcError::InvalidDateTime);
+            }
+        }
+
+        #[cfg(not(rcc_h7rm0433))]
+        {
+            let r = RTC::regs();
+            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)
+        }
     }
 }
 
@@ -175,18 +204,18 @@ impl Rtc {
     }
 
     fn frequency() -> Hertz {
-        #[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
+        #[cfg(any(rcc_wb, rcc_f4, rcc_f410, rcc_h5, rcc_h50, rcc_h7, rcc_h7rm0433, rcc_h7ab))]
         let freqs = unsafe { crate::rcc::get_freqs() };
 
         // Load the clock frequency from the rcc mod, if supported
-        #[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
+        #[cfg(any(rcc_wb, rcc_f4, rcc_f410, rcc_h5, rcc_h50, rcc_h7, rcc_h7rm0433, rcc_h7ab))]
         match freqs.rtc {
             Some(hertz) => hertz,
             None => freqs.rtc_hse.unwrap(),
         }
 
         // Assume the  default value, if not supported
-        #[cfg(not(any(rcc_wb, rcc_f4, rcc_f410)))]
+        #[cfg(not(any(rcc_wb, rcc_f4, rcc_f410, rcc_h5, rcc_h50, rcc_h7, rcc_h7rm0433, rcc_h7ab)))]
         Hertz(32_768)
     }
 
diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs
index 4974f6ee6..eeb23e1f1 100644
--- a/embassy-stm32/src/rtc/v2.rs
+++ b/embassy-stm32/src/rtc/v2.rs
@@ -157,6 +157,8 @@ impl super::Rtc {
                 w.set_fmt(stm32_metapac::rtc::vals::Fmt::TWENTY_FOUR_HOUR);
                 w.set_osel(Osel::DISABLED);
                 w.set_pol(Pol::HIGH);
+                #[cfg(rcc_h7rm0433)]
+                w.set_bypshad(true);
             });
 
             rtc.prer().modify(|w| {
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml
index 3a3927a9a..7bcdf2b3e 100644
--- a/examples/stm32h7/Cargo.toml
+++ b/examples/stm32h7/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 
 [dependencies]
 # Change stm32h743bi to your chip name, if necessary.
-embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "memory-x", "unstable-pac", "unstable-traits"] }
+embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "memory-x", "unstable-pac", "unstable-traits", "chrono"] }
 embassy-sync = { version = "0.3.0", path = "../../embassy-sync", features = ["defmt"] }
 embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
@@ -32,6 +32,7 @@ micromath = "2.0.0"
 stm32-fmc = "0.3.0"
 embedded-storage = "0.3.0"
 static_cell = { version = "1.1", features = ["nightly"]}
+chrono = { version = "^0.4", default-features = false }
 
 # cargo build/run
 [profile.dev]
diff --git a/examples/stm32h7/src/bin/rtc.rs b/examples/stm32h7/src/bin/rtc.rs
new file mode 100644
index 000000000..eeb94073b
--- /dev/null
+++ b/examples/stm32h7/src/bin/rtc.rs
@@ -0,0 +1,39 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+use chrono::{NaiveDate, NaiveDateTime};
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_stm32::rcc::Lse;
+use embassy_stm32::rtc::{Rtc, RtcClockSource, RtcConfig};
+use embassy_stm32::Config;
+use embassy_time::{Duration, Timer};
+use {defmt_rtt as _, panic_probe as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) {
+    let p = {
+        let mut config = Config::default();
+        config.rcc.lse = Some(Lse::Oscillator);
+        config.rcc.rtc_mux = Some(RtcClockSource::LSE);
+        embassy_stm32::init(config)
+    };
+    info!("Hello World!");
+
+    let now = NaiveDate::from_ymd_opt(2020, 5, 15)
+        .unwrap()
+        .and_hms_opt(10, 30, 15)
+        .unwrap();
+
+    let mut rtc = Rtc::new(p.RTC, RtcConfig::default());
+    info!("Got RTC! {:?}", now.timestamp());
+
+    rtc.set_datetime(now.into()).expect("datetime not set");
+
+    // In reality the delay would be much longer
+    Timer::after(Duration::from_millis(20000)).await;
+
+    let then: NaiveDateTime = rtc.now().unwrap().into();
+    info!("Got RTC! {:?}", then.timestamp());
+}