From 9f4d320d676a5177535db0e1d78159126db20644 Mon Sep 17 00:00:00 2001
From: Michael Zill <michael.zill@accenture.com>
Date: Tue, 9 Apr 2024 06:56:15 +0200
Subject: [PATCH 01/16] stm32/spi,crc: update for new PAC

---
 embassy-stm32/Cargo.toml      |  4 ++--
 embassy-stm32/src/crc/v1.rs   | 12 ++++++++++++
 embassy-stm32/src/crc/v2v3.rs | 16 ++++++++--------
 embassy-stm32/src/spi/mod.rs  | 17 ++++++++++++-----
 4 files changed, 34 insertions(+), 15 deletions(-)

diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 89b24f0eb..93792ecf8 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -70,7 +70,7 @@ rand_core = "0.6.3"
 sdio-host = "0.5.0"
 critical-section = "1.1"
 #stm32-metapac = { version = "15" }
-stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b7ec569a5510c324693f0515ac8ea20b12917a9" }
+stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0c4baf478324e19741c7a9795ab0aa8217c3691c" }
 
 vcell = "0.1.3"
 nb = "1.0.0"
@@ -96,7 +96,7 @@ proc-macro2 = "1.0.36"
 quote = "1.0.15"
 
 #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
-stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b7ec569a5510c324693f0515ac8ea20b12917a9", default-features = false, features = ["metadata"]}
+stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0c4baf478324e19741c7a9795ab0aa8217c3691c", default-features = false, features = ["metadata"]}
 
 [features]
 default = ["rt"]
diff --git a/embassy-stm32/src/crc/v1.rs b/embassy-stm32/src/crc/v1.rs
index f8909d438..e8e0270af 100644
--- a/embassy-stm32/src/crc/v1.rs
+++ b/embassy-stm32/src/crc/v1.rs
@@ -32,6 +32,9 @@ impl<'d> Crc<'d> {
     /// Feeds a word to the peripheral and returns the current CRC value
     pub fn feed_word(&mut self, word: u32) -> u32 {
         // write a single byte to the device, and return the result
+        #[cfg(not(crc_v1))]
+        PAC_CRC.dr32().write_value(word);
+        #[cfg(crc_v1)]
         PAC_CRC.dr().write_value(word);
         self.read()
     }
@@ -39,6 +42,9 @@ impl<'d> Crc<'d> {
     /// Feed a slice of words to the peripheral and return the result.
     pub fn feed_words(&mut self, words: &[u32]) -> u32 {
         for word in words {
+            #[cfg(not(crc_v1))]
+            PAC_CRC.dr32().write_value(*word);
+            #[cfg(crc_v1)]
             PAC_CRC.dr().write_value(*word);
         }
 
@@ -46,6 +52,12 @@ impl<'d> Crc<'d> {
     }
 
     /// Read the CRC result value.
+    #[cfg(not(crc_v1))]
+    pub fn read(&self) -> u32 {
+        PAC_CRC.dr32().read()
+    }
+    /// Read the CRC result value.
+    #[cfg(crc_v1)]
     pub fn read(&self) -> u32 {
         PAC_CRC.dr().read()
     }
diff --git a/embassy-stm32/src/crc/v2v3.rs b/embassy-stm32/src/crc/v2v3.rs
index 46f5ea1be..13fb6778c 100644
--- a/embassy-stm32/src/crc/v2v3.rs
+++ b/embassy-stm32/src/crc/v2v3.rs
@@ -136,7 +136,7 @@ impl<'d> Crc<'d> {
     /// Feeds a byte into the CRC peripheral. Returns the computed checksum.
     pub fn feed_byte(&mut self, byte: u8) -> u32 {
         PAC_CRC.dr8().write_value(byte);
-        PAC_CRC.dr().read()
+        PAC_CRC.dr32().read()
     }
 
     /// Feeds an slice of bytes into the CRC peripheral. Returns the computed checksum.
@@ -144,30 +144,30 @@ impl<'d> Crc<'d> {
         for byte in bytes {
             PAC_CRC.dr8().write_value(*byte);
         }
-        PAC_CRC.dr().read()
+        PAC_CRC.dr32().read()
     }
     /// Feeds a halfword into the CRC peripheral. Returns the computed checksum.
     pub fn feed_halfword(&mut self, halfword: u16) -> u32 {
         PAC_CRC.dr16().write_value(halfword);
-        PAC_CRC.dr().read()
+        PAC_CRC.dr32().read()
     }
     /// Feeds an slice of halfwords into the CRC peripheral. Returns the computed checksum.
     pub fn feed_halfwords(&mut self, halfwords: &[u16]) -> u32 {
         for halfword in halfwords {
             PAC_CRC.dr16().write_value(*halfword);
         }
-        PAC_CRC.dr().read()
+        PAC_CRC.dr32().read()
     }
     /// Feeds a words into the CRC peripheral. Returns the computed checksum.
     pub fn feed_word(&mut self, word: u32) -> u32 {
-        PAC_CRC.dr().write_value(word as u32);
-        PAC_CRC.dr().read()
+        PAC_CRC.dr32().write_value(word as u32);
+        PAC_CRC.dr32().read()
     }
     /// Feeds an slice of words into the CRC peripheral. Returns the computed checksum.
     pub fn feed_words(&mut self, words: &[u32]) -> u32 {
         for word in words {
-            PAC_CRC.dr().write_value(*word as u32);
+            PAC_CRC.dr32().write_value(*word as u32);
         }
-        PAC_CRC.dr().read()
+        PAC_CRC.dr32().read()
     }
 }
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs
index 0b38c4288..340cfde03 100644
--- a/embassy-stm32/src/spi/mod.rs
+++ b/embassy-stm32/src/spi/mod.rs
@@ -735,18 +735,22 @@ trait RegsExt {
 
 impl RegsExt for Regs {
     fn tx_ptr<W>(&self) -> *mut W {
-        #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
+        #[cfg(any(spi_v1, spi_f1))]
         let dr = self.dr();
+        #[cfg(spi_v2)]
+        let dr = self.dr16();
         #[cfg(any(spi_v3, spi_v4, spi_v5))]
-        let dr = self.txdr();
+        let dr = self.txdr32();
         dr.as_ptr() as *mut W
     }
 
     fn rx_ptr<W>(&self) -> *mut W {
-        #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
+        #[cfg(any(spi_v1, spi_f1))]
         let dr = self.dr();
+        #[cfg(spi_v2)]
+        let dr = self.dr16();
         #[cfg(any(spi_v3, spi_v4, spi_v5))]
-        let dr = self.rxdr();
+        let dr = self.rxdr32();
         dr.as_ptr() as *mut W
     }
 }
@@ -815,11 +819,14 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> {
 fn flush_rx_fifo(regs: Regs) {
     #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
     while regs.sr().read().rxne() {
+        #[cfg(spi_v1)]
         let _ = regs.dr().read();
+        #[cfg(spi_v2)]
+        let _ = regs.dr16().read();
     }
     #[cfg(any(spi_v3, spi_v4, spi_v5))]
     while regs.sr().read().rxp() {
-        let _ = regs.rxdr().read();
+        let _ = regs.rxdr32().read();
     }
 }
 

From d9426549c3cd485c3d8abbf629213e3c04f5018b Mon Sep 17 00:00:00 2001
From: Dario Nieuwenhuis <dirbaio@dirbaio.net>
Date: Sat, 13 Apr 2024 02:55:15 +0200
Subject: [PATCH 02/16] stm32/flash: remove otp flash regions (removed in newer
 metapacs).

---
 embassy-stm32/build.rs         | 2 --
 embassy-stm32/src/flash/f4.rs  | 5 +----
 embassy-stm32/src/flash/h50.rs | 6 ++----
 embassy-stm32/src/flash/mod.rs | 2 --
 4 files changed, 3 insertions(+), 12 deletions(-)

diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 38b6c480c..0f87dd8ac 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -272,8 +272,6 @@ fn main() {
                 "Bank1"
             } else if region.name.starts_with("BANK_2") {
                 "Bank2"
-            } else if region.name == "OTP" {
-                "Otp"
             } else {
                 continue;
             }
diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs
index 00e61f2d2..90f13ff29 100644
--- a/embassy-stm32/src/flash/f4.rs
+++ b/embassy-stm32/src/flash/f4.rs
@@ -16,7 +16,7 @@ mod alt_regions {
     use embassy_hal_internal::PeripheralRef;
     use stm32_metapac::FLASH_SIZE;
 
-    use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION};
+    use crate::_generated::flash_regions::{BANK1_REGION1, BANK1_REGION2, BANK1_REGION3};
     use crate::flash::{asynch, Async, Bank1Region1, Bank1Region2, Blocking, Error, Flash, FlashBank, FlashRegion};
     use crate::peripherals::FLASH;
 
@@ -62,7 +62,6 @@ mod alt_regions {
         pub bank2_region1: AltBank2Region1<'d, MODE>,
         pub bank2_region2: AltBank2Region2<'d, MODE>,
         pub bank2_region3: AltBank2Region3<'d, MODE>,
-        pub otp_region: OTPRegion<'d, MODE>,
     }
 
     impl<'d> Flash<'d> {
@@ -79,7 +78,6 @@ mod alt_regions {
                 bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData),
                 bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData),
                 bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData),
-                otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData),
             }
         }
 
@@ -96,7 +94,6 @@ mod alt_regions {
                 bank2_region1: AltBank2Region1(&ALT_BANK2_REGION1, unsafe { p.clone_unchecked() }, PhantomData),
                 bank2_region2: AltBank2Region2(&ALT_BANK2_REGION2, unsafe { p.clone_unchecked() }, PhantomData),
                 bank2_region3: AltBank2Region3(&ALT_BANK2_REGION3, unsafe { p.clone_unchecked() }, PhantomData),
-                otp_region: OTPRegion(&OTP_REGION, unsafe { p.clone_unchecked() }, PhantomData),
             }
         }
     }
diff --git a/embassy-stm32/src/flash/h50.rs b/embassy-stm32/src/flash/h50.rs
index db05bef5d..5b15be261 100644
--- a/embassy-stm32/src/flash/h50.rs
+++ b/embassy-stm32/src/flash/h50.rs
@@ -55,7 +55,6 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE])
 }
 
 pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
-    assert!(sector.bank != FlashBank::Otp);
     assert!(sector.index_in_bank < 8);
 
     while busy() {}
