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 \