From 9f4d320d676a5177535db0e1d78159126db20644 Mon Sep 17 00:00:00 2001 From: Michael Zill Date: Tue, 9 Apr 2024 06:56:15 +0200 Subject: [PATCH 01/28] 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(&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(&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 Date: Sat, 13 Apr 2024 02:55:15 +0200 Subject: [PATCH 02/28] 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 Date: Sat, 13 Apr 2024 03:30:30 +0200 Subject: [PATCH 03/28] 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 InternalChannel for VrefInt {} +impl super::SealedInternalChannel for VrefInt { + fn channel(&self) -> u8 { + VREF_CHANNEL + } +} + +/// Internal temperature channel. +pub struct Temperature; +impl InternalChannel for Temperature {} +impl super::SealedInternalChannel for Temperature { + fn channel(&self) -> u8 { + TEMP_CHANNEL + } +} + +/// Internal battery voltage channel. +pub struct Vbat; +impl InternalChannel for Vbat {} +impl super::SealedInternalChannel 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

+ '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

(&mut self, pin: &mut P) -> u16 + where + P: AdcPin, + 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) -> 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

{ 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

+ 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 Date: Sat, 13 Apr 2024 02:13:41 +0200 Subject: [PATCH 04/28] 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 Date: Sat, 13 Apr 2024 14:07:01 +0200 Subject: [PATCH 05/28] 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 Date: Sat, 13 Apr 2024 18:40:46 +0200 Subject: [PATCH 06/28] 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 EndpointBuffer { 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; +}); + +#[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; +}); + +#[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; +}); + +#[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 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 Date: Sun, 14 Apr 2024 00:04:13 +0200 Subject: [PATCH 07/28] 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

{ 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 super::SealedAdcPin 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 super::SealedAdcPin 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 super::SealedAdcPin 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 AdcPin for DacOut {} + impl super::SealedAdcPin for DacOut { + fn channel(&self) -> u8 { + 19 + } + } + } +} + impl<'d, T: Instance> Adc<'d, T> { pub fn new(adc: impl Peripheral

+ '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 Date: Sun, 14 Apr 2024 00:08:06 +0200 Subject: [PATCH 08/28] 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 Date: Sun, 14 Apr 2024 00:45:53 +0200 Subject: [PATCH 09/28] 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 Date: Sun, 14 Apr 2024 01:10:20 +0200 Subject: [PATCH 10/28] 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 8988df9ac15eccfaa1f2e5abe862cf2e7043ee46 Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 16 Apr 2024 11:07:42 +0200 Subject: [PATCH 11/28] FAQ: Common STM32 stumbling blocks Adds some commonly encountered issues. Help wanted: if anyone has a working BDMA linker script setup, it would be good to add here. --- docs/modules/ROOT/pages/faq.adoc | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/modules/ROOT/pages/faq.adoc b/docs/modules/ROOT/pages/faq.adoc index c6b893de5..0944127a5 100644 --- a/docs/modules/ROOT/pages/faq.adoc +++ b/docs/modules/ROOT/pages/faq.adoc @@ -231,3 +231,34 @@ Please refer to the STM32 documentation for the specific values suitable for you Look for the `MEMORY` section and try to determine the FLASH and RAM sizes and section start. If you find a case where the memory.x is wrong, please report it on [this Github issue](https://github.com/embassy-rs/stm32-data/issues/301) so other users are not caught by surprise. + +== Known issues (details and/or mitigations) + +These are issues that are commonly reported. Help wanted fixing them, or improving the UX when possible! + +=== STM32H5 and STM32H7 power issues + +STM32 chips with built-in power management (SMPS and LDO) settings often cause user problems when the configuration does not match how the board was designed. + +Settings from the examples, or even from other working boards, may not work on YOUR board, because they are wired differently. + +Additionally, some PWR settings require a full device reboot (and enough time to discharge any power capacitors!), making this hard to troubleshoot. Also, some +"wrong" power settings will ALMOST work, meaning it will sometimes work on some boots, or for a while, but crash unexpectedly. + +There is not a fix for this yet, as it is board/hardware dependant. See link:https://github.com/embassy-rs/embassy/issues/2806[this tracking issue] for more details + +=== STM32 BDMA only work out of some RAM regions + +The STM32 BDMA controller included in some chips (TODO: list which ones) has a limitation in that it only works out of certain regions of RAM (TODO: list which ones), otherwise the transfer +will fail. + +If you see errors that look like this: + +[source,plain] +---- +DMA: error on BDMA@1234ABCD channel 4 +---- + +You likely need to set up your linker script to define a special region for this area, and copy data to that region before using with BDMA. + +TODO: show how to do that From 75352d181c7076300b939b487931b3c783dc1581 Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 16 Apr 2024 12:07:40 +0200 Subject: [PATCH 12/28] Add critical sections to avoid USB OTG Errata --- embassy-stm32/src/usb/otg.rs | 108 +++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 48 deletions(-) diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index cabc06367..dc5d47006 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -672,45 +672,51 @@ impl<'d, T: Instance> Bus<'d, T> { let r = T::regs(); - // Configure RX fifo size. All endpoints share the same FIFO area. - let rx_fifo_size_words = RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out); - trace!("configuring rx fifo size={}", rx_fifo_size_words); + // ERRATA NOTE: Don't interrupt FIFOs being written to. The interrupt + // handler COULD interrupt us here and do FIFO operations, so ensure + // the interrupt does not occur. + critical_section::with(|_| { + // Configure RX fifo size. All endpoints share the same FIFO area. + let rx_fifo_size_words = RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out); + trace!("configuring rx fifo size={}", rx_fifo_size_words); - r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words)); + r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words)); - // Configure TX (USB in direction) fifo size for each endpoint - let mut fifo_top = rx_fifo_size_words; - for i in 0..T::ENDPOINT_COUNT { - if let Some(ep) = self.ep_in[i] { - trace!( - "configuring tx fifo ep={}, offset={}, size={}", - i, - fifo_top, - ep.fifo_size_words - ); + // Configure TX (USB in direction) fifo size for each endpoint + let mut fifo_top = rx_fifo_size_words; + for i in 0..T::ENDPOINT_COUNT { + if let Some(ep) = self.ep_in[i] { + trace!( + "configuring tx fifo ep={}, offset={}, size={}", + i, + fifo_top, + ep.fifo_size_words + ); - let dieptxf = if i == 0 { r.dieptxf0() } else { r.dieptxf(i - 1) }; + let dieptxf = if i == 0 { r.dieptxf0() } else { r.dieptxf(i - 1) }; - dieptxf.write(|w| { - w.set_fd(ep.fifo_size_words); - w.set_sa(fifo_top); - }); + dieptxf.write(|w| { + w.set_fd(ep.fifo_size_words); + w.set_sa(fifo_top); + }); - fifo_top += ep.fifo_size_words; + fifo_top += ep.fifo_size_words; + } } - } - assert!( - fifo_top <= T::FIFO_DEPTH_WORDS, - "FIFO allocations exceeded maximum capacity" - ); + assert!( + fifo_top <= T::FIFO_DEPTH_WORDS, + "FIFO allocations exceeded maximum capacity" + ); - // Flush fifos - r.grstctl().write(|w| { - w.set_rxfflsh(true); - w.set_txfflsh(true); - w.set_txfnum(0x10); + // Flush fifos + r.grstctl().write(|w| { + w.set_rxfflsh(true); + w.set_txfflsh(true); + w.set_txfnum(0x10); + }); }); + loop { let x = r.grstctl().read(); if !x.rxfflsh() && !x.txfflsh() { @@ -1208,27 +1214,33 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { .await } - // Setup transfer size - r.dieptsiz(index).write(|w| { - w.set_mcnt(1); - w.set_pktcnt(1); - w.set_xfrsiz(buf.len() as _); - }); - + // ERRATA: Transmit data FIFO is corrupted when a write sequence to the FIFO is interrupted with + // accesses to certain OTG_FS registers. + // + // Prevent the interrupt (which might poke FIFOs) from executing while copying data to FIFOs. critical_section::with(|_| { - // Enable endpoint - r.diepctl(index).modify(|w| { - w.set_cnak(true); - w.set_epena(true); + // Setup transfer size + r.dieptsiz(index).write(|w| { + w.set_mcnt(1); + w.set_pktcnt(1); + w.set_xfrsiz(buf.len() as _); }); - }); - // Write data to FIFO - for chunk in buf.chunks(4) { - let mut tmp = [0u8; 4]; - tmp[0..chunk.len()].copy_from_slice(chunk); - r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))); - } + critical_section::with(|_| { + // Enable endpoint + r.diepctl(index).modify(|w| { + w.set_cnak(true); + w.set_epena(true); + }); + }); + + // Write data to FIFO + for chunk in buf.chunks(4) { + let mut tmp = [0u8; 4]; + tmp[0..chunk.len()].copy_from_slice(chunk); + r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))); + } + }); trace!("write done ep={:?}", self.info.addr); From 2315a39293488488ced2369a1d23629af57cc0fb Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 16 Apr 2024 13:39:00 +0200 Subject: [PATCH 13/28] Remove nested CS --- embassy-stm32/src/usb/otg.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index dc5d47006..b386c6977 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -1226,12 +1226,10 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { w.set_xfrsiz(buf.len() as _); }); - critical_section::with(|_| { - // Enable endpoint - r.diepctl(index).modify(|w| { - w.set_cnak(true); - w.set_epena(true); - }); + // Enable endpoint + r.diepctl(index).modify(|w| { + w.set_cnak(true); + w.set_epena(true); }); // Write data to FIFO From 53cb84d3d65b96b5f92f434b113cffe62810c0ad Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 16 Apr 2024 15:24:20 +0200 Subject: [PATCH 14/28] 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 Date: Tue, 16 Apr 2024 15:33:23 +0200 Subject: [PATCH 15/28] 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; -}); - #[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 d928663baeb851e830e04e469f3a338ad3f4bc15 Mon Sep 17 00:00:00 2001 From: Torin Cooper-Bennun Date: Tue, 16 Apr 2024 15:13:31 +0100 Subject: [PATCH 16/28] stm32: adc: fix blocking_delay_us() overflowing when sys freq is high e.g. H503 running at 250 MHz resulted in an upper bound of 17 us here. casting up to u64 for intermediate calc allows the upper bound to be increased by a factor of 1e6 --- embassy-stm32/src/adc/mod.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 12c5751bd..2ff2ed6a8 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -76,7 +76,12 @@ pub(crate) fn blocking_delay_us(us: u32) { #[cfg(time)] embassy_time::block_for(embassy_time::Duration::from_micros(us)); #[cfg(not(time))] - cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.unwrap().0 * us / 1_000_000); + { + let freq = unsafe { crate::rcc::get_freqs() }.sys.unwrap().0 as u64; + let us = us as u64; + let cycles = freq * us / 1_000_000; + cortex_m::asm::delay(cycles as u32); + } } /// ADC instance. From 90f1d29c2470d51337a3666cde2efacad3e054e5 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 16 Apr 2024 16:36:10 +0200 Subject: [PATCH 17/28] 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 4202dac8a6cc3a8ef32696d7f551025bf19cca4b Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 16 Apr 2024 17:03:44 +0200 Subject: [PATCH 18/28] L4 Example: probe-run to probe-rs Reverts some change made in https://github.com/embassy-rs/embassy/pull/1792 --- examples/stm32l4/.cargo/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32l4/.cargo/config.toml b/examples/stm32l4/.cargo/config.toml index db3a7ceff..83fc6d6f8 100644 --- a/examples/stm32l4/.cargo/config.toml +++ b/examples/stm32l4/.cargo/config.toml @@ -2,7 +2,7 @@ # replace STM32F429ZITx with your chip as listed in `probe-rs chip list` #runner = "probe-rs run --chip STM32L475VGT6" #runner = "probe-rs run --chip STM32L475VG" -runner = "probe-run --chip STM32L4S5QI" +runner = "probe-rs run --chip STM32L4S5QI" [build] target = "thumbv7em-none-eabi" From bd13b5c0604787a52322308353bec9996f9a199d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 16 Apr 2024 17:07:13 +0200 Subject: [PATCH 19/28] More probe-run -> probe-rs run. --- examples/stm32f334/.cargo/config.toml | 2 +- examples/stm32wb/.cargo/config.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/stm32f334/.cargo/config.toml b/examples/stm32f334/.cargo/config.toml index caf947be6..f38c90a31 100644 --- a/examples/stm32f334/.cargo/config.toml +++ b/examples/stm32f334/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` -runner = "probe-run --chip STM32F334R8" +runner = "probe-rs run --chip STM32F334R8" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/stm32wb/.cargo/config.toml b/examples/stm32wb/.cargo/config.toml index 51c499ee7..8b6d6d754 100644 --- a/examples/stm32wb/.cargo/config.toml +++ b/examples/stm32wb/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # replace STM32WB55CCUx with your chip as listed in `probe-rs chip list` -# runner = "probe-run --chip STM32WB55RGVx --speed 1000 --connect-under-reset" +# runner = "probe-rs run --chip STM32WB55RGVx --speed 1000 --connect-under-reset" runner = "teleprobe local run --chip STM32WB55RG --elf" [build] From 32b1b4067fe0645b36ab0727183ac6a9a5f7ac14 Mon Sep 17 00:00:00 2001 From: Daniel Igaz Date: Tue, 16 Apr 2024 20:27:12 +0200 Subject: [PATCH 20/28] Bug: There are at most 18 channels for the ADC. --- embassy-stm32/src/adc/g4.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index f6741f019..221cc2a40 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -170,7 +170,7 @@ impl<'d, T: Instance> Adc<'d, T> { fn configure_differential_inputs(&mut self) { T::regs().difsel().modify(|w| { - for n in 0..20 { + for n in 0..18 { w.set_difsel(n, Difsel::SINGLEENDED); } }); From e5e9fb78af71871216111023f8ad850e80ae7bda Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 16 Apr 2024 20:37:10 +0200 Subject: [PATCH 21/28] 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 Date: Tue, 16 Apr 2024 20:37:42 +0200 Subject: [PATCH 22/28] 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() { ) } - #[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 Date: Tue, 16 Apr 2024 20:38:09 +0200 Subject: [PATCH 23/28] 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 \ From 34362b77ccdef42e7a7accbb23dcab96d5515f92 Mon Sep 17 00:00:00 2001 From: Cyril Marpaud <9333398+cyril-marpaud@users.noreply.github.com> Date: Wed, 17 Apr 2024 12:56:18 +0200 Subject: [PATCH 24/28] fix: typo --- embassy-nrf/src/pdm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index ef2662c85..c1501e02e 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -1,4 +1,4 @@ -//! Pulse Density Modulation (PDM) mirophone driver +//! Pulse Density Modulation (PDM) microphone driver #![macro_use] From 901bdfc7b8d8e1dc5d04b28a69feb50b99d0be57 Mon Sep 17 00:00:00 2001 From: Torin Cooper-Bennun Date: Wed, 17 Apr 2024 14:54:47 +0100 Subject: [PATCH 25/28] stm32: can: fd: on_interrupt: simplify, rm redundant code PED, PEA are never enabled in the interrupt enable code in peripheral.rs; no need to process the flags here --- embassy-stm32/src/can/fdcan.rs | 64 ++++++++++++++-------------------- 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index e31821ca2..23a35168b 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -44,50 +44,40 @@ impl interrupt::typelevel::Handler for IT0Interrup let ir = regs.ir().read(); - { - if ir.tc() { - regs.ir().write(|w| w.set_tc(true)); - } - if ir.tefn() { - regs.ir().write(|w| w.set_tefn(true)); - } - - match &T::state().tx_mode { - TxMode::NonBuffered(waker) => waker.wake(), - TxMode::ClassicBuffered(buf) => { - if !T::registers().tx_queue_is_full() { - match buf.tx_receiver.try_receive() { - Ok(frame) => { - _ = T::registers().write(&frame); - } - Err(_) => {} - } - } - } - TxMode::FdBuffered(buf) => { - if !T::registers().tx_queue_is_full() { - match buf.tx_receiver.try_receive() { - Ok(frame) => { - _ = T::registers().write(&frame); - } - Err(_) => {} - } - } - } - } + if ir.tc() { + regs.ir().write(|w| w.set_tc(true)); + } + if ir.tefn() { + regs.ir().write(|w| w.set_tefn(true)); } - if ir.ped() || ir.pea() { - regs.ir().write(|w| { - w.set_ped(true); - w.set_pea(true); - }); + match &T::state().tx_mode { + TxMode::NonBuffered(waker) => waker.wake(), + TxMode::ClassicBuffered(buf) => { + if !T::registers().tx_queue_is_full() { + match buf.tx_receiver.try_receive() { + Ok(frame) => { + _ = T::registers().write(&frame); + } + Err(_) => {} + } + } + } + TxMode::FdBuffered(buf) => { + if !T::registers().tx_queue_is_full() { + match buf.tx_receiver.try_receive() { + Ok(frame) => { + _ = T::registers().write(&frame); + } + Err(_) => {} + } + } + } } if ir.rfn(0) { T::state().rx_mode.on_interrupt::(0); } - if ir.rfn(1) { T::state().rx_mode.on_interrupt::(1); } From 80b3db4ea6b5b85ab87ec002c2029b868515f8ef Mon Sep 17 00:00:00 2001 From: Torin Cooper-Bennun Date: Wed, 17 Apr 2024 14:58:08 +0100 Subject: [PATCH 26/28] stm32: can: fd: implement bus-off recovery as per RM0492 and other relevant RMs, bus-off recovery is not automatic. CCCR.INIT is set by the device upon bus-off; the CPU must reset CCCR.INIT to initiate the recovery. --- embassy-stm32/src/can/fd/peripheral.rs | 1 + embassy-stm32/src/can/fdcan.rs | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs index e32f19d91..e5cfee528 100644 --- a/embassy-stm32/src/can/fd/peripheral.rs +++ b/embassy-stm32/src/can/fd/peripheral.rs @@ -368,6 +368,7 @@ impl Registers { w.set_rfne(0, true); // Rx Fifo 0 New Msg w.set_rfne(1, true); // Rx Fifo 1 New Msg w.set_tce(true); // Tx Complete + w.set_boe(true); // Bus-Off Status Changed }); self.regs.ile().modify(|w| { w.set_eint0(true); // Interrupt Line 0 diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index 23a35168b..563f542d4 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -81,6 +81,14 @@ impl interrupt::typelevel::Handler for IT0Interrup if ir.rfn(1) { T::state().rx_mode.on_interrupt::(1); } + + if ir.bo() { + regs.ir().write(|w| w.set_bo(true)); + if regs.psr().read().bo() { + // Initiate bus-off recovery sequence by resetting CCCR.INIT + regs.cccr().modify(|w| w.set_init(false)); + } + } } } From ce58cd0f1c8d524b9ca0c274a51722a7bcde2ada Mon Sep 17 00:00:00 2001 From: qwerty19106 Date: Wed, 17 Apr 2024 18:07:24 +0400 Subject: [PATCH 27/28] Allow Uart::new_half_duplex for all usart_vx --- embassy-stm32/src/usart/mod.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index e7fdf4da6..d21e5c47c 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -933,7 +933,6 @@ impl<'d, T: BasicInstance> Uart<'d, T, Async> { /// I/O in idle or in reception. /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict /// on the line must be managed by software (for instance by using a centralized arbiter). - #[cfg(not(any(usart_v1, usart_v2)))] #[doc(alias("HDSEL"))] pub fn new_half_duplex( peri: impl Peripheral

+ 'd, @@ -943,7 +942,10 @@ impl<'d, T: BasicInstance> Uart<'d, T, Async> { rx_dma: impl Peripheral

> + 'd, mut config: Config, ) -> Result { - config.swap_rx_tx = false; + #[cfg(not(any(usart_v1, usart_v2)))] + { + config.swap_rx_tx = false; + } config.half_duplex = true; Self::new_inner( @@ -1084,14 +1086,16 @@ impl<'d, T: BasicInstance> Uart<'d, T, Blocking> { /// I/O in idle or in reception. /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict /// on the line must be managed by software (for instance by using a centralized arbiter). - #[cfg(not(any(usart_v1, usart_v2)))] #[doc(alias("HDSEL"))] pub fn new_blocking_half_duplex( peri: impl Peripheral

+ 'd, tx: impl Peripheral

> + 'd, mut config: Config, ) -> Result { - config.swap_rx_tx = false; + #[cfg(not(any(usart_v1, usart_v2)))] + { + config.swap_rx_tx = false; + } config.half_duplex = true; Self::new_inner( @@ -1354,10 +1358,9 @@ fn configure( } }); - #[cfg(not(usart_v1))] r.cr3().modify(|w| { + #[cfg(not(usart_v1))] w.set_onebit(config.assume_noise_free); - #[cfg(any(usart_v3, usart_v4))] w.set_hdsel(config.half_duplex); }); From 0dcf0348575bcf3fde7b9a829bcb66217c55320b Mon Sep 17 00:00:00 2001 From: Oleksandr Kuksenko Date: Fri, 19 Apr 2024 13:37:58 +0300 Subject: [PATCH 28/28] Update layer_by_layer.adoc Fixed typo --- docs/modules/ROOT/pages/layer_by_layer.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules/ROOT/pages/layer_by_layer.adoc b/docs/modules/ROOT/pages/layer_by_layer.adoc index 1d7bdc89b..fa419f75e 100644 --- a/docs/modules/ROOT/pages/layer_by_layer.adoc +++ b/docs/modules/ROOT/pages/layer_by_layer.adoc @@ -63,7 +63,7 @@ Luckily, there is an elegant solution to this problem when using Embassy. == Async version -It's time to use the Embassy capabilities to its fullest. At the core, Embassy has an async excecutor, or a runtime for async tasks if you will. The executor polls a set of tasks (defined at compile time), and whenever a task `blocks`, the executor will run another task, or put the microcontroller to sleep. +It's time to use the Embassy capabilities to its fullest. At the core, Embassy has an async executor, or a runtime for async tasks if you will. The executor polls a set of tasks (defined at compile time), and whenever a task `blocks`, the executor will run another task, or put the microcontroller to sleep. [source,rust] ----