@@ -63,9 +62,8 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E
     interrupt::free(|_| {
         pac::FLASH.nscr().modify(|w| {
             w.set_bksel(match sector.bank {
-                FlashBank::Bank1 => Bksel::B_0X0,
-                FlashBank::Bank2 => Bksel::B_0X1,
-                _ => unreachable!(),
+                FlashBank::Bank1 => Bksel::BANK1,
+                FlashBank::Bank2 => Bksel::BANK2,
             });
             w.set_snb(sector.index_in_bank);
             w.set_ser(true);
diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs
index 1d8031e82..9d7861816 100644
--- a/embassy-stm32/src/flash/mod.rs
+++ b/embassy-stm32/src/flash/mod.rs
@@ -89,8 +89,6 @@ pub enum FlashBank {
     Bank1 = 0,
     /// Bank 2
     Bank2 = 1,
-    /// OTP region
-    Otp,
 }
 
 #[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")]

From 52bd24499c0e164fd3525a02514af50e3fc35424 Mon Sep 17 00:00:00 2001
From: Dario Nieuwenhuis <dirbaio@dirbaio.net>
Date: Sat, 13 Apr 2024 03:30:30 +0200
Subject: [PATCH 03/16] stm32/adc: update g4 for new pac.

---
 embassy-stm32/src/adc/g4.rs     | 304 ++++++++++++++++++++++++++++++++
 embassy-stm32/src/adc/mod.rs    |  29 ++-
 examples/stm32g4/src/bin/adc.rs |   2 +-
 3 files changed, 332 insertions(+), 3 deletions(-)
 create mode 100644 embassy-stm32/src/adc/g4.rs

diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs
new file mode 100644
index 000000000..f6741f019
--- /dev/null
+++ b/embassy-stm32/src/adc/g4.rs
@@ -0,0 +1,304 @@
+#[allow(unused)]
+use pac::adc::vals::{Adcaldif, Difsel, Exten};
+use pac::adccommon::vals::Presc;
+
+use super::{blocking_delay_us, Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime};
+use crate::time::Hertz;
+use crate::{pac, Peripheral};
+
+/// Default VREF voltage used for sample conversion to millivolts.
+pub const VREF_DEFAULT_MV: u32 = 3300;
+/// VREF voltage used for factory calibration of VREFINTCAL register.
+pub const VREF_CALIB_MV: u32 = 3300;
+
+/// Max single ADC operation clock frequency
+#[cfg(stm32g4)]
+const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60);
+#[cfg(stm32h7)]
+const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50);
+
+#[cfg(stm32g4)]
+const VREF_CHANNEL: u8 = 18;
+#[cfg(stm32g4)]
+const TEMP_CHANNEL: u8 = 16;
+
+#[cfg(stm32h7)]
+const VREF_CHANNEL: u8 = 19;
+#[cfg(stm32h7)]
+const TEMP_CHANNEL: u8 = 18;
+
+// TODO this should be 14 for H7a/b/35
+const VBAT_CHANNEL: u8 = 17;
+
+// NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs
+/// Internal voltage reference channel.
+pub struct VrefInt;
+impl<T: Instance> InternalChannel<T> for VrefInt {}
+impl<T: Instance> super::SealedInternalChannel<T> for VrefInt {
+    fn channel(&self) -> u8 {
+        VREF_CHANNEL
+    }
+}
+
+/// Internal temperature channel.
+pub struct Temperature;
+impl<T: Instance> InternalChannel<T> for Temperature {}
+impl<T: Instance> super::SealedInternalChannel<T> for Temperature {
+    fn channel(&self) -> u8 {
+        TEMP_CHANNEL
+    }
+}
+
+/// Internal battery voltage channel.
+pub struct Vbat;
+impl<T: Instance> InternalChannel<T> for Vbat {}
+impl<T: Instance> super::SealedInternalChannel<T> for Vbat {
+    fn channel(&self) -> u8 {
+        VBAT_CHANNEL
+    }
+}
+
+// NOTE (unused): The prescaler enum closely copies the hardware capabilities,
+// but high prescaling doesn't make a lot of sense in the current implementation and is ommited.
+#[allow(unused)]
+enum Prescaler {
+    NotDivided,
+    DividedBy2,
+    DividedBy4,
+    DividedBy6,
+    DividedBy8,
+    DividedBy10,
+    DividedBy12,
+    DividedBy16,
+    DividedBy32,
+    DividedBy64,
+    DividedBy128,
+    DividedBy256,
+}
+
+impl Prescaler {
+    fn from_ker_ck(frequency: Hertz) -> Self {
+        let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0;
+        match raw_prescaler {
+            0 => Self::NotDivided,
+            1 => Self::DividedBy2,
+            2..=3 => Self::DividedBy4,
+            4..=5 => Self::DividedBy6,
+            6..=7 => Self::DividedBy8,
+            8..=9 => Self::DividedBy10,
+            10..=11 => Self::DividedBy12,
+            _ => unimplemented!(),
+        }
+    }
+
+    fn divisor(&self) -> u32 {
+        match self {
+            Prescaler::NotDivided => 1,
+            Prescaler::DividedBy2 => 2,
+            Prescaler::DividedBy4 => 4,
+            Prescaler::DividedBy6 => 6,
+            Prescaler::DividedBy8 => 8,
+            Prescaler::DividedBy10 => 10,
+            Prescaler::DividedBy12 => 12,
+            Prescaler::DividedBy16 => 16,
+            Prescaler::DividedBy32 => 32,
+            Prescaler::DividedBy64 => 64,
+            Prescaler::DividedBy128 => 128,
+            Prescaler::DividedBy256 => 256,
+        }
+    }
+
+    fn presc(&self) -> Presc {
+        match self {
+            Prescaler::NotDivided => Presc::DIV1,
+            Prescaler::DividedBy2 => Presc::DIV2,
+            Prescaler::DividedBy4 => Presc::DIV4,
+            Prescaler::DividedBy6 => Presc::DIV6,
+            Prescaler::DividedBy8 => Presc::DIV8,
+            Prescaler::DividedBy10 => Presc::DIV10,
+            Prescaler::DividedBy12 => Presc::DIV12,
+            Prescaler::DividedBy16 => Presc::DIV16,
+            Prescaler::DividedBy32 => Presc::DIV32,
+            Prescaler::DividedBy64 => Presc::DIV64,
+            Prescaler::DividedBy128 => Presc::DIV128,
+            Prescaler::DividedBy256 => Presc::DIV256,
+        }
+    }
+}
+
+impl<'d, T: Instance> Adc<'d, T> {
+    /// Create a new ADC driver.
+    pub fn new(adc: impl Peripheral<P = T> + 'd) -> Self {
+        embassy_hal_internal::into_ref!(adc);
+        T::enable_and_reset();
+
+        let prescaler = Prescaler::from_ker_ck(T::frequency());
+
+        T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc()));
+
+        let frequency = Hertz(T::frequency().0 / prescaler.divisor());
+        info!("ADC frequency set to {} Hz", frequency.0);
+
+        if frequency > MAX_ADC_CLK_FREQ {
+            panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 /  1_000_000 );
+        }
+
+        let mut s = Self {
+            adc,
+            sample_time: SampleTime::from_bits(0),
+        };
+        s.power_up();
+        s.configure_differential_inputs();
+
+        s.calibrate();
+        blocking_delay_us(1);
+
+        s.enable();
+        s.configure();
+
+        s
+    }
+
+    fn power_up(&mut self) {
+        T::regs().cr().modify(|reg| {
+            reg.set_deeppwd(false);
+            reg.set_advregen(true);
+        });
+
+        blocking_delay_us(10);
+    }
+
+    fn configure_differential_inputs(&mut self) {
+        T::regs().difsel().modify(|w| {
+            for n in 0..20 {
+                w.set_difsel(n, Difsel::SINGLEENDED);
+            }
+        });
+    }
+
+    fn calibrate(&mut self) {
+        T::regs().cr().modify(|w| {
+            w.set_adcaldif(Adcaldif::SINGLEENDED);
+        });
+
+        T::regs().cr().modify(|w| w.set_adcal(true));
+
+        while T::regs().cr().read().adcal() {}
+    }
+
+    fn enable(&mut self) {
+        T::regs().isr().write(|w| w.set_adrdy(true));
+        T::regs().cr().modify(|w| w.set_aden(true));
+        while !T::regs().isr().read().adrdy() {}
+        T::regs().isr().write(|w| w.set_adrdy(true));
+    }
+
+    fn configure(&mut self) {
+        // single conversion mode, software trigger
+        T::regs().cfgr().modify(|w| {
+            w.set_cont(false);
+            w.set_exten(Exten::DISABLED);
+        });
+    }
+
+    /// Enable reading the voltage reference internal channel.
+    pub fn enable_vrefint(&self) -> VrefInt {
+        T::common_regs().ccr().modify(|reg| {
+            reg.set_vrefen(true);
+        });
+
+        VrefInt {}
+    }
+
+    /// Enable reading the temperature internal channel.
+    pub fn enable_temperature(&self) -> Temperature {
+        T::common_regs().ccr().modify(|reg| {
+            reg.set_vsenseen(true);
+        });
+
+        Temperature {}
+    }
+
+    /// Enable reading the vbat internal channel.
+    pub fn enable_vbat(&self) -> Vbat {
+        T::common_regs().ccr().modify(|reg| {
+            reg.set_vbaten(true);
+        });
+
+        Vbat {}
+    }
+
+    /// Set the ADC sample time.
+    pub fn set_sample_time(&mut self, sample_time: SampleTime) {
+        self.sample_time = sample_time;
+    }
+
+    /// Set the ADC resolution.
+    pub fn set_resolution(&mut self, resolution: Resolution) {
+        T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
+    }
+
+    /// Perform a single conversion.
+    fn convert(&mut self) -> u16 {
+        T::regs().isr().modify(|reg| {
+            reg.set_eos(true);
+            reg.set_eoc(true);
+        });
+
+        // Start conversion
+        T::regs().cr().modify(|reg| {
+            reg.set_adstart(true);
+        });
+
+        while !T::regs().isr().read().eos() {
+            // spin
+        }
+
+        T::regs().dr().read().0 as u16
+    }
+
+    /// Read an ADC pin.
+    pub fn read<P>(&mut self, pin: &mut P) -> u16
+    where
+        P: AdcPin<T>,
+        P: crate::gpio::Pin,
+    {
+        pin.set_as_analog();
+
+        self.read_channel(pin.channel())
+    }
+
+    /// Read an ADC internal channel.
+    pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 {
+        self.read_channel(channel.channel())
+    }
+
+    fn read_channel(&mut self, channel: u8) -> u16 {
+        // Configure channel
+        Self::set_channel_sample_time(channel, self.sample_time);
+
+        #[cfg(stm32h7)]
+        {
+            T::regs().cfgr2().modify(|w| w.set_lshift(0));
+            T::regs()
+                .pcsel()
+                .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED));
+        }
+
+        T::regs().sqr1().write(|reg| {
+            reg.set_sq(0, channel);
+            reg.set_l(0);
+        });
+
+        self.convert()
+    }
+
+    fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
+        let sample_time = sample_time.into();
+        if ch <= 9 {
+            T::regs().smpr().modify(|reg| reg.set_smp(ch as _, sample_time));
+        } else {
+            T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time));
+        }
+    }
+}
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs
index 24dd7cc3c..12c5751bd 100644
--- a/embassy-stm32/src/adc/mod.rs
+++ b/embassy-stm32/src/adc/mod.rs
@@ -12,6 +12,7 @@
 #[cfg_attr(adc_v2, path = "v2.rs")]
 #[cfg_attr(any(adc_v3, adc_g0, adc_h5), path = "v3.rs")]
 #[cfg_attr(adc_v4, path = "v4.rs")]
+#[cfg_attr(adc_g4, path = "g4.rs")]
 mod _version;
 
 #[allow(unused)]
@@ -79,13 +80,37 @@ pub(crate) fn blocking_delay_us(us: u32) {
 }
 
 /// ADC instance.
-#[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0, adc_h5)))]
+#[cfg(not(any(
+    adc_f1,
+    adc_v1,
+    adc_l0,
+    adc_v2,
+    adc_v3,
+    adc_v4,
+    adc_g4,
+    adc_f3,
+    adc_f3_v1_1,
+    adc_g0,
+    adc_h5
+)))]
 #[allow(private_bounds)]
 pub trait Instance: SealedInstance + crate::Peripheral<P = Self> {
     type Interrupt: crate::interrupt::typelevel::Interrupt;
 }
 /// ADC instance.
-#[cfg(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0, adc_h5))]
+#[cfg(any(
+    adc_f1,
+    adc_v1,
+    adc_l0,
+    adc_v2,
+    adc_v3,
+    adc_v4,
+    adc_g4,
+    adc_f3,
+    adc_f3_v1_1,
+    adc_g0,
+    adc_h5
+))]
 #[allow(private_bounds)]
 pub trait Instance: SealedInstance + crate::Peripheral<P = Self> + crate::rcc::RccPeripheral {
     type Interrupt: crate::interrupt::typelevel::Interrupt;
diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs
index 68b54e406..3de38cbd6 100644
--- a/examples/stm32g4/src/bin/adc.rs
+++ b/examples/stm32g4/src/bin/adc.rs
@@ -29,7 +29,7 @@ async fn main(_spawner: Spawner) {
     info!("Hello World!");
 
     let mut adc = Adc::new(p.ADC2);
-    adc.set_sample_time(SampleTime::CYCLES32_5);
+    adc.set_sample_time(SampleTime::CYCLES24_5);
 
     loop {
         let measured = adc.read(&mut p.PA7);

From ec6ff217ca0f895898ecc8e2cf84d503f71c988c Mon Sep 17 00:00:00 2001
From: Dario Nieuwenhuis <dirbaio@dirbaio.net>
Date: Sat, 13 Apr 2024 02:13:41 +0200
Subject: [PATCH 04/16] Add stm32u0 support.

---
 ci.sh                                   |  3 ++
 embassy-stm32/Cargo.toml                | 38 +++++++++++++++++++++++--
 embassy-stm32/src/exti.rs               | 16 +++++------
 embassy-stm32/src/rcc/l.rs              | 28 +++++++++++++++---
 embassy-stm32/src/rcc/mco.rs            |  2 +-
 embassy-stm32/src/rcc/mod.rs            |  2 +-
 examples/stm32u0/.cargo/config.toml     |  9 ++++++
 examples/stm32u0/Cargo.toml             | 25 ++++++++++++++++
 examples/stm32u0/build.rs               |  5 ++++
 examples/stm32u0/src/bin/blinky.rs      | 26 +++++++++++++++++
 examples/stm32u0/src/bin/button.rs      | 24 ++++++++++++++++
 examples/stm32u0/src/bin/button_exti.rs | 25 ++++++++++++++++
 12 files changed, 187 insertions(+), 16 deletions(-)
 create mode 100644 examples/stm32u0/.cargo/config.toml
 create mode 100644 examples/stm32u0/Cargo.toml
 create mode 100644 examples/stm32u0/build.rs
 create mode 100644 examples/stm32u0/src/bin/blinky.rs
 create mode 100644 examples/stm32u0/src/bin/button.rs
 create mode 100644 examples/stm32u0/src/bin/button_exti.rs

diff --git a/ci.sh b/ci.sh
index d17f4e13e..1393b9d47 100755
--- a/ci.sh
+++ b/ci.sh
@@ -149,6 +149,9 @@ cargo batch \
     --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h503rb,defmt,exti,time-driver-any,time \
     --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h562ag,defmt,exti,time-driver-any,time \
     --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb35ce,defmt,exti,time-driver-any,time \
+    --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u031r8,defmt,exti,time-driver-any,time \
+    --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u073mb,defmt,exti,time-driver-any,time \
+    --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc,defmt,exti,time-driver-any,time \
     --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features ''\
     --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log' \
     --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 93792ecf8..459d2e370 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -30,6 +30,7 @@ flavors = [
     { regex_feature = "stm32l1.*", target = "thumbv7m-none-eabi" },
     { regex_feature = "stm32l4.*", target = "thumbv7em-none-eabi" },
     { regex_feature = "stm32l5.*", target = "thumbv8m.main-none-eabihf", features = ["low-power"] },
+    { regex_feature = "stm32u0.*", target = "thumbv6m-none-eabi" },
     { regex_feature = "stm32u5.*", target = "thumbv8m.main-none-eabihf" },
     { regex_feature = "stm32wb.*", target = "thumbv7em-none-eabi" },
     { regex_feature = "stm32wba.*", target = "thumbv8m.main-none-eabihf" },
@@ -70,7 +71,7 @@ rand_core = "0.6.3"
 sdio-host = "0.5.0"
 critical-section = "1.1"
 #stm32-metapac = { version = "15" }
-stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0c4baf478324e19741c7a9795ab0aa8217c3691c" }
+stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-d674277b78ca7400ecfeeb1b5af4e460a65c1a61" }
 
 vcell = "0.1.3"
 nb = "1.0.0"
@@ -96,7 +97,7 @@ proc-macro2 = "1.0.36"
 quote = "1.0.15"
 
 #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
-stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0c4baf478324e19741c7a9795ab0aa8217c3691c", default-features = false, features = ["metadata"]}
+stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-d674277b78ca7400ecfeeb1b5af4e460a65c1a61", default-features = false, features = ["metadata"]}
 
 [features]
 default = ["rt"]
@@ -1419,6 +1420,38 @@ stm32l562qe = [ "stm32-metapac/stm32l562qe" ]
 stm32l562re = [ "stm32-metapac/stm32l562re" ]
 stm32l562ve = [ "stm32-metapac/stm32l562ve" ]
 stm32l562ze = [ "stm32-metapac/stm32l562ze" ]
+stm32u031c6 = [ "stm32-metapac/stm32u031c6" ]
+stm32u031c8 = [ "stm32-metapac/stm32u031c8" ]
+stm32u031f4 = [ "stm32-metapac/stm32u031f4" ]
+stm32u031f6 = [ "stm32-metapac/stm32u031f6" ]
+stm32u031f8 = [ "stm32-metapac/stm32u031f8" ]
+stm32u031g6 = [ "stm32-metapac/stm32u031g6" ]
+stm32u031g8 = [ "stm32-metapac/stm32u031g8" ]
+stm32u031k4 = [ "stm32-metapac/stm32u031k4" ]
+stm32u031k6 = [ "stm32-metapac/stm32u031k6" ]
+stm32u031k8 = [ "stm32-metapac/stm32u031k8" ]
+stm32u031r6 = [ "stm32-metapac/stm32u031r6" ]
+stm32u031r8 = [ "stm32-metapac/stm32u031r8" ]
+stm32u073c8 = [ "stm32-metapac/stm32u073c8" ]
+stm32u073cb = [ "stm32-metapac/stm32u073cb" ]
+stm32u073cc = [ "stm32-metapac/stm32u073cc" ]
+stm32u073h8 = [ "stm32-metapac/stm32u073h8" ]
+stm32u073hb = [ "stm32-metapac/stm32u073hb" ]
+stm32u073hc = [ "stm32-metapac/stm32u073hc" ]
+stm32u073k8 = [ "stm32-metapac/stm32u073k8" ]
+stm32u073kb = [ "stm32-metapac/stm32u073kb" ]
+stm32u073kc = [ "stm32-metapac/stm32u073kc" ]
+stm32u073m8 = [ "stm32-metapac/stm32u073m8" ]
+stm32u073mb = [ "stm32-metapac/stm32u073mb" ]
+stm32u073mc = [ "stm32-metapac/stm32u073mc" ]
+stm32u073r8 = [ "stm32-metapac/stm32u073r8" ]
+stm32u073rb = [ "stm32-metapac/stm32u073rb" ]
+stm32u073rc = [ "stm32-metapac/stm32u073rc" ]
+stm32u083cc = [ "stm32-metapac/stm32u083cc" ]
+stm32u083hc = [ "stm32-metapac/stm32u083hc" ]
+stm32u083kc = [ "stm32-metapac/stm32u083kc" ]
+stm32u083mc = [ "stm32-metapac/stm32u083mc" ]
+stm32u083rc = [ "stm32-metapac/stm32u083rc" ]
 stm32u535cb = [ "stm32-metapac/stm32u535cb" ]
 stm32u535cc = [ "stm32-metapac/stm32u535cc" ]
 stm32u535ce = [ "stm32-metapac/stm32u535ce" ]
@@ -1474,6 +1507,7 @@ stm32u599vj = [ "stm32-metapac/stm32u599vj" ]
 stm32u599zi = [ "stm32-metapac/stm32u599zi" ]
 stm32u599zj = [ "stm32-metapac/stm32u599zj" ]
 stm32u5a5aj = [ "stm32-metapac/stm32u5a5aj" ]
+stm32u5a5qi = [ "stm32-metapac/stm32u5a5qi" ]
 stm32u5a5qj = [ "stm32-metapac/stm32u5a5qj" ]
 stm32u5a5rj = [ "stm32-metapac/stm32u5a5rj" ]
 stm32u5a5vj = [ "stm32-metapac/stm32u5a5vj" ]
diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs
index 8d5dae436..224d51b84 100644
--- a/embassy-stm32/src/exti.rs
+++ b/embassy-stm32/src/exti.rs
@@ -27,11 +27,11 @@ fn cpu_regs() -> pac::exti::Exti {
     EXTI
 }
 
-#[cfg(not(any(exti_c0, exti_g0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50)))]
+#[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, gpio_v1, exti_u5, exti_h5, exti_h50)))]
 fn exticr_regs() -> pac::syscfg::Syscfg {
     pac::SYSCFG
 }
-#[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))]
+#[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))]
 fn exticr_regs() -> pac::exti::Exti {
     EXTI
 }
@@ -44,9 +44,9 @@ unsafe fn on_irq() {
     #[cfg(feature = "low-power")]
     crate::low_power::on_wakeup_irq();
 
-    #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))]
+    #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))]
     let bits = EXTI.pr(0).read().0;
-    #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))]
+    #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))]
     let bits = EXTI.rpr(0).read().0 | EXTI.fpr(0).read().0;
 
     // We don't handle or change any EXTI lines above 16.
@@ -61,9 +61,9 @@ unsafe fn on_irq() {
     }
 
     // Clear pending
-    #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))]
+    #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))]
     EXTI.pr(0).write_value(Lines(bits));
-    #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))]
+    #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))]
     {
         EXTI.rpr(0).write_value(Lines(bits));
         EXTI.fpr(0).write_value(Lines(bits));
@@ -241,9 +241,9 @@ impl<'a> ExtiInputFuture<'a> {
             EXTI.ftsr(0).modify(|w| w.set_line(pin, falling));
 
             // clear pending bit
-            #[cfg(not(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50)))]
+            #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))]
             EXTI.pr(0).write(|w| w.set_line(pin, true));
-            #[cfg(any(exti_c0, exti_g0, exti_l5, exti_u5, exti_h5, exti_h50))]
+            #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))]
             {
                 EXTI.rpr(0).write(|w| w.set_line(pin, true));
                 EXTI.fpr(0).write(|w| w.set_line(pin, true));
diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs
index 9079ddd41..d7235ac7f 100644
--- a/embassy-stm32/src/rcc/l.rs
+++ b/embassy-stm32/src/rcc/l.rs
@@ -49,6 +49,7 @@ pub struct Config {
     pub sys: Sysclk,
     pub ahb_pre: AHBPrescaler,
     pub apb1_pre: APBPrescaler,
+    #[cfg(not(stm32u0))]
     pub apb2_pre: APBPrescaler,
     #[cfg(any(stm32wl5x, stm32wb))]
     pub core2_ahb_pre: AHBPrescaler,
@@ -75,6 +76,7 @@ impl Default for Config {
             sys: Sysclk::MSI,
             ahb_pre: AHBPrescaler::DIV1,
             apb1_pre: APBPrescaler::DIV1,
+            #[cfg(not(stm32u0))]
             apb2_pre: APBPrescaler::DIV1,
             #[cfg(any(stm32wl5x, stm32wb))]
             core2_ahb_pre: AHBPrescaler::DIV1,
@@ -130,7 +132,7 @@ pub const WPAN_DEFAULT: Config = Config {
 };
 
 fn msi_enable(range: MSIRange) {
-    #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
+    #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))]
     RCC.cr().modify(|w| {
         #[cfg(not(stm32wb))]
         w.set_msirgsel(crate::pac::rcc::vals::Msirgsel::CR);
@@ -240,7 +242,7 @@ pub(crate) unsafe fn init(config: Config) {
     let pll_input = PllInput {
         hse,
         hsi,
-        #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
+        #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))]
         msi,
     };
     let pll = init_pll(PllInstance::Pll, config.pll, &pll_input);
@@ -254,6 +256,10 @@ pub(crate) unsafe fn init(config: Config) {
         Sysclk::HSI => hsi.unwrap(),
         Sysclk::MSI => msi.unwrap(),
         Sysclk::PLL1_R => pll.r.unwrap(),
+        #[cfg(stm32u0)]
+        Sysclk::LSI | Sysclk::LSE => todo!(),
+        #[cfg(stm32u0)]
+        Sysclk::_RESERVED_6 | Sysclk::_RESERVED_7 => unreachable!(),
     };
 
     #[cfg(rcc_l4plus)]
@@ -263,6 +269,7 @@ pub(crate) unsafe fn init(config: Config) {
 
     let hclk1 = sys_clk / config.ahb_pre;
     let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk1, config.apb1_pre);
+    #[cfg(not(stm32u0))]
     let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk1, config.apb2_pre);
     #[cfg(any(stm32l4, stm32l5, stm32wlex))]
     let hclk2 = hclk1;
@@ -315,6 +322,13 @@ pub(crate) unsafe fn init(config: Config) {
         ..=64_000_000 => 3,
         _ => 4,
     };
+    #[cfg(stm32u0)]
+    let latency = match hclk1.0 {
+        // VOS RANGE1, others TODO.
+        ..=24_000_000 => 0,
+        ..=48_000_000 => 1,
+        _ => 2,
+    };
 
     #[cfg(stm32l1)]
     FLASH.acr().write(|w| w.set_acc64(true));
@@ -326,7 +340,11 @@ pub(crate) unsafe fn init(config: Config) {
     RCC.cfgr().modify(|w| {
         w.set_sw(config.sys);
         w.set_hpre(config.ahb_pre);
+        #[cfg(stm32u0)]
+        w.set_ppre(config.apb1_pre);
+        #[cfg(not(stm32u0))]
         w.set_ppre1(config.apb1_pre);
+        #[cfg(not(stm32u0))]
         w.set_ppre2(config.apb2_pre);
     });
     while RCC.cfgr().read().sws() != config.sys {}
@@ -353,8 +371,10 @@ pub(crate) unsafe fn init(config: Config) {
         #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
         hclk3: Some(hclk3),
         pclk1: Some(pclk1),
+        #[cfg(not(stm32u0))]
         pclk2: Some(pclk2),
         pclk1_tim: Some(pclk1_tim),
+        #[cfg(not(stm32u0))]
         pclk2_tim: Some(pclk2_tim),
         #[cfg(stm32wl)]
         pclk3: Some(hclk3),
@@ -408,7 +428,7 @@ fn msirange_to_hertz(range: MSIRange) -> Hertz {
     Hertz(32_768 * (1 << (range as u8 + 1)))
 }
 
-#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
+#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))]
 fn msirange_to_hertz(range: MSIRange) -> Hertz {
     match range {
         MSIRange::RANGE100K => Hertz(100_000),
@@ -521,7 +541,7 @@ mod pll {
     }
 }
 
-#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl))]
+#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))]
 mod pll {
     use super::{pll_enable, PllInstance};
     pub use crate::pac::rcc::vals::{
diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs
index d8604e07e..4b22a099d 100644
--- a/embassy-stm32/src/rcc/mco.rs
+++ b/embassy-stm32/src/rcc/mco.rs
@@ -52,7 +52,7 @@ macro_rules! impl_peri {
     };
 }
 
-#[cfg(any(rcc_c0, rcc_g0))]
+#[cfg(any(rcc_c0, rcc_g0, rcc_u0))]
 #[allow(unused_imports)]
 use self::{McoSource as Mco1Source, McoSource as Mco2Source};
 
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index c328344aa..00f21ed68 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -25,7 +25,7 @@ pub use hsi48::*;
 #[cfg_attr(stm32g0, path = "g0.rs")]
 #[cfg_attr(stm32g4, path = "g4.rs")]
 #[cfg_attr(any(stm32h5, stm32h7), path = "h.rs")]
-#[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl), path = "l.rs")]
+#[cfg_attr(any(stm32l0, stm32l1, stm32l4, stm32l5, stm32wb, stm32wl, stm32u0), path = "l.rs")]
 #[cfg_attr(stm32u5, path = "u5.rs")]
 #[cfg_attr(stm32wba, path = "wba.rs")]
 mod _version;
diff --git a/examples/stm32u0/.cargo/config.toml b/examples/stm32u0/.cargo/config.toml
new file mode 100644
index 000000000..c8022ae39
--- /dev/null
+++ b/examples/stm32u0/.cargo/config.toml
@@ -0,0 +1,9 @@
+[target.'cfg(all(target_arch = "arm", target_os = "none"))']
+# replace STM32G071C8Rx with your chip as listed in `probe-rs chip list`
+runner = "probe-rs run --speed 100 --chip STM32u083rctx"
+
+[build]
+target = "thumbv6m-none-eabi"
+
+[env]
+DEFMT_LOG = "trace"
diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml
new file mode 100644
index 000000000..af3d7ff61
--- /dev/null
+++ b/examples/stm32u0/Cargo.toml
@@ -0,0 +1,25 @@
+[package]
+edition = "2021"
+name = "embassy-stm32u0-examples"
+version = "0.1.0"
+license = "MIT OR Apache-2.0"
+
+[dependencies]
+# Change stm32c031c6 to your chip name, if necessary.
+embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti"]  }
+embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] }
+embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
+embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
+
+defmt = "0.3"
+defmt-rtt = "0.4"
+
+cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
+cortex-m-rt = "0.7.0"
+embedded-hal = "0.2.6"
+panic-probe = { version = "0.3", features = ["print-defmt"] }
+futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
+heapless = { version = "0.8", default-features = false }
+
+[profile.release]
+debug = 2
diff --git a/examples/stm32u0/build.rs b/examples/stm32u0/build.rs
new file mode 100644
index 000000000..8cd32d7ed
--- /dev/null
+++ b/examples/stm32u0/build.rs
@@ -0,0 +1,5 @@
+fn main() {
+    println!("cargo:rustc-link-arg-bins=--nmagic");
+    println!("cargo:rustc-link-arg-bins=-Tlink.x");
+    println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
+}
diff --git a/examples/stm32u0/src/bin/blinky.rs b/examples/stm32u0/src/bin/blinky.rs
new file mode 100644
index 000000000..90e479aae
--- /dev/null
+++ b/examples/stm32u0/src/bin/blinky.rs
@@ -0,0 +1,26 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_stm32::gpio::{Level, Output, Speed};
+use embassy_time::Timer;
+use {defmt_rtt as _, panic_probe as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) {
+    let p = embassy_stm32::init(Default::default());
+    info!("Hello World!");
+
+    let mut led = Output::new(p.PA5, Level::High, Speed::Low);
+
+    loop {
+        info!("high");
+        led.set_high();
+        Timer::after_millis(300).await;
+
+        info!("low");
+        led.set_low();
+        Timer::after_millis(300).await;
+    }
+}
diff --git a/examples/stm32u0/src/bin/button.rs b/examples/stm32u0/src/bin/button.rs
new file mode 100644
index 000000000..8017f0274
--- /dev/null
+++ b/examples/stm32u0/src/bin/button.rs
@@ -0,0 +1,24 @@
+#![no_std]
+#![no_main]
+
+use cortex_m_rt::entry;
+use defmt::*;
+use embassy_stm32::gpio::{Input, Pull};
+use {defmt_rtt as _, panic_probe as _};
+
+#[entry]
+fn main() -> ! {
+    info!("Hello World!");
+
+    let p = embassy_stm32::init(Default::default());
+
+    let button = Input::new(p.PC13, Pull::Up);
+
+    loop {
+        if button.is_high() {
+            info!("high");
+        } else {
+            info!("low");
+        }
+    }
+}
diff --git a/examples/stm32u0/src/bin/button_exti.rs b/examples/stm32u0/src/bin/button_exti.rs
new file mode 100644
index 000000000..34a08bbc6
--- /dev/null
+++ b/examples/stm32u0/src/bin/button_exti.rs
@@ -0,0 +1,25 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_stm32::exti::ExtiInput;
+use embassy_stm32::gpio::Pull;
+use {defmt_rtt as _, panic_probe as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) {
+    let p = embassy_stm32::init(Default::default());
+    info!("Hello World!");
+
+    let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up);
+
+    info!("Press the USER button...");
+
+    loop {
+        button.wait_for_falling_edge().await;
+        info!("Pressed!");
+        button.wait_for_rising_edge().await;
+        info!("Released!");
+    }
+}

From af03e1653a711e12566ee7cac2d018e94b1d267b Mon Sep 17 00:00:00 2001
From: Dion Dokter <diondokter@gmail.com>
Date: Sat, 13 Apr 2024 14:07:01 +0200
Subject: [PATCH 05/16] Disable the speed limitation

---
 examples/stm32u0/.cargo/config.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/examples/stm32u0/.cargo/config.toml b/examples/stm32u0/.cargo/config.toml
index c8022ae39..28b8af96e 100644
--- a/examples/stm32u0/.cargo/config.toml
+++ b/examples/stm32u0/.cargo/config.toml
@@ -1,6 +1,6 @@
 [target.'cfg(all(target_arch = "arm", target_os = "none"))']
 # replace STM32G071C8Rx with your chip as listed in `probe-rs chip list`
-runner = "probe-rs run --speed 100 --chip STM32u083rctx"
+runner = "probe-rs run --chip STM32u083rctx"
 
 [build]
 target = "thumbv6m-none-eabi"

From 5f23e3905226551a001da9bf4051c6efb552f636 Mon Sep 17 00:00:00 2001
From: Dion Dokter <diondokter@gmail.com>
Date: Sat, 13 Apr 2024 18:40:46 +0200
Subject: [PATCH 06/16] Add some examples.

- usart works
- dac works
- rng gets stuck on while loop
- usb_serial works, but cannot test due to lack of user usb port
- adc needs work and does not work yet
---
 embassy-stm32/src/dac/tsel.rs          |  17 ++++
 embassy-stm32/src/usb/usb.rs           |  14 ++--
 examples/stm32u0/Cargo.toml            |   3 +
 examples/stm32u0/src/bin/adc.rs        |  33 ++++++++
 examples/stm32u0/src/bin/dac.rs        |  35 ++++++++
 examples/stm32u0/src/bin/rng.rs        |  41 ++++++++++
 examples/stm32u0/src/bin/usart.rs      |  31 +++++++
 examples/stm32u0/src/bin/usb_serial.rs | 109 +++++++++++++++++++++++++
 8 files changed, 276 insertions(+), 7 deletions(-)
 create mode 100644 examples/stm32u0/src/bin/adc.rs
 create mode 100644 examples/stm32u0/src/bin/dac.rs
 create mode 100644 examples/stm32u0/src/bin/rng.rs
 create mode 100644 examples/stm32u0/src/bin/usart.rs
 create mode 100644 examples/stm32u0/src/bin/usb_serial.rs

diff --git a/embassy-stm32/src/dac/tsel.rs b/embassy-stm32/src/dac/tsel.rs
index 22d8d3dfa..1877954b9 100644
--- a/embassy-stm32/src/dac/tsel.rs
+++ b/embassy-stm32/src/dac/tsel.rs
@@ -235,6 +235,23 @@ pub enum TriggerSel {
     Exti9 = 13,
 }
 
+/// Trigger selection for U0.
+#[cfg(stm32u0)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum TriggerSel {
+    Software = 0,
+    Tim1 = 1,
+    Tim2 = 2,
+    Tim3 = 3,
+    Tim6 = 5,
+    Tim7 = 6,
+    Tim15 = 8,
+    Lptim1 = 11,
+    Lptim2 = 12,
+    Exti9 = 14,
+}
+
 /// Trigger selection for G4.
 #[cfg(stm32g4)]
 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs
index f48808cb3..81a2d2623 100644
--- a/embassy-stm32/src/usb/usb.rs
+++ b/embassy-stm32/src/usb/usb.rs
@@ -107,14 +107,14 @@ const EP_COUNT: usize = 8;
 
 #[cfg(any(usbram_16x1_512, usbram_16x2_512))]
 const USBRAM_SIZE: usize = 512;
-#[cfg(usbram_16x2_1024)]
+#[cfg(any(usbram_16x2_1024, usbram_32_1024))]
 const USBRAM_SIZE: usize = 1024;
 #[cfg(usbram_32_2048)]
 const USBRAM_SIZE: usize = 2048;
 
-#[cfg(not(usbram_32_2048))]
+#[cfg(not(any(usbram_32_2048, usbram_32_1024)))]
 const USBRAM_ALIGN: usize = 2;
-#[cfg(usbram_32_2048)]
+#[cfg(any(usbram_32_2048, usbram_32_1024))]
 const USBRAM_ALIGN: usize = 4;
 
 const NEW_AW: AtomicWaker = AtomicWaker::new();
@@ -159,7 +159,7 @@ fn calc_out_len(len: u16) -> (u16, u16) {
     }
 }
 
-#[cfg(not(usbram_32_2048))]
+#[cfg(not(any(usbram_32_2048, usbram_32_1024)))]
 mod btable {
     use super::*;
 
@@ -180,7 +180,7 @@ mod btable {
         USBRAM.mem(index * 4 + 3).read()
     }
 }
-#[cfg(usbram_32_2048)]
+#[cfg(any(usbram_32_2048, usbram_32_1024))]
 mod btable {
     use super::*;
 
@@ -224,9 +224,9 @@ impl<T: Instance> EndpointBuffer<T> {
             let n = USBRAM_ALIGN.min(buf.len() - i * USBRAM_ALIGN);
             val[..n].copy_from_slice(&buf[i * USBRAM_ALIGN..][..n]);
 
-            #[cfg(not(usbram_32_2048))]
+            #[cfg(not(any(usbram_32_2048, usbram_32_1024)))]
             let val = u16::from_le_bytes(val);
-            #[cfg(usbram_32_2048)]
+            #[cfg(any(usbram_32_2048, usbram_32_1024))]
             let val = u32::from_le_bytes(val);
             USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).write_value(val);
         }
diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml
index af3d7ff61..8219cb038 100644
--- a/examples/stm32u0/Cargo.toml
+++ b/examples/stm32u0/Cargo.toml
@@ -10,6 +10,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [
 embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] }
 embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
+embassy-usb = { version = "0.1.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] }
 
 defmt = "0.3"
 defmt-rtt = "0.4"
@@ -21,5 +22,7 @@ panic-probe = { version = "0.3", features = ["print-defmt"] }
 futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
 heapless = { version = "0.8", default-features = false }
 
+micromath = "2.0.0"
+
 [profile.release]
 debug = 2
diff --git a/examples/stm32u0/src/bin/adc.rs b/examples/stm32u0/src/bin/adc.rs
new file mode 100644
index 000000000..f7145b85d
--- /dev/null
+++ b/examples/stm32u0/src/bin/adc.rs
@@ -0,0 +1,33 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_stm32::adc::{Adc, Resolution};
+use embassy_stm32::Config;
+use embassy_time::Duration;
+use {defmt_rtt as _, panic_probe as _};
+
+#[cortex_m_rt::entry]
+fn main() -> ! {
+    info!("Hello World!");
+
+    let mut config = Config::default();
+    {
+        use embassy_stm32::rcc::*;
+        config.rcc.mux.adcsel = mux::Adcsel::SYS;
+    }
+    let p = embassy_stm32::init(config);
+
+    let mut adc = Adc::new(p.ADC1);
+    let mut temp = adc.enable_temperature();
+    adc.set_resolution(Resolution::BITS8);
+    let mut channel = p.PC0;
+
+    loop {
+        let v = adc.read(&mut channel);
+        info!("--> {}", v);
+        let v = adc.read(&mut temp);
+        info!("Temp: --> {}", v);
+        embassy_time::block_for(Duration::from_millis(1000));
+    }
+}
diff --git a/examples/stm32u0/src/bin/dac.rs b/examples/stm32u0/src/bin/dac.rs
new file mode 100644
index 000000000..fdbf1d374
--- /dev/null
+++ b/examples/stm32u0/src/bin/dac.rs
@@ -0,0 +1,35 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_stm32::dac::{DacCh1, Value};
+use embassy_stm32::dma::NoDma;
+use {defmt_rtt as _, panic_probe as _};
+
+#[cortex_m_rt::entry]
+fn main() -> ! {
+    let p = embassy_stm32::init(Default::default());
+    info!("Hello World!");
+
+    let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4);
+
+    loop {
+        for v in 0..=255 {
+            dac.set(Value::Bit8(to_sine_wave(v)));
+        }
+    }
+}
+
+use micromath::F32Ext;
+
+fn to_sine_wave(v: u8) -> u8 {
+    if v >= 128 {
+        // top half
+        let r = 3.14 * ((v - 128) as f32 / 128.0);
+        (r.sin() * 128.0 + 127.0) as u8
+    } else {
+        // bottom half
+        let r = 3.14 + 3.14 * (v as f32 / 128.0);
+        (r.sin() * 128.0 + 127.0) as u8
+    }
+}
diff --git a/examples/stm32u0/src/bin/rng.rs b/examples/stm32u0/src/bin/rng.rs
new file mode 100644
index 000000000..9b4c2537e
--- /dev/null
+++ b/examples/stm32u0/src/bin/rng.rs
@@ -0,0 +1,41 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_stm32::rng::Rng;
+use embassy_stm32::{bind_interrupts, peripherals, rng, Config};
+use {defmt_rtt as _, panic_probe as _};
+
+bind_interrupts!(struct Irqs {
+    RNG_CRYP => rng::InterruptHandler<peripherals::RNG>;
+});
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) {
+    let mut config = Config::default();
+    {
+        use embassy_stm32::rcc::*;
+        config.rcc.hsi = true;
+        config.rcc.pll = Some(Pll {
+            source: PllSource::HSI, // 16 MHz
+            prediv: PllPreDiv::DIV1,
+            mul: PllMul::MUL7, // 16 * 7 = 112 MHz
+            divp: None,
+            divq: None,
+            divr: Some(PllRDiv::DIV2), // 112 / 2 = 56 MHz
+        });
+        config.rcc.sys = Sysclk::PLL1_R;
+    }
+
+    let p = embassy_stm32::init(config);
+
+    info!("Hello World!");
+
+    let mut rng = Rng::new(p.RNG, Irqs);
+    info!("Hello World 2!");
+
+    let mut buf = [0u8; 16];
+    unwrap!(rng.async_fill_bytes(&mut buf).await);
+    info!("random bytes: {:02x}", buf);
+}
diff --git a/examples/stm32u0/src/bin/usart.rs b/examples/stm32u0/src/bin/usart.rs
new file mode 100644
index 000000000..1fcf0029a
--- /dev/null
+++ b/examples/stm32u0/src/bin/usart.rs
@@ -0,0 +1,31 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_stm32::dma::NoDma;
+use embassy_stm32::usart::{Config, Uart};
+use embassy_stm32::{bind_interrupts, peripherals, usart};
+use {defmt_rtt as _, panic_probe as _};
+
+bind_interrupts!(struct Irqs {
+    USART2_LPUART2 => usart::InterruptHandler<peripherals::USART2>;
+});
+
+#[cortex_m_rt::entry]
+fn main() -> ! {
+    info!("Hello World!");
+
+    let p = embassy_stm32::init(Default::default());
+
+    let config = Config::default();
+    let mut usart = Uart::new(p.USART2, p.PA3, p.PA2, Irqs, NoDma, NoDma, config).unwrap();
+
+    unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n"));
+    info!("wrote Hello, starting echo");
+
+    let mut buf = [0u8; 1];
+    loop {
+        unwrap!(usart.blocking_read(&mut buf));
+        unwrap!(usart.blocking_write(&buf));
+    }
+}
diff --git a/examples/stm32u0/src/bin/usb_serial.rs b/examples/stm32u0/src/bin/usb_serial.rs
new file mode 100644
index 000000000..9b38fd5dc
--- /dev/null
+++ b/examples/stm32u0/src/bin/usb_serial.rs
@@ -0,0 +1,109 @@
+#![no_std]
+#![no_main]
+
+use defmt::{panic, *};
+use defmt_rtt as _; // global logger
+use embassy_executor::Spawner;
+use embassy_stm32::usb::{Driver, Instance};
+use embassy_stm32::{bind_interrupts, peripherals, usb, Config};
+use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
+use embassy_usb::driver::EndpointError;
+use embassy_usb::Builder;
+use futures::future::join;
+use panic_probe as _;
+
+bind_interrupts!(struct Irqs {
+    USB_DRD_FS => usb::InterruptHandler<peripherals::USB>;
+});
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) {
+    let mut config = Config::default();
+    {
+        use embassy_stm32::rcc::*;
+        config.rcc.hsi = true;
+        config.rcc.pll = Some(Pll {
+            source: PllSource::HSI, // 16 MHz
+            prediv: PllPreDiv::DIV1,
+            mul: PllMul::MUL7,
+            divp: None,
+            divq: None,
+            divr: Some(PllRDiv::DIV2), // 56 MHz
+        });
+        config.rcc.sys = Sysclk::PLL1_R;
+        config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB
+        config.rcc.mux.clk48sel = mux::Clk48sel::HSI48; // USB uses ICLK
+    }
+
+    let p = embassy_stm32::init(config);
+
+    info!("Hello World!");
+
+    // Create the driver, from the HAL.
+    let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);
+
+    // Create embassy-usb Config
+    let config = embassy_usb::Config::new(0xc0de, 0xcafe);
+    //config.max_packet_size_0 = 64;
+
+    // Create embassy-usb DeviceBuilder using the driver and config.
+    // It needs some buffers for building the descriptors.
+    let mut config_descriptor = [0; 256];
+    let mut bos_descriptor = [0; 256];
+    let mut control_buf = [0; 7];
+
+    let mut state = State::new();
+
+    let mut builder = Builder::new(
+        driver,
+        config,
+        &mut config_descriptor,
+        &mut bos_descriptor,
+        &mut [], // no msos descriptors
+        &mut control_buf,
+    );
+
+    // Create classes on the builder.
+    let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
+
+    // Build the builder.
+    let mut usb = builder.build();
+
+    // Run the USB device.
+    let usb_fut = usb.run();
+
+    // Do stuff with the class!
+    let echo_fut = async {
+        loop {
+            class.wait_connection().await;
+            info!("Connected");
+            let _ = echo(&mut class).await;
+            info!("Disconnected");
+        }
+    };
+
+    // Run everything concurrently.
+    // If we had made everything `'static` above instead, we could do this using separate tasks instead.
+    join(usb_fut, echo_fut).await;
+}
+
+struct Disconnected {}
+
+impl From<EndpointError> for Disconnected {
+    fn from(val: EndpointError) -> Self {
+        match val {
+            EndpointError::BufferOverflow => panic!("Buffer overflow"),
+            EndpointError::Disabled => Disconnected {},
+        }
+    }
+}
+
+async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {
+    let mut buf = [0; 64];
+    loop {
+        let n = class.read_packet(&mut buf).await?;
+        let data = &buf[..n];
+        info!("data: {:x}", data);
+        class.write_packet(data).await?;
+    }
+}

From b659e3d529e427cfc2fb38f514825b60eded784f Mon Sep 17 00:00:00 2001
From: Dion Dokter <diondokter@gmail.com>
Date: Sun, 14 Apr 2024 00:04:13 +0200
Subject: [PATCH 07/16] Add ADC

---
 embassy-stm32/src/adc/mod.rs    |  4 +++-
 embassy-stm32/src/adc/v3.rs     | 40 ++++++++++++++++++++++++---------
 examples/stm32u0/src/bin/adc.rs |  4 +---
 3 files changed, 33 insertions(+), 15 deletions(-)

diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs
index 12c5751bd..1ab2bc5c4 100644
--- a/embassy-stm32/src/adc/mod.rs
+++ b/embassy-stm32/src/adc/mod.rs
@@ -10,7 +10,7 @@
 #[cfg_attr(adc_v1, path = "v1.rs")]
 #[cfg_attr(adc_l0, path = "v1.rs")]
 #[cfg_attr(adc_v2, path = "v2.rs")]
-#[cfg_attr(any(adc_v3, adc_g0, adc_h5), path = "v3.rs")]
+#[cfg_attr(any(adc_v3, adc_g0, adc_h5, adc_u0), path = "v3.rs")]
 #[cfg_attr(adc_v4, path = "v4.rs")]
 #[cfg_attr(adc_g4, path = "g4.rs")]
 mod _version;
@@ -91,6 +91,7 @@ pub(crate) fn blocking_delay_us(us: u32) {
     adc_f3,
     adc_f3_v1_1,
     adc_g0,
+    adc_u0,
     adc_h5
 )))]
 #[allow(private_bounds)]
@@ -109,6 +110,7 @@ pub trait Instance: SealedInstance + crate::Peripheral<P = Self> {
     adc_f3,
     adc_f3_v1_1,
     adc_g0,
+    adc_u0,
     adc_h5
 ))]
 #[allow(private_bounds)]
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs
index 4fd8558ba..dc418297e 100644
--- a/embassy-stm32/src/adc/v3.rs
+++ b/embassy-stm32/src/adc/v3.rs
@@ -19,6 +19,8 @@ impl<T: Instance> super::SealedAdcPin<T> for VrefInt {
                 let val = 13;
             } else if #[cfg(adc_h5)] {
                 let val = 17;
+            } else if #[cfg(adc_u0)] {
+                let val = 12;
             } else {
                 let val = 0;
             }
@@ -36,6 +38,8 @@ impl<T: Instance> super::SealedAdcPin<T> for Temperature {
                 let val = 12;
             } else if #[cfg(adc_h5)] {
                 let val = 16;
+            } else if #[cfg(adc_u0)] {
+                let val = 11;
             } else {
                 let val = 17;
             }
@@ -53,6 +57,8 @@ impl<T: Instance> super::SealedAdcPin<T> for Vbat {
                 let val = 14;
             } else if #[cfg(adc_h5)] {
                 let val = 2;
+            } else if #[cfg(adc_h5)] {
+                let val = 13;
             } else {
                 let val = 18;
             }
@@ -73,17 +79,29 @@ cfg_if! {
     }
 }
 
+cfg_if! {
+    if #[cfg(adc_u0)] {
+        pub struct DacOut;
+        impl<T: Instance> AdcPin<T> for DacOut {}
+        impl<T: Instance> super::SealedAdcPin<T> for DacOut {
+            fn channel(&self) -> u8 {
+                19
+            }
+        }
+    }
+}
+
 impl<'d, T: Instance> Adc<'d, T> {
     pub fn new(adc: impl Peripheral<P = T> + 'd) -> Self {
         into_ref!(adc);
         T::enable_and_reset();
         T::regs().cr().modify(|reg| {
-            #[cfg(not(adc_g0))]
+            #[cfg(not(any(adc_g0, adc_u0)))]
             reg.set_deeppwd(false);
             reg.set_advregen(true);
         });
 
-        #[cfg(adc_g0)]
+        #[cfg(any(adc_g0, adc_u0))]
         T::regs().cfgr1().modify(|reg| {
             reg.set_chselrmod(false);
         });
@@ -107,11 +125,11 @@ impl<'d, T: Instance> Adc<'d, T> {
     }
 
     pub fn enable_vrefint(&self) -> VrefInt {
-        #[cfg(not(adc_g0))]
+        #[cfg(not(any(adc_g0, adc_u0)))]
         T::common_regs().ccr().modify(|reg| {
             reg.set_vrefen(true);
         });
-        #[cfg(adc_g0)]
+        #[cfg(any(adc_g0, adc_u0))]
         T::regs().ccr().modify(|reg| {
             reg.set_vrefen(true);
         });
@@ -125,7 +143,7 @@ impl<'d, T: Instance> Adc<'d, T> {
 
     pub fn enable_temperature(&self) -> Temperature {
         cfg_if! {
-            if #[cfg(adc_g0)] {
+            if #[cfg(any(adc_g0, adc_u0))] {
                 T::regs().ccr().modify(|reg| {
                     reg.set_tsen(true);
                 });
@@ -145,7 +163,7 @@ impl<'d, T: Instance> Adc<'d, T> {
 
     pub fn enable_vbat(&self) -> Vbat {
         cfg_if! {
-            if #[cfg(adc_g0)] {
+            if #[cfg(any(adc_g0, adc_u0))] {
                 T::regs().ccr().modify(|reg| {
                     reg.set_vbaten(true);
                 });
@@ -168,9 +186,9 @@ impl<'d, T: Instance> Adc<'d, T> {
     }
 
     pub fn set_resolution(&mut self, resolution: Resolution) {
-        #[cfg(not(adc_g0))]
+        #[cfg(not(any(adc_g0, adc_u0)))]
         T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
-        #[cfg(adc_g0)]
+        #[cfg(any(adc_g0, adc_u0))]
         T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into()));
     }
 
@@ -231,9 +249,9 @@ impl<'d, T: Instance> Adc<'d, T> {
         Self::set_channel_sample_time(pin.channel(), self.sample_time);
 
         // Select channel
-        #[cfg(not(adc_g0))]
+        #[cfg(not(any(adc_g0, adc_u0)))]
         T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel()));
-        #[cfg(adc_g0)]
+        #[cfg(any(adc_g0, adc_u0))]
         T::regs().chselr().write(|reg| reg.set_chsel(1 << pin.channel()));
 
         // Some models are affected by an erratum:
@@ -261,7 +279,7 @@ impl<'d, T: Instance> Adc<'d, T> {
 
     fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) {
         cfg_if! {
-            if #[cfg(adc_g0)] {
+            if #[cfg(any(adc_g0, adc_u0))] {
                 T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into()));
             } else if #[cfg(adc_h5)] {
                 match _ch {
diff --git a/examples/stm32u0/src/bin/adc.rs b/examples/stm32u0/src/bin/adc.rs
index f7145b85d..c63c4a428 100644
--- a/examples/stm32u0/src/bin/adc.rs
+++ b/examples/stm32u0/src/bin/adc.rs
@@ -26,8 +26,6 @@ fn main() -> ! {
     loop {
         let v = adc.read(&mut channel);
         info!("--> {}", v);
-        let v = adc.read(&mut temp);
-        info!("Temp: --> {}", v);
-        embassy_time::block_for(Duration::from_millis(1000));
+        embassy_time::block_for(Duration::from_millis(200));
     }
 }

From 0a785585bcc268ca1eb5341b1bcc54113cf96298 Mon Sep 17 00:00:00 2001
From: Dion Dokter <diondokter@gmail.com>
Date: Sun, 14 Apr 2024 00:08:06 +0200
Subject: [PATCH 08/16] Remove temp variable

---
 examples/stm32u0/src/bin/adc.rs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/examples/stm32u0/src/bin/adc.rs b/examples/stm32u0/src/bin/adc.rs
index c63c4a428..4410448f1 100644
--- a/examples/stm32u0/src/bin/adc.rs
+++ b/examples/stm32u0/src/bin/adc.rs
@@ -19,7 +19,6 @@ fn main() -> ! {
     let p = embassy_stm32::init(config);
 
     let mut adc = Adc::new(p.ADC1);
-    let mut temp = adc.enable_temperature();
     adc.set_resolution(Resolution::BITS8);
     let mut channel = p.PC0;
 

From ca84be80bcbfb4459212fa02e75cfafc85d6df51 Mon Sep 17 00:00:00 2001
From: Dion Dokter <diondokter@gmail.com>
Date: Sun, 14 Apr 2024 00:45:53 +0200
Subject: [PATCH 09/16] Add wdt and flash

---
 embassy-stm32/src/flash/mod.rs    |  3 +-
 embassy-stm32/src/flash/u0.rs     | 96 +++++++++++++++++++++++++++++++
 examples/stm32u0/src/bin/flash.rs | 43 ++++++++++++++
 examples/stm32u0/src/bin/wdt.rs   | 41 +++++++++++++
 4 files changed, 182 insertions(+), 1 deletion(-)
 create mode 100644 embassy-stm32/src/flash/u0.rs
 create mode 100644 examples/stm32u0/src/bin/flash.rs
 create mode 100644 examples/stm32u0/src/bin/wdt.rs

diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs
index 9d7861816..8c6ca2471 100644
--- a/embassy-stm32/src/flash/mod.rs
+++ b/embassy-stm32/src/flash/mod.rs
@@ -101,10 +101,11 @@ pub enum FlashBank {
 #[cfg_attr(flash_h7ab, path = "h7.rs")]
 #[cfg_attr(flash_u5, path = "u5.rs")]
 #[cfg_attr(flash_h50, path = "h50.rs")]
+#[cfg_attr(flash_u0, path = "u0.rs")]
 #[cfg_attr(
     not(any(
         flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f1, flash_f3, flash_f4, flash_f7, flash_g0,
-        flash_g4, flash_h7, flash_h7ab, flash_u5, flash_h50
+        flash_g4, flash_h7, flash_h7ab, flash_u5, flash_h50, flash_u0
     )),
     path = "other.rs"
 )]
diff --git a/embassy-stm32/src/flash/u0.rs b/embassy-stm32/src/flash/u0.rs
new file mode 100644
index 000000000..dfc5a2f76
--- /dev/null
+++ b/embassy-stm32/src/flash/u0.rs
@@ -0,0 +1,96 @@
+use core::ptr::write_volatile;
+use core::sync::atomic::{fence, Ordering};
+
+use cortex_m::interrupt;
+
+use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE};
+use crate::flash::Error;
+use crate::pac;
+
+pub(crate) const fn is_default_layout() -> bool {
+    true
+}
+
+pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] {
+    &FLASH_REGIONS
+}
+
+pub(crate) unsafe fn lock() {
+    pac::FLASH.cr().modify(|w| w.set_lock(true));
+}
+pub(crate) unsafe fn unlock() {
+    // Wait, while the memory interface is busy.
+    while pac::FLASH.sr().read().bsy1() {}
+
+    // Unlock flash
+    if pac::FLASH.cr().read().lock() {
+        pac::FLASH.keyr().write(|w| w.set_key(0x4567_0123));
+        pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB));
+    }
+}
+
+pub(crate) unsafe fn enable_blocking_write() {
+    assert_eq!(0, WRITE_SIZE % 4);
+    pac::FLASH.cr().write(|w| w.set_pg(true));
+}
+
+pub(crate) unsafe fn disable_blocking_write() {
+    pac::FLASH.cr().write(|w| w.set_pg(false));
+}
+
+pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> {
+    let mut address = start_address;
+    for val in buf.chunks(4) {
+        write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap()));
+        address += val.len() as u32;
+
+        // prevents parallelism errors
+        fence(Ordering::SeqCst);
+    }
+
+    wait_ready_blocking()
+}
+
+pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> {
+    let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32;
+    while pac::FLASH.sr().read().bsy1() {}
+    clear_all_err();
+
+    interrupt::free(|_| {
+        pac::FLASH.cr().modify(|w| {
+            w.set_per(true);
+            w.set_pnb(idx as u8);
+            w.set_strt(true);
+        });
+    });
+
+    let ret: Result<(), Error> = wait_ready_blocking();
+    pac::FLASH.cr().modify(|w| w.set_per(false));
+    ret
+}
+
+pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> {
+    while pac::FLASH.sr().read().bsy1() {}
+
+    let sr = pac::FLASH.sr().read();
+
+    if sr.progerr() {
+        return Err(Error::Prog);
+    }
+
+    if sr.wrperr() {
+        return Err(Error::Protected);
+    }
+
+    if sr.pgaerr() {
+        return Err(Error::Unaligned);
+    }
+
+    Ok(())
+}
+
+pub(crate) unsafe fn clear_all_err() {
+    // read and write back the same value.
+    // This clears all "write 1 to clear" bits.
+    pac::FLASH.sr().modify(|_| {});
+}
diff --git a/examples/stm32u0/src/bin/flash.rs b/examples/stm32u0/src/bin/flash.rs
new file mode 100644
index 000000000..01b80a76b
--- /dev/null
+++ b/examples/stm32u0/src/bin/flash.rs
@@ -0,0 +1,43 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_stm32::flash::Flash;
+use {defmt_rtt as _, panic_probe as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) {
+    let p = embassy_stm32::init(Default::default());
+    info!("Hello World!");
+
+    let addr: u32 = 0x40000 - 2 * 1024;
+
+    let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region;
+
+    info!("Reading...");
+    let mut buf = [0u8; 32];
+    unwrap!(f.blocking_read(addr, &mut buf));
+    info!("Read: {=[u8]:x}", buf);
+    info!("Erasing...");
+    unwrap!(f.blocking_erase(addr, addr + 2 * 1024));
+
+    info!("Reading...");
+    let mut buf = [0u8; 32];
+    unwrap!(f.blocking_read(addr, &mut buf));
+    info!("Read after erase: {=[u8]:x}", buf);
+
+    info!("Writing...");
+    unwrap!(f.blocking_write(
+        addr,
+        &[
+            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+            30, 31, 32
+        ]
+    ));
+
+    info!("Reading...");
+    let mut buf = [0u8; 32];
+    unwrap!(f.blocking_read(addr, &mut buf));
+    info!("Read: {=[u8]:x}", buf);
+}
diff --git a/examples/stm32u0/src/bin/wdt.rs b/examples/stm32u0/src/bin/wdt.rs
new file mode 100644
index 000000000..f6276e2e9
--- /dev/null
+++ b/examples/stm32u0/src/bin/wdt.rs
@@ -0,0 +1,41 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_stm32::gpio::{Level, Output, Speed};
+use embassy_stm32::wdg::IndependentWatchdog;
+use embassy_time::Timer;
+use {defmt_rtt as _, panic_probe as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) {
+    let p = embassy_stm32::init(Default::default());
+    info!("Hello World!");
+
+    let mut led = Output::new(p.PA5, Level::High, Speed::Low);
+
+    let mut wdt = IndependentWatchdog::new(p.IWDG, 1_000_000);
+    wdt.unleash();
+
+    let mut i = 0;
+
+    loop {
+        info!("high");
+        led.set_high();
+        Timer::after_millis(300).await;
+
+        info!("low");
+        led.set_low();
+        Timer::after_millis(300).await;
+
+        // Pet watchdog for 5 iterations and then stop.
+        // MCU should restart in 1 second after the last pet.
+        if i < 5 {
+            info!("Petting watchdog");
+            wdt.pet();
+        }
+
+        i += 1;
+    }
+}

From e224e6cef4e0508f14bb9ffd1d9d0fc9220c07d8 Mon Sep 17 00:00:00 2001
From: Dion Dokter <diondokter@gmail.com>
Date: Sun, 14 Apr 2024 01:10:20 +0200
Subject: [PATCH 10/16] Add CRC

---
 embassy-stm32/src/crc/v2v3.rs   |  2 ++
 examples/stm32u0/src/bin/crc.rs | 31 +++++++++++++++++++++++++++++++
 2 files changed, 33 insertions(+)
 create mode 100644 examples/stm32u0/src/bin/crc.rs

diff --git a/embassy-stm32/src/crc/v2v3.rs b/embassy-stm32/src/crc/v2v3.rs
index 13fb6778c..ad7c79f12 100644
--- a/embassy-stm32/src/crc/v2v3.rs
+++ b/embassy-stm32/src/crc/v2v3.rs
@@ -13,6 +13,8 @@ pub struct Crc<'d> {
 }
 
 /// CRC configuration errlr
+#[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum ConfigError {
     /// The selected polynomial is invalid.
     InvalidPolynomial,
diff --git a/examples/stm32u0/src/bin/crc.rs b/examples/stm32u0/src/bin/crc.rs
new file mode 100644
index 000000000..d1b545d5b
--- /dev/null
+++ b/examples/stm32u0/src/bin/crc.rs
@@ -0,0 +1,31 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_stm32::crc::{Config, Crc, InputReverseConfig, PolySize};
+use {defmt_rtt as _, panic_probe as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) {
+    let p = embassy_stm32::init(Default::default());
+    info!("Hello World!");
+
+    // Setup for: https://crccalc.com/?crc=Life, it never dieWomen are my favorite guy&method=crc32&datatype=ascii&outtype=0
+    let mut crc = Crc::new(
+        p.CRC,
+        unwrap!(Config::new(
+            InputReverseConfig::Byte,
+            true,
+            PolySize::Width32,
+            0xFFFFFFFF,
+            0x04C11DB7
+        )),
+    );
+
+    let output = crc.feed_bytes(b"Life, it never die\nWomen are my favorite guy") ^ 0xFFFFFFFF;
+
+    defmt::assert_eq!(output, 0x33F0E26B);
+
+    cortex_m::asm::bkpt();
+}

From 53cb84d3d65b96b5f92f434b113cffe62810c0ad Mon Sep 17 00:00:00 2001
From: Dion Dokter <diondokter@gmail.com>
Date: Tue, 16 Apr 2024 15:24:20 +0200
Subject: [PATCH 11/16] Added RTC example

---
 examples/stm32u0/Cargo.toml     |  3 +-
 examples/stm32u0/src/bin/rtc.rs | 49 +++++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+), 1 deletion(-)
 create mode 100644 examples/stm32u0/src/bin/rtc.rs

diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml
index 64fd837a2..5868372dd 100644
--- a/examples/stm32u0/Cargo.toml
+++ b/examples/stm32u0/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
 
 [dependencies]
 # Change stm32u083rc to your chip name, if necessary.
-embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti"]  }
+embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti", "chrono"]  }
 embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] }
 embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
@@ -23,6 +23,7 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa
 heapless = { version = "0.8", default-features = false }
 
 micromath = "2.0.0"
+chrono = { version = "0.4.38", default-features = false }
 
 [profile.release]
 debug = 2
diff --git a/examples/stm32u0/src/bin/rtc.rs b/examples/stm32u0/src/bin/rtc.rs
new file mode 100644
index 000000000..72fa0fde4
--- /dev/null
+++ b/examples/stm32u0/src/bin/rtc.rs
@@ -0,0 +1,49 @@
+#![no_std]
+#![no_main]
+
+use chrono::{NaiveDate, NaiveDateTime};
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_stm32::rtc::{Rtc, RtcConfig};
+use embassy_stm32::Config;
+use embassy_time::Timer;
+use {defmt_rtt as _, panic_probe as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) {
+    let mut config = Config::default();
+    {
+        use embassy_stm32::rcc::*;
+        config.rcc.sys = Sysclk::PLL1_R;
+        config.rcc.hsi = true;
+        config.rcc.pll = Some(Pll {
+            source: PllSource::HSI, // 16 MHz
+            prediv: PllPreDiv::DIV1,
+            mul: PllMul::MUL7, // 16 * 7 = 112 MHz
+            divp: None,
+            divq: None,
+            divr: Some(PllRDiv::DIV2), // 112 / 2 = 56 MHz
+        });
+        config.rcc.ls = LsConfig::default();
+    }
+
+    let p = 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.and_utc().timestamp());
+
+    rtc.set_datetime(now.into()).expect("datetime not set");
+
+    // In reality the delay would be much longer
+    Timer::after_millis(20000).await;
+
+    let then: NaiveDateTime = rtc.now().unwrap().into();
+    info!("Got RTC! {:?}", then.and_utc().timestamp());
+}

From a5f754238b77a8ef9ec8c6ad4c59af8fa5d5b374 Mon Sep 17 00:00:00 2001
From: Dion Dokter <diondokter@gmail.com>
Date: Tue, 16 Apr 2024 15:33:23 +0200
Subject: [PATCH 12/16] Add spi, i2c and fix usart

---
 examples/stm32u0/src/bin/i2c.rs   | 21 +++++++++++++++++++++
 examples/stm32u0/src/bin/spi.rs   | 30 ++++++++++++++++++++++++++++++
 examples/stm32u0/src/bin/usart.rs |  8 +-------
 3 files changed, 52 insertions(+), 7 deletions(-)
 create mode 100644 examples/stm32u0/src/bin/i2c.rs
 create mode 100644 examples/stm32u0/src/bin/spi.rs

diff --git a/examples/stm32u0/src/bin/i2c.rs b/examples/stm32u0/src/bin/i2c.rs
new file mode 100644
index 000000000..2861bc091
--- /dev/null
+++ b/examples/stm32u0/src/bin/i2c.rs
@@ -0,0 +1,21 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_stm32::i2c::I2c;
+use embassy_stm32::time::Hertz;
+use {defmt_rtt as _, panic_probe as _};
+
+const ADDRESS: u8 = 0x5F;
+const WHOAMI: u8 = 0x0F;
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) {
+    let p = embassy_stm32::init(Default::default());
+    let mut i2c = I2c::new_blocking(p.I2C2, p.PB10, p.PB11, Hertz(100_000), Default::default());
+
+    let mut data = [0u8; 1];
+    unwrap!(i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data));
+    info!("Whoami: {}", data[0]);
+}
diff --git a/examples/stm32u0/src/bin/spi.rs b/examples/stm32u0/src/bin/spi.rs
new file mode 100644
index 000000000..5693a3765
--- /dev/null
+++ b/examples/stm32u0/src/bin/spi.rs
@@ -0,0 +1,30 @@
+#![no_std]
+#![no_main]
+
+use defmt::*;
+use embassy_stm32::gpio::{Level, Output, Speed};
+use embassy_stm32::spi::{Config, Spi};
+use embassy_stm32::time::Hertz;
+use {defmt_rtt as _, panic_probe as _};
+
+#[cortex_m_rt::entry]
+fn main() -> ! {
+    info!("Hello World!");
+
+    let p = embassy_stm32::init(Default::default());
+
+    let mut spi_config = Config::default();
+    spi_config.frequency = Hertz(1_000_000);
+
+    let mut spi = Spi::new_blocking(p.SPI3, p.PC10, p.PC12, p.PC11, spi_config);
+
+    let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh);
+
+    loop {
+        let mut buf = [0x0Au8; 4];
+        cs.set_low();
+        unwrap!(spi.blocking_transfer_in_place(&mut buf));
+        cs.set_high();
+        info!("xfer {=[u8]:x}", buf);
+    }
+}
diff --git a/examples/stm32u0/src/bin/usart.rs b/examples/stm32u0/src/bin/usart.rs
index 1fcf0029a..037a5c833 100644
--- a/examples/stm32u0/src/bin/usart.rs
+++ b/examples/stm32u0/src/bin/usart.rs
@@ -2,15 +2,9 @@
 #![no_main]
 
 use defmt::*;
-use embassy_stm32::dma::NoDma;
 use embassy_stm32::usart::{Config, Uart};
-use embassy_stm32::{bind_interrupts, peripherals, usart};
 use {defmt_rtt as _, panic_probe as _};
 
-bind_interrupts!(struct Irqs {
-    USART2_LPUART2 => usart::InterruptHandler<peripherals::USART2>;
-});
-
 #[cortex_m_rt::entry]
 fn main() -> ! {
     info!("Hello World!");
@@ -18,7 +12,7 @@ fn main() -> ! {
     let p = embassy_stm32::init(Default::default());
 
     let config = Config::default();
-    let mut usart = Uart::new(p.USART2, p.PA3, p.PA2, Irqs, NoDma, NoDma, config).unwrap();
+    let mut usart = Uart::new_blocking(p.USART2, p.PA3, p.PA2, config).unwrap();
 
     unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n"));
     info!("wrote Hello, starting echo");

From 90f1d29c2470d51337a3666cde2efacad3e054e5 Mon Sep 17 00:00:00 2001
From: Dion Dokter <diondokter@gmail.com>
Date: Tue, 16 Apr 2024 16:36:10 +0200
Subject: [PATCH 13/16] Fix rng clock

---
 examples/stm32u0/src/bin/rng.rs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/examples/stm32u0/src/bin/rng.rs b/examples/stm32u0/src/bin/rng.rs
index 9b4c2537e..89445b042 100644
--- a/examples/stm32u0/src/bin/rng.rs
+++ b/examples/stm32u0/src/bin/rng.rs
@@ -3,6 +3,7 @@
 
 use defmt::*;
 use embassy_executor::Spawner;
+use embassy_stm32::rcc::mux::Clk48sel;
 use embassy_stm32::rng::Rng;
 use embassy_stm32::{bind_interrupts, peripherals, rng, Config};
 use {defmt_rtt as _, panic_probe as _};
@@ -26,6 +27,8 @@ async fn main(_spawner: Spawner) {
             divr: Some(PllRDiv::DIV2), // 112 / 2 = 56 MHz
         });
         config.rcc.sys = Sysclk::PLL1_R;
+        config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: false }); // needed for RNG
+        config.rcc.mux.clk48sel = Clk48sel::HSI48; // needed for RNG (or use MSI or PLLQ if you want)
     }
 
     let p = embassy_stm32::init(config);
@@ -33,7 +36,6 @@ async fn main(_spawner: Spawner) {
     info!("Hello World!");
 
     let mut rng = Rng::new(p.RNG, Irqs);
-    info!("Hello World 2!");
 
     let mut buf = [0u8; 16];
     unwrap!(rng.async_fill_bytes(&mut buf).await);

From e5e9fb78af71871216111023f8ad850e80ae7bda Mon Sep 17 00:00:00 2001
From: Dario Nieuwenhuis <dirbaio@dirbaio.net>
Date: Tue, 16 Apr 2024 20:37:10 +0200
Subject: [PATCH 14/16] update stm32-metapac.

---
 embassy-stm32/Cargo.toml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 1f9a51678..095427171 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -71,7 +71,7 @@ rand_core = "0.6.3"
 sdio-host = "0.5.0"
 critical-section = "1.1"
 #stm32-metapac = { version = "15" }
-stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-d674277b78ca7400ecfeeb1b5af4e460a65c1a61" }
+stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-01ac9bfd035961dc75f32dcd6080501538246d5c" }
 
 vcell = "0.1.3"
 nb = "1.0.0"
@@ -97,7 +97,7 @@ proc-macro2 = "1.0.36"
 quote = "1.0.15"
 
 #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
-stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-d674277b78ca7400ecfeeb1b5af4e460a65c1a61", default-features = false, features = ["metadata"]}
+stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-01ac9bfd035961dc75f32dcd6080501538246d5c", default-features = false, features = ["metadata"]}
 
 [features]
 default = ["rt"]

From 2bd5095991cb5068d23a76a7c32c4bbbcfc7a441 Mon Sep 17 00:00:00 2001
From: Dario Nieuwenhuis <dirbaio@dirbaio.net>
Date: Tue, 16 Apr 2024 20:37:42 +0200
Subject: [PATCH 15/16] stm32/usb: enable USV for U0.

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

diff --git a/embassy-stm32/src/usb/mod.rs b/embassy-stm32/src/usb/mod.rs
index 1e3c44167..349438ec5 100644
--- a/embassy-stm32/src/usb/mod.rs
+++ b/embassy-stm32/src/usb/mod.rs
@@ -23,7 +23,7 @@ fn common_init<T: Instance>() {
         )
     }
 
-    #[cfg(any(stm32l4, stm32l5, stm32wb))]
+    #[cfg(any(stm32l4, stm32l5, stm32wb, stm32u0))]
     critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true)));
 
     #[cfg(pwr_h5)]

From 38e71a2438c8ae934bda646b4c7bbe51f1dd6f61 Mon Sep 17 00:00:00 2001
From: Dario Nieuwenhuis <dirbaio@dirbaio.net>
Date: Tue, 16 Apr 2024 20:38:09 +0200
Subject: [PATCH 16/16] ci: add u0 examples.

---
 ci.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/ci.sh b/ci.sh
index 514c6769b..e95714b65 100755
--- a/ci.sh
+++ b/ci.sh
@@ -189,6 +189,7 @@ cargo batch \
     --- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32l1 \
     --- build --release --manifest-path examples/stm32l4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32l4 \
     --- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32l5 \
+    --- build --release --manifest-path examples/stm32u0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32u0 \
     --- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \
     --- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32wb \
     --- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32wba \