From 081afca3f065dfd91e157d7c9a9477e2d914c99d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0pa=C4=8Dek?= Date: Fri, 24 May 2024 22:04:04 +0200 Subject: [PATCH 01/57] stm32/rcc: replace generated enable/disable code with runtime info --- embassy-stm32/build.rs | 195 +++++++-------------- embassy-stm32/src/adc/f1.rs | 6 +- embassy-stm32/src/adc/f3.rs | 6 +- embassy-stm32/src/adc/f3_v1_1.rs | 6 +- embassy-stm32/src/adc/g4.rs | 4 +- embassy-stm32/src/adc/v1.rs | 6 +- embassy-stm32/src/adc/v2.rs | 6 +- embassy-stm32/src/adc/v3.rs | 4 +- embassy-stm32/src/adc/v4.rs | 4 +- embassy-stm32/src/can/bxcan/mod.rs | 6 +- embassy-stm32/src/can/fdcan.rs | 4 +- embassy-stm32/src/cordic/mod.rs | 6 +- embassy-stm32/src/crc/v1.rs | 5 +- embassy-stm32/src/crc/v2v3.rs | 5 +- embassy-stm32/src/cryp/mod.rs | 4 +- embassy-stm32/src/dac/mod.rs | 16 +- embassy-stm32/src/dcmi.rs | 4 +- embassy-stm32/src/dsihost.rs | 4 +- embassy-stm32/src/fmc.rs | 8 +- embassy-stm32/src/gpio.rs | 2 +- embassy-stm32/src/hash/mod.rs | 5 +- embassy-stm32/src/hrtim/mod.rs | 4 +- embassy-stm32/src/i2c/mod.rs | 8 +- embassy-stm32/src/i2c/v1.rs | 2 +- embassy-stm32/src/i2c/v2.rs | 2 +- embassy-stm32/src/ipcc.rs | 5 +- embassy-stm32/src/lib.rs | 7 +- embassy-stm32/src/ltdc.rs | 4 +- embassy-stm32/src/ospi/mod.rs | 6 +- embassy-stm32/src/qspi/mod.rs | 4 +- embassy-stm32/src/rcc/hsi48.rs | 4 +- embassy-stm32/src/rcc/mod.rs | 252 +++++++++++++++++++++------ embassy-stm32/src/rng.rs | 4 +- embassy-stm32/src/rtc/mod.rs | 2 +- embassy-stm32/src/sai/mod.rs | 6 +- embassy-stm32/src/sdmmc/mod.rs | 4 +- embassy-stm32/src/spi/mod.rs | 10 +- embassy-stm32/src/time_driver.rs | 4 +- embassy-stm32/src/timer/low_level.rs | 5 +- embassy-stm32/src/tsc/mod.rs | 6 +- embassy-stm32/src/ucpd.rs | 8 +- embassy-stm32/src/usart/buffered.rs | 9 +- embassy-stm32/src/usart/mod.rs | 14 +- embassy-stm32/src/usb/mod.rs | 4 +- embassy-stm32/src/usb/otg.rs | 4 +- 45 files changed, 379 insertions(+), 305 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 7bf6ffba2..6c524ced6 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -367,9 +367,6 @@ fn main() { .filter_map(|p| p.registers.as_ref()) .find(|r| r.kind == "rcc") .unwrap(); - for b in rcc_registers.ir.blocks { - eprintln!("{}", b.name); - } let rcc_block = rcc_registers.ir.blocks.iter().find(|b| b.name == "Rcc").unwrap(); // ======== @@ -388,7 +385,6 @@ fn main() { rcc_registers: &'a PeripheralRegisters, chained_muxes: HashMap<&'a str, &'a PeripheralRccRegister>, - refcount_statics: BTreeSet, clock_names: BTreeSet, muxes: BTreeSet<(Ident, Ident, Ident)>, } @@ -397,7 +393,6 @@ fn main() { rcc_registers, chained_muxes: HashMap::new(), - refcount_statics: BTreeSet::new(), clock_names: BTreeSet::new(), muxes: BTreeSet::new(), }; @@ -516,80 +511,64 @@ fn main() { } } + let mut refcount_idxs = HashMap::new(); + for p in METADATA.peripherals { if !singletons.contains(&p.name.to_string()) { continue; } if let Some(rcc) = &p.rcc { - let en = rcc.enable.as_ref().unwrap(); - - let (start_rst, end_rst) = match &rcc.reset { - Some(rst) => { - let rst_reg = format_ident!("{}", rst.register.to_ascii_lowercase()); - let set_rst_field = format_ident!("set_{}", rst.field.to_ascii_lowercase()); - ( - quote! { - crate::pac::RCC.#rst_reg().modify(|w| w.#set_rst_field(true)); - }, - quote! { - crate::pac::RCC.#rst_reg().modify(|w| w.#set_rst_field(false)); - }, - ) - } - None => (TokenStream::new(), TokenStream::new()), - }; - + let rst_reg = rcc.reset.as_ref(); + let en_reg = rcc.enable.as_ref().unwrap(); let pname = format_ident!("{}", p.name); - let en_reg = format_ident!("{}", en.register.to_ascii_lowercase()); - let set_en_field = format_ident!("set_{}", en.field.to_ascii_lowercase()); - let en_reg_offs = rcc_block - .items - .iter() - .find(|i| i.name.eq_ignore_ascii_case(en.register)) - .unwrap() - .byte_offset; - let en_reg_offs: u8 = (en_reg_offs / 4).try_into().unwrap(); - let en_bit_offs = &rcc_registers - .ir - .fieldsets - .iter() - .find(|i| i.name.eq_ignore_ascii_case(en.register)) - .unwrap() - .fields - .iter() - .find(|i| i.name.eq_ignore_ascii_case(en.field)) - .unwrap() - .bit_offset; - let BitOffset::Regular(en_bit_offs) = en_bit_offs else { - panic!("cursed bit offset") + let get_offset_and_bit = |reg: &PeripheralRccRegister| -> TokenStream { + let reg_offset = rcc_block + .items + .iter() + .find(|i| i.name.eq_ignore_ascii_case(reg.register)) + .unwrap() + .byte_offset; + let reg_offset: u8 = (reg_offset / 4).try_into().unwrap(); + + let bit_offset = &rcc_registers + .ir + .fieldsets + .iter() + .find(|i| i.name.eq_ignore_ascii_case(reg.register)) + .unwrap() + .fields + .iter() + .find(|i| i.name.eq_ignore_ascii_case(reg.field)) + .unwrap() + .bit_offset; + let BitOffset::Regular(bit_offset) = bit_offset else { + panic!("cursed bit offset") + }; + let bit_offset: u8 = bit_offset.offset.try_into().unwrap(); + + quote! { (#reg_offset, #bit_offset) } }; - let en_bit_offs: u8 = en_bit_offs.offset.try_into().unwrap(); - let refcount = *rcc_field_count.get(&(en.register, en.field)).unwrap() > 1; - let (before_enable, before_disable) = if refcount { - let refcount_static = - format_ident!("{}_{}", en.register.to_ascii_uppercase(), en.field.to_ascii_uppercase()); + let reset_offset_and_bit = match rst_reg { + Some(rst_reg) => { + let reset_offset_and_bit = get_offset_and_bit(rst_reg); + quote! { Some(#reset_offset_and_bit) } + } + None => quote! { None }, + }; + let enable_offset_and_bit = get_offset_and_bit(en_reg); - clock_gen.refcount_statics.insert(refcount_static.clone()); - - ( - quote! { - unsafe { refcount_statics::#refcount_static += 1 }; - if unsafe { refcount_statics::#refcount_static } > 1 { - return; - } - }, - quote! { - unsafe { refcount_statics::#refcount_static -= 1 }; - if unsafe { refcount_statics::#refcount_static } > 0 { - return; - } - }, - ) + let needs_refcount = *rcc_field_count.get(&(en_reg.register, en_reg.field)).unwrap() > 1; + let refcount_idx = if needs_refcount { + let next_refcount_idx = refcount_idxs.len() as u8; + let refcount_idx = *refcount_idxs + .entry((en_reg.register, en_reg.field)) + .or_insert(next_refcount_idx); + quote! { Some(#refcount_idx) } } else { - (TokenStream::new(), TokenStream::new()) + quote! { None } }; let clock_frequency = match &rcc.kernel_clock { @@ -599,24 +578,10 @@ fn main() { // A refcount leak can result if the same field is shared by peripherals with different stop modes // This condition should be checked in stm32-data - let stop_refcount = match rcc.stop_mode { - StopMode::Standby => None, - StopMode::Stop2 => Some(quote! { REFCOUNT_STOP2 }), - StopMode::Stop1 => Some(quote! { REFCOUNT_STOP1 }), - }; - - let (incr_stop_refcount, decr_stop_refcount) = match stop_refcount { - Some(stop_refcount) => ( - quote! { - #[cfg(feature = "low-power")] - unsafe { crate::rcc::#stop_refcount += 1 }; - }, - quote! { - #[cfg(feature = "low-power")] - unsafe { crate::rcc::#stop_refcount -= 1 }; - }, - ), - None => (TokenStream::new(), TokenStream::new()), + let stop_mode = match rcc.stop_mode { + StopMode::Standby => quote! { crate::rcc::StopMode::Standby }, + StopMode::Stop2 => quote! { crate::rcc::StopMode::Stop2 }, + StopMode::Stop1 => quote! { crate::rcc::StopMode::Stop1 }, }; g.extend(quote! { @@ -624,34 +589,16 @@ fn main() { fn frequency() -> crate::time::Hertz { #clock_frequency } - fn enable_and_reset_with_cs(_cs: critical_section::CriticalSection) { - #before_enable - #incr_stop_refcount - #start_rst - - crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true)); - - // we must wait two peripheral clock cycles before the clock is active - // this seems to work, but might be incorrect - // see http://efton.sk/STM32/gotcha/g183.html - - // dummy read (like in the ST HALs) - let _ = crate::pac::RCC.#en_reg().read(); - - // DSB for good measure - cortex_m::asm::dsb(); - - #end_rst - } - fn disable_with_cs(_cs: critical_section::CriticalSection) { - #before_disable - crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false)); - #decr_stop_refcount - } - - const ENABLE_BIT: crate::rcc::ClockEnableBit = - unsafe { crate::rcc::ClockEnableBit::new(#en_reg_offs, #en_bit_offs) }; + const RCC_INFO: crate::rcc::RccInfo = unsafe { + crate::rcc::RccInfo::new( + #reset_offset_and_bit, + #enable_offset_and_bit, + #refcount_idx, + #[cfg(feature = "low-power")] + #stop_mode, + ) + }; } impl crate::rcc::RccPeripheral for peripherals::#pname {} @@ -659,6 +606,14 @@ fn main() { } } + g.extend({ + let refcounts_len = refcount_idxs.len(); + let refcount_zeros: TokenStream = refcount_idxs.iter().map(|_| quote! { 0u8, }).collect(); + quote! { + pub(crate) static mut REFCOUNTS: [u8; #refcounts_len] = [#refcount_zeros]; + } + }); + let struct_fields: Vec<_> = clock_gen .muxes .iter() @@ -762,22 +717,6 @@ fn main() { } ); - let refcount_mod: TokenStream = clock_gen - .refcount_statics - .iter() - .map(|refcount_static| { - quote! { - pub(crate) static mut #refcount_static: u8 = 0; - } - }) - .collect(); - - g.extend(quote! { - mod refcount_statics { - #refcount_mod - } - }); - // ======== // Generate fns to enable GPIO, DMA in RCC diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index 3822d5032..b37ec260f 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -7,7 +7,7 @@ use embassy_hal_internal::into_ref; use super::blocking_delay_us; use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; use crate::time::Hertz; -use crate::{interrupt, Peripheral}; +use crate::{interrupt, rcc, Peripheral}; pub const VDDA_CALIB_MV: u32 = 3300; pub const ADC_MAX: u32 = (1 << 12) - 1; @@ -50,7 +50,7 @@ impl super::SealedAdcChannel for Temperature { impl<'d, T: Instance> Adc<'d, T> { pub fn new(adc: impl Peripheral

+ 'd) -> Self { into_ref!(adc); - T::enable_and_reset(); + rcc::enable_and_reset::(); T::regs().cr2().modify(|reg| reg.set_adon(true)); // 11.4: Before starting a calibration, the ADC must have been in power-on state (ADON bit = ‘1’) @@ -169,6 +169,6 @@ impl<'d, T: Instance> Drop for Adc<'d, T> { fn drop(&mut self) { T::regs().cr2().modify(|reg| reg.set_adon(false)); - T::disable(); + rcc::disable::(); } } diff --git a/embassy-stm32/src/adc/f3.rs b/embassy-stm32/src/adc/f3.rs index 3f076d64b..ac88c9742 100644 --- a/embassy-stm32/src/adc/f3.rs +++ b/embassy-stm32/src/adc/f3.rs @@ -8,7 +8,7 @@ use super::blocking_delay_us; use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; use crate::interrupt::typelevel::Interrupt; use crate::time::Hertz; -use crate::{interrupt, Peripheral}; +use crate::{interrupt, rcc, Peripheral}; pub const VDDA_CALIB_MV: u32 = 3300; pub const ADC_MAX: u32 = (1 << 12) - 1; @@ -63,7 +63,7 @@ impl<'d, T: Instance> Adc<'d, T> { into_ref!(adc); - T::enable_and_reset(); + rcc::enable_and_reset::(); // Enable the adc regulator T::regs().cr().modify(|w| w.set_advregen(vals::Advregen::INTERMEDIATE)); @@ -188,6 +188,6 @@ impl<'d, T: Instance> Drop for Adc<'d, T> { T::regs().cr().modify(|w| w.set_advregen(vals::Advregen::INTERMEDIATE)); T::regs().cr().modify(|w| w.set_advregen(vals::Advregen::DISABLED)); - T::disable(); + rcc::disable::(); } } diff --git a/embassy-stm32/src/adc/f3_v1_1.rs b/embassy-stm32/src/adc/f3_v1_1.rs index 106956989..689c2871d 100644 --- a/embassy-stm32/src/adc/f3_v1_1.rs +++ b/embassy-stm32/src/adc/f3_v1_1.rs @@ -10,7 +10,7 @@ use super::Resolution; use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; use crate::interrupt::typelevel::Interrupt; use crate::time::Hertz; -use crate::{interrupt, Peripheral}; +use crate::{interrupt, rcc, Peripheral}; const ADC_FREQ: Hertz = crate::rcc::HSI_FREQ; @@ -143,7 +143,7 @@ impl<'d, T: Instance> Adc<'d, T> { ) -> Self { into_ref!(adc); - T::enable_and_reset(); + rcc::enable_and_reset::(); //let r = T::regs(); //r.cr2().write(|w| w.set_align(true)); @@ -403,6 +403,6 @@ impl<'d, T: Instance> Drop for Adc<'d, T> { T::regs().cr2().modify(|w| w.set_adon(false)); - T::disable(); + rcc::disable::(); } } diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index ce7f5db70..6569361fe 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -4,7 +4,7 @@ use pac::adccommon::vals::Presc; use super::{blocking_delay_us, Adc, AdcChannel, Instance, Resolution, SampleTime}; use crate::time::Hertz; -use crate::{pac, Peripheral}; +use crate::{pac, rcc, Peripheral}; /// Default VREF voltage used for sample conversion to millivolts. pub const VREF_DEFAULT_MV: u32 = 3300; @@ -130,7 +130,7 @@ 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(); + rcc::enable_and_reset::(); let prescaler = Prescaler::from_ker_ck(T::frequency()); diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index 090790c39..9bec2e13b 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -10,7 +10,7 @@ use super::blocking_delay_us; use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; use crate::interrupt::typelevel::Interrupt; use crate::peripherals::ADC1; -use crate::{interrupt, Peripheral}; +use crate::{interrupt, rcc, Peripheral}; pub const VDDA_CALIB_MV: u32 = 3300; pub const VREF_INT: u32 = 1230; @@ -67,7 +67,7 @@ impl<'d, T: Instance> Adc<'d, T> { _irq: impl interrupt::typelevel::Binding> + 'd, ) -> Self { into_ref!(adc); - T::enable_and_reset(); + rcc::enable_and_reset::(); // Delay 1μs when using HSI14 as the ADC clock. // @@ -199,6 +199,6 @@ impl<'d, T: Instance> Drop for Adc<'d, T> { T::regs().cr().modify(|reg| reg.set_addis(true)); while T::regs().cr().read().aden() {} - T::disable(); + rcc::disable::(); } } diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 033108195..77e8bb56f 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -4,7 +4,7 @@ use super::blocking_delay_us; use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; use crate::peripherals::ADC1; use crate::time::Hertz; -use crate::Peripheral; +use crate::{rcc, Peripheral}; /// Default VREF voltage used for sample conversion to millivolts. pub const VREF_DEFAULT_MV: u32 = 3300; @@ -96,7 +96,7 @@ where { pub fn new(adc: impl Peripheral

+ 'd) -> Self { into_ref!(adc); - T::enable_and_reset(); + rcc::enable_and_reset::(); let presc = Prescaler::from_pclk2(T::frequency()); T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); @@ -206,6 +206,6 @@ impl<'d, T: Instance> Drop for Adc<'d, T> { reg.set_adon(false); }); - T::disable(); + rcc::disable::(); } } diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index be857f4dd..398c57a92 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -3,7 +3,7 @@ use embassy_hal_internal::into_ref; use super::blocking_delay_us; use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; -use crate::Peripheral; +use crate::{rcc, Peripheral}; /// Default VREF voltage used for sample conversion to millivolts. pub const VREF_DEFAULT_MV: u32 = 3300; @@ -94,7 +94,7 @@ cfg_if! { impl<'d, T: Instance> Adc<'d, T> { pub fn new(adc: impl Peripheral

+ 'd) -> Self { into_ref!(adc); - T::enable_and_reset(); + rcc::enable_and_reset::(); T::regs().cr().modify(|reg| { #[cfg(not(any(adc_g0, adc_u0)))] reg.set_deeppwd(false); diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index f564114c2..50db646fe 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -4,7 +4,7 @@ use pac::adccommon::vals::Presc; use super::{blocking_delay_us, Adc, AdcChannel, Instance, Resolution, SampleTime}; use crate::time::Hertz; -use crate::{pac, Peripheral}; +use crate::{pac, rcc, Peripheral}; /// Default VREF voltage used for sample conversion to millivolts. pub const VREF_DEFAULT_MV: u32 = 3300; @@ -130,7 +130,7 @@ 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(); + rcc::enable_and_reset::(); let prescaler = Prescaler::from_ker_ck(T::frequency()); diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index 912634b84..f6657f57e 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs @@ -19,7 +19,7 @@ use super::util; use crate::can::enums::{BusError, TryReadError}; use crate::gpio::AFType; use crate::interrupt::typelevel::Interrupt; -use crate::rcc::RccPeripheral; +use crate::rcc::{self, RccPeripheral}; use crate::{interrupt, peripherals, Peripheral}; /// Interrupt handler. @@ -183,7 +183,7 @@ impl<'d, T: Instance> Can<'d, T> { rx.set_as_af(rx.af_num(), AFType::Input); tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - T::enable_and_reset(); + rcc::enable_and_reset::(); { T::regs().ier().write(|w| { @@ -720,7 +720,7 @@ impl<'d, T: Instance> Drop for Can<'d, T> { // Cannot call `free()` because it moves the instance. // Manually reset the peripheral. T::regs().mcr().write(|w| w.set_reset(true)); - T::disable(); + rcc::disable::(); } } diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index 81ceb06aa..d7b15274c 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -11,7 +11,7 @@ use embassy_sync::waitqueue::AtomicWaker; use crate::can::fd::peripheral::Registers; use crate::gpio::AFType; use crate::interrupt::typelevel::Interrupt; -use crate::rcc::RccPeripheral; +use crate::rcc::{self, RccPeripheral}; use crate::{interrupt, peripherals, Peripheral}; pub(crate) mod fd; @@ -180,7 +180,7 @@ impl<'d, T: Instance> CanConfigurator<'d, T> { rx.set_as_af(rx.af_num(), AFType::Input); tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - T::enable_and_reset(); + rcc::enable_and_reset::(); let mut config = crate::can::fd::config::FdCanConfig::default(); config.timestamp_source = TimestampSource::Prescaler(TimestampPrescaler::_1); diff --git a/embassy-stm32/src/cordic/mod.rs b/embassy-stm32/src/cordic/mod.rs index 29b11735e..fb342d2e7 100644 --- a/embassy-stm32/src/cordic/mod.rs +++ b/embassy-stm32/src/cordic/mod.rs @@ -4,7 +4,7 @@ use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use crate::pac::cordic::vals; -use crate::{dma, peripherals}; +use crate::{dma, peripherals, rcc}; mod enums; pub use enums::*; @@ -199,7 +199,7 @@ impl<'d, T: Instance> Cordic<'d, T> { /// If you need a peripheral -> CORDIC -> peripheral mode, /// you may want to set Cordic into [Mode::ZeroOverhead] mode, and add extra arguments with [Self::extra_config] pub fn new(peri: impl Peripheral

+ 'd, config: Config) -> Self { - T::enable_and_reset(); + rcc::enable_and_reset::(); into_ref!(peri); @@ -259,7 +259,7 @@ impl<'d, T: Instance> Cordic<'d, T> { impl<'d, T: Instance> Drop for Cordic<'d, T> { fn drop(&mut self) { - T::disable(); + rcc::disable::(); } } diff --git a/embassy-stm32/src/crc/v1.rs b/embassy-stm32/src/crc/v1.rs index e8e0270af..f3d13de7c 100644 --- a/embassy-stm32/src/crc/v1.rs +++ b/embassy-stm32/src/crc/v1.rs @@ -2,8 +2,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::pac::CRC as PAC_CRC; use crate::peripherals::CRC; -use crate::rcc::SealedRccPeripheral; -use crate::Peripheral; +use crate::{rcc, Peripheral}; /// CRC driver. pub struct Crc<'d> { @@ -17,7 +16,7 @@ impl<'d> Crc<'d> { // Note: enable and reset come from RccPeripheral. // enable CRC clock in RCC. - CRC::enable_and_reset(); + rcc::enable_and_reset::(); // Peripheral the peripheral let mut instance = Self { _peri: peripheral }; instance.reset(); diff --git a/embassy-stm32/src/crc/v2v3.rs b/embassy-stm32/src/crc/v2v3.rs index ad7c79f12..09d956d7c 100644 --- a/embassy-stm32/src/crc/v2v3.rs +++ b/embassy-stm32/src/crc/v2v3.rs @@ -3,8 +3,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::pac::crc::vals; use crate::pac::CRC as PAC_CRC; use crate::peripherals::CRC; -use crate::rcc::SealedRccPeripheral; -use crate::Peripheral; +use crate::{rcc, Peripheral}; /// CRC driver. pub struct Crc<'d> { @@ -84,7 +83,7 @@ impl<'d> Crc<'d> { pub fn new(peripheral: impl Peripheral

+ 'd, config: Config) -> Self { // Note: enable and reset come from RccPeripheral. // reset to default values and enable CRC clock in RCC. - CRC::enable_and_reset(); + rcc::enable_and_reset::(); into_ref!(peripheral); let mut instance = Self { _peripheral: peripheral, diff --git a/embassy-stm32/src/cryp/mod.rs b/embassy-stm32/src/cryp/mod.rs index f19c94fda..01ea57d8e 100644 --- a/embassy-stm32/src/cryp/mod.rs +++ b/embassy-stm32/src/cryp/mod.rs @@ -9,7 +9,7 @@ use embassy_sync::waitqueue::AtomicWaker; use crate::dma::{NoDma, Transfer, TransferOptions}; use crate::interrupt::typelevel::Interrupt; -use crate::{interrupt, pac, peripherals, Peripheral}; +use crate::{interrupt, pac, peripherals, rcc, Peripheral}; const DES_BLOCK_SIZE: usize = 8; // 64 bits const AES_BLOCK_SIZE: usize = 16; // 128 bits @@ -1029,7 +1029,7 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> { outdma: impl Peripheral

+ 'd, _irq: impl interrupt::typelevel::Binding> + 'd, ) -> Self { - T::enable_and_reset(); + rcc::enable_and_reset::(); into_ref!(peri, indma, outdma); let instance = Self { _peripheral: peri, diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 8a748ad72..cdd8d1fd7 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -8,7 +8,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::dma::NoDma; #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] use crate::pac::dac; -use crate::rcc::RccPeripheral; +use crate::rcc::{self, RccPeripheral}; use crate::{peripherals, Peripheral}; mod tsel; @@ -131,7 +131,7 @@ impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> { ) -> Self { into_ref!(dma, pin); pin.set_as_analog(); - T::enable_and_reset(); + rcc::enable_and_reset::(); let mut dac = Self { phantom: PhantomData, dma, @@ -157,7 +157,7 @@ impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> { #[cfg(all(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7), not(any(stm32h56x, stm32h57x))))] pub fn new_internal(_peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd) -> Self { into_ref!(dma); - T::enable_and_reset(); + rcc::enable_and_reset::(); let mut dac = Self { phantom: PhantomData, dma, @@ -356,7 +356,7 @@ impl_dma_methods!(2, DacDma2); impl<'d, T: Instance, const N: u8, DMA> Drop for DacChannel<'d, T, N, DMA> { fn drop(&mut self) { - T::disable(); + rcc::disable::(); } } @@ -400,8 +400,8 @@ impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> { pin_ch2.set_as_analog(); // Enable twice to increment the DAC refcount for each channel. - T::enable_and_reset(); - T::enable_and_reset(); + rcc::enable_and_reset::(); + rcc::enable_and_reset::(); let mut ch1 = DacCh1 { phantom: PhantomData, @@ -444,8 +444,8 @@ impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> { ) -> Self { into_ref!(dma_ch1, dma_ch2); // Enable twice to increment the DAC refcount for each channel. - T::enable_and_reset(); - T::enable_and_reset(); + rcc::enable_and_reset::(); + rcc::enable_and_reset::(); let mut ch1 = DacCh1 { phantom: PhantomData, diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index 646ee2ce2..858ae49ca 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -9,7 +9,7 @@ use embassy_sync::waitqueue::AtomicWaker; use crate::dma::Transfer; use crate::gpio::{AFType, Speed}; use crate::interrupt::typelevel::Interrupt; -use crate::{interrupt, Peripheral}; +use crate::{interrupt, rcc, Peripheral}; /// Interrupt handler. pub struct InterruptHandler { @@ -350,7 +350,7 @@ where use_embedded_synchronization: bool, edm: u8, ) -> Self { - T::enable_and_reset(); + rcc::enable_and_reset::(); peri.regs().cr().modify(|r| { r.set_cm(true); // disable continuous mode (snapshot mode) diff --git a/embassy-stm32/src/dsihost.rs b/embassy-stm32/src/dsihost.rs index f1c737fdc..447665669 100644 --- a/embassy-stm32/src/dsihost.rs +++ b/embassy-stm32/src/dsihost.rs @@ -6,7 +6,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; //use crate::gpio::{AnyPin, SealedPin}; use crate::gpio::{AFType, AnyPin, Pull, Speed}; -use crate::rcc::RccPeripheral; +use crate::rcc::{self, RccPeripheral}; use crate::{peripherals, Peripheral}; /// Performs a busy-wait delay for a specified number of microseconds. @@ -77,7 +77,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { pub fn new(_peri: impl Peripheral

+ 'd, te: impl Peripheral

> + 'd) -> Self { into_ref!(te); - T::enable_and_reset(); + rcc::enable_and_reset::(); // Set Tearing Enable pin according to CubeMx example te.set_as_af_pull(te.af_num(), AFType::OutputPushPull, Pull::None); diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs index aced69878..0df64f711 100644 --- a/embassy-stm32/src/fmc.rs +++ b/embassy-stm32/src/fmc.rs @@ -4,7 +4,7 @@ use core::marker::PhantomData; use embassy_hal_internal::into_ref; use crate::gpio::{AFType, Pull, Speed}; -use crate::Peripheral; +use crate::{rcc, Peripheral}; /// FMC driver pub struct Fmc<'d, T: Instance> { @@ -27,7 +27,7 @@ where /// Enable the FMC peripheral and reset it. pub fn enable(&mut self) { - T::enable_and_reset(); + rcc::enable_and_reset::(); } /// Enable the memory controller on applicable chips. @@ -54,7 +54,7 @@ where const REGISTERS: *const () = T::REGS.as_ptr() as *const _; fn enable(&mut self) { - T::enable_and_reset(); + rcc::enable_and_reset::(); } fn memory_controller_enable(&mut self) { @@ -200,7 +200,7 @@ impl<'d, T: Instance> Fmc<'d, T> { )); } -trait SealedInstance: crate::rcc::SealedRccPeripheral { +trait SealedInstance: crate::rcc::RccPeripheral { const REGS: crate::pac::fmc::Fmc; } diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index 808a5bc82..1c5d3fc76 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -813,7 +813,7 @@ foreach_pin!( pub(crate) unsafe fn init(_cs: CriticalSection) { #[cfg(afio)] - ::enable_and_reset_with_cs(_cs); + crate::rcc::enable_and_reset_with_cs::(_cs); crate::_generated::init_gpio(); } diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index 787d5b1c9..4d4a8ec5b 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs @@ -17,8 +17,7 @@ use crate::dma::NoDma; use crate::dma::Transfer; use crate::interrupt::typelevel::Interrupt; use crate::peripherals::HASH; -use crate::rcc::SealedRccPeripheral; -use crate::{interrupt, pac, peripherals, Peripheral}; +use crate::{interrupt, pac, peripherals, rcc, Peripheral}; #[cfg(hash_v1)] const NUM_CONTEXT_REGS: usize = 51; @@ -130,7 +129,7 @@ impl<'d, T: Instance, D> Hash<'d, T, D> { dma: impl Peripheral

+ 'd, _irq: impl interrupt::typelevel::Binding> + 'd, ) -> Self { - HASH::enable_and_reset(); + rcc::enable_and_reset::(); into_ref!(peripheral, dma); let instance = Self { _peripheral: peripheral, diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index 02e45819c..c9d5bff17 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs @@ -9,7 +9,7 @@ pub use traits::Instance; use crate::gpio::{AFType, AnyPin}; use crate::time::Hertz; -use crate::Peripheral; +use crate::{rcc, Peripheral}; /// HRTIM burst controller instance. pub struct BurstController { @@ -172,7 +172,7 @@ impl<'d, T: Instance> AdvancedPwm<'d, T> { fn new_inner(tim: impl Peripheral

+ 'd) -> Self { into_ref!(tim); - T::enable_and_reset(); + rcc::enable_and_reset::(); #[cfg(stm32f334)] if crate::pac::RCC.cfgr3().read().hrtim1sw() == crate::pac::rcc::vals::Timsw::PLL1_P { diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index ef5fd0972..0bf57ef8a 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -18,7 +18,7 @@ use crate::dma::ChannelAndRequest; use crate::gpio::{AFType, Pull}; use crate::interrupt::typelevel::Interrupt; use crate::mode::{Async, Blocking, Mode}; -use crate::rcc::{ClockEnableBit, SealedRccPeripheral}; +use crate::rcc::{self, RccInfo, SealedRccPeripheral}; use crate::time::Hertz; use crate::{interrupt, peripherals}; @@ -128,7 +128,7 @@ impl<'d, M: Mode> I2c<'d, M> { ) -> Self { into_ref!(scl, sda); - T::enable_and_reset(); + rcc::enable_and_reset::(); scl.set_as_af_pull( scl.af_num(), @@ -224,7 +224,7 @@ impl State { struct Info { regs: crate::pac::i2c::I2c, - pub(crate) enable_bit: ClockEnableBit, + rcc: RccInfo, } peri_trait!( @@ -265,7 +265,7 @@ foreach_peripheral!( fn info() -> &'static Info { static INFO: Info = Info{ regs: crate::pac::$inst, - enable_bit: crate::peripherals::$inst::ENABLE_BIT, + rcc: crate::peripherals::$inst::RCC_INFO, }; &INFO } diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 0269e53aa..0e2bd2e40 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -702,7 +702,7 @@ impl<'d> I2c<'d, Async> { impl<'d, M: PeriMode> Drop for I2c<'d, M> { fn drop(&mut self) { - self.info.enable_bit.disable() + self.info.rcc.disable() } } diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index aa6daf786..193f29733 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -673,7 +673,7 @@ impl<'d> I2c<'d, Async> { impl<'d, M: Mode> Drop for I2c<'d, M> { fn drop(&mut self) { - self.info.enable_bit.disable(); + self.info.rcc.disable(); } } diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 4d535cce2..6c8347311 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -6,10 +6,9 @@ use core::task::Poll; use embassy_sync::waitqueue::AtomicWaker; -use crate::interrupt; use crate::interrupt::typelevel::Interrupt; use crate::peripherals::IPCC; -use crate::rcc::SealedRccPeripheral; +use crate::{interrupt, rcc}; /// Interrupt handler. pub struct ReceiveInterruptHandler {} @@ -102,7 +101,7 @@ pub struct Ipcc; impl Ipcc { /// Enable IPCC. pub fn enable(_config: Config) { - IPCC::enable_and_reset(); + rcc::enable_and_reset::(); IPCC::set_cpu2(true); // set RF wake-up clock = LSE diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 81ee60c1c..fe9c0dcb5 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -194,7 +194,6 @@ pub(crate) use stm32_metapac as pac; use crate::interrupt::Priority; #[cfg(feature = "rt")] pub use crate::pac::NVIC_PRIO_BITS; -use crate::rcc::SealedRccPeripheral; /// `embassy-stm32` global configuration. #[non_exhaustive] @@ -310,11 +309,11 @@ pub fn init(config: Config) -> Peripherals { }); #[cfg(not(any(stm32f1, stm32wb, stm32wl)))] - peripherals::SYSCFG::enable_and_reset_with_cs(cs); + rcc::enable_and_reset_with_cs::(cs); #[cfg(not(any(stm32h5, stm32h7, stm32h7rs, stm32wb, stm32wl)))] - peripherals::PWR::enable_and_reset_with_cs(cs); + rcc::enable_and_reset_with_cs::(cs); #[cfg(not(any(stm32f2, stm32f4, stm32f7, stm32l0, stm32h5, stm32h7, stm32h7rs)))] - peripherals::FLASH::enable_and_reset_with_cs(cs); + rcc::enable_and_reset_with_cs::(cs); // Enable the VDDIO2 power supply on chips that have it. // Note that this requires the PWR peripheral to be enabled first. diff --git a/embassy-stm32/src/ltdc.rs b/embassy-stm32/src/ltdc.rs index 0cc8a0557..ac5decb03 100644 --- a/embassy-stm32/src/ltdc.rs +++ b/embassy-stm32/src/ltdc.rs @@ -1,7 +1,7 @@ //! LTDC use core::marker::PhantomData; -use crate::rcc::RccPeripheral; +use crate::rcc::{self, RccPeripheral}; use crate::{peripherals, Peripheral}; /// LTDC driver. @@ -60,7 +60,7 @@ impl<'d, T: Instance> Ltdc<'d, T> { .modify(|w| w.set_pllsaidivr(stm32_metapac::rcc::vals::Pllsaidivr::DIV2)); }); - T::enable_and_reset(); + rcc::enable_and_reset::(); //new_pin!(clk, AFType::OutputPushPull, Speed::VeryHigh, Pull::None); diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 536da4ca0..882781cce 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -16,7 +16,7 @@ use crate::dma::{word, ChannelAndRequest}; use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _, Speed}; use crate::mode::{Async, Blocking, Mode as PeriMode}; use crate::pac::octospi::{vals, Octospi as Regs}; -use crate::rcc::RccPeripheral; +use crate::rcc::{self, RccPeripheral}; use crate::{peripherals, Peripheral}; /// OPSI driver config. @@ -198,7 +198,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { into_ref!(peri); // System configuration - T::enable_and_reset(); + rcc::enable_and_reset::(); while T::REGS.sr().read().busy() {} // Device configuration @@ -1013,7 +1013,7 @@ impl<'d, T: Instance, M: PeriMode> Drop for Ospi<'d, T, M> { self.nss.as_ref().map(|x| x.set_as_disconnected()); self.dqs.as_ref().map(|x| x.set_as_disconnected()); - T::disable(); + rcc::disable::(); } } diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index a82e93b5b..06c8f4812 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -13,7 +13,7 @@ use crate::dma::ChannelAndRequest; use crate::gpio::{AFType, AnyPin, Pull, Speed}; use crate::mode::{Async, Blocking, Mode as PeriMode}; use crate::pac::quadspi::Quadspi as Regs; -use crate::rcc::RccPeripheral; +use crate::rcc::{self, RccPeripheral}; use crate::{peripherals, Peripheral}; /// QSPI transfer configuration. @@ -102,7 +102,7 @@ impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> { ) -> Self { into_ref!(peri); - T::enable_and_reset(); + rcc::enable_and_reset::(); while T::REGS.sr().read().busy() {} diff --git a/embassy-stm32/src/rcc/hsi48.rs b/embassy-stm32/src/rcc/hsi48.rs index da81abc34..efabd059f 100644 --- a/embassy-stm32/src/rcc/hsi48.rs +++ b/embassy-stm32/src/rcc/hsi48.rs @@ -2,7 +2,7 @@ use crate::pac::crs::vals::Syncsrc; use crate::pac::{CRS, RCC}; -use crate::rcc::SealedRccPeripheral; +use crate::rcc::{self, SealedRccPeripheral}; use crate::time::Hertz; /// HSI48 speed @@ -44,7 +44,7 @@ pub(crate) fn init_hsi48(config: Hsi48Config) -> Hertz { while r.read().hsi48rdy() == false {} if config.sync_from_usb { - crate::peripherals::CRS::enable_and_reset(); + rcc::enable_and_reset::(); CRS.cfgr().modify(|w| { w.set_syncsrc(Syncsrc::USB); diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 28816256c..0bf344c40 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -67,23 +67,185 @@ pub(crate) unsafe fn get_freqs() -> &'static Clocks { } pub(crate) trait SealedRccPeripheral { - const ENABLE_BIT: ClockEnableBit; - fn frequency() -> Hertz; - fn enable_and_reset_with_cs(cs: CriticalSection); - fn disable_with_cs(cs: CriticalSection); - - fn enable_and_reset() { - critical_section::with(|cs| Self::enable_and_reset_with_cs(cs)) - } - fn disable() { - critical_section::with(|cs| Self::disable_with_cs(cs)) - } + const RCC_INFO: RccInfo; } #[allow(private_bounds)] pub trait RccPeripheral: SealedRccPeripheral + 'static {} +/// Runtime information necessary to reset, enable and disable a peripheral. +pub(crate) struct RccInfo { + /// Offset in 32-bit words of the xxxRSTR register into the RCC register block, or 0xff if the + /// peripheral has no reset bit (we don't use an `Option` to save one byte of storage). + reset_offset_or_0xff: u8, + /// Position of the xxxRST bit within the xxxRSTR register (0..=31). + reset_bit: u8, + /// Offset in 32-bit words of the xxxENR register into the RCC register block. + enable_offset: u8, + /// Position of the xxxEN bit within the xxxENR register (0..=31). + enable_bit: u8, + /// If this peripheral shares the same xxxRSTR bit and xxxEN bit with other peripherals, we + /// maintain a refcount in `crate::_generated::REFCOUNTS` at this index. If the bit is not + /// shared, this is 0xff (we don't use an `Option` to save one byte of storage). + refcount_idx_or_0xff: u8, + /// Stop mode of the peripheral, used to maintain `REFCOUNT_STOP1` and `REFCOUNT_STOP2`. + #[cfg(feature = "low-power")] + stop_mode: StopMode, +} + +#[cfg(feature = "low-power")] +#[allow(dead_code)] +pub(crate) enum StopMode { + Standby, + Stop2, + Stop1, +} + +impl RccInfo { + /// Safety: + /// - `reset_offset_and_bit`, if set, must correspond to valid xxxRST bit + /// - `enable_offset_and_bit` must correspond to valid xxxEN bit + /// - `refcount_idx`, if set, must correspond to valid refcount in `_generated::REFCOUNTS` + /// - `stop_mode` must be valid + pub(crate) const unsafe fn new( + reset_offset_and_bit: Option<(u8, u8)>, + enable_offset_and_bit: (u8, u8), + refcount_idx: Option, + #[cfg(feature = "low-power")] stop_mode: StopMode, + ) -> Self { + let (reset_offset_or_0xff, reset_bit) = match reset_offset_and_bit { + Some((offset, bit)) => (offset, bit), + None => (0xff, 0xff), + }; + let (enable_offset, enable_bit) = enable_offset_and_bit; + let refcount_idx_or_0xff = match refcount_idx { + Some(idx) => idx, + None => 0xff, + }; + Self { + reset_offset_or_0xff, + reset_bit, + enable_offset, + enable_bit, + refcount_idx_or_0xff, + #[cfg(feature = "low-power")] + stop_mode, + } + } + + // TODO: should this be `unsafe`? + pub(crate) fn enable_and_reset_with_cs(&self, _cs: CriticalSection) { + if self.refcount_idx_or_0xff != 0xff { + let refcount_idx = self.refcount_idx_or_0xff as usize; + unsafe { + crate::_generated::REFCOUNTS[refcount_idx] += 1; + } + if unsafe { crate::_generated::REFCOUNTS[refcount_idx] } > 1 { + return; + } + } + + #[cfg(feature = "low-power")] + match self.stop_mode { + StopMode::Standby => {} + StopMode::Stop2 => unsafe { + REFCOUNT_STOP2 += 1; + }, + StopMode::Stop1 => unsafe { + REFCOUNT_STOP1 += 1; + }, + } + + // set the xxxRST bit + let reset_ptr = self.reset_ptr(); + if let Some(reset_ptr) = reset_ptr { + unsafe { + let val = reset_ptr.read_volatile(); + reset_ptr.write_volatile(val | 1u32 << self.reset_bit); + } + } + + // set the xxxEN bit + let enable_ptr = self.enable_ptr(); + unsafe { + let val = enable_ptr.read_volatile(); + enable_ptr.write_volatile(val | 1u32 << self.enable_bit); + } + + // we must wait two peripheral clock cycles before the clock is active + // this seems to work, but might be incorrect + // see http://efton.sk/STM32/gotcha/g183.html + + // dummy read (like in the ST HALs) + let _ = unsafe { enable_ptr.read_volatile() }; + + // DSB for good measure + cortex_m::asm::dsb(); + + // clear the xxxRST bit + if let Some(reset_ptr) = reset_ptr { + unsafe { + let val = reset_ptr.read_volatile(); + reset_ptr.write_volatile(val & !(1u32 << self.reset_bit)); + } + } + } + + // TODO: should this be `unsafe`? + pub(crate) fn disable_with_cs(&self, _cs: CriticalSection) { + if self.refcount_idx_or_0xff != 0xff { + let refcount_idx = self.refcount_idx_or_0xff as usize; + unsafe { + crate::_generated::REFCOUNTS[refcount_idx] -= 1; + } + if unsafe { crate::_generated::REFCOUNTS[refcount_idx] } > 0 { + return; + } + } + + #[cfg(feature = "low-power")] + match self.stop_mode { + StopMode::Standby => {} + StopMode::Stop2 => unsafe { + REFCOUNT_STOP2 -= 1; + }, + StopMode::Stop1 => unsafe { + REFCOUNT_STOP1 -= 1; + }, + } + + // clear the xxxEN bit + let enable_ptr = self.enable_ptr(); + unsafe { + let val = enable_ptr.read_volatile(); + enable_ptr.write_volatile(val & !(1u32 << self.enable_bit)); + } + } + + // TODO: should this be `unsafe`? + pub(crate) fn enable_and_reset(&self) { + critical_section::with(|cs| self.enable_and_reset_with_cs(cs)) + } + + // TODO: should this be `unsafe`? + pub(crate) fn disable(&self) { + critical_section::with(|cs| self.disable_with_cs(cs)) + } + + fn reset_ptr(&self) -> Option<*mut u32> { + if self.reset_offset_or_0xff != 0xff { + Some(unsafe { (RCC.as_ptr() as *mut u32).add(self.reset_offset_or_0xff as _) }) + } else { + None + } + } + + fn enable_ptr(&self) -> *mut u32 { + unsafe { (RCC.as_ptr() as *mut u32).add(self.enable_offset as _) } + } +} + #[allow(unused)] mod util { use crate::time::Hertz; @@ -128,8 +290,9 @@ pub fn frequency() -> Hertz { /// # Safety /// /// Peripheral must not be in use. -pub unsafe fn enable_and_reset() { - T::enable_and_reset(); +// TODO: should this be `unsafe`? +pub fn enable_and_reset_with_cs(cs: CriticalSection) { + T::RCC_INFO.enable_and_reset_with_cs(cs); } /// Disables peripheral `T`. @@ -137,52 +300,27 @@ pub unsafe fn enable_and_reset() { /// # Safety /// /// Peripheral must not be in use. -pub unsafe fn disable() { - T::disable(); +// TODO: should this be `unsafe`? +pub fn disable_with_cs(cs: CriticalSection) { + T::RCC_INFO.disable_with_cs(cs); } -/// Struct representing some clock enable bit (xxxENR.xxEN), only known at runtime. -#[derive(Clone, Copy)] -pub(crate) struct ClockEnableBit { - /// offset in 32bit words of the xxxENR register into the RCC register block. - offset: u8, - /// bit within the register (0..=31) - bit: u8, +/// Enables and resets peripheral `T`. +/// +/// # Safety +/// +/// Peripheral must not be in use. +// TODO: should this be `unsafe`? +pub fn enable_and_reset() { + T::RCC_INFO.enable_and_reset(); } -impl ClockEnableBit { - /// Safety: offset+bit must correspond to a valid xxxEN bit. - pub(crate) const unsafe fn new(offset: u8, bit: u8) -> Self { - Self { offset, bit } - } - - fn ptr(self) -> *mut u32 { - unsafe { (RCC.as_ptr() as *mut u32).add(self.offset as _) } - } - - #[allow(unused)] - pub(crate) fn enable_with_cs(self, _cs: CriticalSection) { - let p = self.ptr(); - unsafe { - let val = p.read_volatile(); - p.write_volatile(val | 1u32 << self.bit); - } - } - - pub(crate) fn disable_with_cs(self, _cs: CriticalSection) { - let p = self.ptr(); - unsafe { - let val = p.read_volatile(); - p.write_volatile(val & !(1u32 << self.bit)); - } - } - - #[allow(unused)] - pub(crate) fn enable(self) { - critical_section::with(|cs| self.enable_with_cs(cs)) - } - - pub(crate) fn disable(self) { - critical_section::with(|cs| self.disable_with_cs(cs)) - } +/// Disables peripheral `T`. +/// +/// # Safety +/// +/// Peripheral must not be in use. +// TODO: should this be `unsafe`? +pub fn disable() { + T::RCC_INFO.disable(); } diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 7a228e4a4..94491c32f 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -10,7 +10,7 @@ use embassy_sync::waitqueue::AtomicWaker; use rand_core::{CryptoRng, RngCore}; use crate::interrupt::typelevel::Interrupt; -use crate::{interrupt, pac, peripherals, Peripheral}; +use crate::{interrupt, pac, peripherals, rcc, Peripheral}; static RNG_WAKER: AtomicWaker = AtomicWaker::new(); @@ -52,7 +52,7 @@ impl<'d, T: Instance> Rng<'d, T> { inner: impl Peripheral

+ 'd, _irq: impl interrupt::typelevel::Binding> + 'd, ) -> Self { - T::enable_and_reset(); + rcc::enable_and_reset::(); into_ref!(inner); let mut random = Self { _inner: inner }; random.reset(); diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index b12a0db66..f3cd16b4f 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -205,7 +205,7 @@ impl Rtc { /// Create a new RTC instance. pub fn new(_rtc: impl Peripheral

, rtc_config: RtcConfig) -> Self { #[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))] - ::enable_and_reset(); + crate::rcc::enable_and_reset::(); let mut this = Self { #[cfg(feature = "low-power")] diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index 54dd81524..3faecdc33 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs @@ -11,7 +11,7 @@ pub use crate::dma::word; use crate::dma::{ringbuffer, Channel, ReadableRingBuffer, Request, TransferOptions, WritableRingBuffer}; use crate::gpio::{AFType, AnyPin, SealedPin as _}; use crate::pac::sai::{vals, Sai as Regs}; -use crate::rcc::RccPeripheral; +use crate::rcc::{self, RccPeripheral}; use crate::{peripherals, Peripheral}; /// SAI error @@ -722,7 +722,7 @@ pub struct SubBlock<'d, T, S: SubBlockInstance> { /// You can then create a [`Sai`] driver for each each half. pub fn split_subblocks<'d, T: Instance>(peri: impl Peripheral

+ 'd) -> (SubBlock<'d, T, A>, SubBlock<'d, T, B>) { into_ref!(peri); - T::enable_and_reset(); + rcc::enable_and_reset::(); ( SubBlock { @@ -978,7 +978,7 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { /// Reset SAI operation. pub fn reset() { - T::enable_and_reset(); + rcc::enable_and_reset::(); } /// Flush. diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index f79a11606..9c14837e1 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -16,7 +16,7 @@ use crate::dma::NoDma; use crate::gpio::{AFType, AnyPin, Pull, SealedPin, Speed}; use crate::interrupt::typelevel::Interrupt; use crate::pac::sdmmc::Sdmmc as RegBlock; -use crate::rcc::RccPeripheral; +use crate::rcc::{self, RccPeripheral}; use crate::time::Hertz; use crate::{interrupt, peripherals, Peripheral}; @@ -468,7 +468,7 @@ impl<'d, T: Instance, Dma: SdmmcDma + 'd> Sdmmc<'d, T, Dma> { ) -> Self { into_ref!(sdmmc, dma); - T::enable_and_reset(); + rcc::enable_and_reset::(); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 5fc8691ac..33be7a701 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -13,7 +13,7 @@ use crate::dma::{slice_ptr_parts, word, ChannelAndRequest}; use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _, Speed}; use crate::mode::{Async, Blocking, Mode as PeriMode}; use crate::pac::spi::{regs, vals, Spi as Regs}; -use crate::rcc::{ClockEnableBit, SealedRccPeripheral}; +use crate::rcc::{self, RccInfo, SealedRccPeripheral}; use crate::time::Hertz; use crate::Peripheral; @@ -129,7 +129,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { let lsbfirst = config.raw_byte_order(); - T::enable_and_reset(); + rcc::enable_and_reset::(); #[cfg(any(spi_v1, spi_f1))] { @@ -738,7 +738,7 @@ impl<'d, M: PeriMode> Drop for Spi<'d, M> { self.mosi.as_ref().map(|x| x.set_as_disconnected()); self.miso.as_ref().map(|x| x.set_as_disconnected()); - self.info.enable_bit.disable(); + self.info.rcc.disable(); } } @@ -1118,7 +1118,7 @@ mod word_impl { pub(crate) struct Info { pub(crate) regs: Regs, - pub(crate) enable_bit: ClockEnableBit, + pub(crate) rcc: RccInfo, } struct State {} @@ -1145,7 +1145,7 @@ foreach_peripheral!( (spi, $inst:ident) => { peri_trait_impl!($inst, Info { regs: crate::pac::$inst, - enable_bit: crate::peripherals::$inst::ENABLE_BIT, + rcc: crate::peripherals::$inst::RCC_INFO, }); }; ); diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index e592fbf7d..f8041bf1e 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -12,7 +12,7 @@ use stm32_metapac::timer::{regs, TimGp16}; use crate::interrupt::typelevel::Interrupt; use crate::pac::timer::vals; -use crate::rcc::SealedRccPeripheral; +use crate::rcc::{self, SealedRccPeripheral}; #[cfg(feature = "low-power")] use crate::rtc::Rtc; use crate::timer::{CoreInstance, GeneralInstance1Channel}; @@ -276,7 +276,7 @@ impl RtcDriver { fn init(&'static self, cs: critical_section::CriticalSection) { let r = regs_gp16(); - ::enable_and_reset_with_cs(cs); + rcc::enable_and_reset_with_cs::(cs); let timer_freq = T::frequency(); diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 7f533b75c..9932c04cd 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -10,6 +10,7 @@ use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use super::*; use crate::pac::timer::vals; +use crate::rcc; use crate::time::Hertz; /// Input capture mode. @@ -181,7 +182,7 @@ pub struct Timer<'d, T: CoreInstance> { impl<'d, T: CoreInstance> Drop for Timer<'d, T> { fn drop(&mut self) { - T::disable() + rcc::disable::(); } } @@ -190,7 +191,7 @@ impl<'d, T: CoreInstance> Timer<'d, T> { pub fn new(tim: impl Peripheral

+ 'd) -> Self { into_ref!(tim); - T::enable_and_reset(); + rcc::enable_and_reset::(); Self { tim } } diff --git a/embassy-stm32/src/tsc/mod.rs b/embassy-stm32/src/tsc/mod.rs index bf583f04c..045d6317c 100644 --- a/embassy-stm32/src/tsc/mod.rs +++ b/embassy-stm32/src/tsc/mod.rs @@ -72,7 +72,7 @@ pub use enums::*; use crate::gpio::{AFType, AnyPin}; use crate::pac::tsc::Tsc as Regs; -use crate::rcc::RccPeripheral; +use crate::rcc::{self, RccPeripheral}; use crate::{peripherals, Peripheral}; #[cfg(tsc_v1)] @@ -649,7 +649,7 @@ impl<'d, T: Instance> Tsc<'d, T> { ) -> Self { into_ref!(peri); - T::enable_and_reset(); + rcc::enable_and_reset::(); T::REGS.cr().modify(|w| { w.set_tsce(true); @@ -880,7 +880,7 @@ impl<'d, T: Instance> Tsc<'d, T> { impl<'d, T: Instance> Drop for Tsc<'d, T> { fn drop(&mut self) { - T::disable(); + rcc::disable::(); } } diff --git a/embassy-stm32/src/ucpd.rs b/embassy-stm32/src/ucpd.rs index d6d0682b9..89e2f5d49 100644 --- a/embassy-stm32/src/ucpd.rs +++ b/embassy-stm32/src/ucpd.rs @@ -28,7 +28,7 @@ use crate::interrupt; use crate::interrupt::typelevel::Interrupt; use crate::pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk, Txmode}; pub use crate::pac::ucpd::vals::{Phyccsel as CcSel, TypecVstateCc as CcVState}; -use crate::rcc::RccPeripheral; +use crate::rcc::{self, RccPeripheral}; pub(crate) fn init( _cs: critical_section::CriticalSection, @@ -103,7 +103,7 @@ impl<'d, T: Instance> Ucpd<'d, T> { cc1.set_as_analog(); cc2.set_as_analog(); - T::enable_and_reset(); + rcc::enable_and_reset::(); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; @@ -212,7 +212,7 @@ impl<'d, T: Instance> Drop for CcPhy<'d, T> { drop_not_ready.store(true, Ordering::Relaxed); } else { r.cfgr1().write(|w| w.set_ucpden(false)); - T::disable(); + rcc::disable::(); T::Interrupt::disable(); } } @@ -325,7 +325,7 @@ impl<'d, T: Instance> Drop for PdPhy<'d, T> { drop_not_ready.store(true, Ordering::Relaxed); } else { T::REGS.cfgr1().write(|w| w.set_ucpden(false)); - T::disable(); + rcc::disable::(); T::Interrupt::disable(); } } diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 492ad334b..eacf95002 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -18,6 +18,7 @@ use super::{ use crate::gpio::AFType; use crate::interrupt::typelevel::Interrupt as _; use crate::interrupt::{self, InterruptExt}; +use crate::rcc; use crate::time::Hertz; /// Interrupt handler. @@ -206,7 +207,7 @@ impl<'d> BufferedUart<'d> { rx_buffer: &'d mut [u8], config: Config, ) -> Result { - T::enable_and_reset(); + rcc::enable_and_reset::(); Self::new_inner(peri, rx, tx, tx_buffer, rx_buffer, config) } @@ -225,7 +226,7 @@ impl<'d> BufferedUart<'d> { ) -> Result { into_ref!(cts, rts); - T::enable_and_reset(); + rcc::enable_and_reset::(); rts.set_as_af(rts.af_num(), AFType::OutputPushPull); cts.set_as_af(cts.af_num(), AFType::Input); @@ -251,7 +252,7 @@ impl<'d> BufferedUart<'d> { ) -> Result { into_ref!(de); - T::enable_and_reset(); + rcc::enable_and_reset::(); de.set_as_af(de.af_num(), AFType::OutputPushPull); T::info().regs.cr3().write(|w| { @@ -545,7 +546,7 @@ fn drop_tx_rx(info: &Info, state: &State) { refcount == 1 }); if is_last_drop { - info.enable_bit.disable(); + info.rcc.disable(); } } diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index b24335f3a..2a39c6301 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -28,7 +28,7 @@ use crate::pac::usart::Lpuart as Regs; #[cfg(any(usart_v1, usart_v2))] use crate::pac::usart::Usart as Regs; use crate::pac::usart::{regs, vals}; -use crate::rcc::{ClockEnableBit, SealedRccPeripheral}; +use crate::rcc::{self, RccInfo, SealedRccPeripheral}; use crate::time::Hertz; use crate::Peripheral; @@ -429,7 +429,7 @@ impl<'d, M: Mode> UartTx<'d, M> { tx_dma: Option>, config: Config, ) -> Result { - T::enable_and_reset(); + rcc::enable_and_reset::(); let info = T::info(); let state = T::state(); @@ -775,7 +775,7 @@ impl<'d, M: Mode> UartRx<'d, M> { rx_dma: Option>, config: Config, ) -> Result { - T::enable_and_reset(); + rcc::enable_and_reset::(); let info = T::info(); let state = T::state(); @@ -916,7 +916,7 @@ fn drop_tx_rx(info: &Info, state: &State) { refcount == 1 }); if is_last_drop { - info.enable_bit.disable(); + info.rcc.disable(); } } @@ -1228,7 +1228,7 @@ impl<'d, M: Mode> Uart<'d, M> { rx_dma: Option>, config: Config, ) -> Result { - T::enable_and_reset(); + rcc::enable_and_reset::(); let info = T::info(); let state = T::state(); @@ -1718,7 +1718,7 @@ impl State { struct Info { regs: Regs, - enable_bit: ClockEnableBit, + rcc: RccInfo, interrupt: Interrupt, kind: Kind, } @@ -1754,7 +1754,7 @@ macro_rules! impl_usart { fn info() -> &'static Info { static INFO: Info = Info { regs: unsafe { Regs::from_ptr(crate::pac::$inst.as_ptr()) }, - enable_bit: crate::peripherals::$inst::ENABLE_BIT, + rcc: crate::peripherals::$inst::RCC_INFO, interrupt: crate::interrupt::typelevel::$irq::IRQ, kind: $kind, }; diff --git a/embassy-stm32/src/usb/mod.rs b/embassy-stm32/src/usb/mod.rs index 349438ec5..ce9fe0a9b 100644 --- a/embassy-stm32/src/usb/mod.rs +++ b/embassy-stm32/src/usb/mod.rs @@ -6,7 +6,7 @@ mod _version; pub use _version::*; use crate::interrupt::typelevel::Interrupt; -use crate::rcc::SealedRccPeripheral; +use crate::rcc; /// clock, power initialization stuff that's common for USB and OTG. fn common_init() { @@ -65,5 +65,5 @@ fn common_init() { T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; - ::enable_and_reset(); + rcc::enable_and_reset::(); } diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index 3debd5079..e5131250a 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -13,7 +13,7 @@ use embassy_usb_synopsys_otg::{ use crate::gpio::AFType; use crate::interrupt; use crate::interrupt::typelevel::Interrupt; -use crate::rcc::{RccPeripheral, SealedRccPeripheral}; +use crate::rcc::{self, RccPeripheral}; const MAX_EP_COUNT: usize = 9; @@ -246,7 +246,7 @@ impl<'d, T: Instance> Bus<'d, T> { fn disable(&mut self) { T::Interrupt::disable(); - ::disable(); + rcc::disable::(); self.inited = false; #[cfg(stm32l4)] From f9324201b1d9375e12b4af9a6b2424fe3ff85d32 Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Mon, 27 May 2024 15:44:58 +0300 Subject: [PATCH 02/57] add proper rxonly support for spi_v3 and force tx dma stream requirement on others --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/dma/util.rs | 1 + embassy-stm32/src/i2s.rs | 12 +++- embassy-stm32/src/spi/mod.rs | 121 ++++++++++++++++++++++++++++++++-- 4 files changed, 130 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 5ef2366d9..67c61a671 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -72,7 +72,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-34c0188a682b32c32ff147d377e0629b1ebe8318" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ad633a3e266151ea4d8fad630031a075ee02ab34" } 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-34c0188a682b32c32ff147d377e0629b1ebe8318", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ad633a3e266151ea4d8fad630031a075ee02ab34", default-features = false, features = ["metadata"]} [features] default = ["rt"] diff --git a/embassy-stm32/src/dma/util.rs b/embassy-stm32/src/dma/util.rs index 962ea2501..5aaca57c9 100644 --- a/embassy-stm32/src/dma/util.rs +++ b/embassy-stm32/src/dma/util.rs @@ -48,6 +48,7 @@ impl<'d> ChannelAndRequest<'d> { Transfer::new_write_raw(&mut self.channel, self.request, buf, peri_addr, options) } + #[allow(dead_code)] pub unsafe fn write_repeated<'a, W: Word>( &'a mut self, repeated: &'a W, diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index c78810a38..9c0bbbb87 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs @@ -169,7 +169,7 @@ impl<'d> I2S<'d> { ws: impl Peripheral

> + 'd, ck: impl Peripheral

> + 'd, mck: impl Peripheral

> + 'd, - txdma: impl Peripheral

> + 'd, + #[cfg(not(spi_v3))] txdma: impl Peripheral

> + 'd, rxdma: impl Peripheral

> + 'd, freq: Hertz, config: Config, @@ -190,7 +190,15 @@ impl<'d> I2S<'d> { let mut spi_cfg = SpiConfig::default(); spi_cfg.frequency = freq; - let spi = Spi::new_internal(peri, txdma, rxdma, spi_cfg); + let spi = Spi::new_internal( + peri, + #[cfg(not(spi_v3))] + new_dma!(txdma), + #[cfg(spi_v3)] + None, + new_dma!(rxdma), + spi_cfg, + ); // TODO move i2s to the new mux infra. //#[cfg(all(rcc_f4, not(stm32f410)))] diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 5fc8691ac..0a0afafc6 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -508,6 +508,7 @@ impl<'d> Spi<'d, Async> { peri: impl Peripheral

+ 'd, sck: impl Peripheral

> + 'd, miso: impl Peripheral

> + 'd, + #[cfg(not(spi_v3))] tx_dma: impl Peripheral

> + 'd, rx_dma: impl Peripheral

> + 'd, config: Config, ) -> Self { @@ -516,6 +517,9 @@ impl<'d> Spi<'d, Async> { new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()), None, new_pin!(miso, AFType::Input, Speed::VeryHigh), + #[cfg(not(spi_v3))] + new_dma!(tx_dma), + #[cfg(spi_v3)] None, new_dma!(rx_dma), config, @@ -584,11 +588,11 @@ impl<'d> Spi<'d, Async> { #[allow(dead_code)] pub(crate) fn new_internal( peri: impl Peripheral

+ 'd, - tx_dma: impl Peripheral

> + 'd, - rx_dma: impl Peripheral

> + 'd, + tx_dma: Option>, + rx_dma: Option>, config: Config, ) -> Self { - Self::new_inner(peri, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config) + Self::new_inner(peri, None, None, None, tx_dma, rx_dma, config) } /// SPI write, using DMA. @@ -623,11 +627,114 @@ impl<'d> Spi<'d, Async> { /// SPI read, using DMA. pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { + #[cfg(not(spi_v3))] + { + self.transmission_read(data).await + } + #[cfg(spi_v3)] + { + self.tsize_read(data).await + } + } + + #[cfg(spi_v3)] + async fn tsize_read(&mut self, data: &mut [W]) -> Result<(), Error> { + if data.is_empty() { + return Ok(()); + } + + self.info.regs.cr1().modify(|w| { + w.set_spe(false); + }); + + let comm = self.info.regs.cfg2().modify(|w| { + let prev = w.comm(); + w.set_comm(vals::Comm::RECEIVER); + prev + }); + + let i2scfg = self.info.regs.i2scfgr().modify(|w| { + let prev = w.i2scfg(); + w.set_i2scfg(match prev { + vals::I2scfg::SLAVERX | vals::I2scfg::SLAVEFULLDUPLEX => vals::I2scfg::SLAVERX, + vals::I2scfg::MASTERRX | vals::I2scfg::MASTERFULLDUPLEX => vals::I2scfg::MASTERRX, + _ => panic!("unsupported configuration"), + }); + prev + }); + + let tsize = self.info.regs.cr2().read().tsize(); + + let rx_src = self.info.regs.rx_ptr(); + + let mut read = 0; + let mut remaining = data.len(); + + loop { + self.set_word_size(W::CONFIG); + set_rxdmaen(self.info.regs, true); + + let transfer_size = remaining.min(u16::max_value().into()); + + let transfer = unsafe { + self.rx_dma + .as_mut() + .unwrap() + .read(rx_src, &mut data[read..(read + transfer_size)], Default::default()) + }; + + self.info.regs.cr2().modify(|w| { + w.set_tsize(transfer_size as u16); + }); + + self.info.regs.cr1().modify(|w| { + w.set_spe(true); + }); + + self.info.regs.cr1().modify(|w| { + w.set_cstart(true); + }); + + transfer.await; + + finish_dma(self.info.regs); + + remaining -= transfer_size; + + if remaining == 0 { + break; + } + + read += transfer_size; + } + + self.info.regs.cr1().modify(|w| { + w.set_spe(false); + }); + + self.info.regs.cfg2().modify(|w| { + w.set_comm(comm); + }); + + self.info.regs.cr2().modify(|w| { + w.set_tsize(tsize); + }); + + self.info.regs.i2scfgr().modify(|w| { + w.set_i2scfg(i2scfg); + }); + + Ok(()) + } + + #[cfg(not(spi_v3))] + async fn transmission_read(&mut self, data: &mut [W]) -> Result<(), Error> { if data.is_empty() { return Ok(()); } self.set_word_size(W::CONFIG); + self.info.regs.cr1().modify(|w| { w.set_spe(false); }); @@ -907,7 +1014,13 @@ fn finish_dma(regs: Regs) { while regs.sr().read().ftlvl().to_bits() > 0 {} #[cfg(any(spi_v3, spi_v4, spi_v5))] - while !regs.sr().read().txc() {} + { + if regs.cr2().read().tsize() == 0 { + while !regs.sr().read().txc() {} + } else { + while !regs.sr().read().eot() {} + } + } #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] while regs.sr().read().bsy() {} From 25cc5241b1e7c4a35c427626cd8f2f8721948830 Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Mon, 20 May 2024 16:15:41 +0300 Subject: [PATCH 03/57] Add i2s support for spi_v3. --- embassy-stm32/src/i2s.rs | 337 ++++++++++++++++++++++------ embassy-stm32/src/lib.rs | 2 +- examples/stm32f4/src/bin/i2s_dma.rs | 3 +- 3 files changed, 268 insertions(+), 74 deletions(-) diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index 9c0bbbb87..f893dd235 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs @@ -1,7 +1,9 @@ //! Inter-IC Sound (I2S) + use embassy_hal_internal::into_ref; -use crate::gpio::{AFType, AnyPin, SealedPin}; +use crate::dma::ChannelAndRequest; +use crate::gpio::{AFType, AnyPin, SealedPin, Speed}; use crate::mode::Async; use crate::pac::spi::vals; use crate::spi::{Config as SpiConfig, *}; @@ -17,15 +19,6 @@ pub enum Mode { Slave, } -/// I2S function -#[derive(Copy, Clone)] -pub enum Function { - /// Transmit audio data - Transmit, - /// Receive audio data - Receive, -} - /// I2C standard #[derive(Copy, Clone)] pub enum Standard { @@ -42,7 +35,7 @@ pub enum Standard { } impl Standard { - #[cfg(any(spi_v1, spi_f1))] + #[cfg(any(spi_v1, spi_v3, spi_f1))] const fn i2sstd(&self) -> vals::I2sstd { match self { Standard::Philips => vals::I2sstd::PHILIPS, @@ -53,7 +46,7 @@ impl Standard { } } - #[cfg(any(spi_v1, spi_f1))] + #[cfg(any(spi_v1, spi_v3, spi_f1))] const fn pcmsync(&self) -> vals::Pcmsync { match self { Standard::PcmLongSync => vals::Pcmsync::LONG, @@ -76,7 +69,7 @@ pub enum Format { } impl Format { - #[cfg(any(spi_v1, spi_f1))] + #[cfg(any(spi_v1, spi_v3, spi_f1))] const fn datlen(&self) -> vals::Datlen { match self { Format::Data16Channel16 => vals::Datlen::BITS16, @@ -86,7 +79,7 @@ impl Format { } } - #[cfg(any(spi_v1, spi_f1))] + #[cfg(any(spi_v1, spi_v3, spi_f1))] const fn chlen(&self) -> vals::Chlen { match self { Format::Data16Channel16 => vals::Chlen::BITS16, @@ -107,7 +100,7 @@ pub enum ClockPolarity { } impl ClockPolarity { - #[cfg(any(spi_v1, spi_f1))] + #[cfg(any(spi_v1, spi_v3, spi_f1))] const fn ckpol(&self) -> vals::Ckpol { match self { ClockPolarity::IdleHigh => vals::Ckpol::IDLEHIGH, @@ -127,15 +120,13 @@ impl ClockPolarity { pub struct Config { /// Mode pub mode: Mode, - /// Function (transmit, receive) - pub function: Function, /// Which I2S standard to use. pub standard: Standard, /// Data format. pub format: Format, /// Clock polarity. pub clock_polarity: ClockPolarity, - /// True to eanble master clock output from this instance. + /// True to enable master clock output from this instance. pub master_clock: bool, } @@ -143,7 +134,6 @@ impl Default for Config { fn default() -> Self { Self { mode: Mode::Master, - function: Function::Transmit, standard: Standard::Philips, format: Format::Data16Channel16, clock_polarity: ClockPolarity::IdleLow, @@ -155,50 +145,168 @@ impl Default for Config { /// I2S driver. pub struct I2S<'d> { _peri: Spi<'d, Async>, - sd: Option>, + txsd: Option>, + rxsd: Option>, ws: Option>, ck: Option>, mck: Option>, } +/// I2S function +#[derive(Copy, Clone)] +#[allow(dead_code)] +enum Function { + /// Transmit audio data + Transmit, + /// Receive audio data + Receive, + #[cfg(spi_v3)] + /// Transmit and Receive audio data + FullDuplex, +} + impl<'d> I2S<'d> { - /// Note: Full-Duplex modes are not supported at this time - pub fn new( + /// Create a transmitter driver + pub fn new_txonly( peri: impl Peripheral

+ 'd, sd: impl Peripheral

> + 'd, ws: impl Peripheral

> + 'd, ck: impl Peripheral

> + 'd, mck: impl Peripheral

> + 'd, + txdma: impl Peripheral

> + 'd, + freq: Hertz, + config: Config, + ) -> Self { + into_ref!(sd); + Self::new_inner( + peri, + new_pin!(sd, AFType::OutputPushPull, Speed::VeryHigh), + None, + ws, + ck, + mck, + new_dma!(txdma), + None, + freq, + config, + Function::Transmit, + ) + } + + /// Create a receiver driver + pub fn new_rxonly( + peri: impl Peripheral

+ 'd, + sd: impl Peripheral

> + 'd, + ws: impl Peripheral

> + 'd, + ck: impl Peripheral

> + 'd, + mck: impl Peripheral

> + 'd, #[cfg(not(spi_v3))] txdma: impl Peripheral

> + 'd, rxdma: impl Peripheral

> + 'd, freq: Hertz, config: Config, ) -> Self { - into_ref!(sd, ws, ck, mck); - - sd.set_as_af(sd.af_num(), AFType::OutputPushPull); - sd.set_speed(crate::gpio::Speed::VeryHigh); - - ws.set_as_af(ws.af_num(), AFType::OutputPushPull); - ws.set_speed(crate::gpio::Speed::VeryHigh); - - ck.set_as_af(ck.af_num(), AFType::OutputPushPull); - ck.set_speed(crate::gpio::Speed::VeryHigh); - - mck.set_as_af(mck.af_num(), AFType::OutputPushPull); - mck.set_speed(crate::gpio::Speed::VeryHigh); - - let mut spi_cfg = SpiConfig::default(); - spi_cfg.frequency = freq; - let spi = Spi::new_internal( + into_ref!(sd); + Self::new_inner( peri, + None, + new_pin!(sd, AFType::OutputPushPull, Speed::VeryHigh), + ws, + ck, + mck, #[cfg(not(spi_v3))] new_dma!(txdma), #[cfg(spi_v3)] None, new_dma!(rxdma), - spi_cfg, - ); + freq, + config, + #[cfg(not(spi_v3))] + Function::Transmit, + #[cfg(spi_v3)] + Function::Receive, + ) + } + + #[cfg(spi_v3)] + /// Create a full duplex transmitter driver + pub fn new_full_duplex( + peri: impl Peripheral

+ 'd, + txsd: impl Peripheral

> + 'd, + rxsd: impl Peripheral

> + 'd, + ws: impl Peripheral

> + 'd, + ck: impl Peripheral

> + 'd, + mck: impl Peripheral

> + 'd, + txdma: impl Peripheral

> + 'd, + rxdma: impl Peripheral

> + 'd, + freq: Hertz, + config: Config, + ) -> Self { + into_ref!(txsd, rxsd); + Self::new_inner( + peri, + new_pin!(txsd, AFType::OutputPushPull, Speed::VeryHigh), + new_pin!(rxsd, AFType::OutputPushPull, Speed::VeryHigh), + ws, + ck, + mck, + new_dma!(txdma), + new_dma!(rxdma), + freq, + config, + Function::FullDuplex, + ) + } + + /// Write audio data. + pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { + self._peri.read(data).await + } + + /// Write audio data. + pub async fn write(&mut self, data: &[W]) -> Result<(), Error> { + self._peri.write(data).await + } + + /// Transfer audio data. + pub async fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { + self._peri.transfer(read, write).await + } + + /// Transfer audio data in place. + pub async fn transfer_in_place(&mut self, data: &mut [W]) -> Result<(), Error> { + self._peri.transfer_in_place(data).await + } + + fn new_inner( + peri: impl Peripheral

+ 'd, + txsd: Option>, + rxsd: Option>, + ws: impl Peripheral

> + 'd, + ck: impl Peripheral

> + 'd, + mck: impl Peripheral

> + 'd, + txdma: Option>, + rxdma: Option>, + freq: Hertz, + config: Config, + function: Function, + ) -> Self { + into_ref!(ws, ck, mck); + + ws.set_as_af(ws.af_num(), AFType::OutputPushPull); + ws.set_speed(Speed::VeryHigh); + + ck.set_as_af(ck.af_num(), AFType::OutputPushPull); + ck.set_speed(Speed::VeryHigh); + + mck.set_as_af(mck.af_num(), AFType::OutputPushPull); + mck.set_speed(Speed::VeryHigh); + + let mut spi_cfg = SpiConfig::default(); + spi_cfg.frequency = freq; + + let spi = Spi::new_internal(peri, txdma, rxdma, spi_cfg); + + let regs = T::info().regs; // TODO move i2s to the new mux infra. //#[cfg(all(rcc_f4, not(stm32f410)))] @@ -208,26 +316,23 @@ impl<'d> I2S<'d> { let (odd, div) = compute_baud_rate(pclk, freq, config.master_clock, config.format); - #[cfg(any(spi_v1, spi_f1))] + #[cfg(any(spi_v1, spi_v3, spi_f1))] { + #[cfg(spi_v3)] + { + regs.cr1().modify(|w| w.set_spe(false)); + + reset_incompatible_bitfields::(); + } + use stm32_metapac::spi::vals::{I2scfg, Odd}; - // 1. Select the I2SDIV[7:0] bits in the SPI_I2SPR register to define the serial clock baud - // rate to reach the proper audio sample frequency. The ODD bit in the SPI_I2SPR - // register also has to be defined. - - spi.info.regs.i2spr().modify(|w| { - w.set_i2sdiv(div); - w.set_odd(match odd { - true => Odd::ODD, - false => Odd::EVEN, - }); - - w.set_mckoe(config.master_clock); - }); + // 1. Select the I2SDIV[7:0] bits in the SPI_I2SPR/SPI_I2SCFGR register to define the serial clock baud + // rate to reach the proper audio sample frequency. The ODD bit in the + // SPI_I2SPR/SPI_I2SCFGR register also has to be defined. // 2. Select the CKPOL bit to define the steady level for the communication clock. Set the - // MCKOE bit in the SPI_I2SPR register if the master clock MCK needs to be provided to + // MCKOE bit in the SPI_I2SPR/SPI_I2SCFGR register if the master clock MCK needs to be provided to // the external DAC/ADC audio component (the I2SDIV and ODD values should be // computed depending on the state of the MCK output, for more details refer to // Section 28.4.4: Clock generator). @@ -243,50 +348,72 @@ impl<'d> I2S<'d> { // 5. The I2SE bit in SPI_I2SCFGR register must be set. - spi.info.regs.i2scfgr().modify(|w| { + let clk_reg = { + #[cfg(any(spi_v1, spi_f1))] + { + regs.i2spr() + } + #[cfg(spi_v3)] + { + regs.i2scfgr() + } + }; + + clk_reg.modify(|w| { + w.set_i2sdiv(div); + w.set_odd(match odd { + true => Odd::ODD, + false => Odd::EVEN, + }); + + w.set_mckoe(config.master_clock); + }); + + regs.i2scfgr().modify(|w| { w.set_ckpol(config.clock_polarity.ckpol()); w.set_i2smod(true); + w.set_i2sstd(config.standard.i2sstd()); w.set_pcmsync(config.standard.pcmsync()); w.set_datlen(config.format.datlen()); w.set_chlen(config.format.chlen()); - w.set_i2scfg(match (config.mode, config.function) { + w.set_i2scfg(match (config.mode, function) { (Mode::Master, Function::Transmit) => I2scfg::MASTERTX, (Mode::Master, Function::Receive) => I2scfg::MASTERRX, + #[cfg(spi_v3)] + (Mode::Master, Function::FullDuplex) => I2scfg::MASTERFULLDUPLEX, (Mode::Slave, Function::Transmit) => I2scfg::SLAVETX, (Mode::Slave, Function::Receive) => I2scfg::SLAVERX, + #[cfg(spi_v3)] + (Mode::Slave, Function::FullDuplex) => I2scfg::SLAVEFULLDUPLEX, }); - w.set_i2se(true) + #[cfg(any(spi_v1, spi_f1))] + w.set_i2se(true); }); + + #[cfg(spi_v3)] + regs.cr1().modify(|w| w.set_spe(true)); } Self { _peri: spi, - sd: Some(sd.map_into()), + txsd: txsd.map(|w| w.map_into()), + rxsd: rxsd.map(|w| w.map_into()), ws: Some(ws.map_into()), ck: Some(ck.map_into()), mck: Some(mck.map_into()), } } - - /// Write audio data. - pub async fn write(&mut self, data: &[W]) -> Result<(), Error> { - self._peri.write(data).await - } - - /// Read audio data. - pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { - self._peri.read(data).await - } } impl<'d> Drop for I2S<'d> { fn drop(&mut self) { - self.sd.as_ref().map(|x| x.set_as_disconnected()); + self.txsd.as_ref().map(|x| x.set_as_disconnected()); + self.rxsd.as_ref().map(|x| x.set_as_disconnected()); self.ws.as_ref().map(|x| x.set_as_disconnected()); self.ck.as_ref().map(|x| x.set_as_disconnected()); self.mck.as_ref().map(|x| x.set_as_disconnected()); @@ -328,3 +455,71 @@ fn compute_baud_rate(i2s_clock: Hertz, request_freq: Hertz, mclk: bool, data_for ((division & 1) == 1, (division >> 1) as u8) } } + +#[cfg(spi_v3)] +// The STM32H7 reference manual specifies that any incompatible bitfields should be reset +// to their reset values while operating in I2S mode. +fn reset_incompatible_bitfields() { + let regs = T::info().regs; + regs.cr1().modify(|w| { + let iolock = w.iolock(); + let csusp = w.csusp(); + let spe = w.cstart(); + let cstart = w.cstart(); + w.0 = 0; + w.set_iolock(iolock); + w.set_csusp(csusp); + w.set_spe(spe); + w.set_cstart(cstart); + }); + + regs.cr2().write(|w| w.0 = 0); + + regs.cfg1().modify(|w| { + let txdmaen = w.txdmaen(); + let rxdmaen = w.rxdmaen(); + let fthlv = w.fthlv(); + w.0 = 0; + w.set_txdmaen(txdmaen); + w.set_rxdmaen(rxdmaen); + w.set_fthlv(fthlv); + }); + + regs.cfg2().modify(|w| { + let afcntr = w.afcntr(); + let lsbfirst = w.lsbfirst(); + let ioswp = w.ioswp(); + w.0 = 0; + w.set_afcntr(afcntr); + w.set_lsbfirst(lsbfirst); + w.set_ioswp(ioswp); + }); + + regs.ier().modify(|w| { + let tifreie = w.tifreie(); + let ovrie = w.ovrie(); + let udrie = w.udrie(); + let txpie = w.txpie(); + let rxpie = w.rxpie(); + + w.0 = 0; + + w.set_tifreie(tifreie); + w.set_ovrie(ovrie); + w.set_udrie(udrie); + w.set_txpie(txpie); + w.set_rxpie(rxpie); + }); + + regs.ifcr().write(|w| { + w.set_suspc(true); + w.set_tifrec(true); + w.set_ovrc(true); + w.set_udrc(true); + }); + + regs.crcpoly().write(|w| w.0 = 0x107); + regs.txcrc().write(|w| w.0 = 0); + regs.rxcrc().write(|w| w.0 = 0); + regs.udrdr().write(|w| w.0 = 0); +} diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 81ee60c1c..990bde98a 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -83,7 +83,7 @@ pub mod hrtim; pub mod hsem; #[cfg(i2c)] pub mod i2c; -#[cfg(all(spi_v1, rcc_f4))] +#[cfg(any(all(spi_v1, rcc_f4), spi_v3))] pub mod i2s; #[cfg(stm32wb)] pub mod ipcc; diff --git a/examples/stm32f4/src/bin/i2s_dma.rs b/examples/stm32f4/src/bin/i2s_dma.rs index 97a04b2aa..27b165f1b 100644 --- a/examples/stm32f4/src/bin/i2s_dma.rs +++ b/examples/stm32f4/src/bin/i2s_dma.rs @@ -15,14 +15,13 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut i2s = I2S::new( + let mut i2s = I2S::new_txonly( p.SPI2, p.PC3, // sd p.PB12, // ws p.PB10, // ck p.PC6, // mck p.DMA1_CH4, - p.DMA1_CH3, Hertz(1_000_000), Config::default(), ); From 76fbec74da30c5cd04b441aa49414a286170a491 Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Mon, 27 May 2024 20:43:17 +0300 Subject: [PATCH 04/57] fix spi panic on read due to i2s configuration conversion check --- embassy-stm32/src/spi/mod.rs | 52 ++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 0a0afafc6..0b12bc9b6 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -643,36 +643,40 @@ impl<'d> Spi<'d, Async> { return Ok(()); } - self.info.regs.cr1().modify(|w| { + let regs = self.info.regs; + + regs.cr1().modify(|w| { w.set_spe(false); }); - let comm = self.info.regs.cfg2().modify(|w| { + let comm = regs.cfg2().modify(|w| { let prev = w.comm(); w.set_comm(vals::Comm::RECEIVER); prev }); - let i2scfg = self.info.regs.i2scfgr().modify(|w| { - let prev = w.i2scfg(); - w.set_i2scfg(match prev { - vals::I2scfg::SLAVERX | vals::I2scfg::SLAVEFULLDUPLEX => vals::I2scfg::SLAVERX, - vals::I2scfg::MASTERRX | vals::I2scfg::MASTERFULLDUPLEX => vals::I2scfg::MASTERRX, - _ => panic!("unsupported configuration"), - }); - prev + let i2scfg = regs.i2scfgr().modify(|w| { + w.i2smod().then(|| { + let prev = w.i2scfg(); + w.set_i2scfg(match prev { + vals::I2scfg::SLAVERX | vals::I2scfg::SLAVEFULLDUPLEX => vals::I2scfg::SLAVERX, + vals::I2scfg::MASTERRX | vals::I2scfg::MASTERFULLDUPLEX => vals::I2scfg::MASTERRX, + _ => panic!("unsupported configuration"), + }); + prev + }) }); - let tsize = self.info.regs.cr2().read().tsize(); + let tsize = regs.cr2().read().tsize(); - let rx_src = self.info.regs.rx_ptr(); + let rx_src = regs.rx_ptr(); let mut read = 0; let mut remaining = data.len(); loop { self.set_word_size(W::CONFIG); - set_rxdmaen(self.info.regs, true); + set_rxdmaen(regs, true); let transfer_size = remaining.min(u16::max_value().into()); @@ -683,21 +687,21 @@ impl<'d> Spi<'d, Async> { .read(rx_src, &mut data[read..(read + transfer_size)], Default::default()) }; - self.info.regs.cr2().modify(|w| { + regs.cr2().modify(|w| { w.set_tsize(transfer_size as u16); }); - self.info.regs.cr1().modify(|w| { + regs.cr1().modify(|w| { w.set_spe(true); }); - self.info.regs.cr1().modify(|w| { + regs.cr1().modify(|w| { w.set_cstart(true); }); transfer.await; - finish_dma(self.info.regs); + finish_dma(regs); remaining -= transfer_size; @@ -708,21 +712,23 @@ impl<'d> Spi<'d, Async> { read += transfer_size; } - self.info.regs.cr1().modify(|w| { + regs.cr1().modify(|w| { w.set_spe(false); }); - self.info.regs.cfg2().modify(|w| { + regs.cfg2().modify(|w| { w.set_comm(comm); }); - self.info.regs.cr2().modify(|w| { + regs.cr2().modify(|w| { w.set_tsize(tsize); }); - self.info.regs.i2scfgr().modify(|w| { - w.set_i2scfg(i2scfg); - }); + if let Some(i2scfg) = i2scfg { + regs.i2scfgr().modify(|w| { + w.set_i2scfg(i2scfg); + }); + } Ok(()) } From 1d05015a1c6b79d9cf7f93d16718508f3f80fc7e Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Mon, 27 May 2024 20:42:29 +0300 Subject: [PATCH 05/57] broaden tsize read support to other spi versions --- embassy-stm32/src/spi/mod.rs | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 0b12bc9b6..a3578dfde 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -508,7 +508,7 @@ impl<'d> Spi<'d, Async> { peri: impl Peripheral

+ 'd, sck: impl Peripheral

> + 'd, miso: impl Peripheral

> + 'd, - #[cfg(not(spi_v3))] tx_dma: impl Peripheral

> + 'd, + #[cfg(any(spi_v1, spi_f1))] tx_dma: impl Peripheral

> + 'd, rx_dma: impl Peripheral

> + 'd, config: Config, ) -> Self { @@ -517,9 +517,9 @@ impl<'d> Spi<'d, Async> { new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()), None, new_pin!(miso, AFType::Input, Speed::VeryHigh), - #[cfg(not(spi_v3))] + #[cfg(any(spi_v1, spi_f1))] new_dma!(tx_dma), - #[cfg(spi_v3)] + #[cfg(any(spi_v2, spi_v3, spi_v4, spi_v5))] None, new_dma!(rx_dma), config, @@ -626,19 +626,8 @@ impl<'d> Spi<'d, Async> { } /// SPI read, using DMA. + #[cfg(any(spi_v2, spi_v3, spi_v4, spi_v5))] pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { - #[cfg(not(spi_v3))] - { - self.transmission_read(data).await - } - #[cfg(spi_v3)] - { - self.tsize_read(data).await - } - } - - #[cfg(spi_v3)] - async fn tsize_read(&mut self, data: &mut [W]) -> Result<(), Error> { if data.is_empty() { return Ok(()); } @@ -733,8 +722,9 @@ impl<'d> Spi<'d, Async> { Ok(()) } - #[cfg(not(spi_v3))] - async fn transmission_read(&mut self, data: &mut [W]) -> Result<(), Error> { + /// SPI read, using DMA. + #[cfg(any(spi_v1, spi_f1))] + pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { if data.is_empty() { return Ok(()); } From 9a6ed79ad124c8a22f6833c2c4f9637661a5bbdb Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Mon, 27 May 2024 20:51:50 +0300 Subject: [PATCH 06/57] remove rx-only support for new spi versions with missing I2SCFGR register --- embassy-stm32/src/spi/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index a3578dfde..d9b6f0003 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -508,7 +508,7 @@ impl<'d> Spi<'d, Async> { peri: impl Peripheral

+ 'd, sck: impl Peripheral

> + 'd, miso: impl Peripheral

> + 'd, - #[cfg(any(spi_v1, spi_f1))] tx_dma: impl Peripheral

> + 'd, + #[cfg(any(spi_v1, spi_f1, spi_v4, spi_v5))] tx_dma: impl Peripheral

> + 'd, rx_dma: impl Peripheral

> + 'd, config: Config, ) -> Self { @@ -517,9 +517,9 @@ impl<'d> Spi<'d, Async> { new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()), None, new_pin!(miso, AFType::Input, Speed::VeryHigh), - #[cfg(any(spi_v1, spi_f1))] + #[cfg(any(spi_v1, spi_f1, spi_v4, spi_v5))] new_dma!(tx_dma), - #[cfg(any(spi_v2, spi_v3, spi_v4, spi_v5))] + #[cfg(any(spi_v2, spi_v3))] None, new_dma!(rx_dma), config, @@ -626,7 +626,7 @@ impl<'d> Spi<'d, Async> { } /// SPI read, using DMA. - #[cfg(any(spi_v2, spi_v3, spi_v4, spi_v5))] + #[cfg(any(spi_v2, spi_v3))] pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { if data.is_empty() { return Ok(()); @@ -723,7 +723,7 @@ impl<'d> Spi<'d, Async> { } /// SPI read, using DMA. - #[cfg(any(spi_v1, spi_f1))] + #[cfg(any(spi_v1, spi_f1, spi_v4, spi_v5))] pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { if data.is_empty() { return Ok(()); From 46fc15783d226d66aa93e38c0b36c8cac9b71700 Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Tue, 28 May 2024 12:50:24 +0300 Subject: [PATCH 07/57] add spi_v4 and spi_v5 support but block i2scfg rx-only configuration --- embassy-stm32/src/spi/mod.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index d9b6f0003..109b2738b 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -508,7 +508,7 @@ impl<'d> Spi<'d, Async> { peri: impl Peripheral

+ 'd, sck: impl Peripheral

> + 'd, miso: impl Peripheral

> + 'd, - #[cfg(any(spi_v1, spi_f1, spi_v4, spi_v5))] tx_dma: impl Peripheral

> + 'd, + #[cfg(any(spi_v1, spi_f1, spi_v2))] tx_dma: impl Peripheral

> + 'd, rx_dma: impl Peripheral

> + 'd, config: Config, ) -> Self { @@ -517,9 +517,9 @@ impl<'d> Spi<'d, Async> { new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()), None, new_pin!(miso, AFType::Input, Speed::VeryHigh), - #[cfg(any(spi_v1, spi_f1, spi_v4, spi_v5))] + #[cfg(any(spi_v1, spi_f1, spi_v2))] new_dma!(tx_dma), - #[cfg(any(spi_v2, spi_v3))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] None, new_dma!(rx_dma), config, @@ -626,7 +626,7 @@ impl<'d> Spi<'d, Async> { } /// SPI read, using DMA. - #[cfg(any(spi_v2, spi_v3))] + #[cfg(any(spi_v3, spi_v4, spi_v5))] pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { if data.is_empty() { return Ok(()); @@ -644,6 +644,7 @@ impl<'d> Spi<'d, Async> { prev }); + #[cfg(spi_v3)] let i2scfg = regs.i2scfgr().modify(|w| { w.i2smod().then(|| { let prev = w.i2scfg(); @@ -713,6 +714,7 @@ impl<'d> Spi<'d, Async> { w.set_tsize(tsize); }); + #[cfg(spi_v3)] if let Some(i2scfg) = i2scfg { regs.i2scfgr().modify(|w| { w.set_i2scfg(i2scfg); @@ -723,7 +725,7 @@ impl<'d> Spi<'d, Async> { } /// SPI read, using DMA. - #[cfg(any(spi_v1, spi_f1, spi_v4, spi_v5))] + #[cfg(any(spi_v1, spi_f1, spi_v2))] pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { if data.is_empty() { return Ok(()); From 4d1fbcd9cd42ebc3169008e189fcd98dce1c427d Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Tue, 28 May 2024 13:38:27 +0300 Subject: [PATCH 08/57] address review comments --- embassy-stm32/src/spi/mod.rs | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 109b2738b..7fb8da5ac 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -657,28 +657,23 @@ impl<'d> Spi<'d, Async> { }) }); - let tsize = regs.cr2().read().tsize(); - let rx_src = regs.rx_ptr(); - let mut read = 0; - let mut remaining = data.len(); - - loop { + for mut chunk in data.chunks_mut(u16::max_value().into()) { self.set_word_size(W::CONFIG); set_rxdmaen(regs, true); - let transfer_size = remaining.min(u16::max_value().into()); + let tsize = chunk.len(); let transfer = unsafe { self.rx_dma .as_mut() .unwrap() - .read(rx_src, &mut data[read..(read + transfer_size)], Default::default()) + .read(rx_src, &mut chunk, Default::default()) }; regs.cr2().modify(|w| { - w.set_tsize(transfer_size as u16); + w.set_tsize(tsize as u16); }); regs.cr1().modify(|w| { @@ -692,14 +687,6 @@ impl<'d> Spi<'d, Async> { transfer.await; finish_dma(regs); - - remaining -= transfer_size; - - if remaining == 0 { - break; - } - - read += transfer_size; } regs.cr1().modify(|w| { @@ -711,7 +698,7 @@ impl<'d> Spi<'d, Async> { }); regs.cr2().modify(|w| { - w.set_tsize(tsize); + w.set_tsize(0); }); #[cfg(spi_v3)] From 4f76f6b9df72c10f039911d0e35ca504be750564 Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Tue, 28 May 2024 15:24:05 +0300 Subject: [PATCH 09/57] add spi_dma rx-only test --- tests/stm32/src/bin/spi_dma.rs | 65 +++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index 5d46726dd..30e679f2a 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -8,27 +8,33 @@ use defmt::assert_eq; use embassy_executor::Spawner; use embassy_stm32::spi::{self, Spi}; use embassy_stm32::time::Hertz; +use embassy_stm32::{into_ref, Peripheral as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config()); info!("Hello World!"); - let spi = peri!(p, SPI); + let spi_peri = peri!(p, SPI); let sck = peri!(p, SPI_SCK); let mosi = peri!(p, SPI_MOSI); let miso = peri!(p, SPI_MISO); let tx_dma = peri!(p, SPI_TX_DMA); let rx_dma = peri!(p, SPI_RX_DMA); + into_ref!(spi_peri, sck, mosi, miso, tx_dma, rx_dma); + let mut spi_config = spi::Config::default(); spi_config.frequency = Hertz(1_000_000); let mut spi = Spi::new( - spi, sck, // Arduino D13 - mosi, // Arduino D11 - miso, // Arduino D12 - tx_dma, rx_dma, spi_config, + spi_peri.reborrow(), + sck.reborrow(), // Arduino D13 + mosi.reborrow(), // Arduino D11 + miso.reborrow(), // Arduino D12 + tx_dma.reborrow(), + rx_dma.reborrow(), + spi_config, ); let data: [u8; 9] = [0x00, 0xFF, 0xAA, 0x55, 0xC0, 0xFF, 0xEE, 0xC0, 0xDE]; @@ -76,6 +82,55 @@ async fn main(_spawner: Spawner) { spi.blocking_read(&mut buf).unwrap(); spi.write(&buf).await.unwrap(); + core::mem::drop(spi); + + // test rx-only configuration + + // stm32f207zg - spi_v1 + // stm32f103c8 - spi_f1 + // stm32g491re - spi_v2 + // stm32h753zi - spi_v3 + // stm32h563zi - spi_v4 + // stm32wba52cg - spi_v5 + + #[cfg(any(stm32f207zg, stm32f103c8, stm32g491re, stm32h753zi, stm32h563zi, stm32wba52cg))] + { + let mut spi = { + #[cfg(stm32f207zg, stm32f103c8, stm32g491re)] + { + Spi::new_rxonly( + spi_peri.reborrow(), + sck.reborrow(), + miso.reborrow(), + tx_dma.reborrow(), + rx_dma.reborrow(), + spi_config, + ) + } + #[cfg(stm32h753zi, stm32h563zi, stm32wba52cg)] + { + Spi::new_rxonly( + spi_peri.reborrow(), + sck.reborrow(), + miso.reborrow(), + rx_dma.reborrow(), + spi_config, + ) + } + }; + + use embassy_stm32::gpio; + let mut mosi = gpio::Output::new(mosi.reborrow(), gpio::Level::Low, gpio::Speed::Low); + + mosi.set_high(); + spi.read(&mut buf).await.unwrap(); + assert_eq!(buf, [0xff; 9]); + + mosi.set_low(); + spi.read(&mut buf).await.unwrap(); + assert_eq!(buf, [0x00; 9]); + }; + info!("Test OK"); cortex_m::asm::bkpt(); } From 642465a7da64333b9339283f3b3a14cb04bd349e Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Tue, 28 May 2024 20:20:15 -0400 Subject: [PATCH 10/57] add stm32f446 files --- examples/stm32f4/.cargo/config.toml | 6 +++-- examples/stm32f4/.vscode/launch.json | 33 +++++++++++++++++++++++ examples/stm32f4/.vscode/tasks.json | 21 +++++++++++++++ examples/stm32f4/Cargo.toml | 2 +- examples/stm32f4/openocd.cfg | 5 ++++ examples/stm32f4/openocd.gdb | 40 ++++++++++++++++++++++++++++ 6 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 examples/stm32f4/.vscode/launch.json create mode 100644 examples/stm32f4/.vscode/tasks.json create mode 100644 examples/stm32f4/openocd.cfg create mode 100644 examples/stm32f4/openocd.gdb diff --git a/examples/stm32f4/.cargo/config.toml b/examples/stm32f4/.cargo/config.toml index 16efa8e6f..f5a4af51a 100644 --- a/examples/stm32f4/.cargo/config.toml +++ b/examples/stm32f4/.cargo/config.toml @@ -1,9 +1,11 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # replace STM32F429ZITx with your chip as listed in `probe-rs chip list` -runner = "probe-rs run --chip STM32F429ZITx" +# runner = "probe-rs run --chip STM32F429ZITx" +runner = "arm-none-eabi-gdb -q -x openocd.gdb" [build] -target = "thumbv7em-none-eabi" +# target = "thumbv7em-none-eabi" +target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) [env] DEFMT_LOG = "trace" diff --git a/examples/stm32f4/.vscode/launch.json b/examples/stm32f4/.vscode/launch.json new file mode 100644 index 000000000..59f98070d --- /dev/null +++ b/examples/stm32f4/.vscode/launch.json @@ -0,0 +1,33 @@ +{ + /* + * Requires the Rust Language Server (rust-analyzer) and Cortex-Debug extensions + * https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer + * https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug + */ + "version": "0.2.0", + "configurations": [ + { + /* Configuration for the STM32F446 Discovery board */ + "type": "cortex-debug", + "request": "launch", + "name": "Debug (OpenOCD)", + "servertype": "openocd", + "cwd": "${workspaceRoot}", + "preLaunchTask": "Cargo Build (debug)", + "runToEntryPoint": "main", + "executable": "./target/thumbv7em-none-eabihf/debug/multiprio", + /* Run `cargo build --example itm` and uncomment this line to run itm example */ + // "executable": "./target/thumbv7em-none-eabihf/debug/examples/itm", + "device": "STM32F446RET6", + "configFiles": [ + "interface/stlink.cfg", + "target/stm32f4x.cfg" + ], + "postLaunchCommands": [ + "monitor arm semihosting enable" + ], + "postRestartCommands": [], + "postResetCommands": [], + } + ] +} \ No newline at end of file diff --git a/examples/stm32f4/.vscode/tasks.json b/examples/stm32f4/.vscode/tasks.json new file mode 100644 index 000000000..3cbed84b4 --- /dev/null +++ b/examples/stm32f4/.vscode/tasks.json @@ -0,0 +1,21 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "cargo", + "command": "build", + "problemMatcher": [ + "$rustc" + ], + "args": [ + "--bin", + "multiprio" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "label": "Cargo Build (debug)", + } + ] +} \ No newline at end of file diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 1eb1ae6db..ee7594c95 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32f429zi to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f446re", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32f4/openocd.cfg b/examples/stm32f4/openocd.cfg new file mode 100644 index 000000000..e41d52b1a --- /dev/null +++ b/examples/stm32f4/openocd.cfg @@ -0,0 +1,5 @@ +# Sample OpenOCD configuration for the STM32F3DISCOVERY development board + +source [find interface/stlink.cfg] + +source [find target/stm32f4x.cfg] diff --git a/examples/stm32f4/openocd.gdb b/examples/stm32f4/openocd.gdb new file mode 100644 index 000000000..7795319fb --- /dev/null +++ b/examples/stm32f4/openocd.gdb @@ -0,0 +1,40 @@ +target extended-remote :3333 + +# print demangled symbols +set print asm-demangle on + +# set backtrace limit to not have infinite backtrace loops +set backtrace limit 32 + +# detect unhandled exceptions, hard faults and panics +break DefaultHandler +break HardFault +break rust_begin_unwind +# # run the next few lines so the panic message is printed immediately +# # the number needs to be adjusted for your panic handler +# commands $bpnum +# next 4 +# end + +# *try* to stop at the user entry point (it might be gone due to inlining) +break main + +monitor arm semihosting enable + +# # send captured ITM to the file itm.fifo +# # (the microcontroller SWO pin must be connected to the programmer SWO pin) +# # 8000000 must match the core clock frequency +# monitor tpiu config internal itm.txt uart off 8000000 + +# # OR: make the microcontroller SWO pin output compatible with UART (8N1) +# # 8000000 must match the core clock frequency +# # 2000000 is the frequency of the SWO pin +# monitor tpiu config external uart off 8000000 2000000 + +# # enable ITM port 0 +# monitor itm port 0 on + +load + +# start the process but immediately halt the processor +stepi From 41b9a12574f9ad992ca986b87926838fbe1f1775 Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Tue, 28 May 2024 20:36:23 -0400 Subject: [PATCH 11/57] compile pwm_input example --- examples/stm32f4/.vscode/launch.json | 2 +- examples/stm32f4/.vscode/tasks.json | 2 +- examples/stm32f4/src/bin/pwm_input.rs | 52 +++++++++++++++++++++++++++ rust-toolchain.toml | 2 +- 4 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 examples/stm32f4/src/bin/pwm_input.rs diff --git a/examples/stm32f4/.vscode/launch.json b/examples/stm32f4/.vscode/launch.json index 59f98070d..a9849e0da 100644 --- a/examples/stm32f4/.vscode/launch.json +++ b/examples/stm32f4/.vscode/launch.json @@ -15,7 +15,7 @@ "cwd": "${workspaceRoot}", "preLaunchTask": "Cargo Build (debug)", "runToEntryPoint": "main", - "executable": "./target/thumbv7em-none-eabihf/debug/multiprio", + "executable": "./target/thumbv7em-none-eabihf/debug/pwm_input", /* Run `cargo build --example itm` and uncomment this line to run itm example */ // "executable": "./target/thumbv7em-none-eabihf/debug/examples/itm", "device": "STM32F446RET6", diff --git a/examples/stm32f4/.vscode/tasks.json b/examples/stm32f4/.vscode/tasks.json index 3cbed84b4..de7013b12 100644 --- a/examples/stm32f4/.vscode/tasks.json +++ b/examples/stm32f4/.vscode/tasks.json @@ -9,7 +9,7 @@ ], "args": [ "--bin", - "multiprio" + "pwm_input" ], "group": { "kind": "build", diff --git a/examples/stm32f4/src/bin/pwm_input.rs b/examples/stm32f4/src/bin/pwm_input.rs new file mode 100644 index 000000000..49de33d2b --- /dev/null +++ b/examples/stm32f4/src/bin/pwm_input.rs @@ -0,0 +1,52 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::time::khz; +use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; +use embassy_stm32::timer::{self, Channel}; +use embassy_stm32::{bind_interrupts, peripherals}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +/// Connect PB2 and PB10 with a 1k Ohm resistor + +#[embassy_executor::task] +async fn blinky(led: peripherals::PB2) { + let mut led = Output::new(led, 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; + } +} + +bind_interrupts!(struct Irqs { + TIM2 => timer::CaptureCompareInterruptHandler; +}); + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + unwrap!(spawner.spawn(blinky(p.PB2))); + + let ch3 = CapturePin::new_ch3(p.PB10, Pull::None); + let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); + + loop { + info!("wait for risign edge"); + ic.wait_for_rising_edge(Channel::Ch3).await; + + let capture_value = ic.get_capture_value(Channel::Ch3); + info!("new capture! {}", capture_value); + } +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 57185e217..2f5d17069 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.78" +channel = "1.77" components = [ "rust-src", "rustfmt", "llvm-tools" ] targets = [ "thumbv7em-none-eabi", From 7c1e1ee288bc46dbf030749c242d60e222d1fbe5 Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Tue, 28 May 2024 22:30:10 -0400 Subject: [PATCH 12/57] example is working now --- embassy-stm32/src/timer/input_capture.rs | 63 +++++++++++++++++++----- examples/stm32f4/.vscode/launch.json | 2 +- examples/stm32f4/.vscode/tasks.json | 2 +- 3 files changed, 54 insertions(+), 13 deletions(-) diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index a1c1486f9..ab6f10a1d 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -26,6 +26,10 @@ pub enum Ch3 {} /// Channel 4 marker type. pub enum Ch4 {} +fn regs_gp16(ptr: *mut ()) -> crate::pac::timer::TimGp16 { + unsafe { crate::pac::timer::TimGp16::from_ptr(ptr) } +} + /// Capture pin wrapper. /// /// This wraps a pin to make it usable with capture. @@ -76,10 +80,6 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { freq: Hertz, counting_mode: CountingMode, ) -> Self { - Self::new_inner(tim, freq, counting_mode) - } - - fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz, counting_mode: CountingMode) -> Self { let mut this = Self { inner: Timer::new(tim) }; this.inner.set_counting_mode(counting_mode); @@ -148,11 +148,52 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { } fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> InputCaptureFuture { - self.inner.enable_channel(channel, true); - self.inner.set_input_capture_mode(channel, mode); - self.inner.set_input_ti_selection(channel, tisel); - self.inner.clear_input_interrupt(channel); - self.inner.enable_input_interrupt(channel, true); + use stm32_metapac::timer::vals::*; + + let regs = regs_gp16(T::regs()); + let idx = channel.index(); + + // Select the active input: TIMx_CCR1 must be linked to the TI1 input, so write the CC1S + // bits to 01 in the TIMx_CCMR1 register. As soon as CC1S becomes different from 00, + // the channel is configured in input and the TIMx_CCR1 register becomes read-only. + regs.ccmr_input(idx / 2) + .modify(|r| r.set_ccs(idx % 2, CcmrInputCcs::from(tisel))); + + // Program the appropriate input filter duration in relation with the signal connected to the + // timer (by programming the ICxF bits in the TIMx_CCMRx register if the input is one of + // the TIx inputs). Let’s imagine that, when toggling, the input signal is not stable during at + // must 5 internal clock cycles. We must program a filter duration longer than these 5 + // clock cycles. We can validate a transition on TI1 when 8 consecutive samples with the + // new level have been detected (sampled at fDTS frequency). Then write IC1F bits to + // 0011 in the TIMx_CCMR1 register. + regs.ccmr_input(idx / 2) + .modify(|r| r.set_icf(idx % 2, FilterValue::NOFILTER)); + + // Select the edge of the active transition on the TI1 channel by writing the CC1P and + // CC1NP bits to 00 in the TIMx_CCER register (rising edge in this case). + let ccpnp = match mode { + InputCaptureMode::Rising => (false, false), + InputCaptureMode::Falling => (false, true), + InputCaptureMode::BothEdges => (true, true), + }; + regs.ccer().modify(|r| { + r.set_ccp(idx, ccpnp.0); + r.set_ccnp(idx, ccpnp.1); + }); + + // Program the input prescaler. In our example, we wish the capture to be performed at + // each valid transition, so the prescaler is disabled (write IC1PS bits to 00 in the + // TIMx_CCMR1 register). + regs.ccmr_input(idx / 2).modify(|r| r.set_icpsc(idx % 2, 0)); + + // Enable capture from the counter into the capture register by setting the CC1E bit in the + // TIMx_CCER register. + regs.ccer().modify(|r| r.set_cce(idx, true)); + + // If needed, enable the related interrupt request by setting the CC1IE bit in the + // TIMx_DIER register, and/or the DMA request by setting the CC1DE bit in the + // TIMx_DIER register. + regs.dier().modify(|r| r.set_ccie(idx, true)); InputCaptureFuture { channel, @@ -206,7 +247,7 @@ struct InputCaptureFuture { impl Drop for InputCaptureFuture { fn drop(&mut self) { critical_section::with(|_| { - let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }; + let regs = regs_gp16(T::regs()); // disable interrupt enable regs.dier().modify(|w| w.set_ccie(self.channel.index(), false)); @@ -220,7 +261,7 @@ impl Future for InputCaptureFuture { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { T::state().cc_waker[self.channel.index()].register(cx.waker()); - let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }; + let regs = regs_gp16(T::regs()); let dier = regs.dier().read(); if !dier.ccie(self.channel.index()) { diff --git a/examples/stm32f4/.vscode/launch.json b/examples/stm32f4/.vscode/launch.json index a9849e0da..20cd4d2e8 100644 --- a/examples/stm32f4/.vscode/launch.json +++ b/examples/stm32f4/.vscode/launch.json @@ -15,7 +15,7 @@ "cwd": "${workspaceRoot}", "preLaunchTask": "Cargo Build (debug)", "runToEntryPoint": "main", - "executable": "./target/thumbv7em-none-eabihf/debug/pwm_input", + "executable": "./target/thumbv7em-none-eabihf/debug/input_capture", /* Run `cargo build --example itm` and uncomment this line to run itm example */ // "executable": "./target/thumbv7em-none-eabihf/debug/examples/itm", "device": "STM32F446RET6", diff --git a/examples/stm32f4/.vscode/tasks.json b/examples/stm32f4/.vscode/tasks.json index de7013b12..e153722da 100644 --- a/examples/stm32f4/.vscode/tasks.json +++ b/examples/stm32f4/.vscode/tasks.json @@ -9,7 +9,7 @@ ], "args": [ "--bin", - "pwm_input" + "input_capture" ], "group": { "kind": "build", From a52841041de7cbc6d315b3616768b531716056a0 Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Tue, 28 May 2024 22:38:08 -0400 Subject: [PATCH 13/57] use timer LL --- embassy-stm32/src/timer/input_capture.rs | 52 ++++-------------------- 1 file changed, 7 insertions(+), 45 deletions(-) diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index ab6f10a1d..5dadc9131 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -148,52 +148,14 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { } fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> InputCaptureFuture { - use stm32_metapac::timer::vals::*; + use stm32_metapac::timer::vals::FilterValue; - let regs = regs_gp16(T::regs()); - let idx = channel.index(); - - // Select the active input: TIMx_CCR1 must be linked to the TI1 input, so write the CC1S - // bits to 01 in the TIMx_CCMR1 register. As soon as CC1S becomes different from 00, - // the channel is configured in input and the TIMx_CCR1 register becomes read-only. - regs.ccmr_input(idx / 2) - .modify(|r| r.set_ccs(idx % 2, CcmrInputCcs::from(tisel))); - - // Program the appropriate input filter duration in relation with the signal connected to the - // timer (by programming the ICxF bits in the TIMx_CCMRx register if the input is one of - // the TIx inputs). Let’s imagine that, when toggling, the input signal is not stable during at - // must 5 internal clock cycles. We must program a filter duration longer than these 5 - // clock cycles. We can validate a transition on TI1 when 8 consecutive samples with the - // new level have been detected (sampled at fDTS frequency). Then write IC1F bits to - // 0011 in the TIMx_CCMR1 register. - regs.ccmr_input(idx / 2) - .modify(|r| r.set_icf(idx % 2, FilterValue::NOFILTER)); - - // Select the edge of the active transition on the TI1 channel by writing the CC1P and - // CC1NP bits to 00 in the TIMx_CCER register (rising edge in this case). - let ccpnp = match mode { - InputCaptureMode::Rising => (false, false), - InputCaptureMode::Falling => (false, true), - InputCaptureMode::BothEdges => (true, true), - }; - regs.ccer().modify(|r| { - r.set_ccp(idx, ccpnp.0); - r.set_ccnp(idx, ccpnp.1); - }); - - // Program the input prescaler. In our example, we wish the capture to be performed at - // each valid transition, so the prescaler is disabled (write IC1PS bits to 00 in the - // TIMx_CCMR1 register). - regs.ccmr_input(idx / 2).modify(|r| r.set_icpsc(idx % 2, 0)); - - // Enable capture from the counter into the capture register by setting the CC1E bit in the - // TIMx_CCER register. - regs.ccer().modify(|r| r.set_cce(idx, true)); - - // If needed, enable the related interrupt request by setting the CC1IE bit in the - // TIMx_DIER register, and/or the DMA request by setting the CC1DE bit in the - // TIMx_DIER register. - regs.dier().modify(|r| r.set_ccie(idx, true)); + self.inner.set_input_ti_selection(channel, tisel); + self.inner.set_input_capture_filter(channel, FilterValue::NOFILTER); + self.inner.set_input_capture_mode(channel, mode); + self.inner.set_input_capture_prescaler(channel, 0); + self.inner.enable_channel(channel, true); + self.inner.enable_input_interrupt(channel, true); InputCaptureFuture { channel, From f1d5f4ca21629036f510e52aeee617ffb70db51b Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Tue, 28 May 2024 22:43:23 -0400 Subject: [PATCH 14/57] undo minor changes --- embassy-stm32/src/timer/input_capture.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index 5dadc9131..000938a70 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -26,10 +26,6 @@ pub enum Ch3 {} /// Channel 4 marker type. pub enum Ch4 {} -fn regs_gp16(ptr: *mut ()) -> crate::pac::timer::TimGp16 { - unsafe { crate::pac::timer::TimGp16::from_ptr(ptr) } -} - /// Capture pin wrapper. /// /// This wraps a pin to make it usable with capture. @@ -80,6 +76,10 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { freq: Hertz, counting_mode: CountingMode, ) -> Self { + Self::new_inner(tim, freq, counting_mode) + } + + fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz, counting_mode: CountingMode) -> Self { let mut this = Self { inner: Timer::new(tim) }; this.inner.set_counting_mode(counting_mode); @@ -150,6 +150,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> InputCaptureFuture { use stm32_metapac::timer::vals::FilterValue; + // Configuration steps from ST RM0390 chapter 17.3.5 Input Capture Mode self.inner.set_input_ti_selection(channel, tisel); self.inner.set_input_capture_filter(channel, FilterValue::NOFILTER); self.inner.set_input_capture_mode(channel, mode); @@ -200,6 +201,11 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { } } +/// Convert pointer to TIM instance to TimGp16 object +fn regs_gp16(ptr: *mut ()) -> crate::pac::timer::TimGp16 { + unsafe { crate::pac::timer::TimGp16::from_ptr(ptr) } +} + #[must_use = "futures do nothing unless you `.await` or poll them"] struct InputCaptureFuture { channel: Channel, From a6c419d096bf2a4d14243fe90a7e2c1881b33fdf Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Tue, 28 May 2024 23:12:08 -0400 Subject: [PATCH 15/57] add f103 example for input_capture --- examples/stm32f1/.vscode/launch.json | 33 +++++++++++++++ examples/stm32f1/.vscode/tasks.json | 21 ++++++++++ examples/stm32f1/openocd.cfg | 5 +++ examples/stm32f1/openocd.gdb | 40 +++++++++++++++++++ .../src/bin/input_capture.rs} | 8 ++-- 5 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 examples/stm32f1/.vscode/launch.json create mode 100644 examples/stm32f1/.vscode/tasks.json create mode 100644 examples/stm32f1/openocd.cfg create mode 100644 examples/stm32f1/openocd.gdb rename examples/{stm32f4/src/bin/pwm_input.rs => stm32f1/src/bin/input_capture.rs} (86%) diff --git a/examples/stm32f1/.vscode/launch.json b/examples/stm32f1/.vscode/launch.json new file mode 100644 index 000000000..7d1504a39 --- /dev/null +++ b/examples/stm32f1/.vscode/launch.json @@ -0,0 +1,33 @@ +{ + /* + * Requires the Rust Language Server (rust-analyzer) and Cortex-Debug extensions + * https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer + * https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug + */ + "version": "0.2.0", + "configurations": [ + { + /* Configuration for the STM32F446 Discovery board */ + "type": "cortex-debug", + "request": "launch", + "name": "Debug (OpenOCD)", + "servertype": "openocd", + "cwd": "${workspaceRoot}", + "preLaunchTask": "Cargo Build (debug)", + "runToEntryPoint": "main", + "executable": "./target/thumbv7m-none-eabi/debug/input_capture", + /* Run `cargo build --example itm` and uncomment this line to run itm example */ + // "executable": "./target/thumbv7em-none-eabihf/debug/examples/itm", + "device": "STM32F103TB", + "configFiles": [ + "interface/stlink.cfg", + "target/stm32f1x.cfg" + ], + "postLaunchCommands": [ + "monitor arm semihosting enable" + ], + "postRestartCommands": [], + "postResetCommands": [], + } + ] +} \ No newline at end of file diff --git a/examples/stm32f1/.vscode/tasks.json b/examples/stm32f1/.vscode/tasks.json new file mode 100644 index 000000000..e153722da --- /dev/null +++ b/examples/stm32f1/.vscode/tasks.json @@ -0,0 +1,21 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "cargo", + "command": "build", + "problemMatcher": [ + "$rustc" + ], + "args": [ + "--bin", + "input_capture" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "label": "Cargo Build (debug)", + } + ] +} \ No newline at end of file diff --git a/examples/stm32f1/openocd.cfg b/examples/stm32f1/openocd.cfg new file mode 100644 index 000000000..0325cd651 --- /dev/null +++ b/examples/stm32f1/openocd.cfg @@ -0,0 +1,5 @@ +# Sample OpenOCD configuration for the STM32F3DISCOVERY development board + +source [find interface/stlink.cfg] + +source [find target/stm32f1x.cfg] diff --git a/examples/stm32f1/openocd.gdb b/examples/stm32f1/openocd.gdb new file mode 100644 index 000000000..7795319fb --- /dev/null +++ b/examples/stm32f1/openocd.gdb @@ -0,0 +1,40 @@ +target extended-remote :3333 + +# print demangled symbols +set print asm-demangle on + +# set backtrace limit to not have infinite backtrace loops +set backtrace limit 32 + +# detect unhandled exceptions, hard faults and panics +break DefaultHandler +break HardFault +break rust_begin_unwind +# # run the next few lines so the panic message is printed immediately +# # the number needs to be adjusted for your panic handler +# commands $bpnum +# next 4 +# end + +# *try* to stop at the user entry point (it might be gone due to inlining) +break main + +monitor arm semihosting enable + +# # send captured ITM to the file itm.fifo +# # (the microcontroller SWO pin must be connected to the programmer SWO pin) +# # 8000000 must match the core clock frequency +# monitor tpiu config internal itm.txt uart off 8000000 + +# # OR: make the microcontroller SWO pin output compatible with UART (8N1) +# # 8000000 must match the core clock frequency +# # 2000000 is the frequency of the SWO pin +# monitor tpiu config external uart off 8000000 2000000 + +# # enable ITM port 0 +# monitor itm port 0 on + +load + +# start the process but immediately halt the processor +stepi diff --git a/examples/stm32f4/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/input_capture.rs similarity index 86% rename from examples/stm32f4/src/bin/pwm_input.rs rename to examples/stm32f1/src/bin/input_capture.rs index 49de33d2b..417830231 100644 --- a/examples/stm32f4/src/bin/pwm_input.rs +++ b/examples/stm32f1/src/bin/input_capture.rs @@ -11,10 +11,10 @@ use embassy_stm32::{bind_interrupts, peripherals}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; -/// Connect PB2 and PB10 with a 1k Ohm resistor +/// Connect PA2 and PC13 with a 1k Ohm resistor #[embassy_executor::task] -async fn blinky(led: peripherals::PB2) { +async fn blinky(led: peripherals::PC13) { let mut led = Output::new(led, Level::High, Speed::Low); loop { @@ -37,9 +37,9 @@ async fn main(spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - unwrap!(spawner.spawn(blinky(p.PB2))); + unwrap!(spawner.spawn(blinky(p.PC13))); - let ch3 = CapturePin::new_ch3(p.PB10, Pull::None); + let ch3 = CapturePin::new_ch3(p.PA2, Pull::None); let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); loop { From 521332bdd1d682b1d43ab7896b54e280c17b9771 Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Wed, 29 May 2024 00:28:26 -0400 Subject: [PATCH 16/57] pwm_input is working on F446 --- embassy-stm32/src/timer/input_capture.rs | 33 ++---- embassy-stm32/src/timer/low_level.rs | 16 +++ embassy-stm32/src/timer/mod.rs | 1 + embassy-stm32/src/timer/pwm_input.rs | 134 +++++++++++++++++++++++ examples/stm32f1/.vscode/launch.json | 2 +- examples/stm32f1/.vscode/tasks.json | 2 +- examples/stm32f1/src/bin/pwm_input.rs | 50 +++++++++ examples/stm32f4/.vscode/launch.json | 2 +- examples/stm32f4/.vscode/tasks.json | 2 +- examples/stm32f4/src/bin/pwm_input.rs | 52 +++++++++ 10 files changed, 265 insertions(+), 29 deletions(-) create mode 100644 embassy-stm32/src/timer/pwm_input.rs create mode 100644 examples/stm32f1/src/bin/pwm_input.rs create mode 100644 examples/stm32f4/src/bin/pwm_input.rs diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index 000938a70..b3434ae63 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -80,18 +80,18 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { } fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz, counting_mode: CountingMode) -> Self { - let mut this = Self { inner: Timer::new(tim) }; + let mut inner = Timer::new(tim); - this.inner.set_counting_mode(counting_mode); - this.set_tick_freq(freq); - this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details - this.inner.start(); + inner.set_counting_mode(counting_mode); + inner.set_tick_freq(freq); + inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details + inner.start(); // enable NVIC interrupt T::CaptureCompareInterrupt::unpend(); unsafe { T::CaptureCompareInterrupt::enable() }; - this + Self { inner } } /// Enable the given channel. @@ -109,24 +109,6 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { self.inner.get_channel_enable_state(channel) } - /// Set tick frequency. - /// - /// Note: when you call this, the max period value changes - pub fn set_tick_freq(&mut self, freq: Hertz) { - let f = freq; - assert!(f.0 > 0); - let timer_f = self.inner.get_clock_frequency(); - - let pclk_ticks_per_timer_period = timer_f / f; - let psc: u16 = unwrap!((pclk_ticks_per_timer_period - 1).try_into()); - - let regs = self.inner.regs_core(); - regs.psc().write_value(psc); - - // Generate an Update Request - regs.egr().write(|r| r.set_ug(true)); - } - /// Set the input capture mode for a given channel. pub fn set_input_capture_mode(&mut self, channel: Channel, mode: InputCaptureMode) { self.inner.set_input_capture_mode(channel, mode); @@ -150,7 +132,8 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> InputCaptureFuture { use stm32_metapac::timer::vals::FilterValue; - // Configuration steps from ST RM0390 chapter 17.3.5 Input Capture Mode + // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.5 + // or ST RM0008 (STM32F103) chapter 15.3.5 Input capture mode self.inner.set_input_ti_selection(channel, tisel); self.inner.set_input_capture_filter(channel, FilterValue::NOFILTER); self.inner.set_input_capture_mode(channel, mode); diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 7f533b75c..141e96894 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -273,6 +273,22 @@ impl<'d, T: CoreInstance> Timer<'d, T> { } } + /// Set tick frequency. + pub fn set_tick_freq(&mut self, freq: Hertz) { + let f = freq; + assert!(f.0 > 0); + let timer_f = self.get_clock_frequency(); + + let pclk_ticks_per_timer_period = timer_f / f; + let psc: u16 = unwrap!((pclk_ticks_per_timer_period - 1).try_into()); + + let regs = self.regs_core(); + regs.psc().write_value(psc); + + // Generate an Update Request + regs.egr().write(|r| r.set_ug(true)); + } + /// Clear update interrupt. /// /// Returns whether the update interrupt flag was set. diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 314b6006b..25782ee13 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -8,6 +8,7 @@ use embassy_sync::waitqueue::AtomicWaker; pub mod complementary_pwm; pub mod input_capture; pub mod low_level; +pub mod pwm_input; pub mod qei; pub mod simple_pwm; diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs new file mode 100644 index 000000000..d34ba086f --- /dev/null +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -0,0 +1,134 @@ +//! Input capture driver. + +use embassy_hal_internal::into_ref; + +use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, Timer}; +use super::{Channel, Channel1Pin, Channel2Pin, GeneralInstance4Channel}; +use crate::gpio::{AFType, Pull}; +use crate::time::Hertz; +use crate::Peripheral; + +/// Input capture driver. +pub struct PwmInput<'d, T: GeneralInstance4Channel> { + channel: Channel, + inner: Timer<'d, T>, +} + +/// Convert pointer to TIM instance to TimGp16 object +fn regs_gp16(ptr: *mut ()) -> crate::pac::timer::TimGp16 { + unsafe { crate::pac::timer::TimGp16::from_ptr(ptr) } +} + +impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { + /// Create a new input capture driver. + pub fn new( + tim: impl Peripheral

+ 'd, + pin: impl Peripheral

> + 'd, + pull_type: Pull, + freq: Hertz, + ) -> Self { + into_ref!(pin); + critical_section::with(|_| { + pin.set_as_af_pull(pin.af_num(), AFType::Input, pull_type); + #[cfg(gpio_v2)] + pin.set_speed(crate::gpio::Speed::VeryHigh); + }); + + Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2) + } + + /// Create a new input capture driver. + pub fn new_alt( + tim: impl Peripheral

+ 'd, + pin: impl Peripheral

> + 'd, + pull_type: Pull, + freq: Hertz, + ) -> Self { + into_ref!(pin); + critical_section::with(|_| { + pin.set_as_af_pull(pin.af_num(), AFType::Input, pull_type); + #[cfg(gpio_v2)] + pin.set_speed(crate::gpio::Speed::VeryHigh); + }); + + Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1) + } + + fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz, ch1: Channel, ch2: Channel) -> Self { + use stm32_metapac::timer::vals::{Sms, Ts}; + + let mut inner = Timer::new(tim); + + inner.set_counting_mode(CountingMode::EdgeAlignedUp); + inner.set_tick_freq(freq); + inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details + inner.start(); + + // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6 + // or ST RM0008 (STM32F103) chapter 15.3.6 Input capture mode + inner.set_input_ti_selection(ch1, InputTISelection::Normal); + inner.set_input_capture_mode(ch1, InputCaptureMode::Rising); + + inner.set_input_ti_selection(ch2, InputTISelection::Alternate); + inner.set_input_capture_mode(ch2, InputCaptureMode::Falling); + + let regs = regs_gp16(T::regs()); + regs.smcr().modify(|r| { + // Select the valid trigger input: write the TS bits to 101 in the TIMx_SMCR register + // (TI1FP1 selected). + r.set_ts(match ch1 { + Channel::Ch1 => Ts::TI1FP1, + Channel::Ch2 => Ts::TI2FP2, + _ => panic!("Invalid channel for PWM input"), + }); + + // Configure the slave mode controller in reset mode: write the SMS bits to 100 in the + // TIMx_SMCR register. + r.set_sms(Sms::RESET_MODE); + }); + + // Must call the `enable` function after + + Self { channel: ch1, inner } + } + + /// Enable the given channel. + pub fn enable(&mut self) { + self.inner.enable_channel(Channel::Ch1, true); + self.inner.enable_channel(Channel::Ch2, true); + } + + /// Disable the given channel. + pub fn disable(&mut self) { + self.inner.enable_channel(Channel::Ch1, false); + self.inner.enable_channel(Channel::Ch2, false); + } + + /// Check whether given channel is enabled + pub fn is_enabled(&self) -> bool { + self.inner.get_channel_enable_state(Channel::Ch1) + } + + /// Get the period tick count + pub fn get_period_ticks(&self) -> u32 { + self.inner.get_capture_value(self.channel) + } + + /// Get the duty tick count + pub fn get_duty_ticks(&self) -> u32 { + self.inner.get_capture_value(match self.channel { + Channel::Ch1 => Channel::Ch2, + Channel::Ch2 => Channel::Ch1, + _ => panic!("Invalid channel for PWM input"), + }) + } + + /// Get the duty cycle in 100% + pub fn get_duty_cycle(&self) -> f32 { + let period = self.get_period_ticks(); + if period == 0 { + return 0.; + } + 100. * (self.get_duty_ticks() as f32) / (period as f32) + } +} diff --git a/examples/stm32f1/.vscode/launch.json b/examples/stm32f1/.vscode/launch.json index 7d1504a39..998508867 100644 --- a/examples/stm32f1/.vscode/launch.json +++ b/examples/stm32f1/.vscode/launch.json @@ -15,7 +15,7 @@ "cwd": "${workspaceRoot}", "preLaunchTask": "Cargo Build (debug)", "runToEntryPoint": "main", - "executable": "./target/thumbv7m-none-eabi/debug/input_capture", + "executable": "./target/thumbv7m-none-eabi/debug/pwm_input", /* Run `cargo build --example itm` and uncomment this line to run itm example */ // "executable": "./target/thumbv7em-none-eabihf/debug/examples/itm", "device": "STM32F103TB", diff --git a/examples/stm32f1/.vscode/tasks.json b/examples/stm32f1/.vscode/tasks.json index e153722da..de7013b12 100644 --- a/examples/stm32f1/.vscode/tasks.json +++ b/examples/stm32f1/.vscode/tasks.json @@ -9,7 +9,7 @@ ], "args": [ "--bin", - "input_capture" + "pwm_input" ], "group": { "kind": "build", diff --git a/examples/stm32f1/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/pwm_input.rs new file mode 100644 index 000000000..14978f817 --- /dev/null +++ b/examples/stm32f1/src/bin/pwm_input.rs @@ -0,0 +1,50 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::time::khz; +use embassy_stm32::timer::{self, pwm_input::PwmInput}; +use embassy_stm32::{bind_interrupts, peripherals}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +/// Connect PB2 and PB10 with a 1k Ohm resistor + +#[embassy_executor::task] +async fn blinky(led: peripherals::PC13) { + let mut led = Output::new(led, 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; + } +} + +bind_interrupts!(struct Irqs { + TIM2 => timer::CaptureCompareInterruptHandler; +}); + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + unwrap!(spawner.spawn(blinky(p.PC13))); + + let pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, khz(1000)); + + loop { + Timer::after_millis(500).await; + let _per = pwm_input.get_period_ticks(); + let _dc = pwm_input.get_duty_ticks(); + let _pc = pwm_input.get_duty_cycle(); + asm::nop(); + } +} diff --git a/examples/stm32f4/.vscode/launch.json b/examples/stm32f4/.vscode/launch.json index 20cd4d2e8..a9849e0da 100644 --- a/examples/stm32f4/.vscode/launch.json +++ b/examples/stm32f4/.vscode/launch.json @@ -15,7 +15,7 @@ "cwd": "${workspaceRoot}", "preLaunchTask": "Cargo Build (debug)", "runToEntryPoint": "main", - "executable": "./target/thumbv7em-none-eabihf/debug/input_capture", + "executable": "./target/thumbv7em-none-eabihf/debug/pwm_input", /* Run `cargo build --example itm` and uncomment this line to run itm example */ // "executable": "./target/thumbv7em-none-eabihf/debug/examples/itm", "device": "STM32F446RET6", diff --git a/examples/stm32f4/.vscode/tasks.json b/examples/stm32f4/.vscode/tasks.json index e153722da..de7013b12 100644 --- a/examples/stm32f4/.vscode/tasks.json +++ b/examples/stm32f4/.vscode/tasks.json @@ -9,7 +9,7 @@ ], "args": [ "--bin", - "input_capture" + "pwm_input" ], "group": { "kind": "build", diff --git a/examples/stm32f4/src/bin/pwm_input.rs b/examples/stm32f4/src/bin/pwm_input.rs new file mode 100644 index 000000000..e57e58c22 --- /dev/null +++ b/examples/stm32f4/src/bin/pwm_input.rs @@ -0,0 +1,52 @@ +#![no_std] +#![no_main] + +use cortex_m::asm; +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::time::khz; +use embassy_stm32::timer::{self, pwm_input::PwmInput}; +use embassy_stm32::{bind_interrupts, peripherals}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +/// Connect PB2 and PB10 with a 1k Ohm resistor + +#[embassy_executor::task] +async fn blinky(led: peripherals::PB2) { + let mut led = Output::new(led, 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; + } +} + +bind_interrupts!(struct Irqs { + TIM2 => timer::CaptureCompareInterruptHandler; +}); + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + unwrap!(spawner.spawn(blinky(p.PB2))); + + let mut pwm_input = PwmInput::new(p.TIM3, p.PA6, Pull::None, khz(10)); + pwm_input.enable(); + + loop { + Timer::after_millis(500).await; + let _per = pwm_input.get_period_ticks(); + let _dc = pwm_input.get_duty_ticks(); + let _pc = pwm_input.get_duty_cycle(); + asm::nop(); + } +} From 50039b17a78bbefeca1a3dd6b689ea478079ea6b Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Wed, 29 May 2024 00:33:35 -0400 Subject: [PATCH 17/57] fix F103 example --- examples/stm32f1/.vscode/launch.json | 2 +- examples/stm32f1/src/bin/pwm_input.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/stm32f1/.vscode/launch.json b/examples/stm32f1/.vscode/launch.json index 998508867..71f203614 100644 --- a/examples/stm32f1/.vscode/launch.json +++ b/examples/stm32f1/.vscode/launch.json @@ -18,7 +18,7 @@ "executable": "./target/thumbv7m-none-eabi/debug/pwm_input", /* Run `cargo build --example itm` and uncomment this line to run itm example */ // "executable": "./target/thumbv7em-none-eabihf/debug/examples/itm", - "device": "STM32F103TB", + "device": "STM32F103T8", "configFiles": [ "interface/stlink.cfg", "target/stm32f1x.cfg" diff --git a/examples/stm32f1/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/pwm_input.rs index 14978f817..718bf0fcf 100644 --- a/examples/stm32f1/src/bin/pwm_input.rs +++ b/examples/stm32f1/src/bin/pwm_input.rs @@ -1,6 +1,7 @@ #![no_std] #![no_main] +use cortex_m::asm; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; @@ -38,7 +39,8 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(blinky(p.PC13))); - let pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, khz(1000)); + let mut pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, khz(10)); + pwm_input.enable(); loop { Timer::after_millis(500).await; From 69badfb845c0fb43b4e4c1c2e9f74a535fcd317a Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Wed, 29 May 2024 00:37:50 -0400 Subject: [PATCH 18/57] remove dev files --- examples/stm32f1/.vscode/launch.json | 33 ----------------------- examples/stm32f1/.vscode/tasks.json | 21 --------------- examples/stm32f1/openocd.cfg | 5 ---- examples/stm32f1/openocd.gdb | 40 ---------------------------- examples/stm32f4/.cargo/config.toml | 6 ++--- examples/stm32f4/.vscode/launch.json | 33 ----------------------- examples/stm32f4/.vscode/tasks.json | 21 --------------- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f4/openocd.cfg | 5 ---- examples/stm32f4/openocd.gdb | 40 ---------------------------- rust-toolchain.toml | 2 +- 11 files changed, 4 insertions(+), 204 deletions(-) delete mode 100644 examples/stm32f1/.vscode/launch.json delete mode 100644 examples/stm32f1/.vscode/tasks.json delete mode 100644 examples/stm32f1/openocd.cfg delete mode 100644 examples/stm32f1/openocd.gdb delete mode 100644 examples/stm32f4/.vscode/launch.json delete mode 100644 examples/stm32f4/.vscode/tasks.json delete mode 100644 examples/stm32f4/openocd.cfg delete mode 100644 examples/stm32f4/openocd.gdb diff --git a/examples/stm32f1/.vscode/launch.json b/examples/stm32f1/.vscode/launch.json deleted file mode 100644 index 71f203614..000000000 --- a/examples/stm32f1/.vscode/launch.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - /* - * Requires the Rust Language Server (rust-analyzer) and Cortex-Debug extensions - * https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer - * https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug - */ - "version": "0.2.0", - "configurations": [ - { - /* Configuration for the STM32F446 Discovery board */ - "type": "cortex-debug", - "request": "launch", - "name": "Debug (OpenOCD)", - "servertype": "openocd", - "cwd": "${workspaceRoot}", - "preLaunchTask": "Cargo Build (debug)", - "runToEntryPoint": "main", - "executable": "./target/thumbv7m-none-eabi/debug/pwm_input", - /* Run `cargo build --example itm` and uncomment this line to run itm example */ - // "executable": "./target/thumbv7em-none-eabihf/debug/examples/itm", - "device": "STM32F103T8", - "configFiles": [ - "interface/stlink.cfg", - "target/stm32f1x.cfg" - ], - "postLaunchCommands": [ - "monitor arm semihosting enable" - ], - "postRestartCommands": [], - "postResetCommands": [], - } - ] -} \ No newline at end of file diff --git a/examples/stm32f1/.vscode/tasks.json b/examples/stm32f1/.vscode/tasks.json deleted file mode 100644 index de7013b12..000000000 --- a/examples/stm32f1/.vscode/tasks.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "type": "cargo", - "command": "build", - "problemMatcher": [ - "$rustc" - ], - "args": [ - "--bin", - "pwm_input" - ], - "group": { - "kind": "build", - "isDefault": true - }, - "label": "Cargo Build (debug)", - } - ] -} \ No newline at end of file diff --git a/examples/stm32f1/openocd.cfg b/examples/stm32f1/openocd.cfg deleted file mode 100644 index 0325cd651..000000000 --- a/examples/stm32f1/openocd.cfg +++ /dev/null @@ -1,5 +0,0 @@ -# Sample OpenOCD configuration for the STM32F3DISCOVERY development board - -source [find interface/stlink.cfg] - -source [find target/stm32f1x.cfg] diff --git a/examples/stm32f1/openocd.gdb b/examples/stm32f1/openocd.gdb deleted file mode 100644 index 7795319fb..000000000 --- a/examples/stm32f1/openocd.gdb +++ /dev/null @@ -1,40 +0,0 @@ -target extended-remote :3333 - -# print demangled symbols -set print asm-demangle on - -# set backtrace limit to not have infinite backtrace loops -set backtrace limit 32 - -# detect unhandled exceptions, hard faults and panics -break DefaultHandler -break HardFault -break rust_begin_unwind -# # run the next few lines so the panic message is printed immediately -# # the number needs to be adjusted for your panic handler -# commands $bpnum -# next 4 -# end - -# *try* to stop at the user entry point (it might be gone due to inlining) -break main - -monitor arm semihosting enable - -# # send captured ITM to the file itm.fifo -# # (the microcontroller SWO pin must be connected to the programmer SWO pin) -# # 8000000 must match the core clock frequency -# monitor tpiu config internal itm.txt uart off 8000000 - -# # OR: make the microcontroller SWO pin output compatible with UART (8N1) -# # 8000000 must match the core clock frequency -# # 2000000 is the frequency of the SWO pin -# monitor tpiu config external uart off 8000000 2000000 - -# # enable ITM port 0 -# monitor itm port 0 on - -load - -# start the process but immediately halt the processor -stepi diff --git a/examples/stm32f4/.cargo/config.toml b/examples/stm32f4/.cargo/config.toml index f5a4af51a..16efa8e6f 100644 --- a/examples/stm32f4/.cargo/config.toml +++ b/examples/stm32f4/.cargo/config.toml @@ -1,11 +1,9 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # replace STM32F429ZITx with your chip as listed in `probe-rs chip list` -# runner = "probe-rs run --chip STM32F429ZITx" -runner = "arm-none-eabi-gdb -q -x openocd.gdb" +runner = "probe-rs run --chip STM32F429ZITx" [build] -# target = "thumbv7em-none-eabi" -target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) +target = "thumbv7em-none-eabi" [env] DEFMT_LOG = "trace" diff --git a/examples/stm32f4/.vscode/launch.json b/examples/stm32f4/.vscode/launch.json deleted file mode 100644 index a9849e0da..000000000 --- a/examples/stm32f4/.vscode/launch.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - /* - * Requires the Rust Language Server (rust-analyzer) and Cortex-Debug extensions - * https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer - * https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug - */ - "version": "0.2.0", - "configurations": [ - { - /* Configuration for the STM32F446 Discovery board */ - "type": "cortex-debug", - "request": "launch", - "name": "Debug (OpenOCD)", - "servertype": "openocd", - "cwd": "${workspaceRoot}", - "preLaunchTask": "Cargo Build (debug)", - "runToEntryPoint": "main", - "executable": "./target/thumbv7em-none-eabihf/debug/pwm_input", - /* Run `cargo build --example itm` and uncomment this line to run itm example */ - // "executable": "./target/thumbv7em-none-eabihf/debug/examples/itm", - "device": "STM32F446RET6", - "configFiles": [ - "interface/stlink.cfg", - "target/stm32f4x.cfg" - ], - "postLaunchCommands": [ - "monitor arm semihosting enable" - ], - "postRestartCommands": [], - "postResetCommands": [], - } - ] -} \ No newline at end of file diff --git a/examples/stm32f4/.vscode/tasks.json b/examples/stm32f4/.vscode/tasks.json deleted file mode 100644 index de7013b12..000000000 --- a/examples/stm32f4/.vscode/tasks.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "type": "cargo", - "command": "build", - "problemMatcher": [ - "$rustc" - ], - "args": [ - "--bin", - "pwm_input" - ], - "group": { - "kind": "build", - "isDefault": true - }, - "label": "Cargo Build (debug)", - } - ] -} \ No newline at end of file diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index ee7594c95..1eb1ae6db 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32f429zi to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f446re", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32f4/openocd.cfg b/examples/stm32f4/openocd.cfg deleted file mode 100644 index e41d52b1a..000000000 --- a/examples/stm32f4/openocd.cfg +++ /dev/null @@ -1,5 +0,0 @@ -# Sample OpenOCD configuration for the STM32F3DISCOVERY development board - -source [find interface/stlink.cfg] - -source [find target/stm32f4x.cfg] diff --git a/examples/stm32f4/openocd.gdb b/examples/stm32f4/openocd.gdb deleted file mode 100644 index 7795319fb..000000000 --- a/examples/stm32f4/openocd.gdb +++ /dev/null @@ -1,40 +0,0 @@ -target extended-remote :3333 - -# print demangled symbols -set print asm-demangle on - -# set backtrace limit to not have infinite backtrace loops -set backtrace limit 32 - -# detect unhandled exceptions, hard faults and panics -break DefaultHandler -break HardFault -break rust_begin_unwind -# # run the next few lines so the panic message is printed immediately -# # the number needs to be adjusted for your panic handler -# commands $bpnum -# next 4 -# end - -# *try* to stop at the user entry point (it might be gone due to inlining) -break main - -monitor arm semihosting enable - -# # send captured ITM to the file itm.fifo -# # (the microcontroller SWO pin must be connected to the programmer SWO pin) -# # 8000000 must match the core clock frequency -# monitor tpiu config internal itm.txt uart off 8000000 - -# # OR: make the microcontroller SWO pin output compatible with UART (8N1) -# # 8000000 must match the core clock frequency -# # 2000000 is the frequency of the SWO pin -# monitor tpiu config external uart off 8000000 2000000 - -# # enable ITM port 0 -# monitor itm port 0 on - -load - -# start the process but immediately halt the processor -stepi diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 2f5d17069..57185e217 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.77" +channel = "1.78" components = [ "rust-src", "rustfmt", "llvm-tools" ] targets = [ "thumbv7em-none-eabi", From 61f1f80e90c3fd8ec505936f3bab0a50d2ba9b79 Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Wed, 29 May 2024 00:52:55 -0400 Subject: [PATCH 19/57] fix fmt --- examples/stm32f1/src/bin/pwm_input.rs | 3 ++- examples/stm32f4/src/bin/pwm_input.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/stm32f1/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/pwm_input.rs index 718bf0fcf..9ca44d048 100644 --- a/examples/stm32f1/src/bin/pwm_input.rs +++ b/examples/stm32f1/src/bin/pwm_input.rs @@ -6,7 +6,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; use embassy_stm32::time::khz; -use embassy_stm32::timer::{self, pwm_input::PwmInput}; +use embassy_stm32::timer; +use embassy_stm32::timer::pwm_input::PwmInput; use embassy_stm32::{bind_interrupts, peripherals}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f4/src/bin/pwm_input.rs b/examples/stm32f4/src/bin/pwm_input.rs index e57e58c22..98ea50df4 100644 --- a/examples/stm32f4/src/bin/pwm_input.rs +++ b/examples/stm32f4/src/bin/pwm_input.rs @@ -6,7 +6,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; use embassy_stm32::time::khz; -use embassy_stm32::timer::{self, pwm_input::PwmInput}; +use embassy_stm32::timer; +use embassy_stm32::timer::pwm_input::PwmInput; use embassy_stm32::{bind_interrupts, peripherals}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; From 7f4803ddaf8f6eeeec45418f23b092bff95222d2 Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Wed, 29 May 2024 00:55:49 -0400 Subject: [PATCH 20/57] fix fmt again --- examples/stm32f1/src/bin/pwm_input.rs | 3 +-- examples/stm32f4/src/bin/pwm_input.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/stm32f1/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/pwm_input.rs index 9ca44d048..c051d1328 100644 --- a/examples/stm32f1/src/bin/pwm_input.rs +++ b/examples/stm32f1/src/bin/pwm_input.rs @@ -6,9 +6,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; use embassy_stm32::time::khz; -use embassy_stm32::timer; use embassy_stm32::timer::pwm_input::PwmInput; -use embassy_stm32::{bind_interrupts, peripherals}; +use embassy_stm32::{bind_interrupts, peripherals, timer}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f4/src/bin/pwm_input.rs b/examples/stm32f4/src/bin/pwm_input.rs index 98ea50df4..eb1e7cb87 100644 --- a/examples/stm32f4/src/bin/pwm_input.rs +++ b/examples/stm32f4/src/bin/pwm_input.rs @@ -6,9 +6,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; use embassy_stm32::time::khz; -use embassy_stm32::timer; use embassy_stm32::timer::pwm_input::PwmInput; -use embassy_stm32::{bind_interrupts, peripherals}; +use embassy_stm32::{bind_interrupts, peripherals, timer}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; From 35feb1bf230a1af7fbac6dced25ed3cce727543d Mon Sep 17 00:00:00 2001 From: Corey Schuhen Date: Sun, 26 May 2024 19:54:46 +1000 Subject: [PATCH 21/57] Remove generic argument for STM32 FDCAN. --- embassy-stm32/src/can/fdcan.rs | 413 ++++++++++++++++++------------ tests/stm32/src/bin/can.rs | 8 +- tests/stm32/src/bin/can_common.rs | 9 +- tests/stm32/src/bin/fdcan.rs | 4 + 4 files changed, 258 insertions(+), 176 deletions(-) diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index 81ceb06aa..b772a3ca0 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -3,6 +3,7 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::task::Poll; +use embassy_hal_internal::interrupt::InterruptExt; use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::{Channel, DynamicReceiver, DynamicSender}; @@ -40,7 +41,7 @@ pub struct IT0InterruptHandler { // We use IT0 for everything currently impl interrupt::typelevel::Handler for IT0InterruptHandler { unsafe fn on_interrupt() { - let regs = T::regs(); + let regs = T::registers().regs; let ir = regs.ir().read(); @@ -140,22 +141,13 @@ pub enum OperatingMode { //TestMode, } -/// FDCAN Configuration instance instance -/// Create instance of this first -pub struct CanConfigurator<'d, T: Instance> { - config: crate::can::fd::config::FdCanConfig, - /// Reference to internals. - instance: FdcanInstance<'d, T>, - properties: Properties, -} - fn calc_ns_per_timer_tick(mode: crate::can::fd::config::FrameTransmissionConfig) -> u64 { match mode { // Use timestamp from Rx FIFO to adjust timestamp reported to user crate::can::fd::config::FrameTransmissionConfig::ClassicCanOnly => { let freq = T::frequency(); - let prescale: u64 = - ({ T::regs().nbtp().read().nbrp() } + 1) as u64 * ({ T::regs().tscc().read().tcp() } + 1) as u64; + let prescale: u64 = ({ T::registers().regs.nbtp().read().nbrp() } + 1) as u64 + * ({ T::registers().regs.tscc().read().tcp() } + 1) as u64; 1_000_000_000 as u64 / (freq.0 as u64 * prescale) } // For VBR this is too hard because the FDCAN timer switches clock rate you need to configure to use @@ -164,6 +156,18 @@ fn calc_ns_per_timer_tick(mode: crate::can::fd::config::FrameTransm } } +/// FDCAN Configuration instance instance +/// Create instance of this first +pub struct CanConfigurator<'d, T: Instance> { + config: crate::can::fd::config::FdCanConfig, + info: &'static Info, + state: &'static State, + /// Reference to internals. + _instance: FdcanInstance<'d, T>, + properties: Properties, + periph_clock: crate::time::Hertz, +} + impl<'d, T: Instance> CanConfigurator<'d, T> { /// Creates a new Fdcan instance, keeping the peripheral in sleep mode. /// You must call [Fdcan::enable_non_blocking] to use the peripheral. @@ -196,16 +200,18 @@ impl<'d, T: Instance> CanConfigurator<'d, T> { T::IT1Interrupt::unpend(); // Not unsafe T::IT1Interrupt::enable(); } - Self { config, - instance: FdcanInstance(peri), - properties: Properties::new(), + info: T::info(), + state: T::state(), + _instance: FdcanInstance(peri), + properties: Properties::new(T::info()), + periph_clock: T::frequency(), } } /// Get driver properties - pub fn properties(&self) -> &Properties { + pub fn properties(&self) -> &Properties { &self.properties } @@ -221,7 +227,7 @@ impl<'d, T: Instance> CanConfigurator<'d, T> { /// Configures the bit timings calculated from supplied bitrate. pub fn set_bitrate(&mut self, bitrate: u32) { - let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap(); + let bit_timing = util::calc_can_timings(self.periph_clock, bitrate).unwrap(); let nbtr = crate::can::fd::config::NominalBitTiming { sync_jump_width: bit_timing.sync_jump_width, @@ -234,7 +240,7 @@ impl<'d, T: Instance> CanConfigurator<'d, T> { /// Configures the bit timings for VBR data calculated from supplied bitrate. This also sets confit to allow can FD and VBR pub fn set_fd_data_bitrate(&mut self, bitrate: u32, transceiver_delay_compensation: bool) { - let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap(); + let bit_timing = util::calc_can_timings(self.periph_clock, bitrate).unwrap(); // Note, used existing calcluation for normal(non-VBR) bitrate, appears to work for 250k/1M let nbtr = crate::can::fd::config::DataBitTiming { transceiver_delay_compensation, @@ -248,62 +254,68 @@ impl<'d, T: Instance> CanConfigurator<'d, T> { } /// Start in mode. - pub fn start(self, mode: OperatingMode) -> Can<'d, T> { + pub fn start(self, mode: OperatingMode) -> Can<'d> { let ns_per_timer_tick = calc_ns_per_timer_tick::(self.config.frame_transmit); - critical_section::with(|_| unsafe { - T::mut_state().ns_per_timer_tick = ns_per_timer_tick; + critical_section::with(|_| { + let state = self.state as *const State; + unsafe { + let mut_state = state as *mut State; + (*mut_state).ns_per_timer_tick = ns_per_timer_tick; + } }); T::registers().into_mode(self.config, mode); - let ret = Can { + Can { config: self.config, - instance: self.instance, + info: self.info, + state: self.state, + instance: T::info().regs.regs, _mode: mode, - properties: self.properties, - }; - ret + properties: Properties::new(T::info()), + } } /// Start, entering mode. Does same as start(mode) - pub fn into_normal_mode(self) -> Can<'d, T> { + pub fn into_normal_mode(self) -> Can<'d> { self.start(OperatingMode::NormalOperationMode) } /// Start, entering mode. Does same as start(mode) - pub fn into_internal_loopback_mode(self) -> Can<'d, T> { + pub fn into_internal_loopback_mode(self) -> Can<'d> { self.start(OperatingMode::InternalLoopbackMode) } /// Start, entering mode. Does same as start(mode) - pub fn into_external_loopback_mode(self) -> Can<'d, T> { + pub fn into_external_loopback_mode(self) -> Can<'d> { self.start(OperatingMode::ExternalLoopbackMode) } } /// FDCAN Instance -pub struct Can<'d, T: Instance> { +pub struct Can<'d> { config: crate::can::fd::config::FdCanConfig, - /// Reference to internals. - instance: FdcanInstance<'d, T>, + info: &'static Info, + state: &'static State, + instance: &'d crate::pac::can::Fdcan, _mode: OperatingMode, - properties: Properties, + properties: Properties, } -impl<'d, T: Instance> Can<'d, T> { +impl<'d> Can<'d> { /// Get driver properties - pub fn properties(&self) -> &Properties { + pub fn properties(&self) -> &Properties { &self.properties } /// Flush one of the TX mailboxes. pub async fn flush(&self, idx: usize) { poll_fn(|cx| { - T::state().tx_mode.register(cx.waker()); + self.state.tx_mode.register(cx.waker()); if idx > 3 { panic!("Bad mailbox"); } let idx = 1 << idx; - if !T::regs().txbrp().read().trp(idx) { + if !self.info.regs.regs.txbrp().read().trp(idx) { return Poll::Ready(()); } @@ -317,12 +329,12 @@ impl<'d, T: Instance> Can<'d, T> { /// can be replaced, this call asynchronously waits for a frame to be successfully /// transmitted, then tries again. pub async fn write(&mut self, frame: &Frame) -> Option { - T::state().tx_mode.write::(frame).await + self.state.tx_mode.write(self.info, frame).await } /// Returns the next received message frame pub async fn read(&mut self) -> Result { - T::state().rx_mode.read_classic::().await + self.state.rx_mode.read_classic(self.info, self.state).await } /// Queues the message to be sent but exerts backpressure. If a lower-priority @@ -330,58 +342,61 @@ impl<'d, T: Instance> Can<'d, T> { /// can be replaced, this call asynchronously waits for a frame to be successfully /// transmitted, then tries again. pub async fn write_fd(&mut self, frame: &FdFrame) -> Option { - T::state().tx_mode.write_fd::(frame).await + self.state.tx_mode.write_fd(self.info, frame).await } /// Returns the next received message frame pub async fn read_fd(&mut self) -> Result { - T::state().rx_mode.read_fd::().await + self.state.rx_mode.read_fd(self.info, self.state).await } /// Split instance into separate portions: Tx(write), Rx(read), common properties - pub fn split(self) -> (CanTx<'d, T>, CanRx<'d, T>, Properties) { + pub fn split(self) -> (CanTx<'d>, CanRx<'d>, Properties) { ( CanTx { + info: self.info, + state: self.state, config: self.config, _instance: self.instance, _mode: self._mode, }, CanRx { - _instance1: PhantomData::, - _instance2: T::regs(), + info: self.info, + state: self.state, + _instance: self.instance, _mode: self._mode, }, self.properties, ) } - /// Join split rx and tx portions back together - pub fn join(tx: CanTx<'d, T>, rx: CanRx<'d, T>) -> Self { + pub fn join(tx: CanTx<'d>, rx: CanRx<'d>) -> Self { Can { config: tx.config, - //_instance2: T::regs(), + info: tx.info, + state: tx.state, instance: tx._instance, _mode: rx._mode, - properties: Properties::new(), + properties: Properties::new(tx.info), } } /// Return a buffered instance of driver without CAN FD support. User must supply Buffers pub fn buffered( - &self, + self, tx_buf: &'static mut TxBuf, rxb: &'static mut RxBuf, - ) -> BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> { - BufferedCan::new(PhantomData::, T::regs(), self._mode, tx_buf, rxb) + ) -> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { + BufferedCan::new(self.info, self.state, self.info.regs.regs, self._mode, tx_buf, rxb) } /// Return a buffered instance of driver with CAN FD support. User must supply Buffers pub fn buffered_fd( - &self, + self, tx_buf: &'static mut TxFdBuf, rxb: &'static mut RxFdBuf, - ) -> BufferedCanFd<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> { - BufferedCanFd::new(PhantomData::, T::regs(), self._mode, tx_buf, rxb) + ) -> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { + BufferedCanFd::new(self.info, self.state, self.info.regs.regs, self._mode, tx_buf, rxb) } } @@ -392,52 +407,57 @@ pub type RxBuf = Channel = Channel; /// Buffered FDCAN Instance -pub struct BufferedCan<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { - _instance1: PhantomData, - _instance2: &'d crate::pac::can::Fdcan, +pub struct BufferedCan<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { + info: &'static Info, + state: &'static State, + _instance: &'d crate::pac::can::Fdcan, _mode: OperatingMode, tx_buf: &'static TxBuf, rx_buf: &'static RxBuf, - properties: Properties, + properties: Properties, } -impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> - BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> -{ +impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { fn new( - _instance1: PhantomData, - _instance2: &'d crate::pac::can::Fdcan, + info: &'static Info, + state: &'static State, + _instance: &'d crate::pac::can::Fdcan, _mode: OperatingMode, tx_buf: &'static TxBuf, rx_buf: &'static RxBuf, ) -> Self { BufferedCan { - _instance1, - _instance2, + info, + state, + _instance, _mode, tx_buf, rx_buf, - properties: Properties::new(), + properties: Properties::new(info), } .setup() } /// Get driver properties - pub fn properties(&self) -> &Properties { + pub fn properties(&self) -> &Properties { &self.properties } fn setup(self) -> Self { // We don't want interrupts being processed while we change modes. - critical_section::with(|_| unsafe { + critical_section::with(|_| { let rx_inner = super::common::ClassicBufferedRxInner { rx_sender: self.rx_buf.sender().into(), }; let tx_inner = super::common::ClassicBufferedTxInner { tx_receiver: self.tx_buf.receiver().into(), }; - T::mut_state().rx_mode = RxMode::ClassicBuffered(rx_inner); - T::mut_state().tx_mode = TxMode::ClassicBuffered(tx_inner); + let state = self.state as *const State; + unsafe { + let mut_state = state as *mut State; + (*mut_state).rx_mode = RxMode::ClassicBuffered(rx_inner); + (*mut_state).tx_mode = TxMode::ClassicBuffered(tx_inner); + } }); self } @@ -445,7 +465,8 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> /// Async write frame to TX buffer. pub async fn write(&mut self, frame: Frame) { self.tx_buf.send(frame).await; - T::IT0Interrupt::pend(); // Wake for Tx + self.info.interrupt0.pend(); // Wake for Tx + //T::IT0Interrupt::pend(); // Wake for Tx } /// Async read frame from RX buffer. @@ -457,7 +478,7 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> pub fn writer(&self) -> BufferedCanSender { BufferedCanSender { tx_buf: self.tx_buf.sender().into(), - waker: T::IT0Interrupt::pend, + waker: self.info.tx_waker, } } @@ -467,13 +488,15 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> } } -impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Drop - for BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> -{ +impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Drop for BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { fn drop(&mut self) { - critical_section::with(|_| unsafe { - T::mut_state().rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); - T::mut_state().tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + critical_section::with(|_| { + let state = self.state as *const State; + unsafe { + let mut_state = state as *mut State; + (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + } }); } } @@ -484,16 +507,6 @@ pub type RxFdBuf = Channel = Channel; -/// Buffered FDCAN Instance -pub struct BufferedCanFd<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { - _instance1: PhantomData, - _instance2: &'d crate::pac::can::Fdcan, - _mode: OperatingMode, - tx_buf: &'static TxFdBuf, - rx_buf: &'static RxFdBuf, - properties: Properties, -} - /// Sender that can be used for sending CAN frames. #[derive(Copy, Clone)] pub struct BufferedFdCanSender { @@ -524,43 +537,58 @@ impl BufferedFdCanSender { /// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. pub type BufferedFdCanReceiver = DynamicReceiver<'static, Result>; -impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> - BufferedCanFd<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> -{ +/// Buffered FDCAN Instance +pub struct BufferedCanFd<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { + info: &'static Info, + state: &'static State, + _instance: &'d crate::pac::can::Fdcan, + _mode: OperatingMode, + tx_buf: &'static TxFdBuf, + rx_buf: &'static RxFdBuf, + properties: Properties, +} + +impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { fn new( - _instance1: PhantomData, - _instance2: &'d crate::pac::can::Fdcan, + info: &'static Info, + state: &'static State, + _instance: &'d crate::pac::can::Fdcan, _mode: OperatingMode, tx_buf: &'static TxFdBuf, rx_buf: &'static RxFdBuf, ) -> Self { BufferedCanFd { - _instance1, - _instance2, + info, + state, + _instance, _mode, tx_buf, rx_buf, - properties: Properties::new(), + properties: Properties::new(info), } .setup() } /// Get driver properties - pub fn properties(&self) -> &Properties { + pub fn properties(&self) -> &Properties { &self.properties } fn setup(self) -> Self { // We don't want interrupts being processed while we change modes. - critical_section::with(|_| unsafe { + critical_section::with(|_| { let rx_inner = super::common::FdBufferedRxInner { rx_sender: self.rx_buf.sender().into(), }; let tx_inner = super::common::FdBufferedTxInner { tx_receiver: self.tx_buf.receiver().into(), }; - T::mut_state().rx_mode = RxMode::FdBuffered(rx_inner); - T::mut_state().tx_mode = TxMode::FdBuffered(tx_inner); + let state = self.state as *const State; + unsafe { + let mut_state = state as *mut State; + (*mut_state).rx_mode = RxMode::FdBuffered(rx_inner); + (*mut_state).tx_mode = TxMode::FdBuffered(tx_inner); + } }); self } @@ -568,7 +596,8 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> /// Async write frame to TX buffer. pub async fn write(&mut self, frame: FdFrame) { self.tx_buf.send(frame).await; - T::IT0Interrupt::pend(); // Wake for Tx + self.info.interrupt0.pend(); // Wake for Tx + //T::IT0Interrupt::pend(); // Wake for Tx } /// Async read frame from RX buffer. @@ -580,7 +609,7 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> pub fn writer(&self) -> BufferedFdCanSender { BufferedFdCanSender { tx_buf: self.tx_buf.sender().into(), - waker: T::IT0Interrupt::pend, + waker: self.info.tx_waker, } } @@ -590,38 +619,55 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> } } -impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Drop - for BufferedCanFd<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> -{ +impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Drop for BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { fn drop(&mut self) { - critical_section::with(|_| unsafe { - T::mut_state().rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); - T::mut_state().tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + critical_section::with(|_| { + let state = self.state as *const State; + unsafe { + let mut_state = state as *mut State; + (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + } }); } } /// FDCAN Rx only Instance -pub struct CanRx<'d, T: Instance> { - _instance1: PhantomData, - _instance2: &'d crate::pac::can::Fdcan, +pub struct CanRx<'d> { + info: &'static Info, + state: &'static State, + _instance: &'d crate::pac::can::Fdcan, _mode: OperatingMode, } +impl<'d> CanRx<'d> { + /// Returns the next received message frame + pub async fn read(&mut self) -> Result { + self.state.rx_mode.read_classic(&self.info, &self.state).await + } + + /// Returns the next received message frame + pub async fn read_fd(&mut self) -> Result { + self.state.rx_mode.read_fd(&self.info, &self.state).await + } +} + /// FDCAN Tx only Instance -pub struct CanTx<'d, T: Instance> { +pub struct CanTx<'d> { + info: &'static Info, + state: &'static State, config: crate::can::fd::config::FdCanConfig, - _instance: FdcanInstance<'d, T>, //(PeripheralRef<'a, T>); + _instance: &'d crate::pac::can::Fdcan, _mode: OperatingMode, } -impl<'c, 'd, T: Instance> CanTx<'d, T> { +impl<'c, 'd> CanTx<'d> { /// Queues the message to be sent but exerts backpressure. If a lower-priority /// frame is dropped from the mailbox, it is returned. If no lower-priority frames /// can be replaced, this call asynchronously waits for a frame to be successfully /// transmitted, then tries again. pub async fn write(&mut self, frame: &Frame) -> Option { - T::state().tx_mode.write::(frame).await + self.state.tx_mode.write(self.info, frame).await } /// Queues the message to be sent but exerts backpressure. If a lower-priority @@ -629,19 +675,7 @@ impl<'c, 'd, T: Instance> CanTx<'d, T> { /// can be replaced, this call asynchronously waits for a frame to be successfully /// transmitted, then tries again. pub async fn write_fd(&mut self, frame: &FdFrame) -> Option { - T::state().tx_mode.write_fd::(frame).await - } -} - -impl<'c, 'd, T: Instance> CanRx<'d, T> { - /// Returns the next received message frame - pub async fn read(&mut self) -> Result { - T::state().rx_mode.read_classic::().await - } - - /// Returns the next received message frame - pub async fn read_fd(&mut self) -> Result { - T::state().rx_mode.read_fd::().await + self.state.tx_mode.write_fd(self.info, frame).await } } @@ -662,7 +696,7 @@ impl RxMode { } fn on_interrupt(&self, fifonr: usize) { - T::regs().ir().write(|w| w.set_rfn(fifonr, true)); + T::registers().regs.ir().write(|w| w.set_rfn(fifonr, true)); match self { RxMode::NonBuffered(waker) => { waker.wake(); @@ -696,7 +730,6 @@ impl RxMode { } } - //async fn read_classic(&self) -> Result { fn try_read_fd(&self) -> Option> { if let Some((frame, ts)) = T::registers().read(0) { let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); @@ -712,14 +745,18 @@ impl RxMode { } } - fn read(&self) -> Option> { - if let Some((msg, ts)) = T::registers().read(0) { - let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); + fn read( + &self, + info: &'static Info, + state: &'static State, + ) -> Option> { + if let Some((msg, ts)) = info.regs.read(0) { + let ts = info.calc_timestamp(state.ns_per_timer_tick, ts); Some(Ok((msg, ts))) - } else if let Some((msg, ts)) = T::registers().read(1) { - let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); + } else if let Some((msg, ts)) = info.regs.read(1) { + let ts = info.calc_timestamp(state.ns_per_timer_tick, ts); Some(Ok((msg, ts))) - } else if let Some(err) = T::registers().curr_error() { + } else if let Some(err) = info.regs.curr_error() { // TODO: this is probably wrong Some(Err(err)) } else { @@ -727,11 +764,16 @@ impl RxMode { } } - async fn read_async(&self) -> Result<(F, Timestamp), BusError> { - poll_fn(|cx| { - T::state().err_waker.register(cx.waker()); + async fn read_async( + &self, + info: &'static Info, + state: &'static State, + ) -> Result<(F, Timestamp), BusError> { + //let _ = self.read::(info, state); + poll_fn(move |cx| { + state.err_waker.register(cx.waker()); self.register(cx.waker()); - match self.read::() { + match self.read::<_>(info, state) { Some(result) => Poll::Ready(result), None => Poll::Pending, } @@ -739,15 +781,15 @@ impl RxMode { .await } - async fn read_classic(&self) -> Result { - match self.read_async::().await { + async fn read_classic(&self, info: &'static Info, state: &'static State) -> Result { + match self.read_async::<_>(info, state).await { Ok((frame, ts)) => Ok(Envelope { ts, frame }), Err(e) => Err(e), } } - async fn read_fd(&self) -> Result { - match self.read_async::().await { + async fn read_fd(&self, info: &'static Info, state: &'static State) -> Result { + match self.read_async::<_>(info, state).await { Ok((frame, ts)) => Ok(FdEnvelope { ts, frame }), Err(e) => Err(e), } @@ -776,11 +818,11 @@ impl TxMode { /// frame is dropped from the mailbox, it is returned. If no lower-priority frames /// can be replaced, this call asynchronously waits for a frame to be successfully /// transmitted, then tries again. - async fn write_generic(&self, frame: &F) -> Option { + async fn write_generic(&self, info: &'static Info, frame: &F) -> Option { poll_fn(|cx| { self.register(cx.waker()); - if let Ok(dropped) = T::registers().write(frame) { + if let Ok(dropped) = info.regs.write(frame) { return Poll::Ready(dropped); } @@ -795,68 +837,70 @@ impl TxMode { /// frame is dropped from the mailbox, it is returned. If no lower-priority frames /// can be replaced, this call asynchronously waits for a frame to be successfully /// transmitted, then tries again. - async fn write(&self, frame: &Frame) -> Option { - self.write_generic::(frame).await + async fn write(&self, info: &'static Info, frame: &Frame) -> Option { + self.write_generic::<_>(info, frame).await } /// Queues the message to be sent but exerts backpressure. If a lower-priority /// frame is dropped from the mailbox, it is returned. If no lower-priority frames /// can be replaced, this call asynchronously waits for a frame to be successfully /// transmitted, then tries again. - async fn write_fd(&self, frame: &FdFrame) -> Option { - self.write_generic::(frame).await + async fn write_fd(&self, info: &'static Info, frame: &FdFrame) -> Option { + self.write_generic::<_>(info, frame).await } } /// Common driver properties, including filters and error counters -pub struct Properties { +pub struct Properties { + info: &'static Info, // phantom pointer to ensure !Sync - instance: PhantomData<*const T>, + //instance: PhantomData<*const T>, } -impl Properties { - fn new() -> Self { +impl Properties { + fn new(info: &'static Info) -> Self { Self { - instance: Default::default(), + info, + //instance: Default::default(), } } /// Set a standard address CAN filter in the specified slot in FDCAN memory. #[inline] pub fn set_standard_filter(&self, slot: StandardFilterSlot, filter: StandardFilter) { - T::registers().msg_ram_mut().filters.flssa[slot as usize].activate(filter); + self.info.regs.msg_ram_mut().filters.flssa[slot as usize].activate(filter); } /// Set the full array of standard address CAN filters in FDCAN memory. /// Overwrites all standard address filters in memory. pub fn set_standard_filters(&self, filters: &[StandardFilter; STANDARD_FILTER_MAX as usize]) { for (i, f) in filters.iter().enumerate() { - T::registers().msg_ram_mut().filters.flssa[i].activate(*f); + self.info.regs.msg_ram_mut().filters.flssa[i].activate(*f); } } /// Set an extended address CAN filter in the specified slot in FDCAN memory. #[inline] pub fn set_extended_filter(&self, slot: ExtendedFilterSlot, filter: ExtendedFilter) { - T::registers().msg_ram_mut().filters.flesa[slot as usize].activate(filter); + self.info.regs.msg_ram_mut().filters.flesa[slot as usize].activate(filter); } /// Set the full array of extended address CAN filters in FDCAN memory. /// Overwrites all extended address filters in memory. pub fn set_extended_filters(&self, filters: &[ExtendedFilter; EXTENDED_FILTER_MAX as usize]) { for (i, f) in filters.iter().enumerate() { - T::registers().msg_ram_mut().filters.flesa[i].activate(*f); + self.info.regs.msg_ram_mut().filters.flesa[i].activate(*f); } } /// Get the CAN RX error counter pub fn rx_error_count(&self) -> u8 { - T::regs().ecr().read().rec() + self.info.regs.regs.ecr().read().rec() } /// Get the CAN TX error counter pub fn tx_error_count(&self) -> u8 { - T::regs().ecr().read().tec() + self.info.regs.regs.ecr().read().tec() } /// Get the current bus error mode @@ -864,7 +908,7 @@ impl Properties { // This read will clear LEC and DLEC. This is not ideal, but protocol // error reporting in this driver should have a big ol' FIXME on it // anyway! - let psr = T::regs().psr().read(); + let psr = self.info.regs.regs.psr().read(); match (psr.bo(), psr.ep()) { (false, false) => BusErrorMode::ErrorActive, (false, true) => BusErrorMode::ErrorPassive, @@ -892,10 +936,37 @@ impl State { } } +struct Info { + regs: Registers, + interrupt0: crate::interrupt::Interrupt, + _interrupt1: crate::interrupt::Interrupt, + tx_waker: fn(), +} + +impl Info { + #[cfg(feature = "time")] + fn calc_timestamp(&self, ns_per_timer_tick: u64, ts_val: u16) -> Timestamp { + let now_embassy = embassy_time::Instant::now(); + if ns_per_timer_tick == 0 { + return now_embassy; + } + let cantime = { self.regs.regs.tscv().read().tsc() }; + let delta = cantime.overflowing_sub(ts_val).0 as u64; + let ns = ns_per_timer_tick * delta as u64; + now_embassy - embassy_time::Duration::from_nanos(ns) + } + + #[cfg(not(feature = "time"))] + fn calc_timestamp(&self, _ns_per_timer_tick: u64, ts_val: u16) -> Timestamp { + ts_val + } +} + trait SealedInstance { const MSG_RAM_OFFSET: usize; - fn regs() -> &'static crate::pac::can::Fdcan; + fn info() -> &'static Info; + //fn regs() -> &'static crate::pac::can::Fdcan; fn registers() -> crate::can::fd::peripheral::Registers; fn state() -> &'static State; unsafe fn mut_state() -> &'static mut State; @@ -915,12 +986,20 @@ pub trait Instance: SealedInstance + RccPeripheral + 'static { pub struct FdcanInstance<'a, T>(PeripheralRef<'a, T>); macro_rules! impl_fdcan { - ($inst:ident, $msg_ram_inst:ident, $msg_ram_offset:literal) => { + ($inst:ident, + //$irq0:ident, $irq1:ident, + $msg_ram_inst:ident, $msg_ram_offset:literal) => { impl SealedInstance for peripherals::$inst { const MSG_RAM_OFFSET: usize = $msg_ram_offset; - fn regs() -> &'static crate::pac::can::Fdcan { - &crate::pac::$inst + fn info() -> &'static Info { + static INFO: Info = Info { + regs: Registers{regs: &crate::pac::$inst, msgram: &crate::pac::$msg_ram_inst, msg_ram_offset: $msg_ram_offset}, + interrupt0: crate::_generated::peripheral_interrupts::$inst::IT0::IRQ, + _interrupt1: crate::_generated::peripheral_interrupts::$inst::IT1::IRQ, + tx_waker: crate::_generated::peripheral_interrupts::$inst::IT0::pend, + }; + &INFO } fn registers() -> Registers { Registers{regs: &crate::pac::$inst, msgram: &crate::pac::$msg_ram_inst, msg_ram_offset: Self::MSG_RAM_OFFSET} @@ -939,7 +1018,7 @@ macro_rules! impl_fdcan { if ns_per_timer_tick == 0 { return now_embassy; } - let cantime = { Self::regs().tscv().read().tsc() }; + let cantime = { Self::registers().regs.tscv().read().tsc() }; let delta = cantime.overflowing_sub(ts_val).0 as u64; let ns = ns_per_timer_tick * delta as u64; now_embassy - embassy_time::Duration::from_nanos(ns) diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs index 551764458..004b1a729 100644 --- a/tests/stm32/src/bin/can.rs +++ b/tests/stm32/src/bin/can.rs @@ -9,9 +9,7 @@ use common::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::can::filter::Mask32; -use embassy_stm32::can::{ - Can, Fifo, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler, -}; +use embassy_stm32::can::{Fifo, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::peripherals::CAN1; use embassy_time::Duration; @@ -20,6 +18,10 @@ use {defmt_rtt as _, panic_probe as _}; mod can_common; use can_common::*; +type Can<'d> = embassy_stm32::can::Can<'d, embassy_stm32::peripherals::CAN1>; +type CanTx<'d> = embassy_stm32::can::CanTx<'d, embassy_stm32::peripherals::CAN1>; +type CanRx<'d> = embassy_stm32::can::CanRx<'d, embassy_stm32::peripherals::CAN1>; + bind_interrupts!(struct Irqs { CAN1_RX0 => Rx0InterruptHandler; CAN1_RX1 => Rx1InterruptHandler; diff --git a/tests/stm32/src/bin/can_common.rs b/tests/stm32/src/bin/can_common.rs index 4b39269cc..fbfbcdc21 100644 --- a/tests/stm32/src/bin/can_common.rs +++ b/tests/stm32/src/bin/can_common.rs @@ -8,7 +8,8 @@ pub struct TestOptions { pub max_buffered: u8, } -pub async fn run_can_tests<'d, T: can::Instance>(can: &mut can::Can<'d, T>, options: &TestOptions) { +pub async fn run_can_tests<'d>(can: &mut crate::Can<'d>, options: &TestOptions) { + //pub async fn run_can_tests<'d, T: can::Instance>(can: &mut can::Can<'d, T>, options: &TestOptions) { let mut i: u8 = 0; loop { //let tx_frame = can::frame::Frame::new_standard(0x123, &[i, 0x12 as u8, 0x34 as u8, 0x56 as u8, 0x78 as u8, 0x9A as u8, 0xBC as u8 ]).unwrap(); @@ -79,11 +80,7 @@ pub async fn run_can_tests<'d, T: can::Instance>(can: &mut can::Can<'d, T>, opti } } -pub async fn run_split_can_tests<'d, T: can::Instance>( - tx: &mut can::CanTx<'d, T>, - rx: &mut can::CanRx<'d, T>, - options: &TestOptions, -) { +pub async fn run_split_can_tests<'d>(tx: &mut crate::CanTx<'d>, rx: &mut crate::CanRx<'d>, options: &TestOptions) { for i in 0..options.max_buffered { // Try filling up the RX FIFO0 buffers //let tx_frame = if 0 != (i & 0x01) { diff --git a/tests/stm32/src/bin/fdcan.rs b/tests/stm32/src/bin/fdcan.rs index 20bd3f7e3..8534f92e8 100644 --- a/tests/stm32/src/bin/fdcan.rs +++ b/tests/stm32/src/bin/fdcan.rs @@ -15,6 +15,10 @@ use {defmt_rtt as _, panic_probe as _}; mod can_common; use can_common::*; +type Can<'d> = can::Can<'d>; +type CanTx<'d> = can::CanTx<'d>; +type CanRx<'d> = can::CanRx<'d>; + bind_interrupts!(struct Irqs2 { FDCAN2_IT0 => can::IT0InterruptHandler; FDCAN2_IT1 => can::IT1InterruptHandler; From a23fa8dcb2e22ed2d8bac15fd875dfe53af43eda Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Wed, 29 May 2024 09:14:05 -0400 Subject: [PATCH 22/57] Apply suggestions from code review Co-authored-by: Romain Reignier --- embassy-stm32/src/timer/pwm_input.rs | 8 ++++---- examples/stm32f1/src/bin/input_capture.rs | 2 +- examples/stm32f1/src/bin/pwm_input.rs | 2 +- examples/stm32f4/src/bin/pwm_input.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index d34ba086f..d3fe7632a 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -1,4 +1,4 @@ -//! Input capture driver. +//! PWM Input driver. use embassy_hal_internal::into_ref; @@ -8,7 +8,7 @@ use crate::gpio::{AFType, Pull}; use crate::time::Hertz; use crate::Peripheral; -/// Input capture driver. +/// PWM Input driver. pub struct PwmInput<'d, T: GeneralInstance4Channel> { channel: Channel, inner: Timer<'d, T>, @@ -20,7 +20,7 @@ fn regs_gp16(ptr: *mut ()) -> crate::pac::timer::TimGp16 { } impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { - /// Create a new input capture driver. + /// Create a new PWM input driver. pub fn new( tim: impl Peripheral

+ 'd, pin: impl Peripheral

> + 'd, @@ -37,7 +37,7 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2) } - /// Create a new input capture driver. + /// Create a new PWM input driver. pub fn new_alt( tim: impl Peripheral

+ 'd, pin: impl Peripheral

> + 'd, diff --git a/examples/stm32f1/src/bin/input_capture.rs b/examples/stm32f1/src/bin/input_capture.rs index 417830231..5e2dab9e6 100644 --- a/examples/stm32f1/src/bin/input_capture.rs +++ b/examples/stm32f1/src/bin/input_capture.rs @@ -43,7 +43,7 @@ async fn main(spawner: Spawner) { let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); loop { - info!("wait for risign edge"); + info!("wait for rising edge"); ic.wait_for_rising_edge(Channel::Ch3).await; let capture_value = ic.get_capture_value(Channel::Ch3); diff --git a/examples/stm32f1/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/pwm_input.rs index c051d1328..de6949eb4 100644 --- a/examples/stm32f1/src/bin/pwm_input.rs +++ b/examples/stm32f1/src/bin/pwm_input.rs @@ -11,7 +11,7 @@ use embassy_stm32::{bind_interrupts, peripherals, timer}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; -/// Connect PB2 and PB10 with a 1k Ohm resistor +/// Connect PA0 and PC13 with a 1k Ohm resistor #[embassy_executor::task] async fn blinky(led: peripherals::PC13) { diff --git a/examples/stm32f4/src/bin/pwm_input.rs b/examples/stm32f4/src/bin/pwm_input.rs index eb1e7cb87..30cefac3a 100644 --- a/examples/stm32f4/src/bin/pwm_input.rs +++ b/examples/stm32f4/src/bin/pwm_input.rs @@ -11,7 +11,7 @@ use embassy_stm32::{bind_interrupts, peripherals, timer}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; -/// Connect PB2 and PB10 with a 1k Ohm resistor +/// Connect PB2 and PA6 with a 1k Ohm resistor #[embassy_executor::task] async fn blinky(led: peripherals::PB2) { From 292c1dd0b858fe8dde74edd2f60a96f4c9e588da Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Wed, 29 May 2024 09:58:46 -0400 Subject: [PATCH 23/57] rename get_width_ticks and add info!() in examples --- embassy-stm32/src/timer/pwm_input.rs | 6 +++--- examples/stm32f1/src/bin/pwm_input.rs | 9 ++++----- examples/stm32f4/src/bin/pwm_input.rs | 9 ++++----- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index d3fe7632a..7bcb7802a 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -114,8 +114,8 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { self.inner.get_capture_value(self.channel) } - /// Get the duty tick count - pub fn get_duty_ticks(&self) -> u32 { + /// Get the pulse width tick count + pub fn get_width_ticks(&self) -> u32 { self.inner.get_capture_value(match self.channel { Channel::Ch1 => Channel::Ch2, Channel::Ch2 => Channel::Ch1, @@ -129,6 +129,6 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { if period == 0 { return 0.; } - 100. * (self.get_duty_ticks() as f32) / (period as f32) + 100. * (self.get_width_ticks() as f32) / (period as f32) } } diff --git a/examples/stm32f1/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/pwm_input.rs index de6949eb4..9883280cf 100644 --- a/examples/stm32f1/src/bin/pwm_input.rs +++ b/examples/stm32f1/src/bin/pwm_input.rs @@ -1,7 +1,6 @@ #![no_std] #![no_main] -use cortex_m::asm; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; @@ -44,9 +43,9 @@ async fn main(spawner: Spawner) { loop { Timer::after_millis(500).await; - let _per = pwm_input.get_period_ticks(); - let _dc = pwm_input.get_duty_ticks(); - let _pc = pwm_input.get_duty_cycle(); - asm::nop(); + let period = pwm_input.get_period_ticks(); + let width = pwm_input.get_width_ticks(); + let duty_cycle = pwm_input.get_duty_cycle(); + info!("period ticks: {} width ticks: {} duty cycle: {}", period, width, duty_cycle); } } diff --git a/examples/stm32f4/src/bin/pwm_input.rs b/examples/stm32f4/src/bin/pwm_input.rs index 30cefac3a..8fe1fdb5b 100644 --- a/examples/stm32f4/src/bin/pwm_input.rs +++ b/examples/stm32f4/src/bin/pwm_input.rs @@ -1,7 +1,6 @@ #![no_std] #![no_main] -use cortex_m::asm; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; @@ -44,9 +43,9 @@ async fn main(spawner: Spawner) { loop { Timer::after_millis(500).await; - let _per = pwm_input.get_period_ticks(); - let _dc = pwm_input.get_duty_ticks(); - let _pc = pwm_input.get_duty_cycle(); - asm::nop(); + let period = pwm_input.get_period_ticks(); + let width = pwm_input.get_width_ticks(); + let duty_cycle = pwm_input.get_duty_cycle(); + info!("period ticks: {} width ticks: {} duty cycle: {}", period, width, duty_cycle); } } From a87b33303403ba3601d0c631b9efe1cb3853c73b Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Wed, 29 May 2024 10:02:54 -0400 Subject: [PATCH 24/57] fix fmt --- examples/stm32f1/src/bin/pwm_input.rs | 5 ++++- examples/stm32f4/src/bin/pwm_input.rs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/stm32f1/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/pwm_input.rs index 9883280cf..f74853d4e 100644 --- a/examples/stm32f1/src/bin/pwm_input.rs +++ b/examples/stm32f1/src/bin/pwm_input.rs @@ -46,6 +46,9 @@ async fn main(spawner: Spawner) { let period = pwm_input.get_period_ticks(); let width = pwm_input.get_width_ticks(); let duty_cycle = pwm_input.get_duty_cycle(); - info!("period ticks: {} width ticks: {} duty cycle: {}", period, width, duty_cycle); + info!( + "period ticks: {} width ticks: {} duty cycle: {}", + period, width, duty_cycle + ); } } diff --git a/examples/stm32f4/src/bin/pwm_input.rs b/examples/stm32f4/src/bin/pwm_input.rs index 8fe1fdb5b..ce200549d 100644 --- a/examples/stm32f4/src/bin/pwm_input.rs +++ b/examples/stm32f4/src/bin/pwm_input.rs @@ -46,6 +46,9 @@ async fn main(spawner: Spawner) { let period = pwm_input.get_period_ticks(); let width = pwm_input.get_width_ticks(); let duty_cycle = pwm_input.get_duty_cycle(); - info!("period ticks: {} width ticks: {} duty cycle: {}", period, width, duty_cycle); + info!( + "period ticks: {} width ticks: {} duty cycle: {}", + period, width, duty_cycle + ); } } From c46172acac0be14b99148c2ec7aa38c9ac605fe1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 30 May 2024 13:07:18 +0200 Subject: [PATCH 25/57] stm32: remove pointer-to-pointer-to-registers. in chiptool pacs the register block struct is already a pointer, so using pointers to it is redundant. --- embassy-stm32/src/can/fd/peripheral.rs | 5 +- embassy-stm32/src/can/fdcan.rs | 30 ++++--- embassy-stm32/src/cryp/mod.rs | 104 ++++++++++++------------- embassy-stm32/src/dac/mod.rs | 6 +- embassy-stm32/src/dsihost.rs | 6 +- embassy-stm32/src/ltdc.rs | 6 +- embassy-stm32/src/rcc/f013.rs | 2 +- embassy-stm32/src/rtc/mod.rs | 8 +- embassy-stm32/src/rtc/v2.rs | 8 +- embassy-stm32/src/rtc/v3.rs | 8 +- 10 files changed, 93 insertions(+), 90 deletions(-) diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs index 9cd5f0785..7321ab230 100644 --- a/embassy-stm32/src/can/fd/peripheral.rs +++ b/embassy-stm32/src/can/fd/peripheral.rs @@ -20,8 +20,9 @@ enum LoopbackMode { } pub struct Registers { - pub regs: &'static crate::pac::can::Fdcan, - pub msgram: &'static crate::pac::fdcanram::Fdcanram, + pub regs: crate::pac::can::Fdcan, + pub msgram: crate::pac::fdcanram::Fdcanram, + #[allow(dead_code)] pub msg_ram_offset: usize, } diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index b772a3ca0..d0c8db090 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -265,6 +265,7 @@ impl<'d, T: Instance> CanConfigurator<'d, T> { }); T::registers().into_mode(self.config, mode); Can { + _phantom: PhantomData, config: self.config, info: self.info, state: self.state, @@ -292,10 +293,11 @@ impl<'d, T: Instance> CanConfigurator<'d, T> { /// FDCAN Instance pub struct Can<'d> { + _phantom: PhantomData<&'d ()>, config: crate::can::fd::config::FdCanConfig, info: &'static Info, state: &'static State, - instance: &'d crate::pac::can::Fdcan, + instance: crate::pac::can::Fdcan, _mode: OperatingMode, properties: Properties, } @@ -354,6 +356,7 @@ impl<'d> Can<'d> { pub fn split(self) -> (CanTx<'d>, CanRx<'d>, Properties) { ( CanTx { + _phantom: PhantomData, info: self.info, state: self.state, config: self.config, @@ -361,6 +364,7 @@ impl<'d> Can<'d> { _mode: self._mode, }, CanRx { + _phantom: PhantomData, info: self.info, state: self.state, _instance: self.instance, @@ -372,6 +376,7 @@ impl<'d> Can<'d> { /// Join split rx and tx portions back together pub fn join(tx: CanTx<'d>, rx: CanRx<'d>) -> Self { Can { + _phantom: PhantomData, config: tx.config, info: tx.info, state: tx.state, @@ -408,9 +413,10 @@ pub type TxBuf = Channel { + _phantom: PhantomData<&'d ()>, info: &'static Info, state: &'static State, - _instance: &'d crate::pac::can::Fdcan, + _instance: crate::pac::can::Fdcan, _mode: OperatingMode, tx_buf: &'static TxBuf, rx_buf: &'static RxBuf, @@ -421,12 +427,13 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, fn new( info: &'static Info, state: &'static State, - _instance: &'d crate::pac::can::Fdcan, + _instance: crate::pac::can::Fdcan, _mode: OperatingMode, tx_buf: &'static TxBuf, rx_buf: &'static RxBuf, ) -> Self { BufferedCan { + _phantom: PhantomData, info, state, _instance, @@ -539,9 +546,10 @@ pub type BufferedFdCanReceiver = DynamicReceiver<'static, Result { + _phantom: PhantomData<&'d ()>, info: &'static Info, state: &'static State, - _instance: &'d crate::pac::can::Fdcan, + _instance: crate::pac::can::Fdcan, _mode: OperatingMode, tx_buf: &'static TxFdBuf, rx_buf: &'static RxFdBuf, @@ -552,12 +560,13 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<' fn new( info: &'static Info, state: &'static State, - _instance: &'d crate::pac::can::Fdcan, + _instance: crate::pac::can::Fdcan, _mode: OperatingMode, tx_buf: &'static TxFdBuf, rx_buf: &'static RxFdBuf, ) -> Self { BufferedCanFd { + _phantom: PhantomData, info, state, _instance, @@ -634,9 +643,10 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Drop for Buffer /// FDCAN Rx only Instance pub struct CanRx<'d> { + _phantom: PhantomData<&'d ()>, info: &'static Info, state: &'static State, - _instance: &'d crate::pac::can::Fdcan, + _instance: crate::pac::can::Fdcan, _mode: OperatingMode, } @@ -654,10 +664,11 @@ impl<'d> CanRx<'d> { /// FDCAN Tx only Instance pub struct CanTx<'d> { + _phantom: PhantomData<&'d ()>, info: &'static Info, state: &'static State, config: crate::can::fd::config::FdCanConfig, - _instance: &'d crate::pac::can::Fdcan, + _instance: crate::pac::can::Fdcan, _mode: OperatingMode, } @@ -966,7 +977,6 @@ trait SealedInstance { const MSG_RAM_OFFSET: usize; fn info() -> &'static Info; - //fn regs() -> &'static crate::pac::can::Fdcan; fn registers() -> crate::can::fd::peripheral::Registers; fn state() -> &'static State; unsafe fn mut_state() -> &'static mut State; @@ -994,7 +1004,7 @@ macro_rules! impl_fdcan { fn info() -> &'static Info { static INFO: Info = Info { - regs: Registers{regs: &crate::pac::$inst, msgram: &crate::pac::$msg_ram_inst, msg_ram_offset: $msg_ram_offset}, + regs: Registers{regs: crate::pac::$inst, msgram: crate::pac::$msg_ram_inst, msg_ram_offset: $msg_ram_offset}, interrupt0: crate::_generated::peripheral_interrupts::$inst::IT0::IRQ, _interrupt1: crate::_generated::peripheral_interrupts::$inst::IT1::IRQ, tx_waker: crate::_generated::peripheral_interrupts::$inst::IT0::pend, @@ -1002,7 +1012,7 @@ macro_rules! impl_fdcan { &INFO } fn registers() -> Registers { - Registers{regs: &crate::pac::$inst, msgram: &crate::pac::$msg_ram_inst, msg_ram_offset: Self::MSG_RAM_OFFSET} + Registers{regs: crate::pac::$inst, msgram: crate::pac::$msg_ram_inst, msg_ram_offset: Self::MSG_RAM_OFFSET} } unsafe fn mut_state() -> &'static mut State { static mut STATE: State = State::new(); diff --git a/embassy-stm32/src/cryp/mod.rs b/embassy-stm32/src/cryp/mod.rs index f19c94fda..e7808b454 100644 --- a/embassy-stm32/src/cryp/mod.rs +++ b/embassy-stm32/src/cryp/mod.rs @@ -51,16 +51,16 @@ pub trait Cipher<'c> { fn iv(&self) -> &[u8]; /// Sets the processor algorithm mode according to the associated cipher. - fn set_algomode(&self, p: &pac::cryp::Cryp); + fn set_algomode(&self, p: pac::cryp::Cryp); /// Performs any key preparation within the processor, if necessary. - fn prepare_key(&self, _p: &pac::cryp::Cryp) {} + fn prepare_key(&self, _p: pac::cryp::Cryp) {} /// Performs any cipher-specific initialization. - fn init_phase_blocking(&self, _p: &pac::cryp::Cryp, _cryp: &Cryp) {} + fn init_phase_blocking(&self, _p: pac::cryp::Cryp, _cryp: &Cryp) {} /// Performs any cipher-specific initialization. - async fn init_phase(&self, _p: &pac::cryp::Cryp, _cryp: &mut Cryp<'_, T, DmaIn, DmaOut>) + async fn init_phase(&self, _p: pac::cryp::Cryp, _cryp: &mut Cryp<'_, T, DmaIn, DmaOut>) where DmaIn: crate::cryp::DmaIn, DmaOut: crate::cryp::DmaOut, @@ -68,14 +68,14 @@ pub trait Cipher<'c> { } /// Called prior to processing the last data block for cipher-specific operations. - fn pre_final(&self, _p: &pac::cryp::Cryp, _dir: Direction, _padding_len: usize) -> [u32; 4] { + fn pre_final(&self, _p: pac::cryp::Cryp, _dir: Direction, _padding_len: usize) -> [u32; 4] { return [0; 4]; } /// Called after processing the last data block for cipher-specific operations. fn post_final_blocking( &self, - _p: &pac::cryp::Cryp, + _p: pac::cryp::Cryp, _cryp: &Cryp, _dir: Direction, _int_data: &mut [u8; AES_BLOCK_SIZE], @@ -87,7 +87,7 @@ pub trait Cipher<'c> { /// Called after processing the last data block for cipher-specific operations. async fn post_final( &self, - _p: &pac::cryp::Cryp, + _p: pac::cryp::Cryp, _cryp: &mut Cryp<'_, T, DmaIn, DmaOut>, _dir: Direction, _int_data: &mut [u8; AES_BLOCK_SIZE], @@ -142,7 +142,7 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for TdesEcb<'c, KEY_SIZE> { self.iv } - fn set_algomode(&self, p: &pac::cryp::Cryp) { + fn set_algomode(&self, p: pac::cryp::Cryp) { #[cfg(cryp_v1)] { p.cr().modify(|w| w.set_algomode(0)); @@ -184,7 +184,7 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for TdesCbc<'c, KEY_SIZE> { self.iv } - fn set_algomode(&self, p: &pac::cryp::Cryp) { + fn set_algomode(&self, p: pac::cryp::Cryp) { #[cfg(cryp_v1)] { p.cr().modify(|w| w.set_algomode(1)); @@ -226,7 +226,7 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for DesEcb<'c, KEY_SIZE> { self.iv } - fn set_algomode(&self, p: &pac::cryp::Cryp) { + fn set_algomode(&self, p: pac::cryp::Cryp) { #[cfg(cryp_v1)] { p.cr().modify(|w| w.set_algomode(2)); @@ -267,7 +267,7 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for DesCbc<'c, KEY_SIZE> { self.iv } - fn set_algomode(&self, p: &pac::cryp::Cryp) { + fn set_algomode(&self, p: pac::cryp::Cryp) { #[cfg(cryp_v1)] { p.cr().modify(|w| w.set_algomode(3)); @@ -308,7 +308,7 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesEcb<'c, KEY_SIZE> { self.iv } - fn prepare_key(&self, p: &pac::cryp::Cryp) { + fn prepare_key(&self, p: pac::cryp::Cryp) { #[cfg(cryp_v1)] { p.cr().modify(|w| w.set_algomode(7)); @@ -322,7 +322,7 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesEcb<'c, KEY_SIZE> { while p.sr().read().busy() {} } - fn set_algomode(&self, p: &pac::cryp::Cryp) { + fn set_algomode(&self, p: pac::cryp::Cryp) { #[cfg(cryp_v1)] { p.cr().modify(|w| w.set_algomode(2)); @@ -365,7 +365,7 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesCbc<'c, KEY_SIZE> { self.iv } - fn prepare_key(&self, p: &pac::cryp::Cryp) { + fn prepare_key(&self, p: pac::cryp::Cryp) { #[cfg(cryp_v1)] { p.cr().modify(|w| w.set_algomode(7)); @@ -379,7 +379,7 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesCbc<'c, KEY_SIZE> { while p.sr().read().busy() {} } - fn set_algomode(&self, p: &pac::cryp::Cryp) { + fn set_algomode(&self, p: pac::cryp::Cryp) { #[cfg(cryp_v1)] { p.cr().modify(|w| w.set_algomode(5)); @@ -421,7 +421,7 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesCtr<'c, KEY_SIZE> { self.iv } - fn set_algomode(&self, p: &pac::cryp::Cryp) { + fn set_algomode(&self, p: pac::cryp::Cryp) { #[cfg(cryp_v1)] { p.cr().modify(|w| w.set_algomode(6)); @@ -469,29 +469,25 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGcm<'c, KEY_SIZE> { self.iv.as_slice() } - fn set_algomode(&self, p: &pac::cryp::Cryp) { + fn set_algomode(&self, p: pac::cryp::Cryp) { p.cr().modify(|w| w.set_algomode0(0)); p.cr().modify(|w| w.set_algomode3(true)); } - fn init_phase_blocking(&self, p: &pac::cryp::Cryp, _cryp: &Cryp) { + fn init_phase_blocking(&self, p: pac::cryp::Cryp, _cryp: &Cryp) { p.cr().modify(|w| w.set_gcm_ccmph(0)); p.cr().modify(|w| w.set_crypen(true)); while p.cr().read().crypen() {} } - async fn init_phase( - &self, - p: &pac::cryp::Cryp, - _cryp: &mut Cryp<'_, T, DmaIn, DmaOut>, - ) { + async fn init_phase(&self, p: pac::cryp::Cryp, _cryp: &mut Cryp<'_, T, DmaIn, DmaOut>) { p.cr().modify(|w| w.set_gcm_ccmph(0)); p.cr().modify(|w| w.set_crypen(true)); while p.cr().read().crypen() {} } #[cfg(cryp_v2)] - fn pre_final(&self, p: &pac::cryp::Cryp, dir: Direction, _padding_len: usize) -> [u32; 4] { + fn pre_final(&self, p: pac::cryp::Cryp, dir: Direction, _padding_len: usize) -> [u32; 4] { //Handle special GCM partial block process. if dir == Direction::Encrypt { p.cr().modify(|w| w.set_crypen(false)); @@ -505,7 +501,7 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGcm<'c, KEY_SIZE> { } #[cfg(any(cryp_v3, cryp_v4))] - fn pre_final(&self, p: &pac::cryp::Cryp, _dir: Direction, padding_len: usize) -> [u32; 4] { + fn pre_final(&self, p: pac::cryp::Cryp, _dir: Direction, padding_len: usize) -> [u32; 4] { //Handle special GCM partial block process. p.cr().modify(|w| w.set_npblb(padding_len as u8)); [0; 4] @@ -514,7 +510,7 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGcm<'c, KEY_SIZE> { #[cfg(cryp_v2)] fn post_final_blocking( &self, - p: &pac::cryp::Cryp, + p: pac::cryp::Cryp, cryp: &Cryp, dir: Direction, int_data: &mut [u8; AES_BLOCK_SIZE], @@ -540,7 +536,7 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGcm<'c, KEY_SIZE> { #[cfg(cryp_v2)] async fn post_final( &self, - p: &pac::cryp::Cryp, + p: pac::cryp::Cryp, cryp: &mut Cryp<'_, T, DmaIn, DmaOut>, dir: Direction, int_data: &mut [u8; AES_BLOCK_SIZE], @@ -614,29 +610,25 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGmac<'c, KEY_SIZE> { self.iv.as_slice() } - fn set_algomode(&self, p: &pac::cryp::Cryp) { + fn set_algomode(&self, p: pac::cryp::Cryp) { p.cr().modify(|w| w.set_algomode0(0)); p.cr().modify(|w| w.set_algomode3(true)); } - fn init_phase_blocking(&self, p: &pac::cryp::Cryp, _cryp: &Cryp) { + fn init_phase_blocking(&self, p: pac::cryp::Cryp, _cryp: &Cryp) { p.cr().modify(|w| w.set_gcm_ccmph(0)); p.cr().modify(|w| w.set_crypen(true)); while p.cr().read().crypen() {} } - async fn init_phase( - &self, - p: &pac::cryp::Cryp, - _cryp: &mut Cryp<'_, T, DmaIn, DmaOut>, - ) { + async fn init_phase(&self, p: pac::cryp::Cryp, _cryp: &mut Cryp<'_, T, DmaIn, DmaOut>) { p.cr().modify(|w| w.set_gcm_ccmph(0)); p.cr().modify(|w| w.set_crypen(true)); while p.cr().read().crypen() {} } #[cfg(cryp_v2)] - fn pre_final(&self, p: &pac::cryp::Cryp, dir: Direction, _padding_len: usize) -> [u32; 4] { + fn pre_final(&self, p: pac::cryp::Cryp, dir: Direction, _padding_len: usize) -> [u32; 4] { //Handle special GCM partial block process. if dir == Direction::Encrypt { p.cr().modify(|w| w.set_crypen(false)); @@ -650,7 +642,7 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGmac<'c, KEY_SIZE> { } #[cfg(any(cryp_v3, cryp_v4))] - fn pre_final(&self, p: &pac::cryp::Cryp, _dir: Direction, padding_len: usize) -> [u32; 4] { + fn pre_final(&self, p: pac::cryp::Cryp, _dir: Direction, padding_len: usize) -> [u32; 4] { //Handle special GCM partial block process. p.cr().modify(|w| w.set_npblb(padding_len as u8)); [0; 4] @@ -659,7 +651,7 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGmac<'c, KEY_SIZE> { #[cfg(cryp_v2)] fn post_final_blocking( &self, - p: &pac::cryp::Cryp, + p: pac::cryp::Cryp, cryp: &Cryp, dir: Direction, int_data: &mut [u8; AES_BLOCK_SIZE], @@ -685,7 +677,7 @@ impl<'c, const KEY_SIZE: usize> Cipher<'c> for AesGmac<'c, KEY_SIZE> { #[cfg(cryp_v2)] async fn post_final( &self, - p: &pac::cryp::Cryp, + p: pac::cryp::Cryp, cryp: &mut Cryp<'_, T, DmaIn, DmaOut>, dir: Direction, int_data: &mut [u8; AES_BLOCK_SIZE], @@ -815,12 +807,12 @@ impl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize, const IV_SIZE: usize> Cip self.ctr.as_slice() } - fn set_algomode(&self, p: &pac::cryp::Cryp) { + fn set_algomode(&self, p: pac::cryp::Cryp) { p.cr().modify(|w| w.set_algomode0(1)); p.cr().modify(|w| w.set_algomode3(true)); } - fn init_phase_blocking(&self, p: &pac::cryp::Cryp, cryp: &Cryp) { + fn init_phase_blocking(&self, p: pac::cryp::Cryp, cryp: &Cryp) { p.cr().modify(|w| w.set_gcm_ccmph(0)); cryp.write_bytes_blocking(Self::BLOCK_SIZE, &self.block0); @@ -829,7 +821,7 @@ impl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize, const IV_SIZE: usize> Cip while p.cr().read().crypen() {} } - async fn init_phase(&self, p: &pac::cryp::Cryp, cryp: &mut Cryp<'_, T, DmaIn, DmaOut>) + async fn init_phase(&self, p: pac::cryp::Cryp, cryp: &mut Cryp<'_, T, DmaIn, DmaOut>) where DmaIn: crate::cryp::DmaIn, DmaOut: crate::cryp::DmaOut, @@ -847,7 +839,7 @@ impl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize, const IV_SIZE: usize> Cip } #[cfg(cryp_v2)] - fn pre_final(&self, p: &pac::cryp::Cryp, dir: Direction, _padding_len: usize) -> [u32; 4] { + fn pre_final(&self, p: pac::cryp::Cryp, dir: Direction, _padding_len: usize) -> [u32; 4] { //Handle special CCM partial block process. let mut temp1 = [0; 4]; if dir == Direction::Decrypt { @@ -866,7 +858,7 @@ impl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize, const IV_SIZE: usize> Cip } #[cfg(any(cryp_v3, cryp_v4))] - fn pre_final(&self, p: &pac::cryp::Cryp, _dir: Direction, padding_len: usize) -> [u32; 4] { + fn pre_final(&self, p: pac::cryp::Cryp, _dir: Direction, padding_len: usize) -> [u32; 4] { //Handle special GCM partial block process. p.cr().modify(|w| w.set_npblb(padding_len as u8)); [0; 4] @@ -875,7 +867,7 @@ impl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize, const IV_SIZE: usize> Cip #[cfg(cryp_v2)] fn post_final_blocking( &self, - p: &pac::cryp::Cryp, + p: pac::cryp::Cryp, cryp: &Cryp, dir: Direction, int_data: &mut [u8; AES_BLOCK_SIZE], @@ -912,7 +904,7 @@ impl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize, const IV_SIZE: usize> Cip #[cfg(cryp_v2)] async fn post_final( &self, - p: &pac::cryp::Cryp, + p: pac::cryp::Cryp, cryp: &mut Cryp<'_, T, DmaIn, DmaOut>, dir: Direction, int_data: &mut [u8; AES_BLOCK_SIZE], @@ -1083,9 +1075,9 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> { // Set data type to 8-bit. This will match software implementations. T::regs().cr().modify(|w| w.set_datatype(2)); - ctx.cipher.prepare_key(&T::regs()); + ctx.cipher.prepare_key(T::regs()); - ctx.cipher.set_algomode(&T::regs()); + ctx.cipher.set_algomode(T::regs()); // Set encrypt/decrypt if dir == Direction::Encrypt { @@ -1115,7 +1107,7 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> { // Flush in/out FIFOs T::regs().cr().modify(|w| w.fflush()); - ctx.cipher.init_phase_blocking(&T::regs(), self); + ctx.cipher.init_phase_blocking(T::regs(), self); self.store_context(&mut ctx); @@ -1166,9 +1158,9 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> { // Set data type to 8-bit. This will match software implementations. T::regs().cr().modify(|w| w.set_datatype(2)); - ctx.cipher.prepare_key(&T::regs()); + ctx.cipher.prepare_key(T::regs()); - ctx.cipher.set_algomode(&T::regs()); + ctx.cipher.set_algomode(T::regs()); // Set encrypt/decrypt if dir == Direction::Encrypt { @@ -1198,7 +1190,7 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> { // Flush in/out FIFOs T::regs().cr().modify(|w| w.fflush()); - ctx.cipher.init_phase(&T::regs(), self).await; + ctx.cipher.init_phase(T::regs(), self).await; self.store_context(&mut ctx); @@ -1462,7 +1454,7 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> { // Handle the final block, which is incomplete. if last_block_remainder > 0 { let padding_len = C::BLOCK_SIZE - last_block_remainder; - let temp1 = ctx.cipher.pre_final(&T::regs(), ctx.dir, padding_len); + let temp1 = ctx.cipher.pre_final(T::regs(), ctx.dir, padding_len); let mut intermediate_data: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE]; let mut last_block: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE]; @@ -1478,7 +1470,7 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> { let mut mask: [u8; 16] = [0; 16]; mask[..last_block_remainder].fill(0xFF); ctx.cipher - .post_final_blocking(&T::regs(), self, ctx.dir, &mut intermediate_data, temp1, mask); + .post_final_blocking(T::regs(), self, ctx.dir, &mut intermediate_data, temp1, mask); } ctx.payload_len += input.len() as u64; @@ -1559,7 +1551,7 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> { // Handle the final block, which is incomplete. if last_block_remainder > 0 { let padding_len = C::BLOCK_SIZE - last_block_remainder; - let temp1 = ctx.cipher.pre_final(&T::regs(), ctx.dir, padding_len); + let temp1 = ctx.cipher.pre_final(T::regs(), ctx.dir, padding_len); let mut intermediate_data: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE]; let mut last_block: [u8; AES_BLOCK_SIZE] = [0; AES_BLOCK_SIZE]; @@ -1576,7 +1568,7 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> { let mut mask: [u8; 16] = [0; 16]; mask[..last_block_remainder].fill(0xFF); ctx.cipher - .post_final(&T::regs(), self, ctx.dir, &mut intermediate_data, temp1, mask) + .post_final(T::regs(), self, ctx.dir, &mut intermediate_data, temp1, mask) .await; } @@ -1758,7 +1750,7 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> { self.load_key(ctx.cipher.key()); // Prepare key if applicable. - ctx.cipher.prepare_key(&T::regs()); + ctx.cipher.prepare_key(T::regs()); T::regs().cr().write(|w| w.0 = ctx.cr); // Enable crypto processor. diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 8a748ad72..489342a4a 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -508,7 +508,7 @@ impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> { } trait SealedInstance { - fn regs() -> &'static crate::pac::dac::Dac; + fn regs() -> crate::pac::dac::Dac; } /// DAC instance. @@ -523,8 +523,8 @@ pub trait DacPin: crate::gpio::Pin + 'static {} foreach_peripheral!( (dac, $inst:ident) => { impl crate::dac::SealedInstance for peripherals::$inst { - fn regs() -> &'static crate::pac::dac::Dac { - &crate::pac::$inst + fn regs() -> crate::pac::dac::Dac { + crate::pac::$inst } } diff --git a/embassy-stm32/src/dsihost.rs b/embassy-stm32/src/dsihost.rs index f1c737fdc..253939394 100644 --- a/embassy-stm32/src/dsihost.rs +++ b/embassy-stm32/src/dsihost.rs @@ -407,7 +407,7 @@ impl<'d, T: Instance> Drop for DsiHost<'d, T> { } trait SealedInstance: crate::rcc::SealedRccPeripheral { - fn regs() -> &'static crate::pac::dsihost::Dsihost; + fn regs() -> crate::pac::dsihost::Dsihost; } /// DSI instance trait. @@ -419,8 +419,8 @@ pin_trait!(TePin, Instance); foreach_peripheral!( (dsihost, $inst:ident) => { impl crate::dsihost::SealedInstance for peripherals::$inst { - fn regs() -> &'static crate::pac::dsihost::Dsihost { - &crate::pac::$inst + fn regs() -> crate::pac::dsihost::Dsihost { + crate::pac::$inst } } diff --git a/embassy-stm32/src/ltdc.rs b/embassy-stm32/src/ltdc.rs index 0cc8a0557..a2d6a3cee 100644 --- a/embassy-stm32/src/ltdc.rs +++ b/embassy-stm32/src/ltdc.rs @@ -93,7 +93,7 @@ impl<'d, T: Instance> Drop for Ltdc<'d, T> { } trait SealedInstance: crate::rcc::SealedRccPeripheral { - fn regs() -> &'static crate::pac::ltdc::Ltdc; + fn regs() -> crate::pac::ltdc::Ltdc; } /// DSI instance trait. @@ -132,8 +132,8 @@ pin_trait!(B7Pin, Instance); foreach_peripheral!( (ltdc, $inst:ident) => { impl crate::ltdc::SealedInstance for peripherals::$inst { - fn regs() -> &'static crate::pac::ltdc::Ltdc { - &crate::pac::$inst + fn regs() -> crate::pac::ltdc::Ltdc { + crate::pac::$inst } } diff --git a/embassy-stm32/src/rcc/f013.rs b/embassy-stm32/src/rcc/f013.rs index 0946287ea..1c951a22b 100644 --- a/embassy-stm32/src/rcc/f013.rs +++ b/embassy-stm32/src/rcc/f013.rs @@ -276,7 +276,7 @@ pub(crate) unsafe fn init(config: Config) { // Set prescalers // CFGR has been written before (PLL, PLL48) don't overwrite these settings - RCC.cfgr().modify(|w: &mut stm32_metapac::rcc::regs::Cfgr| { + RCC.cfgr().modify(|w| { #[cfg(not(stm32f0))] { w.set_ppre1(config.apb1_pre); diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index b12a0db66..92a58ee9a 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -320,7 +320,7 @@ impl Rtc { /// The registers retain their values during wakes from standby mode or system resets. They also /// retain their value when Vdd is switched off as long as V_BAT is powered. pub fn read_backup_register(&self, register: usize) -> Option { - RTC::read_backup_register(&RTC::regs(), register) + RTC::read_backup_register(RTC::regs(), register) } /// Set content of the backup register. @@ -328,7 +328,7 @@ impl Rtc { /// The registers retain their values during wakes from standby mode or system resets. They also /// retain their value when Vdd is switched off as long as V_BAT is powered. pub fn write_backup_register(&self, register: usize, value: u32) { - RTC::write_backup_register(&RTC::regs(), register, value) + RTC::write_backup_register(RTC::regs(), register, value) } #[cfg(feature = "low-power")] @@ -482,13 +482,13 @@ trait SealedInstance { /// /// The registers retain their values during wakes from standby mode or system resets. They also /// retain their value when Vdd is switched off as long as V_BAT is powered. - fn read_backup_register(rtc: &crate::pac::rtc::Rtc, register: usize) -> Option; + fn read_backup_register(rtc: crate::pac::rtc::Rtc, register: usize) -> Option; /// Set content of the backup register. /// /// The registers retain their values during wakes from standby mode or system resets. They also /// retain their value when Vdd is switched off as long as V_BAT is powered. - fn write_backup_register(rtc: &crate::pac::rtc::Rtc, register: usize, value: u32); + fn write_backup_register(rtc: crate::pac::rtc::Rtc, register: usize, value: u32); // fn apply_config(&mut self, rtc_config: RtcConfig); } diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 92f9de846..cdc1cb299 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -95,7 +95,7 @@ impl super::Rtc { pub(super) fn write(&self, init_mode: bool, f: F) -> R where - F: FnOnce(&crate::pac::rtc::Rtc) -> R, + F: FnOnce(crate::pac::rtc::Rtc) -> R, { let r = RTC::regs(); // Disable write protection. @@ -112,7 +112,7 @@ impl super::Rtc { while !r.isr().read().initf() {} } - let result = f(&r); + let result = f(r); if init_mode { r.isr().modify(|w| w.set_init(false)); // Exits init mode @@ -140,7 +140,7 @@ impl SealedInstance for crate::peripherals::RTC { #[cfg(all(feature = "low-power", stm32l0))] type WakeupInterrupt = crate::interrupt::typelevel::RTC; - fn read_backup_register(rtc: &Rtc, register: usize) -> Option { + fn read_backup_register(rtc: Rtc, register: usize) -> Option { if register < Self::BACKUP_REGISTER_COUNT { Some(rtc.bkpr(register).read().bkp()) } else { @@ -148,7 +148,7 @@ impl SealedInstance for crate::peripherals::RTC { } } - fn write_backup_register(rtc: &Rtc, register: usize, value: u32) { + fn write_backup_register(rtc: Rtc, register: usize, value: u32) { if register < Self::BACKUP_REGISTER_COUNT { rtc.bkpr(register).write(|w| w.set_bkp(value)); } diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index e51e09e7c..02fd5272e 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -97,7 +97,7 @@ impl super::Rtc { pub(super) fn write(&self, init_mode: bool, f: F) -> R where - F: FnOnce(&crate::pac::rtc::Rtc) -> R, + F: FnOnce(crate::pac::rtc::Rtc) -> R, { let r = RTC::regs(); // Disable write protection. @@ -112,7 +112,7 @@ impl super::Rtc { while !r.icsr().read().initf() {} } - let result = f(&r); + let result = f(r); if init_mode { r.icsr().modify(|w| w.set_init(false)); // Exits init mode @@ -143,7 +143,7 @@ impl SealedInstance for crate::peripherals::RTC { } ); - fn read_backup_register(_rtc: &Rtc, register: usize) -> Option { + fn read_backup_register(_rtc: Rtc, register: usize) -> Option { #[allow(clippy::if_same_then_else)] if register < Self::BACKUP_REGISTER_COUNT { //Some(rtc.bkpr()[register].read().bits()) @@ -153,7 +153,7 @@ impl SealedInstance for crate::peripherals::RTC { } } - fn write_backup_register(_rtc: &Rtc, register: usize, _value: u32) { + fn write_backup_register(_rtc: Rtc, register: usize, _value: u32) { if register < Self::BACKUP_REGISTER_COUNT { // RTC3 backup registers come from the TAMP peripheral, not RTC. Not() even in the L412 PAC //self.rtc.bkpr()[register].write(|w| w.bits(value)) From 2c691baadd28f0072c606a843ba14db1ea08ef86 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 30 May 2024 13:26:14 +0200 Subject: [PATCH 26/57] stm32/rtc: move lowpower stuff to a separate mod. --- embassy-stm32/src/rtc/datetime.rs | 59 -------- embassy-stm32/src/rtc/low_power.rs | 230 +++++++++++++++++++++++++++++ embassy-stm32/src/rtc/mod.rs | 196 +----------------------- 3 files changed, 236 insertions(+), 249 deletions(-) create mode 100644 embassy-stm32/src/rtc/low_power.rs diff --git a/embassy-stm32/src/rtc/datetime.rs b/embassy-stm32/src/rtc/datetime.rs index 77d89293d..32732e96e 100644 --- a/embassy-stm32/src/rtc/datetime.rs +++ b/embassy-stm32/src/rtc/datetime.rs @@ -1,65 +1,6 @@ #[cfg(feature = "chrono")] use chrono::{Datelike, NaiveDate, Timelike, Weekday}; -#[cfg(any(feature = "defmt", feature = "time"))] -use crate::peripherals::RTC; -#[cfg(any(feature = "defmt", feature = "time"))] -use crate::rtc::SealedInstance; - -/// Represents an instant in time that can be substracted to compute a duration -pub struct RtcInstant { - /// 0..59 - pub second: u8, - /// 0..256 - pub subsecond: u16, -} - -impl RtcInstant { - #[cfg(not(rtc_v2f2))] - pub(super) const fn from(second: u8, subsecond: u16) -> Result { - if second > 59 { - Err(Error::InvalidSecond) - } else { - Ok(Self { second, subsecond }) - } - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for RtcInstant { - fn format(&self, fmt: defmt::Formatter) { - defmt::write!( - fmt, - "{}:{}", - self.second, - RTC::regs().prer().read().prediv_s() - self.subsecond, - ) - } -} - -#[cfg(feature = "time")] -impl core::ops::Sub for RtcInstant { - type Output = embassy_time::Duration; - - fn sub(self, rhs: Self) -> Self::Output { - use embassy_time::{Duration, TICK_HZ}; - - let second = if self.second < rhs.second { - self.second + 60 - } else { - self.second - }; - - let psc = RTC::regs().prer().read().prediv_s() as u32; - - let self_ticks = second as u32 * (psc + 1) + (psc - self.subsecond as u32); - let other_ticks = rhs.second as u32 * (psc + 1) + (psc - rhs.subsecond as u32); - let rtc_ticks = self_ticks - other_ticks; - - Duration::from_ticks(((rtc_ticks * TICK_HZ as u32) / (psc + 1)) as u64) - } -} - /// Errors regarding the [`DateTime`] struct. #[derive(Clone, Debug, PartialEq, Eq)] pub enum Error { diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs new file mode 100644 index 000000000..a4ff1acbc --- /dev/null +++ b/embassy-stm32/src/rtc/low_power.rs @@ -0,0 +1,230 @@ +use super::{bcd2_to_byte, DateTimeError, Rtc, RtcError}; +use crate::peripherals::RTC; +use crate::rtc::SealedInstance; + +/// Represents an instant in time that can be substracted to compute a duration +pub(super) struct RtcInstant { + /// 0..59 + second: u8, + /// 0..256 + subsecond: u16, +} + +impl RtcInstant { + #[cfg(not(rtc_v2f2))] + const fn from(second: u8, subsecond: u16) -> Result { + if second > 59 { + Err(DateTimeError::InvalidSecond) + } else { + Ok(Self { second, subsecond }) + } + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for RtcInstant { + fn format(&self, fmt: defmt::Formatter) { + defmt::write!( + fmt, + "{}:{}", + self.second, + RTC::regs().prer().read().prediv_s() - self.subsecond, + ) + } +} + +#[cfg(feature = "time")] +impl core::ops::Sub for RtcInstant { + type Output = embassy_time::Duration; + + fn sub(self, rhs: Self) -> Self::Output { + use embassy_time::{Duration, TICK_HZ}; + + let second = if self.second < rhs.second { + self.second + 60 + } else { + self.second + }; + + let psc = RTC::regs().prer().read().prediv_s() as u32; + + let self_ticks = second as u32 * (psc + 1) + (psc - self.subsecond as u32); + let other_ticks = rhs.second as u32 * (psc + 1) + (psc - rhs.subsecond as u32); + let rtc_ticks = self_ticks - other_ticks; + + Duration::from_ticks(((rtc_ticks * TICK_HZ as u32) / (psc + 1)) as u64) + } +} + +#[repr(u8)] +#[derive(Clone, Copy, Debug)] +pub(crate) enum WakeupPrescaler { + Div2 = 2, + Div4 = 4, + Div8 = 8, + Div16 = 16, +} + +#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l5, stm32wb, stm32h5, stm32g0))] +impl From for crate::pac::rtc::vals::Wucksel { + fn from(val: WakeupPrescaler) -> Self { + use crate::pac::rtc::vals::Wucksel; + + match val { + WakeupPrescaler::Div2 => Wucksel::DIV2, + WakeupPrescaler::Div4 => Wucksel::DIV4, + WakeupPrescaler::Div8 => Wucksel::DIV8, + WakeupPrescaler::Div16 => Wucksel::DIV16, + } + } +} + +#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l5, stm32wb, stm32h5, stm32g0))] +impl From for WakeupPrescaler { + fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { + use crate::pac::rtc::vals::Wucksel; + + match val { + Wucksel::DIV2 => WakeupPrescaler::Div2, + Wucksel::DIV4 => WakeupPrescaler::Div4, + Wucksel::DIV8 => WakeupPrescaler::Div8, + Wucksel::DIV16 => WakeupPrescaler::Div16, + _ => unreachable!(), + } + } +} + +impl WakeupPrescaler { + pub fn compute_min(val: u32) -> Self { + *[ + WakeupPrescaler::Div2, + WakeupPrescaler::Div4, + WakeupPrescaler::Div8, + WakeupPrescaler::Div16, + ] + .iter() + .find(|psc| **psc as u32 > val) + .unwrap_or(&WakeupPrescaler::Div16) + } +} + +impl Rtc { + /// Return the current instant. + fn instant(&self) -> Result { + self.time_provider().read(|_, tr, ss| { + let second = bcd2_to_byte((tr.st(), tr.su())); + + RtcInstant::from(second, ss).map_err(RtcError::InvalidDateTime) + }) + } + + /// start the wakeup alarm and with a duration that is as close to but less than + /// the requested duration, and record the instant the wakeup alarm was started + pub(crate) fn start_wakeup_alarm( + &self, + requested_duration: embassy_time::Duration, + cs: critical_section::CriticalSection, + ) { + use embassy_time::{Duration, TICK_HZ}; + + #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] + use crate::pac::rtc::vals::Calrf; + + // Panic if the rcc mod knows we're not using low-power rtc + #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] + unsafe { crate::rcc::get_freqs() }.rtc.unwrap(); + + let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64); + let rtc_hz = Self::frequency().0 as u64; + let rtc_ticks = requested_duration * rtc_hz / TICK_HZ; + let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); + + // adjust the rtc ticks to the prescaler and subtract one rtc tick + let rtc_ticks = rtc_ticks / prescaler as u64; + let rtc_ticks = rtc_ticks.clamp(0, (u16::MAX - 1) as u64).saturating_sub(1) as u16; + + self.write(false, |regs| { + regs.cr().modify(|w| w.set_wute(false)); + + #[cfg(any( + rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb + ))] + { + regs.isr().modify(|w| w.set_wutf(false)); + while !regs.isr().read().wutwf() {} + } + + #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] + { + regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR)); + while !regs.icsr().read().wutwf() {} + } + + regs.cr().modify(|w| w.set_wucksel(prescaler.into())); + regs.wutr().write(|w| w.set_wut(rtc_ticks)); + regs.cr().modify(|w| w.set_wute(true)); + regs.cr().modify(|w| w.set_wutie(true)); + }); + + let instant = self.instant().unwrap(); + trace!( + "rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}", + Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(), + prescaler as u32, + rtc_ticks, + instant, + ); + + assert!(self.stop_time.borrow(cs).replace(Some(instant)).is_none()) + } + + /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` + /// was called, otherwise none + pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option { + use crate::interrupt::typelevel::Interrupt; + #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] + use crate::pac::rtc::vals::Calrf; + + let instant = self.instant().unwrap(); + if RTC::regs().cr().read().wute() { + trace!("rtc: stop wakeup alarm at {}", instant); + + self.write(false, |regs| { + regs.cr().modify(|w| w.set_wutie(false)); + regs.cr().modify(|w| w.set_wute(false)); + + #[cfg(any( + rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb + ))] + regs.isr().modify(|w| w.set_wutf(false)); + + #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] + regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR)); + + // Check RM for EXTI and/or NVIC section, "Event event input mapping" or "EXTI interrupt/event mapping" or something similar, + // there is a table for every "Event input" / "EXTI Line". + // If you find the EXTI line related to "RTC wakeup" marks as "Configurable" (not "Direct"), + // then write 1 to related field of Pending Register, to clean it's pending state. + #[cfg(any(exti_v1, stm32h7, stm32wb))] + crate::pac::EXTI + .pr(0) + .modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); + + ::WakeupInterrupt::unpend(); + }); + } + + self.stop_time.borrow(cs).take().map(|stop_time| instant - stop_time) + } + + pub(crate) fn enable_wakeup_line(&self) { + use crate::interrupt::typelevel::Interrupt; + use crate::pac::EXTI; + + ::WakeupInterrupt::unpend(); + unsafe { ::WakeupInterrupt::enable() }; + + EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); + EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); + } +} diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 92a58ee9a..cb9c10676 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -1,6 +1,9 @@ //! Real Time Clock (RTC) mod datetime; +#[cfg(feature = "low-power")] +mod low_power; + #[cfg(feature = "low-power")] use core::cell::Cell; @@ -9,8 +12,6 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; #[cfg(feature = "low-power")] use embassy_sync::blocking_mutex::Mutex; -#[cfg(not(rtc_v2f2))] -use self::datetime::RtcInstant; use self::datetime::{day_of_week_from_u8, day_of_week_to_u8}; pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; use crate::pac::rtc::regs::{Dr, Tr}; @@ -32,60 +33,6 @@ use embassy_hal_internal::Peripheral; use crate::peripherals::RTC; -#[allow(dead_code)] -#[repr(u8)] -#[derive(Clone, Copy, Debug)] -pub(crate) enum WakeupPrescaler { - Div2 = 2, - Div4 = 4, - Div8 = 8, - Div16 = 16, -} - -#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l5, stm32wb, stm32h5, stm32g0))] -impl From for crate::pac::rtc::vals::Wucksel { - fn from(val: WakeupPrescaler) -> Self { - use crate::pac::rtc::vals::Wucksel; - - match val { - WakeupPrescaler::Div2 => Wucksel::DIV2, - WakeupPrescaler::Div4 => Wucksel::DIV4, - WakeupPrescaler::Div8 => Wucksel::DIV8, - WakeupPrescaler::Div16 => Wucksel::DIV16, - } - } -} - -#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l5, stm32wb, stm32h5, stm32g0))] -impl From for WakeupPrescaler { - fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { - use crate::pac::rtc::vals::Wucksel; - - match val { - Wucksel::DIV2 => WakeupPrescaler::Div2, - Wucksel::DIV4 => WakeupPrescaler::Div4, - Wucksel::DIV8 => WakeupPrescaler::Div8, - Wucksel::DIV16 => WakeupPrescaler::Div16, - _ => unreachable!(), - } - } -} - -#[cfg(feature = "low-power")] -impl WakeupPrescaler { - pub fn compute_min(val: u32) -> Self { - *[ - WakeupPrescaler::Div2, - WakeupPrescaler::Div4, - WakeupPrescaler::Div8, - WakeupPrescaler::Div16, - ] - .iter() - .find(|psc| **psc as u32 > val) - .unwrap_or(&WakeupPrescaler::Div16) - } -} - /// Errors that can occur on methods on [RtcClock] #[non_exhaustive] #[derive(Clone, Debug, PartialEq, Eq)] @@ -106,15 +53,6 @@ pub struct RtcTimeProvider { } impl RtcTimeProvider { - #[cfg(not(rtc_v2f2))] - pub(crate) fn instant(&self) -> Result { - self.read(|_, tr, ss| { - let second = bcd2_to_byte((tr.st(), tr.su())); - - RtcInstant::from(second, ss).map_err(RtcError::InvalidDateTime) - }) - } - /// Return the current datetime. /// /// # Errors @@ -165,8 +103,7 @@ impl RtcTimeProvider { /// RTC driver. pub struct Rtc { #[cfg(feature = "low-power")] - stop_time: Mutex>>, - #[cfg(not(feature = "low-power"))] + stop_time: Mutex>>, _private: (), } @@ -210,7 +147,6 @@ impl Rtc { let mut this = Self { #[cfg(feature = "low-power")] stop_time: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), - #[cfg(not(feature = "low-power"))] _private: (), }; @@ -223,9 +159,8 @@ impl Rtc { // Wait for the clock to update after initialization #[cfg(not(rtc_v2f2))] { - let now = this.instant().unwrap(); - - while this.instant().unwrap().subsecond == now.subsecond {} + let now = this.time_provider().read(|_, _, ss| Ok(ss)).unwrap(); + while now == this.time_provider().read(|_, _, ss| Ok(ss)).unwrap() {} } this @@ -284,12 +219,6 @@ impl Rtc { Ok(()) } - #[cfg(not(rtc_v2f2))] - /// Return the current instant. - fn instant(&self) -> Result { - self.time_provider().instant() - } - /// Return the current datetime. /// /// # Errors @@ -330,119 +259,6 @@ impl Rtc { pub fn write_backup_register(&self, register: usize, value: u32) { RTC::write_backup_register(RTC::regs(), register, value) } - - #[cfg(feature = "low-power")] - /// start the wakeup alarm and with a duration that is as close to but less than - /// the requested duration, and record the instant the wakeup alarm was started - pub(crate) fn start_wakeup_alarm( - &self, - requested_duration: embassy_time::Duration, - cs: critical_section::CriticalSection, - ) { - use embassy_time::{Duration, TICK_HZ}; - - #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] - use crate::pac::rtc::vals::Calrf; - - // Panic if the rcc mod knows we're not using low-power rtc - #[cfg(any(rcc_wb, rcc_f4, rcc_f410))] - unsafe { crate::rcc::get_freqs() }.rtc.unwrap(); - - let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64); - let rtc_hz = Self::frequency().0 as u64; - let rtc_ticks = requested_duration * rtc_hz / TICK_HZ; - let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); - - // adjust the rtc ticks to the prescaler and subtract one rtc tick - let rtc_ticks = rtc_ticks / prescaler as u64; - let rtc_ticks = rtc_ticks.clamp(0, (u16::MAX - 1) as u64).saturating_sub(1) as u16; - - self.write(false, |regs| { - regs.cr().modify(|w| w.set_wute(false)); - - #[cfg(any( - rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb - ))] - { - regs.isr().modify(|w| w.set_wutf(false)); - while !regs.isr().read().wutwf() {} - } - - #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] - { - regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR)); - while !regs.icsr().read().wutwf() {} - } - - regs.cr().modify(|w| w.set_wucksel(prescaler.into())); - regs.wutr().write(|w| w.set_wut(rtc_ticks)); - regs.cr().modify(|w| w.set_wute(true)); - regs.cr().modify(|w| w.set_wutie(true)); - }); - - let instant = self.instant().unwrap(); - trace!( - "rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}", - Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(), - prescaler as u32, - rtc_ticks, - instant, - ); - - assert!(self.stop_time.borrow(cs).replace(Some(instant)).is_none()) - } - - #[cfg(feature = "low-power")] - /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm` - /// was called, otherwise none - pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option { - use crate::interrupt::typelevel::Interrupt; - #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] - use crate::pac::rtc::vals::Calrf; - - let instant = self.instant().unwrap(); - if RTC::regs().cr().read().wute() { - trace!("rtc: stop wakeup alarm at {}", instant); - - self.write(false, |regs| { - regs.cr().modify(|w| w.set_wutie(false)); - regs.cr().modify(|w| w.set_wute(false)); - - #[cfg(any( - rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb - ))] - regs.isr().modify(|w| w.set_wutf(false)); - - #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))] - regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR)); - - // Check RM for EXTI and/or NVIC section, "Event event input mapping" or "EXTI interrupt/event mapping" or something similar, - // there is a table for every "Event input" / "EXTI Line". - // If you find the EXTI line related to "RTC wakeup" marks as "Configurable" (not "Direct"), - // then write 1 to related field of Pending Register, to clean it's pending state. - #[cfg(any(exti_v1, stm32h7, stm32wb))] - crate::pac::EXTI - .pr(0) - .modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); - - ::WakeupInterrupt::unpend(); - }); - } - - self.stop_time.borrow(cs).take().map(|stop_time| instant - stop_time) - } - - #[cfg(feature = "low-power")] - pub(crate) fn enable_wakeup_line(&self) { - use crate::interrupt::typelevel::Interrupt; - use crate::pac::EXTI; - - ::WakeupInterrupt::unpend(); - unsafe { ::WakeupInterrupt::enable() }; - - EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); - EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); - } } pub(crate) fn byte_to_bcd2(byte: u8) -> (u8, u8) { From 245c895d090b93582e23fc1573b244bdc5087d72 Mon Sep 17 00:00:00 2001 From: Corey Schuhen Date: Thu, 30 May 2024 21:23:12 +1000 Subject: [PATCH 27/57] Remove generics for BXCAN. --- embassy-stm32/src/can/bxcan/mod.rs | 296 +++++++++++++++-------- embassy-stm32/src/can/bxcan/registers.rs | 12 +- tests/stm32/src/bin/can.rs | 4 +- 3 files changed, 200 insertions(+), 112 deletions(-) diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index 0ac4cdab6..4f516c917 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs @@ -5,6 +5,7 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::task::Poll; +use embassy_hal_internal::interrupt::InterruptExt; use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; @@ -154,7 +155,10 @@ impl Drop for CanConfig<'_, T> { /// CAN driver pub struct Can<'d, T: Instance> { - peri: PeripheralRef<'d, T>, + _peri: PeripheralRef<'d, T>, + instance: &'d crate::pac::can::Can, + info: &'static Info, + state: &'static State, } /// Error returned by `try_write` @@ -179,6 +183,8 @@ impl<'d, T: Instance> Can<'d, T> { + 'd, ) -> Self { into_ref!(peri, rx, tx); + let info = T::info(); + let regs = &T::info().regs; rx.set_as_af(rx.af_num(), AFType::Input); tx.set_as_af(tx.af_num(), AFType::OutputPushPull); @@ -186,7 +192,7 @@ impl<'d, T: Instance> Can<'d, T> { T::enable_and_reset(); { - T::regs().ier().write(|w| { + regs.0.ier().write(|w| { w.set_errie(true); w.set_fmpie(0, true); w.set_fmpie(1, true); @@ -197,7 +203,7 @@ impl<'d, T: Instance> Can<'d, T> { w.set_lecie(true); }); - T::regs().mcr().write(|w| { + regs.0.mcr().write(|w| { // Enable timestamps on rx messages w.set_ttcm(true); @@ -205,17 +211,14 @@ impl<'d, T: Instance> Can<'d, T> { } unsafe { - T::TXInterrupt::unpend(); - T::TXInterrupt::enable(); - - T::RX0Interrupt::unpend(); - T::RX0Interrupt::enable(); - - T::RX1Interrupt::unpend(); - T::RX1Interrupt::enable(); - - T::SCEInterrupt::unpend(); - T::SCEInterrupt::enable(); + info.tx_interrupt.unpend(); + info.tx_interrupt.enable(); + info.rx0_interrupt.unpend(); + info.rx0_interrupt.enable(); + info.rx1_interrupt.unpend(); + info.rx1_interrupt.enable(); + info.sce_interrupt.unpend(); + info.sce_interrupt.enable(); } rx.set_as_af(rx.af_num(), AFType::Input); @@ -223,7 +226,12 @@ impl<'d, T: Instance> Can<'d, T> { Registers(T::regs()).leave_init_mode(); - Self { peri } + Self { + _peri: peri, + instance: &T::info().regs.0, + info: T::info(), + state: T::state(), + } } /// Set CAN bit rate. @@ -265,12 +273,12 @@ impl<'d, T: Instance> Can<'d, T> { /// Waking the peripheral manually does not trigger a wake-up interrupt. /// This will wait until the peripheral has acknowledged it has awoken from sleep mode pub fn wakeup(&mut self) { - Registers(T::regs()).wakeup() + self.info.regs.wakeup() } /// Check if the peripheral is currently in sleep mode pub fn is_sleeping(&self) -> bool { - T::regs().msr().read().slak() + self.info.regs.0.msr().read().slak() } /// Put the peripheral in sleep mode @@ -282,11 +290,11 @@ impl<'d, T: Instance> Can<'d, T> { /// If the peripheral has automatic wakeup enabled, when a Start-of-Frame is detected /// the peripheral will automatically wake and receive the incoming message. pub async fn sleep(&mut self) { - T::regs().ier().modify(|i| i.set_slkie(true)); - T::regs().mcr().modify(|m| m.set_sleep(true)); + self.info.regs.0.ier().modify(|i| i.set_slkie(true)); + self.info.regs.0.mcr().modify(|m| m.set_sleep(true)); poll_fn(|cx| { - T::state().err_waker.register(cx.waker()); + self.state.err_waker.register(cx.waker()); if self.is_sleeping() { Poll::Ready(()) } else { @@ -295,7 +303,7 @@ impl<'d, T: Instance> Can<'d, T> { }) .await; - T::regs().ier().modify(|i| i.set_slkie(false)); + self.info.regs.0.ier().modify(|i| i.set_slkie(false)); } /// Enable FIFO scheduling of outgoing frames. @@ -337,7 +345,13 @@ impl<'d, T: Instance> Can<'d, T> { /// Waits for a specific transmit mailbox to become empty pub async fn flush(&self, mb: Mailbox) { - CanTx::::flush_inner(mb).await + CanTx { + _instance: &self.instance, + info: self.info, + state: self.state, + } + .flush_inner(mb) + .await; } /// Waits until any of the transmit mailboxes become empty @@ -347,12 +361,24 @@ impl<'d, T: Instance> Can<'d, T> { /// This will happen if FIFO scheduling of outgoing frames is not enabled, /// and a frame with equal priority is already queued for transmission. pub async fn flush_any(&self) { - CanTx::::flush_any_inner().await + CanTx { + _instance: &self.instance, + info: self.info, + state: self.state, + } + .flush_any_inner() + .await } /// Waits until all of the transmit mailboxes become empty pub async fn flush_all(&self) { - CanTx::::flush_all_inner().await + CanTx { + _instance: &self.instance, + info: self.info, + state: self.state, + } + .flush_all_inner() + .await } /// Attempts to abort the sending of a frame that is pending in a mailbox. @@ -363,12 +389,12 @@ impl<'d, T: Instance> Can<'d, T> { /// If there is a frame in the provided mailbox, and it is canceled successfully, this function /// returns `true`. pub fn abort(&mut self, mailbox: Mailbox) -> bool { - Registers(T::regs()).abort(mailbox) + self.info.regs.abort(mailbox) } /// Returns `true` if no frame is pending for transmission. pub fn is_transmitter_idle(&self) -> bool { - Registers(T::regs()).is_idle() + self.info.regs.is_idle() } /// Read a CAN frame. @@ -377,31 +403,35 @@ impl<'d, T: Instance> Can<'d, T> { /// /// Returns a tuple of the time the message was received and the message frame pub async fn read(&mut self) -> Result { - T::state().rx_mode.read::().await + self.state.rx_mode.read(self.info, self.state).await } /// Attempts to read a CAN frame without blocking. /// /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. pub fn try_read(&mut self) -> Result { - T::state().rx_mode.try_read::() + self.state.rx_mode.try_read(self.info) } /// Waits while receive queue is empty. pub async fn wait_not_empty(&mut self) { - T::state().rx_mode.wait_not_empty::().await + self.state.rx_mode.wait_not_empty(self.info, self.state).await } /// Split the CAN driver into transmit and receive halves. /// /// Useful for doing separate transmit/receive tasks. - pub fn split<'c>(&'c mut self) -> (CanTx<'d, T>, CanRx<'d, T>) { + pub fn split<'c>(&'c mut self) -> (CanTx<'d>, CanRx<'d>) { ( CanTx { - _peri: unsafe { self.peri.clone_unchecked() }, + _instance: &self.instance, + info: self.info, + state: self.state, }, CanRx { - peri: unsafe { self.peri.clone_unchecked() }, + instance: &self.instance, + info: self.info, + state: self.state, }, ) } @@ -411,7 +441,7 @@ impl<'d, T: Instance> Can<'d, T> { &'c mut self, txb: &'static mut TxBuf, rxb: &'static mut RxBuf, - ) -> BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> { + ) -> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { let (tx, rx) = self.split(); BufferedCan { tx: tx.buffered(txb), @@ -426,17 +456,17 @@ impl<'d, T: FilterOwner> Can<'d, T> { /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master /// peripheral instead. pub fn modify_filters(&mut self) -> MasterFilters<'_, T> { - unsafe { MasterFilters::new(T::regs()) } + unsafe { MasterFilters::new(self.info.regs.0) } } } /// Buffered CAN driver. -pub struct BufferedCan<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { - tx: BufferedCanTx<'d, T, TX_BUF_SIZE>, - rx: BufferedCanRx<'d, T, RX_BUF_SIZE>, +pub struct BufferedCan<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { + tx: BufferedCanTx<'d, TX_BUF_SIZE>, + rx: BufferedCanRx<'d, RX_BUF_SIZE>, } -impl<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> { +impl<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { /// Async write frame to TX buffer. pub async fn write(&mut self, frame: &Frame) { self.tx.write(frame).await @@ -471,18 +501,20 @@ impl<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Buffer } /// CAN driver, transmit half. -pub struct CanTx<'d, T: Instance> { - _peri: PeripheralRef<'d, T>, +pub struct CanTx<'d> { + _instance: &'d crate::pac::can::Can, + info: &'static Info, + state: &'static State, } -impl<'d, T: Instance> CanTx<'d, T> { +impl<'d> CanTx<'d> { /// Queues the message to be sent. /// /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure. pub async fn write(&mut self, frame: &Frame) -> TransmitStatus { poll_fn(|cx| { - T::state().tx_mode.register(cx.waker()); - if let Ok(status) = Registers(T::regs()).transmit(frame) { + self.state.tx_mode.register(cx.waker()); + if let Ok(status) = self.info.regs.transmit(frame) { return Poll::Ready(status); } @@ -501,13 +533,13 @@ impl<'d, T: Instance> CanTx<'d, T> { /// This is done to work around a hardware limitation that could lead to out-of-order delivery /// of frames with the same priority. pub fn try_write(&mut self, frame: &Frame) -> Result { - Registers(T::regs()).transmit(frame).map_err(|_| TryWriteError::Full) + self.info.regs.transmit(frame).map_err(|_| TryWriteError::Full) } - async fn flush_inner(mb: Mailbox) { + async fn flush_inner(&self, mb: Mailbox) { poll_fn(|cx| { - T::state().tx_mode.register(cx.waker()); - if T::regs().tsr().read().tme(mb.index()) { + self.state.tx_mode.register(cx.waker()); + if self.info.regs.0.tsr().read().tme(mb.index()) { return Poll::Ready(()); } @@ -518,14 +550,14 @@ impl<'d, T: Instance> CanTx<'d, T> { /// Waits for a specific transmit mailbox to become empty pub async fn flush(&self, mb: Mailbox) { - Self::flush_inner(mb).await + self.flush_inner(mb).await } - async fn flush_any_inner() { + async fn flush_any_inner(&self) { poll_fn(|cx| { - T::state().tx_mode.register(cx.waker()); + self.state.tx_mode.register(cx.waker()); - let tsr = T::regs().tsr().read(); + let tsr = self.info.regs.0.tsr().read(); if tsr.tme(Mailbox::Mailbox0.index()) || tsr.tme(Mailbox::Mailbox1.index()) || tsr.tme(Mailbox::Mailbox2.index()) @@ -545,14 +577,14 @@ impl<'d, T: Instance> CanTx<'d, T> { /// This will happen if FIFO scheduling of outgoing frames is not enabled, /// and a frame with equal priority is already queued for transmission. pub async fn flush_any(&self) { - Self::flush_any_inner().await + self.flush_any_inner().await } - async fn flush_all_inner() { + async fn flush_all_inner(&self) { poll_fn(|cx| { - T::state().tx_mode.register(cx.waker()); + self.state.tx_mode.register(cx.waker()); - let tsr = T::regs().tsr().read(); + let tsr = self.info.regs.0.tsr().read(); if tsr.tme(Mailbox::Mailbox0.index()) && tsr.tme(Mailbox::Mailbox1.index()) && tsr.tme(Mailbox::Mailbox2.index()) @@ -567,7 +599,7 @@ impl<'d, T: Instance> CanTx<'d, T> { /// Waits until all of the transmit mailboxes become empty pub async fn flush_all(&self) { - Self::flush_all_inner().await + self.flush_all_inner().await } /// Attempts to abort the sending of a frame that is pending in a mailbox. @@ -578,20 +610,20 @@ impl<'d, T: Instance> CanTx<'d, T> { /// If there is a frame in the provided mailbox, and it is canceled successfully, this function /// returns `true`. pub fn abort(&mut self, mailbox: Mailbox) -> bool { - Registers(T::regs()).abort(mailbox) + self.info.regs.abort(mailbox) } /// Returns `true` if no frame is pending for transmission. pub fn is_idle(&self) -> bool { - Registers(T::regs()).is_idle() + self.info.regs.is_idle() } /// Return a buffered instance of driver. User must supply Buffers pub fn buffered( self, txb: &'static mut TxBuf, - ) -> BufferedCanTx<'d, T, TX_BUF_SIZE> { - BufferedCanTx::new(self, txb) + ) -> BufferedCanTx<'d, TX_BUF_SIZE> { + BufferedCanTx::new(self.info, self.state, self, txb) } } @@ -599,23 +631,35 @@ impl<'d, T: Instance> CanTx<'d, T> { pub type TxBuf = Channel; /// Buffered CAN driver, transmit half. -pub struct BufferedCanTx<'d, T: Instance, const TX_BUF_SIZE: usize> { - _tx: CanTx<'d, T>, +pub struct BufferedCanTx<'d, const TX_BUF_SIZE: usize> { + info: &'static Info, + state: &'static State, + _tx: CanTx<'d>, tx_buf: &'static TxBuf, } -impl<'d, T: Instance, const TX_BUF_SIZE: usize> BufferedCanTx<'d, T, TX_BUF_SIZE> { - fn new(_tx: CanTx<'d, T>, tx_buf: &'static TxBuf) -> Self { - Self { _tx, tx_buf }.setup() +impl<'d, const TX_BUF_SIZE: usize> BufferedCanTx<'d, TX_BUF_SIZE> { + fn new(info: &'static Info, state: &'static State, _tx: CanTx<'d>, tx_buf: &'static TxBuf) -> Self { + Self { + info, + state, + _tx, + tx_buf, + } + .setup() } fn setup(self) -> Self { // We don't want interrupts being processed while we change modes. - critical_section::with(|_| unsafe { + critical_section::with(|_| { let tx_inner = super::common::ClassicBufferedTxInner { tx_receiver: self.tx_buf.receiver().into(), }; - T::mut_state().tx_mode = TxMode::Buffered(tx_inner); + let state = self.state as *const State; + unsafe { + let mut_state = state as *mut State; + (*mut_state).tx_mode = TxMode::Buffered(tx_inner); + } }); self } @@ -623,60 +667,67 @@ impl<'d, T: Instance, const TX_BUF_SIZE: usize> BufferedCanTx<'d, T, TX_BUF_SIZE /// Async write frame to TX buffer. pub async fn write(&mut self, frame: &Frame) { self.tx_buf.send(*frame).await; - T::TXInterrupt::pend(); // Wake for Tx + let waker = self.info.tx_waker; + waker(); // Wake for Tx } /// Returns a sender that can be used for sending CAN frames. pub fn writer(&self) -> BufferedCanSender { BufferedCanSender { tx_buf: self.tx_buf.sender().into(), - waker: T::TXInterrupt::pend, + waker: self.info.tx_waker, } } } -impl<'d, T: Instance, const TX_BUF_SIZE: usize> Drop for BufferedCanTx<'d, T, TX_BUF_SIZE> { +impl<'d, const TX_BUF_SIZE: usize> Drop for BufferedCanTx<'d, TX_BUF_SIZE> { fn drop(&mut self) { - critical_section::with(|_| unsafe { - T::mut_state().tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + critical_section::with(|_| { + let state = self.state as *const State; + unsafe { + let mut_state = state as *mut State; + (*mut_state).tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + } }); } } /// CAN driver, receive half. #[allow(dead_code)] -pub struct CanRx<'d, T: Instance> { - peri: PeripheralRef<'d, T>, +pub struct CanRx<'d> { + instance: &'d crate::pac::can::Can, + info: &'static Info, + state: &'static State, } -impl<'d, T: Instance> CanRx<'d, T> { +impl<'d> CanRx<'d> { /// Read a CAN frame. /// /// If no CAN frame is in the RX buffer, this will wait until there is one. /// /// Returns a tuple of the time the message was received and the message frame pub async fn read(&mut self) -> Result { - T::state().rx_mode.read::().await + self.state.rx_mode.read(self.info, self.state).await } /// Attempts to read a CAN frame without blocking. /// /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. pub fn try_read(&mut self) -> Result { - T::state().rx_mode.try_read::() + self.state.rx_mode.try_read(self.info) } /// Waits while receive queue is empty. pub async fn wait_not_empty(&mut self) { - T::state().rx_mode.wait_not_empty::().await + self.state.rx_mode.wait_not_empty(self.info, self.state).await } /// Return a buffered instance of driver. User must supply Buffers pub fn buffered( self, rxb: &'static mut RxBuf, - ) -> BufferedCanRx<'d, T, RX_BUF_SIZE> { - BufferedCanRx::new(self, rxb) + ) -> BufferedCanRx<'d, RX_BUF_SIZE> { + BufferedCanRx::new(self.info, self.state, self, rxb) } } @@ -684,23 +735,35 @@ impl<'d, T: Instance> CanRx<'d, T> { pub type RxBuf = Channel, BUF_SIZE>; /// CAN driver, receive half in Buffered mode. -pub struct BufferedCanRx<'d, T: Instance, const RX_BUF_SIZE: usize> { - _rx: CanRx<'d, T>, +pub struct BufferedCanRx<'d, const RX_BUF_SIZE: usize> { + info: &'static Info, + state: &'static State, + _rx: CanRx<'d>, rx_buf: &'static RxBuf, } -impl<'d, T: Instance, const RX_BUF_SIZE: usize> BufferedCanRx<'d, T, RX_BUF_SIZE> { - fn new(_rx: CanRx<'d, T>, rx_buf: &'static RxBuf) -> Self { - BufferedCanRx { _rx, rx_buf }.setup() +impl<'d, const RX_BUF_SIZE: usize> BufferedCanRx<'d, RX_BUF_SIZE> { + fn new(info: &'static Info, state: &'static State, _rx: CanRx<'d>, rx_buf: &'static RxBuf) -> Self { + BufferedCanRx { + info, + state, + _rx, + rx_buf, + } + .setup() } fn setup(self) -> Self { // We don't want interrupts being processed while we change modes. - critical_section::with(|_| unsafe { + critical_section::with(|_| { let rx_inner = super::common::ClassicBufferedRxInner { rx_sender: self.rx_buf.sender().into(), }; - T::mut_state().rx_mode = RxMode::Buffered(rx_inner); + let state = self.state as *const State; + unsafe { + let mut_state = state as *mut State; + (*mut_state).rx_mode = RxMode::Buffered(rx_inner); + } }); self } @@ -714,7 +777,7 @@ impl<'d, T: Instance, const RX_BUF_SIZE: usize> BufferedCanRx<'d, T, RX_BUF_SIZE /// /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. pub fn try_read(&mut self) -> Result { - match &T::state().rx_mode { + match &self.state.rx_mode { RxMode::Buffered(_) => { if let Ok(result) = self.rx_buf.try_receive() { match result { @@ -722,7 +785,7 @@ impl<'d, T: Instance, const RX_BUF_SIZE: usize> BufferedCanRx<'d, T, RX_BUF_SIZE Err(e) => Err(TryReadError::BusError(e)), } } else { - if let Some(err) = Registers(T::regs()).curr_error() { + if let Some(err) = self.info.regs.curr_error() { return Err(TryReadError::BusError(err)); } else { Err(TryReadError::Empty) @@ -746,10 +809,14 @@ impl<'d, T: Instance, const RX_BUF_SIZE: usize> BufferedCanRx<'d, T, RX_BUF_SIZE } } -impl<'d, T: Instance, const RX_BUF_SIZE: usize> Drop for BufferedCanRx<'d, T, RX_BUF_SIZE> { +impl<'d, const RX_BUF_SIZE: usize> Drop for BufferedCanRx<'d, RX_BUF_SIZE> { fn drop(&mut self) { - critical_section::with(|_| unsafe { - T::mut_state().rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + critical_section::with(|_| { + let state = self.state as *const State; + unsafe { + let mut_state = state as *mut State; + (*mut_state).rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + } }); } } @@ -839,13 +906,13 @@ impl RxMode { } } - pub async fn read(&self) -> Result { + pub(crate) async fn read(&self, info: &Info, state: &State) -> Result { match self { Self::NonBuffered(waker) => { poll_fn(|cx| { - T::state().err_waker.register(cx.waker()); + state.err_waker.register(cx.waker()); waker.register(cx.waker()); - match self.try_read::() { + match self.try_read(info) { Ok(result) => Poll::Ready(Ok(result)), Err(TryReadError::Empty) => Poll::Pending, Err(TryReadError::BusError(be)) => Poll::Ready(Err(be)), @@ -858,17 +925,17 @@ impl RxMode { } } } - pub fn try_read(&self) -> Result { + pub(crate) fn try_read(&self, info: &Info) -> Result { match self { Self::NonBuffered(_) => { - let registers = Registers(T::regs()); + let registers = &info.regs; if let Some(msg) = registers.receive_fifo(RxFifo::Fifo0) { - T::regs().ier().write(|w| { + registers.0.ier().write(|w| { w.set_fmpie(0, true); }); Ok(msg) } else if let Some(msg) = registers.receive_fifo(RxFifo::Fifo1) { - T::regs().ier().write(|w| { + registers.0.ier().write(|w| { w.set_fmpie(1, true); }); Ok(msg) @@ -883,12 +950,12 @@ impl RxMode { } } } - pub async fn wait_not_empty(&self) { - match &T::state().rx_mode { + pub(crate) async fn wait_not_empty(&self, info: &Info, state: &State) { + match &state.rx_mode { Self::NonBuffered(waker) => { poll_fn(|cx| { waker.register(cx.waker()); - if Registers(T::regs()).receive_frame_available() { + if info.regs.receive_frame_available() { Poll::Ready(()) } else { Poll::Pending @@ -903,7 +970,7 @@ impl RxMode { } } -enum TxMode { +pub(crate) enum TxMode { NonBuffered(AtomicWaker), Buffered(super::common::ClassicBufferedTxInner), } @@ -943,7 +1010,7 @@ impl TxMode { } } -struct State { +pub(crate) struct State { pub(crate) rx_mode: RxMode, pub(crate) tx_mode: TxMode, pub err_waker: AtomicWaker, @@ -959,7 +1026,17 @@ impl State { } } +pub(crate) struct Info { + regs: Registers, + tx_interrupt: crate::interrupt::Interrupt, + rx0_interrupt: crate::interrupt::Interrupt, + rx1_interrupt: crate::interrupt::Interrupt, + sce_interrupt: crate::interrupt::Interrupt, + tx_waker: fn(), +} + trait SealedInstance { + fn info() -> &'static Info; fn regs() -> crate::pac::can::Can; fn state() -> &'static State; unsafe fn mut_state() -> &'static mut State; @@ -1012,6 +1089,17 @@ foreach_peripheral!( (can, $inst:ident) => { impl SealedInstance for peripherals::$inst { + fn info() -> &'static Info { + static INFO: Info = Info { + regs: Registers(crate::pac::$inst), + tx_interrupt: crate::_generated::peripheral_interrupts::$inst::TX::IRQ, + rx0_interrupt: crate::_generated::peripheral_interrupts::$inst::RX0::IRQ, + rx1_interrupt: crate::_generated::peripheral_interrupts::$inst::RX1::IRQ, + sce_interrupt: crate::_generated::peripheral_interrupts::$inst::SCE::IRQ, + tx_waker: crate::_generated::peripheral_interrupts::$inst::TX::pend, + }; + &INFO + } fn regs() -> crate::pac::can::Can { crate::pac::$inst } diff --git a/embassy-stm32/src/can/bxcan/registers.rs b/embassy-stm32/src/can/bxcan/registers.rs index ad27e0744..e7c7525d8 100644 --- a/embassy-stm32/src/can/bxcan/registers.rs +++ b/embassy-stm32/src/can/bxcan/registers.rs @@ -131,7 +131,7 @@ impl Registers { /// Note that this will not trigger [`Interrupt::Wakeup`], only reception of an incoming CAN /// frame will cause that interrupt. #[allow(dead_code)] - pub fn wakeup(&mut self) { + pub fn wakeup(&self) { self.0.mcr().modify(|reg| { reg.set_sleep(false); reg.set_inrq(false); @@ -216,7 +216,7 @@ impl Registers { /// If FIFO scheduling is enabled, frames are transmitted in the order that they are passed to this function. /// /// If all transmit mailboxes are full, this function returns [`nb::Error::WouldBlock`]. - pub fn transmit(&mut self, frame: &Frame) -> nb::Result { + pub fn transmit(&self, frame: &Frame) -> nb::Result { // Check if FIFO scheduling is enabled. let fifo_scheduling = self.0.mcr().read().txfp(); @@ -292,7 +292,7 @@ impl Registers { Ok(()) } - fn write_mailbox(&mut self, idx: usize, frame: &Frame) { + fn write_mailbox(&self, idx: usize, frame: &Frame) { debug_assert!(idx < 3); let mb = self.0.tx(idx); @@ -309,7 +309,7 @@ impl Registers { }); } - fn read_pending_mailbox(&mut self, idx: usize) -> Option { + fn read_pending_mailbox(&self, idx: usize) -> Option { if self.abort_by_index(idx) { debug_assert!(idx < 3); @@ -332,7 +332,7 @@ impl Registers { } /// Tries to abort a pending frame. Returns `true` when aborted. - fn abort_by_index(&mut self, idx: usize) -> bool { + fn abort_by_index(&self, idx: usize) -> bool { self.0.tsr().write(|reg| reg.set_abrq(idx, true)); // Wait for the abort request to be finished. @@ -351,7 +351,7 @@ impl Registers { /// /// If there is a frame in the provided mailbox, and it is canceled successfully, this function /// returns `true`. - pub fn abort(&mut self, mailbox: Mailbox) -> bool { + pub fn abort(&self, mailbox: Mailbox) -> bool { // If the mailbox is empty, the value of TXOKx depends on what happened with the previous // frame in that mailbox. Only call abort_by_index() if the mailbox is not empty. let tsr = self.0.tsr().read(); diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs index 004b1a729..f63076512 100644 --- a/tests/stm32/src/bin/can.rs +++ b/tests/stm32/src/bin/can.rs @@ -19,8 +19,8 @@ mod can_common; use can_common::*; type Can<'d> = embassy_stm32::can::Can<'d, embassy_stm32::peripherals::CAN1>; -type CanTx<'d> = embassy_stm32::can::CanTx<'d, embassy_stm32::peripherals::CAN1>; -type CanRx<'d> = embassy_stm32::can::CanRx<'d, embassy_stm32::peripherals::CAN1>; +type CanTx<'d> = embassy_stm32::can::CanTx<'d>; +type CanRx<'d> = embassy_stm32::can::CanRx<'d>; bind_interrupts!(struct Irqs { CAN1_RX0 => Rx0InterruptHandler; From 7fd79857c339ccc55a4df606e25b432ada8f43a1 Mon Sep 17 00:00:00 2001 From: Corey Schuhen Date: Thu, 30 May 2024 22:00:02 +1000 Subject: [PATCH 28/57] Fix example. --- examples/stm32f7/src/bin/can.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/stm32f7/src/bin/can.rs b/examples/stm32f7/src/bin/can.rs index e32b4d3df..f4d6d8c19 100644 --- a/examples/stm32f7/src/bin/can.rs +++ b/examples/stm32f7/src/bin/can.rs @@ -24,7 +24,7 @@ bind_interrupts!(struct Irqs { }); #[embassy_executor::task] -pub async fn send_can_message(tx: &'static mut CanTx<'static, CAN3>) { +pub async fn send_can_message(tx: &'static mut CanTx<'static>) { loop { let frame = Frame::new_data(unwrap!(StandardId::new(0 as _)), &[0]).unwrap(); tx.write(&frame).await; @@ -62,7 +62,7 @@ async fn main(spawner: Spawner) { let (tx, mut rx) = can.split(); - static CAN_TX: StaticCell> = StaticCell::new(); + static CAN_TX: StaticCell> = StaticCell::new(); let tx = CAN_TX.init(tx); spawner.spawn(send_can_message(tx)).unwrap(); From 28f5f0babaa6348c08985f599353e34b48c887fb Mon Sep 17 00:00:00 2001 From: Mattis Kieffer Date: Thu, 30 May 2024 14:07:47 +0200 Subject: [PATCH 29/57] add dac buffer function --- embassy-stm32/src/opamp.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index 487902959..ca94a573d 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs @@ -110,6 +110,32 @@ impl<'d, T: Instance> OpAmp<'d, T> { OpAmpOutput { _inner: self } } + /// Configure the OpAmp as a buffer for the DAC it is connected to, + /// outputting to the provided output pin, and enable the opamp. + /// + /// The output pin is held within the returned [`OpAmpOutput`] struct, + /// preventing it being used elsewhere. The `OpAmpOutput` can then be + /// directly used as an ADC input. The opamp will be disabled when the + /// [`OpAmpOutput`] is dropped. + #[cfg(opamp_g4)] + pub fn buffer_dac( + &'d mut self, + out_pin: impl Peripheral

+ crate::gpio::Pin> + 'd, + ) -> OpAmpOutput<'d, T> { + into_ref!(out_pin); + out_pin.set_as_analog(); + + T::regs().csr().modify(|w| { + use crate::pac::opamp::vals::*; + + w.set_vm_sel(VmSel::OUTPUT); + w.set_vp_sel(VpSel::DAC3_CH1); + w.set_opaintoen(Opaintoen::OUTPUTPIN); + w.set_opampen(true); + }); + + OpAmpOutput { _inner: self } + } /// Configure the OpAmp as a buffer for the provided input pin, /// with the output only used internally, and enable the opamp. From b4a2f7fb70ac3f0b0d9226ab5ff5aab7a5bf8649 Mon Sep 17 00:00:00 2001 From: Corey Schuhen Date: Thu, 30 May 2024 22:10:46 +1000 Subject: [PATCH 30/57] Use phantom for lifetime holder instead of not used pointer to pointer. --- embassy-stm32/src/can/bxcan/mod.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index 4f516c917..c242aea2b 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs @@ -156,7 +156,6 @@ impl Drop for CanConfig<'_, T> { /// CAN driver pub struct Can<'d, T: Instance> { _peri: PeripheralRef<'d, T>, - instance: &'d crate::pac::can::Can, info: &'static Info, state: &'static State, } @@ -228,7 +227,6 @@ impl<'d, T: Instance> Can<'d, T> { Self { _peri: peri, - instance: &T::info().regs.0, info: T::info(), state: T::state(), } @@ -346,7 +344,7 @@ impl<'d, T: Instance> Can<'d, T> { /// Waits for a specific transmit mailbox to become empty pub async fn flush(&self, mb: Mailbox) { CanTx { - _instance: &self.instance, + _phantom: PhantomData, info: self.info, state: self.state, } @@ -362,7 +360,7 @@ impl<'d, T: Instance> Can<'d, T> { /// and a frame with equal priority is already queued for transmission. pub async fn flush_any(&self) { CanTx { - _instance: &self.instance, + _phantom: PhantomData, info: self.info, state: self.state, } @@ -373,7 +371,7 @@ impl<'d, T: Instance> Can<'d, T> { /// Waits until all of the transmit mailboxes become empty pub async fn flush_all(&self) { CanTx { - _instance: &self.instance, + _phantom: PhantomData, info: self.info, state: self.state, } @@ -424,12 +422,12 @@ impl<'d, T: Instance> Can<'d, T> { pub fn split<'c>(&'c mut self) -> (CanTx<'d>, CanRx<'d>) { ( CanTx { - _instance: &self.instance, + _phantom: PhantomData, info: self.info, state: self.state, }, CanRx { - instance: &self.instance, + _phantom: PhantomData, info: self.info, state: self.state, }, @@ -502,7 +500,7 @@ impl<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, TX_ /// CAN driver, transmit half. pub struct CanTx<'d> { - _instance: &'d crate::pac::can::Can, + _phantom: PhantomData<&'d ()>, info: &'static Info, state: &'static State, } @@ -695,7 +693,7 @@ impl<'d, const TX_BUF_SIZE: usize> Drop for BufferedCanTx<'d, TX_BUF_SIZE> { /// CAN driver, receive half. #[allow(dead_code)] pub struct CanRx<'d> { - instance: &'d crate::pac::can::Can, + _phantom: PhantomData<&'d ()>, info: &'static Info, state: &'static State, } From 368893c9cb1b192c9e0d45440cacb271d1039c29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0pa=C4=8Dek?= Date: Sat, 25 May 2024 21:44:37 +0200 Subject: [PATCH 31/57] Emit cargo:rustc-check-cfg instructions from build.rs --- build_common.rs | 101 +++++++++++++++++ embassy-executor/build.rs | 31 +----- embassy-hal-internal/build.rs | 30 +---- embassy-nrf/src/qspi.rs | 2 +- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/build.rs | 173 +++++++++++++++++------------ embassy-stm32/src/adc/mod.rs | 6 +- embassy-stm32/src/adc/v2.rs | 2 +- embassy-stm32/src/can/bxcan/mod.rs | 4 +- embassy-stm32/src/cryp/mod.rs | 4 +- embassy-stm32/src/dsihost.rs | 6 +- embassy-stm32/src/flash/mod.rs | 4 +- embassy-stm32/src/fmc.rs | 4 +- embassy-stm32/src/rcc/bd.rs | 6 +- embassy-stm32/src/rcc/f013.rs | 8 +- embassy-stm32/src/usb/usb.rs | 4 +- embassy-sync/build.rs | 32 +----- embassy-sync/src/lib.rs | 2 +- rust-toolchain-nightly.toml | 2 +- 19 files changed, 242 insertions(+), 183 deletions(-) create mode 100644 build_common.rs diff --git a/build_common.rs b/build_common.rs new file mode 100644 index 000000000..eda83f25d --- /dev/null +++ b/build_common.rs @@ -0,0 +1,101 @@ +use std::collections::HashSet; +use std::env; +use std::ffi::OsString; +use std::process::Command; + +/// Helper for emitting cargo instruction for enabling configs (`cargo:rustc-cfg=X`) and declaring +/// them (`cargo:rust-check-cfg=cfg(X)`). +#[derive(Debug)] +pub struct CfgSet { + enabled: HashSet, + declared: HashSet, + emit_declared: bool, +} + +impl CfgSet { + pub fn new() -> Self { + Self { + enabled: HashSet::new(), + declared: HashSet::new(), + emit_declared: is_rustc_nightly(), + } + } + + /// Enable a config, which can then be used in `#[cfg(...)]` for conditional compilation. + /// + /// All configs that can potentially be enabled should be unconditionally declared using + /// [`Self::declare()`]. + pub fn enable(&mut self, cfg: impl AsRef) { + if self.enabled.insert(cfg.as_ref().to_owned()) { + println!("cargo:rustc-cfg={}", cfg.as_ref()); + } + } + + pub fn enable_all(&mut self, cfgs: &[impl AsRef]) { + for cfg in cfgs.iter() { + self.enable(cfg.as_ref()); + } + } + + /// Declare a valid config for conditional compilation, without enabling it. + /// + /// This enables rustc to check that the configs in `#[cfg(...)]` attributes are valid. + pub fn declare(&mut self, cfg: impl AsRef) { + if self.declared.insert(cfg.as_ref().to_owned()) && self.emit_declared { + println!("cargo:rustc-check-cfg=cfg({})", cfg.as_ref()); + } + } + + pub fn declare_all(&mut self, cfgs: &[impl AsRef]) { + for cfg in cfgs.iter() { + self.declare(cfg.as_ref()); + } + } + + pub fn set(&mut self, cfg: impl Into, enable: bool) { + let cfg = cfg.into(); + if enable { + self.enable(cfg.clone()); + } + self.declare(cfg); + } +} + +fn is_rustc_nightly() -> bool { + let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); + + let output = Command::new(rustc) + .arg("--version") + .output() + .expect("failed to run `rustc --version`"); + + String::from_utf8_lossy(&output.stdout).contains("nightly") +} + +/// Sets configs that describe the target platform. +pub fn set_target_cfgs(cfgs: &mut CfgSet) { + let target = env::var("TARGET").unwrap(); + + if target.starts_with("thumbv6m-") { + cfgs.enable_all(&["cortex_m", "armv6m"]); + } else if target.starts_with("thumbv7m-") { + cfgs.enable_all(&["cortex_m", "armv7m"]); + } else if target.starts_with("thumbv7em-") { + cfgs.enable_all(&["cortex_m", "armv7m", "armv7em"]); + } else if target.starts_with("thumbv8m.base") { + cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_base"]); + } else if target.starts_with("thumbv8m.main") { + cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_main"]); + } + cfgs.declare_all(&[ + "cortex_m", + "armv6m", + "armv7m", + "armv7em", + "armv8m", + "armv8m_base", + "armv8m_main", + ]); + + cfgs.set("has_fpu", target.ends_with("-eabihf")); +} diff --git a/embassy-executor/build.rs b/embassy-executor/build.rs index 07f31e3fb..8af8ccaf3 100644 --- a/embassy-executor/build.rs +++ b/embassy-executor/build.rs @@ -3,6 +3,9 @@ use std::fmt::Write; use std::path::PathBuf; use std::{env, fs}; +#[path = "../build_common.rs"] +mod common; + static CONFIGS: &[(&str, usize)] = &[ // BEGIN AUTOGENERATED CONFIG FEATURES // Generated by gen_config.py. DO NOT EDIT. @@ -91,30 +94,6 @@ fn main() { let out_file = out_dir.join("config.rs").to_string_lossy().to_string(); fs::write(out_file, data).unwrap(); - // cortex-m targets - let target = env::var("TARGET").unwrap(); - - if target.starts_with("thumbv6m-") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv6m"); - } else if target.starts_with("thumbv7m-") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv7m"); - } else if target.starts_with("thumbv7em-") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv7m"); - println!("cargo:rustc-cfg=armv7em"); // (not currently used) - } else if target.starts_with("thumbv8m.base") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv8m"); - println!("cargo:rustc-cfg=armv8m_base"); - } else if target.starts_with("thumbv8m.main") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv8m"); - println!("cargo:rustc-cfg=armv8m_main"); - } - - if target.ends_with("-eabihf") { - println!("cargo:rustc-cfg=has_fpu"); - } + let mut rustc_cfgs = common::CfgSet::new(); + common::set_target_cfgs(&mut rustc_cfgs); } diff --git a/embassy-hal-internal/build.rs b/embassy-hal-internal/build.rs index 6fe82b44f..2649f5d37 100644 --- a/embassy-hal-internal/build.rs +++ b/embassy-hal-internal/build.rs @@ -1,29 +1,7 @@ -use std::env; +#[path = "../build_common.rs"] +mod common; fn main() { - let target = env::var("TARGET").unwrap(); - - if target.starts_with("thumbv6m-") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv6m"); - } else if target.starts_with("thumbv7m-") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv7m"); - } else if target.starts_with("thumbv7em-") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv7m"); - println!("cargo:rustc-cfg=armv7em"); // (not currently used) - } else if target.starts_with("thumbv8m.base") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv8m"); - println!("cargo:rustc-cfg=armv8m_base"); - } else if target.starts_with("thumbv8m.main") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv8m"); - println!("cargo:rustc-cfg=armv8m_main"); - } - - if target.ends_with("-eabihf") { - println!("cargo:rustc-cfg=has_fpu"); - } + let mut cfgs = common::CfgSet::new(); + common::set_target_cfgs(&mut cfgs); } diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index 060fe72cd..d40096edc 100755 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -166,7 +166,7 @@ impl<'d, T: Instance> Qspi<'d, T> { $pin.conf().write(|w| { w.dir().output(); w.drive().h0h1(); - #[cfg(feature = "_nrf5340-s")] + #[cfg(all(feature = "_nrf5340", feature = "_s"))] w.mcusel().peripheral(); w }); diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 4f0d7356d..6a1dfec26 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -72,7 +72,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-ad633a3e266151ea4d8fad630031a075ee02ab34" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-59b1f65bd109c3ef35782e6c44062208d0ef3d0e" } 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-ad633a3e266151ea4d8fad630031a075ee02ab34", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-59b1f65bd109c3ef35782e6c44062208d0ef3d0e", default-features = false, features = ["metadata"] } [features] default = ["rt"] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 6c524ced6..c7b2ea63a 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -9,35 +9,16 @@ use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use stm32_metapac::metadata::ir::BitOffset; use stm32_metapac::metadata::{ - MemoryRegionKind, PeripheralRccKernelClock, PeripheralRccRegister, PeripheralRegisters, StopMode, METADATA, + MemoryRegionKind, PeripheralRccKernelClock, PeripheralRccRegister, PeripheralRegisters, StopMode, ALL_CHIPS, + ALL_PERIPHERAL_VERSIONS, METADATA, }; +#[path = "../build_common.rs"] +mod common; + fn main() { - let target = env::var("TARGET").unwrap(); - - if target.starts_with("thumbv6m-") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv6m"); - } else if target.starts_with("thumbv7m-") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv7m"); - } else if target.starts_with("thumbv7em-") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv7m"); - println!("cargo:rustc-cfg=armv7em"); // (not currently used) - } else if target.starts_with("thumbv8m.base") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv8m"); - println!("cargo:rustc-cfg=armv8m_base"); - } else if target.starts_with("thumbv8m.main") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv8m"); - println!("cargo:rustc-cfg=armv8m_main"); - } - - if target.ends_with("-eabihf") { - println!("cargo:rustc-cfg=has_fpu"); - } + let mut cfgs = common::CfgSet::new(); + common::set_target_cfgs(&mut cfgs); let chip_name = match env::vars() .map(|(a, _)| a) @@ -56,8 +37,15 @@ fn main() { for p in METADATA.peripherals { if let Some(r) = &p.registers { - println!("cargo:rustc-cfg={}", r.kind); - println!("cargo:rustc-cfg={}_{}", r.kind, r.version); + cfgs.enable(r.kind); + cfgs.enable(format!("{}_{}", r.kind, r.version)); + } + } + + for &(kind, versions) in ALL_PERIPHERAL_VERSIONS.iter() { + cfgs.declare(kind); + for &version in versions.iter() { + cfgs.declare(format!("{}_{}", kind, version)); } } @@ -67,7 +55,13 @@ fn main() { let mut singletons: Vec = Vec::new(); for p in METADATA.peripherals { if let Some(r) = &p.registers { - println!("cargo:rustc-cfg=peri_{}", p.name.to_ascii_lowercase()); + if r.kind == "adccommon" || r.kind == "sai" || r.kind == "ucpd" { + // TODO: should we emit this for all peripherals? if so, we will need a list of all + // possible peripherals across all chips, so that we can declare the configs + // (replacing the hard-coded list of `peri_*` cfgs below) + cfgs.enable(format!("peri_{}", p.name.to_ascii_lowercase())); + } + match r.kind { // Generate singletons per pin, not per port "gpio" => { @@ -87,7 +81,7 @@ fn main() { if pin.signal.starts_with("MCO") { let name = pin.signal.replace('_', "").to_string(); if !singletons.contains(&name) { - println!("cargo:rustc-cfg={}", name.to_ascii_lowercase()); + cfgs.enable(name.to_ascii_lowercase()); singletons.push(name); } } @@ -106,6 +100,20 @@ fn main() { } } + cfgs.declare_all(&[ + "peri_adc1_common", + "peri_adc3_common", + "peri_adc12_common", + "peri_adc34_common", + "peri_sai1", + "peri_sai2", + "peri_sai3", + "peri_sai4", + "peri_ucpd1", + "peri_ucpd2", + ]); + cfgs.declare_all(&["mco", "mco1", "mco2"]); + // One singleton per EXTI line for pin_num in 0..16 { singletons.push(format!("EXTI{}", pin_num)); @@ -221,7 +229,13 @@ fn main() { }; if !time_driver_singleton.is_empty() { - println!("cargo:rustc-cfg=time_driver_{}", time_driver_singleton.to_lowercase()); + cfgs.enable(format!("time_driver_{}", time_driver_singleton.to_lowercase())); + } + for tim in [ + "tim1", "tim2", "tim3", "tim4", "tim5", "tim8", "tim9", "tim12", "tim15", "tim20", "tim21", "tim22", "tim23", + "tim24", + ] { + cfgs.declare(format!("time_driver_{}", tim)); } // ======== @@ -1593,54 +1607,65 @@ fn main() { rustfmt(&out_file); // ======== - // Multicore + // Configs for multicore and for targeting groups of chips - let mut s = chip_name.split('_'); - let mut chip_name: String = s.next().unwrap().to_string(); - let core_name = if let Some(c) = s.next() { - if !c.starts_with("CM") { - chip_name.push('_'); - chip_name.push_str(c); + fn get_chip_cfgs(chip_name: &str) -> Vec { + let mut cfgs = Vec::new(); + + // Multicore + + let mut s = chip_name.split('_'); + let mut chip_name: String = s.next().unwrap().to_string(); + let core_name = if let Some(c) = s.next() { + if !c.starts_with("CM") { + chip_name.push('_'); + chip_name.push_str(c); + None + } else { + Some(c) + } + } else { None - } else { - Some(c) - } - } else { - None - }; + }; - if let Some(core) = core_name { - println!("cargo:rustc-cfg={}_{}", &chip_name[..chip_name.len() - 2], core); + if let Some(core) = core_name { + cfgs.push(format!("{}_{}", &chip_name[..chip_name.len() - 2], core)); + } + + // Configs for targeting groups of chips + if &chip_name[..8] == "stm32wba" { + cfgs.push(chip_name[..8].to_owned()); // stm32wba + cfgs.push(chip_name[..10].to_owned()); // stm32wba52 + cfgs.push(format!("package_{}", &chip_name[10..11])); + cfgs.push(format!("flashsize_{}", &chip_name[11..12])); + } else { + if &chip_name[..8] == "stm32h7r" || &chip_name[..8] == "stm32h7s" { + cfgs.push("stm32h7rs".to_owned()); + } else { + cfgs.push(chip_name[..7].to_owned()); // stm32f4 + } + cfgs.push(chip_name[..9].to_owned()); // stm32f429 + cfgs.push(format!("{}x", &chip_name[..8])); // stm32f42x + cfgs.push(format!("{}x{}", &chip_name[..7], &chip_name[8..9])); // stm32f4x9 + cfgs.push(format!("package_{}", &chip_name[9..10])); + cfgs.push(format!("flashsize_{}", &chip_name[10..11])); + } + + // Mark the L4+ chips as they have many differences to regular L4. + if &chip_name[..7] == "stm32l4" { + if "pqrs".contains(&chip_name[7..8]) { + cfgs.push("stm32l4_plus".to_owned()); + } else { + cfgs.push("stm32l4_nonplus".to_owned()); + } + } + + cfgs } - // ======= - // Features for targeting groups of chips - - if &chip_name[..8] == "stm32wba" { - println!("cargo:rustc-cfg={}", &chip_name[..8]); // stm32wba - println!("cargo:rustc-cfg={}", &chip_name[..10]); // stm32wba52 - println!("cargo:rustc-cfg=package_{}", &chip_name[10..11]); - println!("cargo:rustc-cfg=flashsize_{}", &chip_name[11..12]); - } else { - if &chip_name[..8] == "stm32h7r" || &chip_name[..8] == "stm32h7s" { - println!("cargo:rustc-cfg=stm32h7rs"); - } else { - println!("cargo:rustc-cfg={}", &chip_name[..7]); // stm32f4 - } - println!("cargo:rustc-cfg={}", &chip_name[..9]); // stm32f429 - println!("cargo:rustc-cfg={}x", &chip_name[..8]); // stm32f42x - println!("cargo:rustc-cfg={}x{}", &chip_name[..7], &chip_name[8..9]); // stm32f4x9 - println!("cargo:rustc-cfg=package_{}", &chip_name[9..10]); - println!("cargo:rustc-cfg=flashsize_{}", &chip_name[10..11]); - } - - // Mark the L4+ chips as they have many differences to regular L4. - if &chip_name[..7] == "stm32l4" { - if "pqrs".contains(&chip_name[7..8]) { - println!("cargo:rustc-cfg=stm32l4_plus"); - } else { - println!("cargo:rustc-cfg=stm32l4_nonplus"); - } + cfgs.enable_all(&get_chip_cfgs(&chip_name)); + for &chip_name in ALL_CHIPS.iter() { + cfgs.declare_all(&get_chip_cfgs(&chip_name.to_ascii_lowercase())); } println!("cargo:rerun-if-changed=build.rs"); diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 040ee9c53..0c22a7dae 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -71,9 +71,9 @@ pub(crate) trait SealedAdcChannel { /// Performs a busy-wait delay for a specified number of microseconds. #[allow(unused)] pub(crate) fn blocking_delay_us(us: u32) { - #[cfg(time)] - embassy_time::block_for(embassy_time::Duration::from_micros(us)); - #[cfg(not(time))] + #[cfg(feature = "time")] + embassy_time::block_for(embassy_time::Duration::from_micros(us as u64)); + #[cfg(not(feature = "time"))] { let freq = unsafe { crate::rcc::get_freqs() }.sys.unwrap().0 as u64; let us = us as u64; diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 77e8bb56f..e3175dff5 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -31,7 +31,7 @@ impl AdcChannel for Temperature {} impl super::SealedAdcChannel for Temperature { fn channel(&self) -> u8 { cfg_if::cfg_if! { - if #[cfg(any(stm32f2, stm32f40, stm32f41))] { + if #[cfg(any(stm32f2, stm32f40x, stm32f41x))] { 16 } else { 18 diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index 69e20e909..9f7df1e71 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs @@ -1132,8 +1132,8 @@ foreach_peripheral!( (can, CAN1) => { cfg_if::cfg_if! { if #[cfg(all( - any(stm32l4, stm32f72, stm32f73), - not(any(stm32l49, stm32l4a)) + any(stm32l4, stm32f72x, stm32f73x), + not(any(stm32l49x, stm32l4ax)) ))] { // Most L4 devices and some F7 devices use the name "CAN1" // even if there is no "CAN2" peripheral. diff --git a/embassy-stm32/src/cryp/mod.rs b/embassy-stm32/src/cryp/mod.rs index ad118ef6e..8d600c73c 100644 --- a/embassy-stm32/src/cryp/mod.rs +++ b/embassy-stm32/src/cryp/mod.rs @@ -744,7 +744,7 @@ impl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize, const IV_SIZE: usize> Aes } else { aad_header[0] = 0xFF; aad_header[1] = 0xFE; - let aad_len_bytes: [u8; 4] = aad_len.to_be_bytes(); + let aad_len_bytes: [u8; 4] = (aad_len as u32).to_be_bytes(); aad_header[2] = aad_len_bytes[0]; aad_header[3] = aad_len_bytes[1]; aad_header[4] = aad_len_bytes[2]; @@ -765,7 +765,7 @@ impl<'c, const KEY_SIZE: usize, const TAG_SIZE: usize, const IV_SIZE: usize> Aes block0[0] |= ((((TAG_SIZE as u8) - 2) >> 1) & 0x07) << 3; block0[0] |= ((15 - (iv.len() as u8)) - 1) & 0x07; block0[1..1 + iv.len()].copy_from_slice(iv); - let payload_len_bytes: [u8; 4] = payload_len.to_be_bytes(); + let payload_len_bytes: [u8; 4] = (payload_len as u32).to_be_bytes(); if iv.len() <= 11 { block0[12] = payload_len_bytes[0]; } else if payload_len_bytes[0] > 0 { diff --git a/embassy-stm32/src/dsihost.rs b/embassy-stm32/src/dsihost.rs index dc58bca92..e1fb3b0b0 100644 --- a/embassy-stm32/src/dsihost.rs +++ b/embassy-stm32/src/dsihost.rs @@ -11,9 +11,9 @@ use crate::{peripherals, Peripheral}; /// Performs a busy-wait delay for a specified number of microseconds. pub fn blocking_delay_ms(ms: u32) { - #[cfg(time)] - embassy_time::block_for(embassy_time::Duration::from_millis(ms)); - #[cfg(not(time))] + #[cfg(feature = "time")] + embassy_time::block_for(embassy_time::Duration::from_millis(ms as u64)); + #[cfg(not(feature = "time"))] cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.unwrap().0 / 1_000 * ms); } diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 8c6ca2471..ce2d1a04c 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -96,7 +96,7 @@ pub enum FlashBank { #[cfg_attr(any(flash_f1, flash_f3), path = "f1f3.rs")] #[cfg_attr(flash_f4, path = "f4.rs")] #[cfg_attr(flash_f7, path = "f7.rs")] -#[cfg_attr(any(flash_g0, flash_g4), path = "g.rs")] +#[cfg_attr(any(flash_g0, flash_g4c2, flash_g4c3, flash_g4c4), path = "g.rs")] #[cfg_attr(flash_h7, path = "h7.rs")] #[cfg_attr(flash_h7ab, path = "h7.rs")] #[cfg_attr(flash_u5, path = "u5.rs")] @@ -105,7 +105,7 @@ pub enum FlashBank { #[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_u0 + flash_g4c2, flash_g4c3, flash_g4c4, flash_h7, flash_h7ab, flash_u5, flash_h50, flash_u0 )), path = "other.rs" )] diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs index 0df64f711..82d8089f4 100644 --- a/embassy-stm32/src/fmc.rs +++ b/embassy-stm32/src/fmc.rs @@ -35,7 +35,7 @@ where // fmc v1 and v2 does not have the fmcen bit // fsmc v1, v2 and v3 does not have the fmcen bit // This is a "not" because it is expected that all future versions have this bit - #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v1x3, fsmc_v2x3, fsmc_v3x1, fmc_v4)))] + #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v1x3, fmc_v4)))] T::REGS.bcr1().modify(|r| r.set_fmcen(true)); #[cfg(any(fmc_v4))] T::REGS.nor_psram().bcr1().modify(|r| r.set_fmcen(true)); @@ -61,7 +61,7 @@ where // fmc v1 and v2 does not have the fmcen bit // fsmc v1, v2 and v3 does not have the fmcen bit // This is a "not" because it is expected that all future versions have this bit - #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v1x3, fsmc_v2x3, fsmc_v3x1, fmc_v4)))] + #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v1x3, fmc_v4)))] T::REGS.bcr1().modify(|r| r.set_fmcen(true)); #[cfg(any(fmc_v4))] T::REGS.nor_psram().bcr1().modify(|r| r.set_fmcen(true)); diff --git a/embassy-stm32/src/rcc/bd.rs b/embassy-stm32/src/rcc/bd.rs index f3ac4fdbe..4e9c18594 100644 --- a/embassy-stm32/src/rcc/bd.rs +++ b/embassy-stm32/src/rcc/bd.rs @@ -33,7 +33,7 @@ pub enum LseDrive { } // All families but these have the LSEDRV register -#[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f400, rcc_f410, rcc_l1)))] +#[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1)))] impl From for crate::pac::rcc::vals::Lsedrv { fn from(value: LseDrive) -> Self { use crate::pac::rcc::vals::Lsedrv; @@ -186,7 +186,7 @@ impl LsConfig { } ok &= reg.lseon() == lse_en; ok &= reg.lsebyp() == lse_byp; - #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f400, rcc_f410, rcc_l1)))] + #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1)))] if let Some(lse_drv) = lse_drv { ok &= reg.lsedrv() == lse_drv.into(); } @@ -224,7 +224,7 @@ impl LsConfig { if lse_en { bdcr().modify(|w| { - #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f400, rcc_f410, rcc_l1)))] + #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1)))] if let Some(lse_drv) = lse_drv { w.set_lsedrv(lse_drv.into()); } diff --git a/embassy-stm32/src/rcc/f013.rs b/embassy-stm32/src/rcc/f013.rs index 1c951a22b..63dc27bdd 100644 --- a/embassy-stm32/src/rcc/f013.rs +++ b/embassy-stm32/src/rcc/f013.rs @@ -95,7 +95,7 @@ pub struct Config { #[cfg(all(stm32f3, not(rcc_f37)))] pub adc: AdcClockSource, - #[cfg(all(stm32f3, not(rcc_f37), adc3_common))] + #[cfg(all(stm32f3, not(rcc_f37), any(peri_adc3_common, peri_adc34_common)))] pub adc34: AdcClockSource, /// Per-peripheral kernel clock selection muxes @@ -125,7 +125,7 @@ impl Default for Config { #[cfg(all(stm32f3, not(rcc_f37)))] adc: AdcClockSource::Hclk(AdcHclkPrescaler::Div1), - #[cfg(all(stm32f3, not(rcc_f37), adc3_common))] + #[cfg(all(stm32f3, not(rcc_f37), any(peri_adc3_common, peri_adc34_common)))] adc34: AdcClockSource::Hclk(AdcHclkPrescaler::Div1), mux: Default::default(), @@ -339,7 +339,7 @@ pub(crate) unsafe fn init(config: Config) { } }; - #[cfg(all(stm32f3, not(rcc_f37), adc3_common))] + #[cfg(all(stm32f3, not(rcc_f37), any(peri_adc3_common, peri_adc34_common)))] let adc34 = { #[cfg(peri_adc3_common)] let common = crate::pac::ADC3_COMMON; @@ -404,7 +404,7 @@ pub(crate) unsafe fn init(config: Config) { hclk1: Some(hclk), #[cfg(all(stm32f3, not(rcc_f37)))] adc: Some(adc), - #[cfg(all(stm32f3, not(rcc_f37), adc3_common))] + #[cfg(all(stm32f3, not(rcc_f37), any(peri_adc3_common, peri_adc34_common)))] adc34: Some(adc34), rtc: rtc, hsi48: hsi48, diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 81a2d2623..b6c88ac9a 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -267,9 +267,9 @@ impl<'d, T: Instance> Driver<'d, T> { w.set_fres(true); }); - #[cfg(time)] + #[cfg(feature = "time")] embassy_time::block_for(embassy_time::Duration::from_millis(100)); - #[cfg(not(time))] + #[cfg(not(feature = "time"))] cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.unwrap().0 / 10); #[cfg(not(usb_v4))] diff --git a/embassy-sync/build.rs b/embassy-sync/build.rs index afd76dad1..2649f5d37 100644 --- a/embassy-sync/build.rs +++ b/embassy-sync/build.rs @@ -1,31 +1,7 @@ -use std::env; +#[path = "../build_common.rs"] +mod common; fn main() { - println!("cargo:rerun-if-changed=build.rs"); - - let target = env::var("TARGET").unwrap(); - - if target.starts_with("thumbv6m-") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv6m"); - } else if target.starts_with("thumbv7m-") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv7m"); - } else if target.starts_with("thumbv7em-") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv7m"); - println!("cargo:rustc-cfg=armv7em"); // (not currently used) - } else if target.starts_with("thumbv8m.base") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv8m"); - println!("cargo:rustc-cfg=armv8m_base"); - } else if target.starts_with("thumbv8m.main") { - println!("cargo:rustc-cfg=cortex_m"); - println!("cargo:rustc-cfg=armv8m"); - println!("cargo:rustc-cfg=armv8m_main"); - } - - if target.ends_with("-eabihf") { - println!("cargo:rustc-cfg=has_fpu"); - } + let mut cfgs = common::CfgSet::new(); + common::set_target_cfgs(&mut cfgs); } diff --git a/embassy-sync/src/lib.rs b/embassy-sync/src/lib.rs index 1873483f9..a5eee8d02 100644 --- a/embassy-sync/src/lib.rs +++ b/embassy-sync/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] +#![cfg_attr(not(feature = "std"), no_std)] #![allow(async_fn_in_trait)] #![allow(clippy::new_without_default)] #![doc = include_str!("../README.md")] diff --git a/rust-toolchain-nightly.toml b/rust-toolchain-nightly.toml index ac160b995..2143c1b2d 100644 --- a/rust-toolchain-nightly.toml +++ b/rust-toolchain-nightly.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2024-04-14" +channel = "nightly-2024-05-20" components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ] targets = [ "thumbv7em-none-eabi", From 6eaa25934226ee5ff2038a34e6d32b5d69559c1d Mon Sep 17 00:00:00 2001 From: "Andres O. Vela" Date: Thu, 30 May 2024 22:16:56 +0200 Subject: [PATCH 32/57] embassy-time: add timestamp features --- embassy-time/Cargo.toml | 10 +++++++++- embassy-time/src/lib.rs | 17 ++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index ca7ad2d09..ce3f3d8c2 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -27,9 +27,17 @@ features = ["defmt", "std"] std = ["tick-hz-1_000_000", "critical-section/std"] wasm = ["dep:wasm-bindgen", "dep:js-sys", "dep:wasm-timer", "tick-hz-1_000_000"] -## Display a timestamp of the number of seconds since startup next to defmt log messages +## Display the time since startup next to defmt log messages. +## At most 1 `defmt-timestamp-uptime-*` feature can be used. +## `defmt-timestamp-uptime` is provided for backwards compatibility (provides the same format as `uptime-us`). ## To use this you must have a time driver provided. defmt-timestamp-uptime = ["defmt"] +defmt-timestamp-uptime-s = ["defmt"] +defmt-timestamp-uptime-ms = ["defmt"] +defmt-timestamp-uptime-us = ["defmt"] +defmt-timestamp-uptime-ts = ["defmt"] +defmt-timestamp-uptime-tms = ["defmt"] +defmt-timestamp-uptime-tus = ["defmt"] ## Create a `MockDriver` that can be manually advanced for testing purposes. mock-driver = ["tick-hz-1_000_000"] diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 3c8575ee9..24ee51be7 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -46,5 +46,20 @@ pub(crate) const GCD_1K: u64 = gcd(TICK_HZ, 1_000); pub(crate) const GCD_1M: u64 = gcd(TICK_HZ, 1_000_000); pub(crate) const GCD_1G: u64 = gcd(TICK_HZ, 1_000_000_000); -#[cfg(feature = "defmt-timestamp-uptime")] +#[cfg(feature = "defmt-timestamp-uptime-s")] +defmt::timestamp! {"{=u64}", Instant::now().as_secs() } + +#[cfg(feature = "defmt-timestamp-uptime-ms")] +defmt::timestamp! {"{=u64:ms}", Instant::now().as_millis() } + +#[cfg(any(feature = "defmt-timestamp-uptime", feature = "defmt-timestamp-uptime-us"))] defmt::timestamp! {"{=u64:us}", Instant::now().as_micros() } + +#[cfg(feature = "defmt-timestamp-uptime-ts")] +defmt::timestamp! {"{=u64:ts}", Instant::now().as_secs() } + +#[cfg(feature = "defmt-timestamp-uptime-tms")] +defmt::timestamp! {"{=u64:tms}", Instant::now().as_millis() } + +#[cfg(feature = "defmt-timestamp-uptime-tus")] +defmt::timestamp! {"{=u64:tus}", Instant::now().as_micros() } From 84707af5d7f3e62caafed06c416cf12fa82e4664 Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Thu, 30 May 2024 17:43:38 -0400 Subject: [PATCH 33/57] create functions in inner to handle register modification --- embassy-stm32/src/timer/input_capture.rs | 21 ++++-------- embassy-stm32/src/timer/low_level.rs | 14 ++++++++ embassy-stm32/src/timer/pwm_input.rs | 42 +++++++----------------- 3 files changed, 31 insertions(+), 46 deletions(-) diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index b3434ae63..8d1a77867 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -7,7 +7,7 @@ use core::task::{Context, Poll}; use embassy_hal_internal::{into_ref, PeripheralRef}; -use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, Timer}; +use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer}; use super::{ CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel, @@ -40,11 +40,9 @@ macro_rules! channel_impl { #[doc = concat!("Create a new ", stringify!($channel), " capture pin instance.")] pub fn $new_chx(pin: impl Peripheral

> + 'd, pull_type: Pull) -> Self { into_ref!(pin); - critical_section::with(|_| { - pin.set_as_af_pull(pin.af_num(), AFType::Input, pull_type); - #[cfg(gpio_v2)] - pin.set_speed(crate::gpio::Speed::VeryHigh); - }); + + pin.set_as_af_pull(pin.af_num(), AFType::Input, pull_type); + CapturePin { _pin: pin.map_into(), phantom: PhantomData, @@ -130,8 +128,6 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { } fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> InputCaptureFuture { - use stm32_metapac::timer::vals::FilterValue; - // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.5 // or ST RM0008 (STM32F103) chapter 15.3.5 Input capture mode self.inner.set_input_ti_selection(channel, tisel); @@ -184,11 +180,6 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { } } -/// Convert pointer to TIM instance to TimGp16 object -fn regs_gp16(ptr: *mut ()) -> crate::pac::timer::TimGp16 { - unsafe { crate::pac::timer::TimGp16::from_ptr(ptr) } -} - #[must_use = "futures do nothing unless you `.await` or poll them"] struct InputCaptureFuture { channel: Channel, @@ -198,7 +189,7 @@ struct InputCaptureFuture { impl Drop for InputCaptureFuture { fn drop(&mut self) { critical_section::with(|_| { - let regs = regs_gp16(T::regs()); + let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }; // disable interrupt enable regs.dier().modify(|w| w.set_ccie(self.channel.index(), false)); @@ -212,7 +203,7 @@ impl Future for InputCaptureFuture { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { T::state().cc_waker[self.channel.index()].register(cx.waker()); - let regs = regs_gp16(T::regs()); + let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }; let dier = regs.dier().read(); if !dier.ccie(self.channel.index()) { diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 141e96894..8482fad7b 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -12,6 +12,10 @@ use super::*; use crate::pac::timer::vals; use crate::time::Hertz; +pub use stm32_metapac::timer::vals::FilterValue; +pub use stm32_metapac::timer::vals::Sms as SlaveMode; +pub use stm32_metapac::timer::vals::Ts as TriggerSource; + /// Input capture mode. #[derive(Clone, Copy)] pub enum InputCaptureMode { @@ -588,6 +592,16 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { pub fn set_cc_dma_enable_state(&self, channel: Channel, ccde: bool) { self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde)) } + + /// Set Timer Slave Mode + pub fn set_slave_mode(&self, sms: SlaveMode) { + self.regs_gp16().smcr().modify(|r| r.set_sms(sms)); + } + + /// Set Timer Trigger Source + pub fn set_trigger_source(&self, ts: TriggerSource) { + self.regs_gp16().smcr().modify(|r| r.set_ts(ts)); + } } #[cfg(not(stm32l0))] diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index 7bcb7802a..dcf098a78 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -2,7 +2,7 @@ use embassy_hal_internal::into_ref; -use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, Timer}; +use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource}; use super::{Channel, Channel1Pin, Channel2Pin, GeneralInstance4Channel}; use crate::gpio::{AFType, Pull}; use crate::time::Hertz; @@ -14,11 +14,6 @@ pub struct PwmInput<'d, T: GeneralInstance4Channel> { inner: Timer<'d, T>, } -/// Convert pointer to TIM instance to TimGp16 object -fn regs_gp16(ptr: *mut ()) -> crate::pac::timer::TimGp16 { - unsafe { crate::pac::timer::TimGp16::from_ptr(ptr) } -} - impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { /// Create a new PWM input driver. pub fn new( @@ -28,11 +23,8 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { freq: Hertz, ) -> Self { into_ref!(pin); - critical_section::with(|_| { - pin.set_as_af_pull(pin.af_num(), AFType::Input, pull_type); - #[cfg(gpio_v2)] - pin.set_speed(crate::gpio::Speed::VeryHigh); - }); + + pin.set_as_af_pull(pin.af_num(), AFType::Input, pull_type); Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2) } @@ -45,18 +37,13 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { freq: Hertz, ) -> Self { into_ref!(pin); - critical_section::with(|_| { - pin.set_as_af_pull(pin.af_num(), AFType::Input, pull_type); - #[cfg(gpio_v2)] - pin.set_speed(crate::gpio::Speed::VeryHigh); - }); + + pin.set_as_af_pull(pin.af_num(), AFType::Input, pull_type); Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1) } fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz, ch1: Channel, ch2: Channel) -> Self { - use stm32_metapac::timer::vals::{Sms, Ts}; - let mut inner = Timer::new(tim); inner.set_counting_mode(CountingMode::EdgeAlignedUp); @@ -72,21 +59,14 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { inner.set_input_ti_selection(ch2, InputTISelection::Alternate); inner.set_input_capture_mode(ch2, InputCaptureMode::Falling); - let regs = regs_gp16(T::regs()); - regs.smcr().modify(|r| { - // Select the valid trigger input: write the TS bits to 101 in the TIMx_SMCR register - // (TI1FP1 selected). - r.set_ts(match ch1 { - Channel::Ch1 => Ts::TI1FP1, - Channel::Ch2 => Ts::TI2FP2, - _ => panic!("Invalid channel for PWM input"), - }); - - // Configure the slave mode controller in reset mode: write the SMS bits to 100 in the - // TIMx_SMCR register. - r.set_sms(Sms::RESET_MODE); + inner.set_trigger_source(match ch1 { + Channel::Ch1 => TriggerSource::TI1FP1, + Channel::Ch2 => TriggerSource::TI2FP2, + _ => panic!("Invalid channel for PWM input"), }); + inner.set_slave_mode(SlaveMode::RESET_MODE); + // Must call the `enable` function after Self { channel: ch1, inner } From 4d307b5a7763a2d38de201bdef787bee07afe022 Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Thu, 30 May 2024 17:49:20 -0400 Subject: [PATCH 34/57] undo changes in input_capture --- embassy-stm32/src/timer/input_capture.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index 8d1a77867..0258d4077 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -78,18 +78,18 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { } fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz, counting_mode: CountingMode) -> Self { - let mut inner = Timer::new(tim); + let mut this = Self { inner: Timer::new(tim) }; - inner.set_counting_mode(counting_mode); - inner.set_tick_freq(freq); - inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details - inner.start(); + this.inner.set_counting_mode(counting_mode); + this.inner.set_tick_freq(freq); + this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details + this.inner.start(); // enable NVIC interrupt T::CaptureCompareInterrupt::unpend(); unsafe { T::CaptureCompareInterrupt::enable() }; - Self { inner } + this } /// Enable the given channel. From 713d84f7789570c069c4880c1225bed4dc2bc3ae Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Thu, 30 May 2024 17:51:48 -0400 Subject: [PATCH 35/57] fix fmt --- embassy-stm32/src/timer/low_level.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 8482fad7b..a53acc1d5 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -12,9 +12,7 @@ use super::*; use crate::pac::timer::vals; use crate::time::Hertz; -pub use stm32_metapac::timer::vals::FilterValue; -pub use stm32_metapac::timer::vals::Sms as SlaveMode; -pub use stm32_metapac::timer::vals::Ts as TriggerSource; +pub use stm32_metapac::timer::vals::{FilterValue, Sms as SlaveMode, Ts as TriggerSource}; /// Input capture mode. #[derive(Clone, Copy)] From 83b5797b8da1433568f0a584c7e5eb15ed25785e Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Thu, 30 May 2024 17:53:38 -0400 Subject: [PATCH 36/57] fix fmt (again) --- embassy-stm32/src/timer/low_level.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index a53acc1d5..be1692fc2 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -8,12 +8,13 @@ use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; +// Re-export useful enums +pub use stm32_metapac::timer::vals::{FilterValue, Sms as SlaveMode, Ts as TriggerSource}; + use super::*; use crate::pac::timer::vals; use crate::time::Hertz; -pub use stm32_metapac::timer::vals::{FilterValue, Sms as SlaveMode, Ts as TriggerSource}; - /// Input capture mode. #[derive(Clone, Copy)] pub enum InputCaptureMode { From 7d869192571c0e49da425afbdc37433b8a820786 Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Thu, 30 May 2024 17:54:49 -0400 Subject: [PATCH 37/57] rust fmt really does not want blank space there --- embassy-stm32/src/timer/low_level.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index be1692fc2..300ea812c 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -7,7 +7,6 @@ //! The available functionality depends on the timer type. use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; - // Re-export useful enums pub use stm32_metapac::timer::vals::{FilterValue, Sms as SlaveMode, Ts as TriggerSource}; From bfb380e8ca8c220036739e6046df79ac784d935b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0pa=C4=8Dek?= Date: Thu, 30 May 2024 19:59:06 +0200 Subject: [PATCH 38/57] Copy build_common.rs into each crate, to make cargo publish happy --- embassy-executor/build.rs | 2 +- .../build_common.rs | 8 ++ embassy-hal-internal/build.rs | 2 +- embassy-hal-internal/build_common.rs | 109 ++++++++++++++++++ embassy-stm32/build.rs | 2 +- embassy-stm32/build_common.rs | 109 ++++++++++++++++++ embassy-sync/build.rs | 2 +- embassy-sync/build_common.rs | 109 ++++++++++++++++++ 8 files changed, 339 insertions(+), 4 deletions(-) rename build_common.rs => embassy-executor/build_common.rs (86%) create mode 100644 embassy-hal-internal/build_common.rs create mode 100644 embassy-stm32/build_common.rs create mode 100644 embassy-sync/build_common.rs diff --git a/embassy-executor/build.rs b/embassy-executor/build.rs index 8af8ccaf3..8a41d7503 100644 --- a/embassy-executor/build.rs +++ b/embassy-executor/build.rs @@ -3,7 +3,7 @@ use std::fmt::Write; use std::path::PathBuf; use std::{env, fs}; -#[path = "../build_common.rs"] +#[path = "./build_common.rs"] mod common; static CONFIGS: &[(&str, usize)] = &[ diff --git a/build_common.rs b/embassy-executor/build_common.rs similarity index 86% rename from build_common.rs rename to embassy-executor/build_common.rs index eda83f25d..2c65f8529 100644 --- a/build_common.rs +++ b/embassy-executor/build_common.rs @@ -1,3 +1,11 @@ +// NOTE: this file is copy-pasted between several Embassy crates, because there is no +// straightforward way to share this code: +// - it cannot be placed into the root of the repo and linked from each build.rs using `#[path = +// "../build_common.rs"]`, because `cargo publish` requires that all files published with a crate +// reside in the crate's directory, +// - it cannot be symlinked from `embassy-xxx/build_common.rs` to `../build_common.rs`, because +// symlinks don't work on Windows. + use std::collections::HashSet; use std::env; use std::ffi::OsString; diff --git a/embassy-hal-internal/build.rs b/embassy-hal-internal/build.rs index 2649f5d37..ecd2c0c9f 100644 --- a/embassy-hal-internal/build.rs +++ b/embassy-hal-internal/build.rs @@ -1,4 +1,4 @@ -#[path = "../build_common.rs"] +#[path = "./build_common.rs"] mod common; fn main() { diff --git a/embassy-hal-internal/build_common.rs b/embassy-hal-internal/build_common.rs new file mode 100644 index 000000000..2c65f8529 --- /dev/null +++ b/embassy-hal-internal/build_common.rs @@ -0,0 +1,109 @@ +// NOTE: this file is copy-pasted between several Embassy crates, because there is no +// straightforward way to share this code: +// - it cannot be placed into the root of the repo and linked from each build.rs using `#[path = +// "../build_common.rs"]`, because `cargo publish` requires that all files published with a crate +// reside in the crate's directory, +// - it cannot be symlinked from `embassy-xxx/build_common.rs` to `../build_common.rs`, because +// symlinks don't work on Windows. + +use std::collections::HashSet; +use std::env; +use std::ffi::OsString; +use std::process::Command; + +/// Helper for emitting cargo instruction for enabling configs (`cargo:rustc-cfg=X`) and declaring +/// them (`cargo:rust-check-cfg=cfg(X)`). +#[derive(Debug)] +pub struct CfgSet { + enabled: HashSet, + declared: HashSet, + emit_declared: bool, +} + +impl CfgSet { + pub fn new() -> Self { + Self { + enabled: HashSet::new(), + declared: HashSet::new(), + emit_declared: is_rustc_nightly(), + } + } + + /// Enable a config, which can then be used in `#[cfg(...)]` for conditional compilation. + /// + /// All configs that can potentially be enabled should be unconditionally declared using + /// [`Self::declare()`]. + pub fn enable(&mut self, cfg: impl AsRef) { + if self.enabled.insert(cfg.as_ref().to_owned()) { + println!("cargo:rustc-cfg={}", cfg.as_ref()); + } + } + + pub fn enable_all(&mut self, cfgs: &[impl AsRef]) { + for cfg in cfgs.iter() { + self.enable(cfg.as_ref()); + } + } + + /// Declare a valid config for conditional compilation, without enabling it. + /// + /// This enables rustc to check that the configs in `#[cfg(...)]` attributes are valid. + pub fn declare(&mut self, cfg: impl AsRef) { + if self.declared.insert(cfg.as_ref().to_owned()) && self.emit_declared { + println!("cargo:rustc-check-cfg=cfg({})", cfg.as_ref()); + } + } + + pub fn declare_all(&mut self, cfgs: &[impl AsRef]) { + for cfg in cfgs.iter() { + self.declare(cfg.as_ref()); + } + } + + pub fn set(&mut self, cfg: impl Into, enable: bool) { + let cfg = cfg.into(); + if enable { + self.enable(cfg.clone()); + } + self.declare(cfg); + } +} + +fn is_rustc_nightly() -> bool { + let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); + + let output = Command::new(rustc) + .arg("--version") + .output() + .expect("failed to run `rustc --version`"); + + String::from_utf8_lossy(&output.stdout).contains("nightly") +} + +/// Sets configs that describe the target platform. +pub fn set_target_cfgs(cfgs: &mut CfgSet) { + let target = env::var("TARGET").unwrap(); + + if target.starts_with("thumbv6m-") { + cfgs.enable_all(&["cortex_m", "armv6m"]); + } else if target.starts_with("thumbv7m-") { + cfgs.enable_all(&["cortex_m", "armv7m"]); + } else if target.starts_with("thumbv7em-") { + cfgs.enable_all(&["cortex_m", "armv7m", "armv7em"]); + } else if target.starts_with("thumbv8m.base") { + cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_base"]); + } else if target.starts_with("thumbv8m.main") { + cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_main"]); + } + cfgs.declare_all(&[ + "cortex_m", + "armv6m", + "armv7m", + "armv7em", + "armv8m", + "armv8m_base", + "armv8m_main", + ]); + + cfgs.set("has_fpu", target.ends_with("-eabihf")); +} diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index c7b2ea63a..6aedcc228 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -13,7 +13,7 @@ use stm32_metapac::metadata::{ ALL_PERIPHERAL_VERSIONS, METADATA, }; -#[path = "../build_common.rs"] +#[path = "./build_common.rs"] mod common; fn main() { diff --git a/embassy-stm32/build_common.rs b/embassy-stm32/build_common.rs new file mode 100644 index 000000000..2c65f8529 --- /dev/null +++ b/embassy-stm32/build_common.rs @@ -0,0 +1,109 @@ +// NOTE: this file is copy-pasted between several Embassy crates, because there is no +// straightforward way to share this code: +// - it cannot be placed into the root of the repo and linked from each build.rs using `#[path = +// "../build_common.rs"]`, because `cargo publish` requires that all files published with a crate +// reside in the crate's directory, +// - it cannot be symlinked from `embassy-xxx/build_common.rs` to `../build_common.rs`, because +// symlinks don't work on Windows. + +use std::collections::HashSet; +use std::env; +use std::ffi::OsString; +use std::process::Command; + +/// Helper for emitting cargo instruction for enabling configs (`cargo:rustc-cfg=X`) and declaring +/// them (`cargo:rust-check-cfg=cfg(X)`). +#[derive(Debug)] +pub struct CfgSet { + enabled: HashSet, + declared: HashSet, + emit_declared: bool, +} + +impl CfgSet { + pub fn new() -> Self { + Self { + enabled: HashSet::new(), + declared: HashSet::new(), + emit_declared: is_rustc_nightly(), + } + } + + /// Enable a config, which can then be used in `#[cfg(...)]` for conditional compilation. + /// + /// All configs that can potentially be enabled should be unconditionally declared using + /// [`Self::declare()`]. + pub fn enable(&mut self, cfg: impl AsRef) { + if self.enabled.insert(cfg.as_ref().to_owned()) { + println!("cargo:rustc-cfg={}", cfg.as_ref()); + } + } + + pub fn enable_all(&mut self, cfgs: &[impl AsRef]) { + for cfg in cfgs.iter() { + self.enable(cfg.as_ref()); + } + } + + /// Declare a valid config for conditional compilation, without enabling it. + /// + /// This enables rustc to check that the configs in `#[cfg(...)]` attributes are valid. + pub fn declare(&mut self, cfg: impl AsRef) { + if self.declared.insert(cfg.as_ref().to_owned()) && self.emit_declared { + println!("cargo:rustc-check-cfg=cfg({})", cfg.as_ref()); + } + } + + pub fn declare_all(&mut self, cfgs: &[impl AsRef]) { + for cfg in cfgs.iter() { + self.declare(cfg.as_ref()); + } + } + + pub fn set(&mut self, cfg: impl Into, enable: bool) { + let cfg = cfg.into(); + if enable { + self.enable(cfg.clone()); + } + self.declare(cfg); + } +} + +fn is_rustc_nightly() -> bool { + let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); + + let output = Command::new(rustc) + .arg("--version") + .output() + .expect("failed to run `rustc --version`"); + + String::from_utf8_lossy(&output.stdout).contains("nightly") +} + +/// Sets configs that describe the target platform. +pub fn set_target_cfgs(cfgs: &mut CfgSet) { + let target = env::var("TARGET").unwrap(); + + if target.starts_with("thumbv6m-") { + cfgs.enable_all(&["cortex_m", "armv6m"]); + } else if target.starts_with("thumbv7m-") { + cfgs.enable_all(&["cortex_m", "armv7m"]); + } else if target.starts_with("thumbv7em-") { + cfgs.enable_all(&["cortex_m", "armv7m", "armv7em"]); + } else if target.starts_with("thumbv8m.base") { + cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_base"]); + } else if target.starts_with("thumbv8m.main") { + cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_main"]); + } + cfgs.declare_all(&[ + "cortex_m", + "armv6m", + "armv7m", + "armv7em", + "armv8m", + "armv8m_base", + "armv8m_main", + ]); + + cfgs.set("has_fpu", target.ends_with("-eabihf")); +} diff --git a/embassy-sync/build.rs b/embassy-sync/build.rs index 2649f5d37..ecd2c0c9f 100644 --- a/embassy-sync/build.rs +++ b/embassy-sync/build.rs @@ -1,4 +1,4 @@ -#[path = "../build_common.rs"] +#[path = "./build_common.rs"] mod common; fn main() { diff --git a/embassy-sync/build_common.rs b/embassy-sync/build_common.rs new file mode 100644 index 000000000..2c65f8529 --- /dev/null +++ b/embassy-sync/build_common.rs @@ -0,0 +1,109 @@ +// NOTE: this file is copy-pasted between several Embassy crates, because there is no +// straightforward way to share this code: +// - it cannot be placed into the root of the repo and linked from each build.rs using `#[path = +// "../build_common.rs"]`, because `cargo publish` requires that all files published with a crate +// reside in the crate's directory, +// - it cannot be symlinked from `embassy-xxx/build_common.rs` to `../build_common.rs`, because +// symlinks don't work on Windows. + +use std::collections::HashSet; +use std::env; +use std::ffi::OsString; +use std::process::Command; + +/// Helper for emitting cargo instruction for enabling configs (`cargo:rustc-cfg=X`) and declaring +/// them (`cargo:rust-check-cfg=cfg(X)`). +#[derive(Debug)] +pub struct CfgSet { + enabled: HashSet, + declared: HashSet, + emit_declared: bool, +} + +impl CfgSet { + pub fn new() -> Self { + Self { + enabled: HashSet::new(), + declared: HashSet::new(), + emit_declared: is_rustc_nightly(), + } + } + + /// Enable a config, which can then be used in `#[cfg(...)]` for conditional compilation. + /// + /// All configs that can potentially be enabled should be unconditionally declared using + /// [`Self::declare()`]. + pub fn enable(&mut self, cfg: impl AsRef) { + if self.enabled.insert(cfg.as_ref().to_owned()) { + println!("cargo:rustc-cfg={}", cfg.as_ref()); + } + } + + pub fn enable_all(&mut self, cfgs: &[impl AsRef]) { + for cfg in cfgs.iter() { + self.enable(cfg.as_ref()); + } + } + + /// Declare a valid config for conditional compilation, without enabling it. + /// + /// This enables rustc to check that the configs in `#[cfg(...)]` attributes are valid. + pub fn declare(&mut self, cfg: impl AsRef) { + if self.declared.insert(cfg.as_ref().to_owned()) && self.emit_declared { + println!("cargo:rustc-check-cfg=cfg({})", cfg.as_ref()); + } + } + + pub fn declare_all(&mut self, cfgs: &[impl AsRef]) { + for cfg in cfgs.iter() { + self.declare(cfg.as_ref()); + } + } + + pub fn set(&mut self, cfg: impl Into, enable: bool) { + let cfg = cfg.into(); + if enable { + self.enable(cfg.clone()); + } + self.declare(cfg); + } +} + +fn is_rustc_nightly() -> bool { + let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); + + let output = Command::new(rustc) + .arg("--version") + .output() + .expect("failed to run `rustc --version`"); + + String::from_utf8_lossy(&output.stdout).contains("nightly") +} + +/// Sets configs that describe the target platform. +pub fn set_target_cfgs(cfgs: &mut CfgSet) { + let target = env::var("TARGET").unwrap(); + + if target.starts_with("thumbv6m-") { + cfgs.enable_all(&["cortex_m", "armv6m"]); + } else if target.starts_with("thumbv7m-") { + cfgs.enable_all(&["cortex_m", "armv7m"]); + } else if target.starts_with("thumbv7em-") { + cfgs.enable_all(&["cortex_m", "armv7m", "armv7em"]); + } else if target.starts_with("thumbv8m.base") { + cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_base"]); + } else if target.starts_with("thumbv8m.main") { + cfgs.enable_all(&["cortex_m", "armv8m", "armv8m_main"]); + } + cfgs.declare_all(&[ + "cortex_m", + "armv6m", + "armv7m", + "armv7em", + "armv8m", + "armv8m_base", + "armv8m_main", + ]); + + cfgs.set("has_fpu", target.ends_with("-eabihf")); +} From 999a2ad829d05b2a34797cf43d625d09bd796a58 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 31 May 2024 21:41:42 +0200 Subject: [PATCH 39/57] Fix all check-cfg errors in the entire repo. the main ci.sh now passes if running with nightly. --- embassy-boot-nrf/Cargo.toml | 1 + embassy-boot-rp/Cargo.toml | 2 -- embassy-boot-rp/build.rs | 1 + embassy-boot-stm32/Cargo.toml | 2 -- embassy-boot-stm32/build.rs | 1 + embassy-net-adin1110/Cargo.toml | 1 - embassy-net-adin1110/src/lib.rs | 2 +- embassy-stm32-wpan/Cargo.toml | 2 ++ embassy-usb-dfu/Cargo.toml | 4 +++- examples/boot/application/nrf/Cargo.toml | 7 +++++++ examples/boot/application/stm32f3/Cargo.toml | 1 + examples/boot/application/stm32f3/src/bin/a.rs | 2 +- examples/boot/application/stm32f3/src/bin/b.rs | 2 +- examples/boot/application/stm32f7/Cargo.toml | 1 + examples/boot/application/stm32f7/src/bin/a.rs | 2 +- examples/boot/application/stm32f7/src/bin/b.rs | 2 +- examples/boot/application/stm32h7/Cargo.toml | 1 + examples/boot/application/stm32h7/src/bin/a.rs | 2 +- examples/boot/application/stm32h7/src/bin/b.rs | 2 +- examples/boot/application/stm32l0/Cargo.toml | 1 + examples/boot/application/stm32l0/src/bin/a.rs | 2 +- examples/boot/application/stm32l0/src/bin/b.rs | 2 +- examples/boot/application/stm32l1/Cargo.toml | 1 + examples/boot/application/stm32l1/src/bin/a.rs | 2 +- examples/boot/application/stm32l1/src/bin/b.rs | 2 +- examples/boot/application/stm32l4/Cargo.toml | 1 + examples/boot/application/stm32l4/src/bin/a.rs | 2 +- examples/boot/application/stm32l4/src/bin/b.rs | 2 +- examples/boot/application/stm32wb-dfu/src/main.rs | 2 +- examples/boot/application/stm32wl/Cargo.toml | 1 + examples/boot/application/stm32wl/src/bin/a.rs | 2 +- examples/boot/application/stm32wl/src/bin/b.rs | 2 +- examples/boot/bootloader/nrf/Cargo.toml | 2 +- examples/boot/bootloader/rp/Cargo.toml | 2 +- examples/boot/bootloader/stm32-dual-bank/Cargo.toml | 3 +-- examples/boot/bootloader/stm32/Cargo.toml | 2 +- examples/boot/bootloader/stm32wb-dfu/Cargo.toml | 2 +- tests/stm32/src/bin/fdcan.rs | 2 +- 38 files changed, 44 insertions(+), 29 deletions(-) diff --git a/embassy-boot-nrf/Cargo.toml b/embassy-boot-nrf/Cargo.toml index e6bb27043..86bfc21f4 100644 --- a/embassy-boot-nrf/Cargo.toml +++ b/embassy-boot-nrf/Cargo.toml @@ -22,6 +22,7 @@ target = "thumbv7em-none-eabi" [dependencies] defmt = { version = "0.3", optional = true } +log = { version = "0.4.17", optional = true } embassy-sync = { version = "0.6.0", path = "../embassy-sync" } embassy-nrf = { version = "0.1.0", path = "../embassy-nrf", default-features = false } diff --git a/embassy-boot-rp/Cargo.toml b/embassy-boot-rp/Cargo.toml index 0bd8abf4c..de2ae3780 100644 --- a/embassy-boot-rp/Cargo.toml +++ b/embassy-boot-rp/Cargo.toml @@ -21,7 +21,6 @@ target = "thumbv6m-none-eabi" [dependencies] defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.4", optional = true } log = { version = "0.4", optional = true } embassy-sync = { version = "0.6.0", path = "../embassy-sync" } @@ -46,7 +45,6 @@ log = [ "embassy-boot/log", "embassy-rp/log", ] -debug = ["defmt-rtt"] [profile.dev] debug = 2 diff --git a/embassy-boot-rp/build.rs b/embassy-boot-rp/build.rs index 2cbc7ef5e..bfaee3503 100644 --- a/embassy-boot-rp/build.rs +++ b/embassy-boot-rp/build.rs @@ -5,4 +5,5 @@ fn main() { if target.starts_with("thumbv6m-") { println!("cargo:rustc-cfg=armv6m"); } + println!("cargo:rustc-check-cfg=cfg(armv6m)"); } diff --git a/embassy-boot-stm32/Cargo.toml b/embassy-boot-stm32/Cargo.toml index a3661f6cb..d4525a6de 100644 --- a/embassy-boot-stm32/Cargo.toml +++ b/embassy-boot-stm32/Cargo.toml @@ -22,7 +22,6 @@ target = "thumbv7em-none-eabi" [dependencies] defmt = { version = "0.3", optional = true } -defmt-rtt = { version = "0.4", optional = true } log = { version = "0.4", optional = true } embassy-sync = { version = "0.6.0", path = "../embassy-sync" } @@ -37,7 +36,6 @@ cfg-if = "1.0.0" [features] defmt = ["dep:defmt", "embassy-boot/defmt", "embassy-stm32/defmt"] log = ["dep:log", "embassy-boot/log", "embassy-stm32/log"] -debug = ["defmt-rtt"] [profile.dev] debug = 2 diff --git a/embassy-boot-stm32/build.rs b/embassy-boot-stm32/build.rs index 2cbc7ef5e..bfaee3503 100644 --- a/embassy-boot-stm32/build.rs +++ b/embassy-boot-stm32/build.rs @@ -5,4 +5,5 @@ fn main() { if target.starts_with("thumbv6m-") { println!("cargo:rustc-cfg=armv6m"); } + println!("cargo:rustc-check-cfg=cfg(armv6m)"); } diff --git a/embassy-net-adin1110/Cargo.toml b/embassy-net-adin1110/Cargo.toml index 97579a467..4a856fac3 100644 --- a/embassy-net-adin1110/Cargo.toml +++ b/embassy-net-adin1110/Cargo.toml @@ -29,7 +29,6 @@ critical-section = { version = "1.1.2", features = ["std"] } futures-test = "0.3.28" [features] -default = [ ] defmt = [ "dep:defmt", "embedded-hal-1/defmt-03" ] log = ["dep:log"] diff --git a/embassy-net-adin1110/src/lib.rs b/embassy-net-adin1110/src/lib.rs index d98e98422..7f1c772e2 100644 --- a/embassy-net-adin1110/src/lib.rs +++ b/embassy-net-adin1110/src/lib.rs @@ -1,6 +1,6 @@ +#![cfg_attr(not(test), no_std)] #![deny(clippy::pedantic)] #![allow(async_fn_in_trait)] -#![cfg_attr(not(any(test, feature = "std")), no_std)] #![allow(clippy::module_name_repetitions)] #![allow(clippy::missing_errors_doc)] #![allow(clippy::missing_panics_doc)] diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 11a0adab2..9c81a71f7 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -28,6 +28,8 @@ embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true } defmt = { version = "0.3", optional = true } +log = { version = "0.4.17", optional = true } + cortex-m = "0.7.6" heapless = "0.8" aligned = "0.4.1" diff --git a/embassy-usb-dfu/Cargo.toml b/embassy-usb-dfu/Cargo.toml index 267e412c0..ca87bcb62 100644 --- a/embassy-usb-dfu/Cargo.toml +++ b/embassy-usb-dfu/Cargo.toml @@ -26,9 +26,11 @@ flavors = [ features = ["defmt", "cortex-m", "dfu"] [dependencies] +defmt = { version = "0.3.5", optional = true } +log = { version = "0.4.17", optional = true } + bitflags = "2.4.1" cortex-m = { version = "0.7.7", features = ["inline-asm"], optional = true } -defmt = { version = "0.3.5", optional = true } embassy-boot = { version = "0.2.0", path = "../embassy-boot" } embassy-futures = { version = "0.1.1", path = "../embassy-futures" } embassy-sync = { version = "0.6.0", path = "../embassy-sync" } diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index f0a710335..cf34bb7cf 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -25,3 +25,10 @@ cortex-m-rt = "0.7.0" ed25519-dalek = ["embassy-boot/ed25519-dalek"] ed25519-salty = ["embassy-boot/ed25519-salty"] skip-include = [] +defmt = [ + "dep:defmt", + "dep:defmt-rtt", + "embassy-nrf/defmt", + "embassy-boot-nrf/defmt", + "embassy-sync/defmt", +] diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index fe1a6f5b1..9b79b01ce 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -23,6 +23,7 @@ cortex-m-rt = "0.7.0" [features] defmt = [ "dep:defmt", + "dep:defmt-rtt", "embassy-stm32/defmt", "embassy-boot-stm32/defmt", "embassy-sync/defmt", diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index 8858ae3da..b608b2e01 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -#[cfg(feature = "defmt-rtt")] +#[cfg(feature = "defmt")] use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; diff --git a/examples/boot/application/stm32f3/src/bin/b.rs b/examples/boot/application/stm32f3/src/bin/b.rs index 22ba82d5e..b1a505631 100644 --- a/examples/boot/application/stm32f3/src/bin/b.rs +++ b/examples/boot/application/stm32f3/src/bin/b.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -#[cfg(feature = "defmt-rtt")] +#[cfg(feature = "defmt")] use defmt_rtt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Speed}; diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 37e362824..0167dfb76 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -24,6 +24,7 @@ cortex-m-rt = "0.7.0" [features] defmt = [ "dep:defmt", + "dep:defmt-rtt", "embassy-stm32/defmt", "embassy-boot-stm32/defmt", "embassy-sync/defmt", diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index d3df11fe4..172b4c235 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -3,7 +3,7 @@ use core::cell::RefCell; -#[cfg(feature = "defmt-rtt")] +#[cfg(feature = "defmt")] use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; use embassy_executor::Spawner; diff --git a/examples/boot/application/stm32f7/src/bin/b.rs b/examples/boot/application/stm32f7/src/bin/b.rs index 190477204..6bc9c9ab8 100644 --- a/examples/boot/application/stm32f7/src/bin/b.rs +++ b/examples/boot/application/stm32f7/src/bin/b.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -#[cfg(feature = "defmt-rtt")] +#[cfg(feature = "defmt")] use defmt_rtt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Speed}; diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index 52cd0b546..61643d485 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -24,6 +24,7 @@ cortex-m-rt = "0.7.0" [features] defmt = [ "dep:defmt", + "dep:defmt-rtt", "embassy-stm32/defmt", "embassy-boot-stm32/defmt", "embassy-sync/defmt", diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index f61ac1f71..c1b1a267a 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -3,7 +3,7 @@ use core::cell::RefCell; -#[cfg(feature = "defmt-rtt")] +#[cfg(feature = "defmt")] use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; use embassy_executor::Spawner; diff --git a/examples/boot/application/stm32h7/src/bin/b.rs b/examples/boot/application/stm32h7/src/bin/b.rs index 5f3f35207..13bdae1f1 100644 --- a/examples/boot/application/stm32h7/src/bin/b.rs +++ b/examples/boot/application/stm32h7/src/bin/b.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -#[cfg(feature = "defmt-rtt")] +#[cfg(feature = "defmt")] use defmt_rtt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Speed}; diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 0f3cbe654..2990089ac 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -23,6 +23,7 @@ cortex-m-rt = "0.7.0" [features] defmt = [ "dep:defmt", + "dep:defmt-rtt", "embassy-stm32/defmt", "embassy-boot-stm32/defmt", "embassy-sync/defmt", diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs index f066c1139..dcc10e5c6 100644 --- a/examples/boot/application/stm32l0/src/bin/a.rs +++ b/examples/boot/application/stm32l0/src/bin/a.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -#[cfg(feature = "defmt-rtt")] +#[cfg(feature = "defmt")] use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; diff --git a/examples/boot/application/stm32l0/src/bin/b.rs b/examples/boot/application/stm32l0/src/bin/b.rs index 6bf00f41a..a59c6f540 100644 --- a/examples/boot/application/stm32l0/src/bin/b.rs +++ b/examples/boot/application/stm32l0/src/bin/b.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -#[cfg(feature = "defmt-rtt")] +#[cfg(feature = "defmt")] use defmt_rtt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Speed}; diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 3e964df9c..c07d71591 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -23,6 +23,7 @@ cortex-m-rt = "0.7.0" [features] defmt = [ "dep:defmt", + "dep:defmt-rtt", "embassy-stm32/defmt", "embassy-boot-stm32/defmt", "embassy-sync/defmt", diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index f066c1139..dcc10e5c6 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -#[cfg(feature = "defmt-rtt")] +#[cfg(feature = "defmt")] use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; diff --git a/examples/boot/application/stm32l1/src/bin/b.rs b/examples/boot/application/stm32l1/src/bin/b.rs index 6bf00f41a..a59c6f540 100644 --- a/examples/boot/application/stm32l1/src/bin/b.rs +++ b/examples/boot/application/stm32l1/src/bin/b.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -#[cfg(feature = "defmt-rtt")] +#[cfg(feature = "defmt")] use defmt_rtt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Speed}; diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index b154403ac..72b937ca7 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -23,6 +23,7 @@ cortex-m-rt = "0.7.0" [features] defmt = [ "dep:defmt", + "dep:defmt-rtt", "embassy-stm32/defmt", "embassy-boot-stm32/defmt", "embassy-sync/defmt", diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index a0079ee33..7f8015c04 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -#[cfg(feature = "defmt-rtt")] +#[cfg(feature = "defmt")] use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; diff --git a/examples/boot/application/stm32l4/src/bin/b.rs b/examples/boot/application/stm32l4/src/bin/b.rs index 22ba82d5e..b1a505631 100644 --- a/examples/boot/application/stm32l4/src/bin/b.rs +++ b/examples/boot/application/stm32l4/src/bin/b.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -#[cfg(feature = "defmt-rtt")] +#[cfg(feature = "defmt")] use defmt_rtt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Speed}; diff --git a/examples/boot/application/stm32wb-dfu/src/main.rs b/examples/boot/application/stm32wb-dfu/src/main.rs index 929d6802c..0ab99ff90 100644 --- a/examples/boot/application/stm32wb-dfu/src/main.rs +++ b/examples/boot/application/stm32wb-dfu/src/main.rs @@ -3,7 +3,7 @@ use core::cell::RefCell; -#[cfg(feature = "defmt-rtt")] +#[cfg(feature = "defmt")] use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareState, FirmwareUpdaterConfig}; use embassy_executor::Spawner; diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index 93ead617c..a5160b797 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -23,6 +23,7 @@ cortex-m-rt = "0.7.0" [features] defmt = [ "dep:defmt", + "dep:defmt-rtt", "embassy-stm32/defmt", "embassy-boot-stm32/defmt", "embassy-sync/defmt", diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index 2fb16bdc4..9f4f0b238 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -#[cfg(feature = "defmt-rtt")] +#[cfg(feature = "defmt")] use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; diff --git a/examples/boot/application/stm32wl/src/bin/b.rs b/examples/boot/application/stm32wl/src/bin/b.rs index 8dd15d8cd..e954d8b91 100644 --- a/examples/boot/application/stm32wl/src/bin/b.rs +++ b/examples/boot/application/stm32wl/src/bin/b.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -#[cfg(feature = "defmt-rtt")] +#[cfg(feature = "defmt")] use defmt_rtt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Speed}; diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml index 980149bea..9d5d51a13 100644 --- a/examples/boot/bootloader/nrf/Cargo.toml +++ b/examples/boot/bootloader/nrf/Cargo.toml @@ -19,13 +19,13 @@ cfg-if = "1.0.0" [features] defmt = [ "dep:defmt", + "dep:defmt-rtt", "embassy-boot-nrf/defmt", "embassy-nrf/defmt", ] softdevice = [ "embassy-boot-nrf/softdevice", ] -debug = ["defmt-rtt", "defmt"] [profile.dev] debug = 2 diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml index 7eec3df1b..c15c980ca 100644 --- a/examples/boot/bootloader/rp/Cargo.toml +++ b/examples/boot/bootloader/rp/Cargo.toml @@ -23,10 +23,10 @@ cfg-if = "1.0.0" [features] defmt = [ "dep:defmt", + "dep:defmt-rtt", "embassy-boot-rp/defmt", "embassy-rp/defmt", ] -debug = ["defmt-rtt", "defmt"] [profile.release] debug = true diff --git a/examples/boot/bootloader/stm32-dual-bank/Cargo.toml b/examples/boot/bootloader/stm32-dual-bank/Cargo.toml index 55adf84d7..b91b05412 100644 --- a/examples/boot/bootloader/stm32-dual-bank/Cargo.toml +++ b/examples/boot/bootloader/stm32-dual-bank/Cargo.toml @@ -22,8 +22,7 @@ embedded-storage-async = "0.4.0" cfg-if = "1.0.0" [features] -defmt = ["dep:defmt", "embassy-boot-stm32/defmt", "embassy-stm32/defmt"] -debug = ["defmt-rtt", "defmt"] +defmt = ["dep:defmt", "dep:defmt-rtt", "embassy-boot-stm32/defmt", "embassy-stm32/defmt"] [profile.dev] debug = 2 diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index ef2b99404..541186949 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml @@ -21,10 +21,10 @@ cfg-if = "1.0.0" [features] defmt = [ "dep:defmt", + "dep:defmt-rtt", "embassy-boot-stm32/defmt", "embassy-stm32/defmt", ] -debug = ["defmt-rtt", "defmt"] [profile.dev] debug = 2 diff --git a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml index 93b5d8b34..9950ed7b6 100644 --- a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml +++ b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml @@ -24,12 +24,12 @@ embassy-futures = { version = "0.1.1", path = "../../../../embassy-futures" } [features] defmt = [ "dep:defmt", + "dep:defmt-rtt", "embassy-boot-stm32/defmt", "embassy-stm32/defmt", "embassy-usb/defmt", "embassy-usb-dfu/defmt" ] -debug = ["defmt-rtt", "defmt"] [profile.dev] debug = 2 diff --git a/tests/stm32/src/bin/fdcan.rs b/tests/stm32/src/bin/fdcan.rs index 8534f92e8..8a397f661 100644 --- a/tests/stm32/src/bin/fdcan.rs +++ b/tests/stm32/src/bin/fdcan.rs @@ -92,7 +92,7 @@ fn options() -> (Config, TestOptions) { ) } -#[cfg(any(feature = "stm32g491re", feature = "stm32g431cb"))] +#[cfg(any(feature = "stm32g491re"))] fn options() -> (Config, TestOptions) { info!("G4 config"); ( From da197b60165b74d05c3d12b046b5cfda0b9cb126 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 31 May 2024 21:43:03 +0200 Subject: [PATCH 40/57] stm32/spi: fix spiv1 rxonly hanging. --- embassy-stm32/src/spi/mod.rs | 7 +-- tests/stm32/build.rs | 1 + tests/stm32/src/bin/spi_dma.rs | 97 ++++++++++++++-------------------- 3 files changed, 45 insertions(+), 60 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index af8e3fc30..3729ed8de 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -148,9 +148,10 @@ impl<'d, M: PeriMode> Spi<'d, M> { w.set_ssm(true); w.set_crcen(false); w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); - if mosi.is_none() { - w.set_rxonly(vals::Rxonly::OUTPUTDISABLED); - } + // we're doing "fake rxonly", by actually writing one + // byte to TXDR for each byte we want to receive. if we + // set OUTPUTDISABLED here, this hangs. + w.set_rxonly(vals::Rxonly::FULLDUPLEX); w.set_dff(::CONFIG) }); } diff --git a/tests/stm32/build.rs b/tests/stm32/build.rs index 176adff62..675115568 100644 --- a/tests/stm32/build.rs +++ b/tests/stm32/build.rs @@ -14,6 +14,7 @@ fn main() -> Result<(), Box> { feature = "stm32c031c6", feature = "stm32wb55rg", feature = "stm32l073rz", + feature = "stm32h503rb", // wrong ram size in stm32-data feature = "stm32wl55jc", feature = "stm32u5a5zj", diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index 30e679f2a..92f741af5 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -6,34 +6,32 @@ mod common; use common::*; use defmt::assert_eq; use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{self, Spi}; use embassy_stm32::time::Hertz; -use embassy_stm32::{into_ref, Peripheral as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config()); info!("Hello World!"); - let spi_peri = peri!(p, SPI); - let sck = peri!(p, SPI_SCK); - let mosi = peri!(p, SPI_MOSI); - let miso = peri!(p, SPI_MISO); - let tx_dma = peri!(p, SPI_TX_DMA); - let rx_dma = peri!(p, SPI_RX_DMA); - - into_ref!(spi_peri, sck, mosi, miso, tx_dma, rx_dma); + let mut spi_peri = peri!(p, SPI); + let mut sck = peri!(p, SPI_SCK); + let mut mosi = peri!(p, SPI_MOSI); + let mut miso = peri!(p, SPI_MISO); + let mut tx_dma = peri!(p, SPI_TX_DMA); + let mut rx_dma = peri!(p, SPI_RX_DMA); let mut spi_config = spi::Config::default(); spi_config.frequency = Hertz(1_000_000); let mut spi = Spi::new( - spi_peri.reborrow(), - sck.reborrow(), // Arduino D13 - mosi.reborrow(), // Arduino D11 - miso.reborrow(), // Arduino D12 - tx_dma.reborrow(), - rx_dma.reborrow(), + &mut spi_peri, + &mut sck, // Arduino D13 + &mut mosi, // Arduino D11 + &mut miso, // Arduino D12 + &mut tx_dma, + &mut rx_dma, spi_config, ); @@ -85,51 +83,36 @@ async fn main(_spawner: Spawner) { core::mem::drop(spi); // test rx-only configuration + let mut spi = Spi::new_rxonly( + &mut spi_peri, + &mut sck, + &mut miso, + // SPIv1/f1 requires txdma even if rxonly. + #[cfg(not(any( + feature = "stm32h503rb", + feature = "stm32h563zi", + feature = "stm32h753zi", + feature = "stm32h755zi", + feature = "stm32h7a3zi", + feature = "stm32h7s3l8", + feature = "stm32u585ai", + feature = "stm32u5a5zj", + feature = "stm32wba52cg", + )))] + &mut tx_dma, + &mut rx_dma, + spi_config, + ); - // stm32f207zg - spi_v1 - // stm32f103c8 - spi_f1 - // stm32g491re - spi_v2 - // stm32h753zi - spi_v3 - // stm32h563zi - spi_v4 - // stm32wba52cg - spi_v5 + let mut mosi = Output::new(&mut mosi, Level::Low, Speed::VeryHigh); - #[cfg(any(stm32f207zg, stm32f103c8, stm32g491re, stm32h753zi, stm32h563zi, stm32wba52cg))] - { - let mut spi = { - #[cfg(stm32f207zg, stm32f103c8, stm32g491re)] - { - Spi::new_rxonly( - spi_peri.reborrow(), - sck.reborrow(), - miso.reborrow(), - tx_dma.reborrow(), - rx_dma.reborrow(), - spi_config, - ) - } - #[cfg(stm32h753zi, stm32h563zi, stm32wba52cg)] - { - Spi::new_rxonly( - spi_peri.reborrow(), - sck.reborrow(), - miso.reborrow(), - rx_dma.reborrow(), - spi_config, - ) - } - }; + mosi.set_high(); + spi.read(&mut buf).await.unwrap(); + assert_eq!(buf, [0xff; 9]); - use embassy_stm32::gpio; - let mut mosi = gpio::Output::new(mosi.reborrow(), gpio::Level::Low, gpio::Speed::Low); - - mosi.set_high(); - spi.read(&mut buf).await.unwrap(); - assert_eq!(buf, [0xff; 9]); - - mosi.set_low(); - spi.read(&mut buf).await.unwrap(); - assert_eq!(buf, [0x00; 9]); - }; + mosi.set_low(); + spi.read(&mut buf).await.unwrap(); + assert_eq!(buf, [0x00; 9]); info!("Test OK"); cortex_m::asm::bkpt(); From f594ddceecbf00022f1b76bec030fd32c0e03178 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 31 May 2024 21:49:33 +0200 Subject: [PATCH 41/57] stm32/spi: add blocking rxonly test. --- tests/stm32/src/bin/spi.rs | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index c1576bfeb..a0ca5284d 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -6,6 +6,7 @@ mod common; use common::*; use defmt::assert_eq; use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{self, Spi}; use embassy_stm32::time::Hertz; @@ -14,18 +15,19 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config()); info!("Hello World!"); - let spi = peri!(p, SPI); - let sck = peri!(p, SPI_SCK); - let mosi = peri!(p, SPI_MOSI); - let miso = peri!(p, SPI_MISO); + let mut spi_peri = peri!(p, SPI); + let mut sck = peri!(p, SPI_SCK); + let mut mosi = peri!(p, SPI_MOSI); + let mut miso = peri!(p, SPI_MISO); let mut spi_config = spi::Config::default(); spi_config.frequency = Hertz(1_000_000); let mut spi = Spi::new_blocking( - spi, sck, // Arduino D13 - mosi, // Arduino D11 - miso, // Arduino D12 + &mut spi_peri, + &mut sck, // Arduino D13 + &mut mosi, // Arduino D11 + &mut miso, // Arduino D12 spi_config, ); @@ -66,6 +68,22 @@ async fn main(_spawner: Spawner) { defmt::assert!(!embassy_stm32::pac::RCC.apb2enr().read().spi1en()); } + #[cfg(not(feature = "stm32f429zi"))] + core::mem::drop(spi); + + // test rx-only configuration + let mut spi = Spi::new_blocking_rxonly(&mut spi_peri, &mut sck, &mut miso, spi_config); + + let mut mosi = Output::new(&mut mosi, Level::Low, Speed::VeryHigh); + + mosi.set_high(); + spi.blocking_read(&mut buf).unwrap(); + assert_eq!(buf, [0xff; 9]); + + mosi.set_low(); + spi.blocking_read(&mut buf).unwrap(); + assert_eq!(buf, [0x00; 9]); + info!("Test OK"); cortex_m::asm::bkpt(); } From 339dd859686caee4ed55ed1f3cba0320e085db39 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 31 May 2024 22:16:30 +0200 Subject: [PATCH 42/57] stm32/spi: restrict txonly_nosck to SPIv1, it hangs in other versions. --- embassy-stm32/src/spi/mod.rs | 2 + examples/stm32g0/src/bin/spi_neopixel.rs | 2 +- tests/stm32/Cargo.toml | 30 +++++++------ tests/stm32/src/bin/spi.rs | 50 +++++++++++++++------ tests/stm32/src/bin/spi_dma.rs | 56 +++++++++++++++++------- 5 files changed, 94 insertions(+), 46 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 3729ed8de..5d6277c33 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -465,6 +465,7 @@ impl<'d> Spi<'d, Blocking> { /// Create a new SPI driver, in TX-only mode, without SCK pin. /// /// This can be useful for bit-banging non-SPI protocols. + #[cfg(any(spi_v1, spi_f1))] // no SCK pin causes it to hang on spiv2+ for unknown reasons. pub fn new_blocking_txonly_nosck( peri: impl Peripheral

+ 'd, mosi: impl Peripheral

> + 'd, @@ -549,6 +550,7 @@ impl<'d> Spi<'d, Async> { /// Create a new SPI driver, in TX-only mode, without SCK pin. /// /// This can be useful for bit-banging non-SPI protocols. + #[cfg(any(spi_v1, spi_f1))] // no SCK pin causes it to hang on spiv2+ for unknown reasons. pub fn new_txonly_nosck( peri: impl Peripheral

+ 'd, mosi: impl Peripheral

> + 'd, diff --git a/examples/stm32g0/src/bin/spi_neopixel.rs b/examples/stm32g0/src/bin/spi_neopixel.rs index 2deee271d..edcae74f7 100644 --- a/examples/stm32g0/src/bin/spi_neopixel.rs +++ b/examples/stm32g0/src/bin/spi_neopixel.rs @@ -76,7 +76,7 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.frequency = Hertz(4_000_000); - let mut spi = Spi::new_txonly_nosck(p.SPI1, p.PB5, p.DMA1_CH3, config); + let mut spi = Spi::new_txonly(p.SPI1, p.PB3, p.PB5, p.DMA1_CH3, config); // SCK is unused. let mut neopixels = Ws2812::new(); diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index f6d1d98eb..de390ee38 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -7,34 +7,36 @@ autobins = false [features] stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"] -stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] -stm32f207zg = ["embassy-stm32/stm32f207zg", "chrono", "not-gpdma", "eth", "rng"] +stm32f103c8 = ["embassy-stm32/stm32f103c8", "spi-v1", "not-gpdma"] +stm32f207zg = ["embassy-stm32/stm32f207zg", "spi-v1", "chrono", "not-gpdma", "eth", "rng"] stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"] -stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "eth", "stop", "can", "not-gpdma", "dac", "rng"] -stm32f446re = ["embassy-stm32/stm32f446re", "chrono", "stop", "can", "not-gpdma", "dac", "sdmmc"] +stm32f429zi = ["embassy-stm32/stm32f429zi", "spi-v1", "chrono", "eth", "stop", "can", "not-gpdma", "dac", "rng"] +stm32f446re = ["embassy-stm32/stm32f446re", "spi-v1", "chrono", "stop", "can", "not-gpdma", "dac", "sdmmc"] stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"] stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac", "ucpd"] stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng", "fdcan", "cordic"] -stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng", "fdcan", "hash", "cordic", "stop"] -stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng", "fdcan", "hash", "cryp"] -stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan", "hash", "cryp"] -stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng", "fdcan"] +stm32h563zi = ["embassy-stm32/stm32h563zi", "spi-v345", "chrono", "eth", "rng", "fdcan", "hash", "cordic", "stop"] +stm32h753zi = ["embassy-stm32/stm32h753zi", "spi-v345", "chrono", "not-gpdma", "eth", "rng", "fdcan", "hash", "cryp"] +stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "spi-v345", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan", "hash", "cryp"] +stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "spi-v345", "not-gpdma", "rng", "fdcan"] stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng"] -stm32l152re = ["embassy-stm32/stm32l152re", "chrono", "not-gpdma"] +stm32l152re = ["embassy-stm32/stm32l152re", "spi-v1", "chrono", "not-gpdma"] stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"] stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng", "hash"] stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng"] stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash"] -stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng", "hash", "cordic"] -stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng", "hash"] # FIXME: cordic test cause it crash +stm32u585ai = ["embassy-stm32/stm32u585ai", "spi-v345", "chrono", "rng", "hash", "cordic"] +stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "spi-v345", "chrono", "rng", "hash"] # FIXME: cordic test cause it crash stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"] -stm32wba52cg = ["embassy-stm32/stm32wba52cg", "chrono", "rng", "hash"] +stm32wba52cg = ["embassy-stm32/stm32wba52cg", "spi-v345", "chrono", "rng", "hash"] stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"] stm32f091rc = ["embassy-stm32/stm32f091rc", "cm0", "not-gpdma", "chrono"] -stm32h503rb = ["embassy-stm32/stm32h503rb", "rng", "stop"] -stm32h7s3l8 = ["embassy-stm32/stm32h7s3l8", "rng", "cordic", "hash"] # TODO: fdcan crashes, cryp dma hangs. +stm32h503rb = ["embassy-stm32/stm32h503rb", "spi-v345", "rng", "stop"] +stm32h7s3l8 = ["embassy-stm32/stm32h7s3l8", "spi-v345", "rng", "cordic", "hash"] # TODO: fdcan crashes, cryp dma hangs. stm32u083rc = ["embassy-stm32/stm32u083rc", "cm0", "rng", "chrono"] +spi-v1 = [] +spi-v345 = [] cryp = [] hash = [] eth = ["embassy-executor/task-arena-size-16384"] diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index a0ca5284d..0d670b7c1 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -62,27 +62,49 @@ async fn main(_spawner: Spawner) { // Assert the RCC bit gets disabled on drop. #[cfg(feature = "stm32f429zi")] - { - defmt::assert!(embassy_stm32::pac::RCC.apb2enr().read().spi1en()); - drop(spi); - defmt::assert!(!embassy_stm32::pac::RCC.apb2enr().read().spi1en()); - } - - #[cfg(not(feature = "stm32f429zi"))] - core::mem::drop(spi); + defmt::assert!(embassy_stm32::pac::RCC.apb2enr().read().spi1en()); + drop(spi); + #[cfg(feature = "stm32f429zi")] + defmt::assert!(!embassy_stm32::pac::RCC.apb2enr().read().spi1en()); // test rx-only configuration let mut spi = Spi::new_blocking_rxonly(&mut spi_peri, &mut sck, &mut miso, spi_config); - - let mut mosi = Output::new(&mut mosi, Level::Low, Speed::VeryHigh); - - mosi.set_high(); + let mut mosi_out = Output::new(&mut mosi, Level::Low, Speed::VeryHigh); + mosi_out.set_high(); spi.blocking_read(&mut buf).unwrap(); assert_eq!(buf, [0xff; 9]); - - mosi.set_low(); + mosi_out.set_low(); spi.blocking_read(&mut buf).unwrap(); assert_eq!(buf, [0x00; 9]); + drop(mosi_out); + drop(spi); + + // Test tx-only. Just check it doesn't hang, not much else we can do without using SPI slave. + let mut spi = Spi::new_blocking_txonly(&mut spi_peri, &mut sck, &mut mosi, spi_config); + spi.blocking_transfer(&mut buf, &data).unwrap(); + spi.blocking_transfer_in_place(&mut buf).unwrap(); + spi.blocking_write(&buf).unwrap(); + spi.blocking_read(&mut buf).unwrap(); + spi.blocking_transfer::(&mut [], &[]).unwrap(); + spi.blocking_transfer_in_place::(&mut []).unwrap(); + spi.blocking_read::(&mut []).unwrap(); + spi.blocking_write::(&[]).unwrap(); + drop(spi); + + // Test tx-only nosck. + #[cfg(feature = "spi-v1")] + { + let mut spi = Spi::new_blocking_txonly_nosck(&mut spi_peri, &mut mosi, spi_config); + spi.blocking_transfer(&mut buf, &data).unwrap(); + spi.blocking_transfer_in_place(&mut buf).unwrap(); + spi.blocking_write(&buf).unwrap(); + spi.blocking_read(&mut buf).unwrap(); + spi.blocking_transfer::(&mut [], &[]).unwrap(); + spi.blocking_transfer_in_place::(&mut []).unwrap(); + spi.blocking_read::(&mut []).unwrap(); + spi.blocking_write::(&[]).unwrap(); + drop(spi); + } info!("Test OK"); cortex_m::asm::bkpt(); diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index 92f741af5..0cd3690ec 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -88,31 +88,53 @@ async fn main(_spawner: Spawner) { &mut sck, &mut miso, // SPIv1/f1 requires txdma even if rxonly. - #[cfg(not(any( - feature = "stm32h503rb", - feature = "stm32h563zi", - feature = "stm32h753zi", - feature = "stm32h755zi", - feature = "stm32h7a3zi", - feature = "stm32h7s3l8", - feature = "stm32u585ai", - feature = "stm32u5a5zj", - feature = "stm32wba52cg", - )))] + #[cfg(not(feature = "spi-v345"))] &mut tx_dma, &mut rx_dma, spi_config, ); - - let mut mosi = Output::new(&mut mosi, Level::Low, Speed::VeryHigh); - - mosi.set_high(); + let mut mosi_out = Output::new(&mut mosi, Level::Low, Speed::VeryHigh); + mosi_out.set_high(); spi.read(&mut buf).await.unwrap(); assert_eq!(buf, [0xff; 9]); - - mosi.set_low(); + spi.blocking_read(&mut buf).unwrap(); + assert_eq!(buf, [0xff; 9]); + spi.read(&mut buf).await.unwrap(); + assert_eq!(buf, [0xff; 9]); + spi.read(&mut buf).await.unwrap(); + assert_eq!(buf, [0xff; 9]); + spi.blocking_read(&mut buf).unwrap(); + assert_eq!(buf, [0xff; 9]); + spi.blocking_read(&mut buf).unwrap(); + assert_eq!(buf, [0xff; 9]); + mosi_out.set_low(); spi.read(&mut buf).await.unwrap(); assert_eq!(buf, [0x00; 9]); + drop(mosi_out); + drop(spi); + + // Test tx-only. Just check it doesn't hang, not much else we can do without using SPI slave. + let mut spi = Spi::new_txonly(&mut spi_peri, &mut sck, &mut mosi, &mut tx_dma, spi_config); + spi.blocking_write(&buf).unwrap(); + spi.write(&buf).await.unwrap(); + spi.blocking_write(&buf).unwrap(); + spi.blocking_write(&buf).unwrap(); + spi.write(&buf).await.unwrap(); + spi.write(&buf).await.unwrap(); + drop(spi); + + // Test tx-only nosck. + #[cfg(feature = "spi-v1")] + { + let mut spi = Spi::new_txonly_nosck(&mut spi_peri, &mut mosi, &mut tx_dma, spi_config); + spi.blocking_write(&buf).unwrap(); + spi.write(&buf).await.unwrap(); + spi.blocking_write(&buf).unwrap(); + spi.blocking_write(&buf).unwrap(); + spi.write(&buf).await.unwrap(); + spi.write(&buf).await.unwrap(); + drop(spi); + } info!("Test OK"); cortex_m::asm::bkpt(); From 8a1658ab0e946b1fb6e91a9b22e92a51b78704ec Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 31 May 2024 22:19:17 +0200 Subject: [PATCH 43/57] stm32/spi: test zero-length transfers in more cases. --- tests/stm32/src/bin/spi.rs | 2 ++ tests/stm32/src/bin/spi_dma.rs | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index 0d670b7c1..8be3b1a7c 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -76,6 +76,8 @@ async fn main(_spawner: Spawner) { mosi_out.set_low(); spi.blocking_read(&mut buf).unwrap(); assert_eq!(buf, [0x00; 9]); + spi.blocking_read::(&mut []).unwrap(); + spi.blocking_read::(&mut []).unwrap(); drop(mosi_out); drop(spi); diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index 0cd3690ec..a8001a111 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -63,8 +63,12 @@ async fn main(_spawner: Spawner) { spi.transfer_in_place::(&mut []).await.unwrap(); spi.read::(&mut []).await.unwrap(); spi.write::(&[]).await.unwrap(); + spi.blocking_transfer::(&mut [], &[]).unwrap(); + spi.blocking_transfer_in_place::(&mut []).unwrap(); + spi.blocking_read::(&mut []).unwrap(); + spi.blocking_write::(&[]).unwrap(); - // === Check mixing blocking with async. + // Check mixing blocking with async. spi.blocking_transfer(&mut buf, &data).unwrap(); assert_eq!(buf, data); spi.transfer(&mut buf, &data).await.unwrap(); @@ -110,6 +114,8 @@ async fn main(_spawner: Spawner) { mosi_out.set_low(); spi.read(&mut buf).await.unwrap(); assert_eq!(buf, [0x00; 9]); + spi.read::(&mut []).await.unwrap(); + spi.blocking_read::(&mut []).unwrap(); drop(mosi_out); drop(spi); @@ -121,6 +127,8 @@ async fn main(_spawner: Spawner) { spi.blocking_write(&buf).unwrap(); spi.write(&buf).await.unwrap(); spi.write(&buf).await.unwrap(); + spi.write::(&[]).await.unwrap(); + spi.blocking_write::(&[]).unwrap(); drop(spi); // Test tx-only nosck. @@ -133,6 +141,8 @@ async fn main(_spawner: Spawner) { spi.blocking_write(&buf).unwrap(); spi.write(&buf).await.unwrap(); spi.write(&buf).await.unwrap(); + spi.write::(&[]).await.unwrap(); + spi.blocking_write::(&[]).unwrap(); drop(spi); } From ca3c15658dfb75945506c15070fd0deeb1583aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0pa=C4=8Dek?= Date: Sun, 26 May 2024 15:33:29 +0200 Subject: [PATCH 44/57] stm32/spi: move init code to function that's not generic in T --- embassy-stm32/src/spi/mod.rs | 39 ++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 5d6277c33..fb8e79ad8 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -13,7 +13,7 @@ use crate::dma::{slice_ptr_parts, word, ChannelAndRequest}; use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _, Speed}; use crate::mode::{Async, Blocking, Mode as PeriMode}; use crate::pac::spi::{regs, vals, Spi as Regs}; -use crate::rcc::{self, RccInfo, SealedRccPeripheral}; +use crate::rcc::{RccInfo, SealedRccPeripheral}; use crate::time::Hertz; use crate::Peripheral; @@ -120,17 +120,30 @@ impl<'d, M: PeriMode> Spi<'d, M> { rx_dma: Option>, config: Config, ) -> Self { - let regs = T::info().regs; - let kernel_clock = T::frequency(); - let br = compute_baud_rate(kernel_clock, config.frequency); + let mut this = Self { + info: T::info(), + kernel_clock: T::frequency(), + sck, + mosi, + miso, + tx_dma, + rx_dma, + current_word_size: ::CONFIG, + _phantom: PhantomData, + }; + this.enable_and_init(config); + this + } + fn enable_and_init(&mut self, config: Config) { + let br = compute_baud_rate(self.kernel_clock, config.frequency); let cpha = config.raw_phase(); let cpol = config.raw_polarity(); - let lsbfirst = config.raw_byte_order(); - rcc::enable_and_reset::(); + self.info.rcc.enable_and_reset(); + let regs = self.info.regs; #[cfg(any(spi_v1, spi_f1))] { regs.cr2().modify(|w| { @@ -209,18 +222,6 @@ impl<'d, M: PeriMode> Spi<'d, M> { w.set_spe(true); }); } - - Self { - info: T::info(), - kernel_clock, - sck, - mosi, - miso, - tx_dma, - rx_dma, - current_word_size: ::CONFIG, - _phantom: PhantomData, - } } /// Reconfigures it with the supplied config. @@ -578,7 +579,7 @@ impl<'d> Spi<'d, Async> { // see RM0453 rev 1 section 7.2.13 page 291 // The SUBGHZSPI_SCK frequency is obtained by PCLK3 divided by two. // The SUBGHZSPI_SCK clock maximum speed must not exceed 16 MHz. - let pclk3_freq = ::frequency().0; + let pclk3_freq = ::frequency().0; let freq = Hertz(core::cmp::min(pclk3_freq / 2, 16_000_000)); let mut config = Config::default(); config.mode = MODE_0; From 41711195e305e45caec3ff8316688a6b6d6af955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0pa=C4=8Dek?= Date: Sun, 26 May 2024 15:40:42 +0200 Subject: [PATCH 45/57] stm32/i2c: use new_pin! macro --- embassy-stm32/src/i2c/mod.rs | 89 +++++++++++++++++++++++------------- embassy-stm32/src/i2c/v1.rs | 6 --- embassy-stm32/src/i2c/v2.rs | 6 --- 3 files changed, 58 insertions(+), 43 deletions(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 0bf57ef8a..6d12af2cc 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -9,16 +9,16 @@ use core::future::Future; use core::iter; use core::marker::PhantomData; -use embassy_hal_internal::{into_ref, Peripheral}; +use embassy_hal_internal::{Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; use crate::dma::ChannelAndRequest; -use crate::gpio::{AFType, Pull}; +use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _, Speed}; use crate::interrupt::typelevel::Interrupt; use crate::mode::{Async, Blocking, Mode}; -use crate::rcc::{self, RccInfo, SealedRccPeripheral}; +use crate::rcc::{RccInfo, SealedRccPeripheral}; use crate::time::Hertz; use crate::{interrupt, peripherals}; @@ -72,11 +72,29 @@ impl Default for Config { } } +impl Config { + fn scl_pull_mode(&self) -> Pull { + match self.scl_pullup { + true => Pull::Up, + false => Pull::Down, + } + } + + fn sda_pull_mode(&self) -> Pull { + match self.sda_pullup { + true => Pull::Up, + false => Pull::Down, + } + } +} + /// I2C driver. pub struct I2c<'d, M: Mode> { info: &'static Info, state: &'static State, kernel_clock: Hertz, + scl: Option>, + sda: Option>, tx_dma: Option>, rx_dma: Option>, #[cfg(feature = "time")] @@ -98,7 +116,15 @@ impl<'d> I2c<'d, Async> { freq: Hertz, config: Config, ) -> Self { - Self::new_inner(peri, scl, sda, new_dma!(tx_dma), new_dma!(rx_dma), freq, config) + Self::new_inner( + peri, + new_pin!(scl, AFType::OutputOpenDrain, Speed::Medium, config.scl_pull_mode()), + new_pin!(sda, AFType::OutputOpenDrain, Speed::Medium, config.sda_pull_mode()), + new_dma!(tx_dma), + new_dma!(rx_dma), + freq, + config, + ) } } @@ -111,7 +137,15 @@ impl<'d> I2c<'d, Blocking> { freq: Hertz, config: Config, ) -> Self { - Self::new_inner(peri, scl, sda, None, None, freq, config) + Self::new_inner( + peri, + new_pin!(scl, AFType::OutputOpenDrain, Speed::Medium, config.scl_pull_mode()), + new_pin!(sda, AFType::OutputOpenDrain, Speed::Medium, config.sda_pull_mode()), + None, + None, + freq, + config, + ) } } @@ -119,34 +153,13 @@ impl<'d, M: Mode> I2c<'d, M> { /// Create a new I2C driver. fn new_inner( _peri: impl Peripheral

+ 'd, - scl: impl Peripheral

> + 'd, - sda: impl Peripheral

> + 'd, + scl: Option>, + sda: Option>, tx_dma: Option>, rx_dma: Option>, freq: Hertz, config: Config, ) -> Self { - into_ref!(scl, sda); - - rcc::enable_and_reset::(); - - scl.set_as_af_pull( - scl.af_num(), - AFType::OutputOpenDrain, - match config.scl_pullup { - true => Pull::Up, - false => Pull::None, - }, - ); - sda.set_as_af_pull( - sda.af_num(), - AFType::OutputOpenDrain, - match config.sda_pullup { - true => Pull::Up, - false => Pull::None, - }, - ); - unsafe { T::EventInterrupt::enable() }; unsafe { T::ErrorInterrupt::enable() }; @@ -154,18 +167,23 @@ impl<'d, M: Mode> I2c<'d, M> { info: T::info(), state: T::state(), kernel_clock: T::frequency(), + scl, + sda, tx_dma, rx_dma, #[cfg(feature = "time")] timeout: config.timeout, _phantom: PhantomData, }; - - this.init(freq, config); - + this.enable_and_init(freq, config); this } + fn enable_and_init(&mut self, freq: Hertz, config: Config) { + self.info.rcc.enable_and_reset(); + self.init(freq, config); + } + fn timeout(&self) -> Timeout { Timeout { #[cfg(feature = "time")] @@ -174,6 +192,15 @@ impl<'d, M: Mode> I2c<'d, M> { } } +impl<'d, M: Mode> Drop for I2c<'d, M> { + fn drop(&mut self) { + self.scl.as_ref().map(|x| x.set_as_disconnected()); + self.sda.as_ref().map(|x| x.set_as_disconnected()); + + self.info.rcc.disable() + } +} + #[derive(Copy, Clone)] struct Timeout { #[cfg(feature = "time")] diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 0e2bd2e40..28026f83c 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -700,12 +700,6 @@ impl<'d> I2c<'d, Async> { } } -impl<'d, M: PeriMode> Drop for I2c<'d, M> { - fn drop(&mut self) { - self.info.rcc.disable() - } -} - enum Mode { Fast, Standard, diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 193f29733..80163c287 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -671,12 +671,6 @@ impl<'d> I2c<'d, Async> { } } -impl<'d, M: Mode> Drop for I2c<'d, M> { - fn drop(&mut self) { - self.info.rcc.disable(); - } -} - /// I2C Stop Configuration /// /// Peripheral options for generating the STOP condition From ade27b7f212f9548816e5ae21826a230c2345574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0pa=C4=8Dek?= Date: Sun, 26 May 2024 16:21:11 +0200 Subject: [PATCH 46/57] stm32/usart: disconnect pins of RingBufferedUartRx on drop --- embassy-stm32/src/usart/ringbuffered.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index f3a88b93f..8cf75933a 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -4,10 +4,12 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_embedded_hal::SetConfig; +use embassy_hal_internal::PeripheralRef; use futures_util::future::{select, Either}; use super::{clear_interrupt_flags, rdr, reconfigure, sr, Config, ConfigError, Error, Info, State, UartRx}; use crate::dma::ReadableRingBuffer; +use crate::gpio::{AnyPin, SealedPin as _}; use crate::mode::Async; use crate::time::Hertz; use crate::usart::{Regs, Sr}; @@ -19,6 +21,8 @@ pub struct RingBufferedUartRx<'d> { info: &'static Info, state: &'static State, kernel_clock: Hertz, + rx: Option>, + rts: Option>, ring_buf: ReadableRingBuffer<'d, u8>, } @@ -49,6 +53,8 @@ impl<'d> UartRx<'d, Async> { let state = self.state; let kernel_clock = self.kernel_clock; let ring_buf = unsafe { ReadableRingBuffer::new(rx_dma, request, rdr(info.regs), dma_buf, opts) }; + let rx = unsafe { self.rx.as_ref().map(|x| x.clone_unchecked()) }; + let rts = unsafe { self.rts.as_ref().map(|x| x.clone_unchecked()) }; // Don't disable the clock mem::forget(self); @@ -57,6 +63,8 @@ impl<'d> UartRx<'d, Async> { info, state, kernel_clock, + rx, + rts, ring_buf, } } @@ -221,6 +229,8 @@ impl<'d> RingBufferedUartRx<'d> { impl Drop for RingBufferedUartRx<'_> { fn drop(&mut self) { self.teardown_uart(); + self.rx.as_ref().map(|x| x.set_as_disconnected()); + self.rts.as_ref().map(|x| x.set_as_disconnected()); super::drop_tx_rx(self.info, self.state); } } From 44e4a2c9e9c1e5824acb68673c8f6b4b98d1ea07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0pa=C4=8Dek?= Date: Sun, 26 May 2024 16:37:26 +0200 Subject: [PATCH 47/57] stm32/buffered-usart: use new_pin! and disconnect pins on drop --- embassy-stm32/src/usart/buffered.rs | 102 +++++++++++++++++----------- embassy-stm32/src/usart/mod.rs | 5 +- 2 files changed, 65 insertions(+), 42 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index eacf95002..2c19e16db 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -6,7 +6,7 @@ use core::task::Poll; use embassy_embedded_hal::SetConfig; use embassy_hal_internal::atomic_ring_buffer::RingBuffer; -use embassy_hal_internal::{into_ref, Peripheral}; +use embassy_hal_internal::{Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(not(any(usart_v1, usart_v2)))] @@ -15,7 +15,7 @@ use super::{ clear_interrupt_flags, configure, rdr, reconfigure, sr, tdr, Config, ConfigError, CtsPin, Error, Info, Instance, Regs, RtsPin, RxPin, TxPin, }; -use crate::gpio::AFType; +use crate::gpio::{AFType, AnyPin, SealedPin as _}; use crate::interrupt::typelevel::Interrupt as _; use crate::interrupt::{self, InterruptExt}; use crate::rcc; @@ -156,7 +156,9 @@ pub struct BufferedUartTx<'d> { info: &'static Info, state: &'static State, kernel_clock: Hertz, - _phantom: PhantomData<&'d mut ()>, + tx: Option>, + cts: Option>, + de: Option>, } /// Rx-only buffered UART @@ -166,7 +168,8 @@ pub struct BufferedUartRx<'d> { info: &'static Info, state: &'static State, kernel_clock: Hertz, - _phantom: PhantomData<&'d mut ()>, + rx: Option>, + rts: Option>, } impl<'d> SetConfig for BufferedUart<'d> { @@ -207,9 +210,17 @@ impl<'d> BufferedUart<'d> { rx_buffer: &'d mut [u8], config: Config, ) -> Result { - rcc::enable_and_reset::(); - - Self::new_inner(peri, rx, tx, tx_buffer, rx_buffer, config) + Self::new_inner( + peri, + new_pin!(rx, AFType::Input), + new_pin!(tx, AFType::OutputPushPull), + None, + None, + None, + tx_buffer, + rx_buffer, + config, + ) } /// Create a new bidirectional buffered UART driver with request-to-send and clear-to-send pins @@ -224,18 +235,17 @@ impl<'d> BufferedUart<'d> { rx_buffer: &'d mut [u8], config: Config, ) -> Result { - into_ref!(cts, rts); - - rcc::enable_and_reset::(); - - rts.set_as_af(rts.af_num(), AFType::OutputPushPull); - cts.set_as_af(cts.af_num(), AFType::Input); - T::info().regs.cr3().write(|w| { - w.set_rtse(true); - w.set_ctse(true); - }); - - Self::new_inner(peri, rx, tx, tx_buffer, rx_buffer, config) + Self::new_inner( + peri, + new_pin!(rx, AFType::Input), + new_pin!(tx, AFType::OutputPushPull), + new_pin!(rts, AFType::OutputPushPull), + new_pin!(cts, AFType::Input), + None, + tx_buffer, + rx_buffer, + config, + ) } /// Create a new bidirectional buffered UART driver with a driver-enable pin @@ -250,27 +260,31 @@ impl<'d> BufferedUart<'d> { rx_buffer: &'d mut [u8], config: Config, ) -> Result { - into_ref!(de); - - rcc::enable_and_reset::(); - - de.set_as_af(de.af_num(), AFType::OutputPushPull); - T::info().regs.cr3().write(|w| { - w.set_dem(true); - }); - - Self::new_inner(peri, rx, tx, tx_buffer, rx_buffer, config) + Self::new_inner( + peri, + new_pin!(rx, AFType::Input), + new_pin!(tx, AFType::OutputPushPull), + None, + None, + new_pin!(de, AFType::OutputPushPull), + tx_buffer, + rx_buffer, + config, + ) } fn new_inner( _peri: impl Peripheral

+ 'd, - rx: impl Peripheral

> + 'd, - tx: impl Peripheral

> + 'd, + rx: Option>, + tx: Option>, + rts: Option>, + cts: Option>, + de: Option>, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], config: Config, ) -> Result { - into_ref!(_peri, rx, tx); + rcc::enable_and_reset::(); let info = T::info(); let state = T::buffered_state(); @@ -280,13 +294,15 @@ impl<'d> BufferedUart<'d> { let len = rx_buffer.len(); unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; - let r = info.regs; - rx.set_as_af(rx.af_num(), AFType::Input); - tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - + info.regs.cr3().write(|w| { + w.set_rtse(rts.is_some()); + w.set_ctse(cts.is_some()); + #[cfg(not(any(usart_v1, usart_v2)))] + w.set_dem(de.is_some()); + }); configure(info, kernel_clock, &config, true, true)?; - r.cr1().modify(|w| { + info.regs.cr1().modify(|w| { w.set_rxneie(true); w.set_idleie(true); }); @@ -301,13 +317,16 @@ impl<'d> BufferedUart<'d> { info, state, kernel_clock, - _phantom: PhantomData, + rx, + rts, }, tx: BufferedUartTx { info, state, kernel_clock, - _phantom: PhantomData, + tx, + cts, + de, }, }) } @@ -516,6 +535,8 @@ impl<'d> Drop for BufferedUartRx<'d> { } } + self.rx.as_ref().map(|x| x.set_as_disconnected()); + self.rts.as_ref().map(|x| x.set_as_disconnected()); drop_tx_rx(self.info, state); } } @@ -533,6 +554,9 @@ impl<'d> Drop for BufferedUartTx<'d> { } } + self.tx.as_ref().map(|x| x.set_as_disconnected()); + self.cts.as_ref().map(|x| x.set_as_disconnected()); + self.de.as_ref().map(|x| x.set_as_disconnected()); drop_tx_rx(self.info, state); } } diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 2a39c6301..6ef80c54f 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -14,7 +14,7 @@ use embassy_sync::waitqueue::AtomicWaker; use futures_util::future::{select, Either}; use crate::dma::ChannelAndRequest; -use crate::gpio::{AFType, AnyPin, SealedPin}; +use crate::gpio::{AFType, AnyPin, SealedPin as _}; use crate::interrupt::typelevel::Interrupt as _; use crate::interrupt::{self, Interrupt, InterruptExt}; use crate::mode::{Async, Blocking, Mode}; @@ -1233,9 +1233,8 @@ impl<'d, M: Mode> Uart<'d, M> { let info = T::info(); let state = T::state(); let kernel_clock = T::frequency(); - let r = info.regs; - r.cr3().write(|w| { + info.regs.cr3().write(|w| { w.set_rtse(rts.is_some()); w.set_ctse(cts.is_some()); #[cfg(not(any(usart_v1, usart_v2)))] From 664e4a5c032d2291beedf1ead706f5d17b8178da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0pa=C4=8Dek?= Date: Sun, 26 May 2024 16:55:07 +0200 Subject: [PATCH 48/57] stm32/usart: move init code to function that's not generic in T --- embassy-stm32/src/usart/buffered.rs | 68 +++++++++------ embassy-stm32/src/usart/mod.rs | 130 ++++++++++++++++------------ 2 files changed, 113 insertions(+), 85 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 2c19e16db..09d020a7b 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -16,9 +16,7 @@ use super::{ Regs, RtsPin, RxPin, TxPin, }; use crate::gpio::{AFType, AnyPin, SealedPin as _}; -use crate::interrupt::typelevel::Interrupt as _; use crate::interrupt::{self, InterruptExt}; -use crate::rcc; use crate::time::Hertz; /// Interrupt handler. @@ -284,35 +282,11 @@ impl<'d> BufferedUart<'d> { rx_buffer: &'d mut [u8], config: Config, ) -> Result { - rcc::enable_and_reset::(); - let info = T::info(); let state = T::buffered_state(); let kernel_clock = T::frequency(); - let len = tx_buffer.len(); - unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; - let len = rx_buffer.len(); - unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; - info.regs.cr3().write(|w| { - w.set_rtse(rts.is_some()); - w.set_ctse(cts.is_some()); - #[cfg(not(any(usart_v1, usart_v2)))] - w.set_dem(de.is_some()); - }); - configure(info, kernel_clock, &config, true, true)?; - - info.regs.cr1().modify(|w| { - w.set_rxneie(true); - w.set_idleie(true); - }); - - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; - - state.tx_rx_refcount.store(2, Ordering::Relaxed); - - Ok(Self { + let mut this = Self { rx: BufferedUartRx { info, state, @@ -328,7 +302,45 @@ impl<'d> BufferedUart<'d> { cts, de, }, - }) + }; + this.enable_and_configure(tx_buffer, rx_buffer, &config)?; + Ok(this) + } + + fn enable_and_configure( + &mut self, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: &Config, + ) -> Result<(), ConfigError> { + let info = self.rx.info; + let state = self.rx.state; + + info.rcc.enable_and_reset(); + + let len = tx_buffer.len(); + unsafe { state.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; + let len = rx_buffer.len(); + unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) }; + + info.regs.cr3().write(|w| { + w.set_rtse(self.rx.rts.is_some()); + w.set_ctse(self.tx.cts.is_some()); + #[cfg(not(any(usart_v1, usart_v2)))] + w.set_dem(self.tx.de.is_some()); + }); + configure(info, self.rx.kernel_clock, &config, true, true)?; + + info.regs.cr1().modify(|w| { + w.set_rxneie(true); + w.set_idleie(true); + }); + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + state.tx_rx_refcount.store(2, Ordering::Relaxed); + Ok(()) } /// Split the driver into a Tx and Rx part (useful for sending to separate tasks) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 6ef80c54f..df5121f67 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -28,7 +28,7 @@ use crate::pac::usart::Lpuart as Regs; #[cfg(any(usart_v1, usart_v2))] use crate::pac::usart::Usart as Regs; use crate::pac::usart::{regs, vals}; -use crate::rcc::{self, RccInfo, SealedRccPeripheral}; +use crate::rcc::{RccInfo, SealedRccPeripheral}; use crate::time::Hertz; use crate::Peripheral; @@ -429,29 +429,33 @@ impl<'d, M: Mode> UartTx<'d, M> { tx_dma: Option>, config: Config, ) -> Result { - rcc::enable_and_reset::(); - - let info = T::info(); - let state = T::state(); - let kernel_clock = T::frequency(); - let r = info.regs; - r.cr3().modify(|w| { - w.set_ctse(cts.is_some()); - }); - configure(info, kernel_clock, &config, false, true)?; - - state.tx_rx_refcount.store(1, Ordering::Relaxed); - - Ok(Self { - info, - state, - kernel_clock, + let mut this = Self { + info: T::info(), + state: T::state(), + kernel_clock: T::frequency(), tx, cts, de: None, tx_dma, _phantom: PhantomData, - }) + }; + this.enable_and_configure(&config)?; + Ok(this) + } + + fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> { + let info = self.info; + let state = self.state; + + info.rcc.enable_and_reset(); + + info.regs.cr3().modify(|w| { + w.set_ctse(self.cts.is_some()); + }); + configure(info, self.kernel_clock, config, false, true)?; + + state.tx_rx_refcount.store(1, Ordering::Relaxed); + Ok(()) } /// Reconfigure the driver @@ -775,34 +779,38 @@ impl<'d, M: Mode> UartRx<'d, M> { rx_dma: Option>, config: Config, ) -> Result { - rcc::enable_and_reset::(); - - let info = T::info(); - let state = T::state(); - let kernel_clock = T::frequency(); - let r = info.regs; - r.cr3().write(|w| { - w.set_rtse(rts.is_some()); - }); - configure(info, kernel_clock, &config, true, false)?; - - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; - - state.tx_rx_refcount.store(1, Ordering::Relaxed); - - Ok(Self { + let mut this = Self { _phantom: PhantomData, - info, - state, - kernel_clock, + info: T::info(), + state: T::state(), + kernel_clock: T::frequency(), rx, rts, rx_dma, detect_previous_overrun: config.detect_previous_overrun, #[cfg(any(usart_v1, usart_v2))] buffered_sr: stm32_metapac::usart::regs::Sr(0), - }) + }; + this.enable_and_configure(&config)?; + Ok(this) + } + + fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> { + let info = self.info; + let state = self.state; + + info.rcc.enable_and_reset(); + + info.regs.cr3().write(|w| { + w.set_rtse(self.rts.is_some()); + }); + configure(info, self.kernel_clock, &config, true, false)?; + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + state.tx_rx_refcount.store(1, Ordering::Relaxed); + Ok(()) } /// Reconfigure the driver @@ -1228,26 +1236,11 @@ impl<'d, M: Mode> Uart<'d, M> { rx_dma: Option>, config: Config, ) -> Result { - rcc::enable_and_reset::(); - let info = T::info(); let state = T::state(); let kernel_clock = T::frequency(); - info.regs.cr3().write(|w| { - w.set_rtse(rts.is_some()); - w.set_ctse(cts.is_some()); - #[cfg(not(any(usart_v1, usart_v2)))] - w.set_dem(de.is_some()); - }); - configure(info, kernel_clock, &config, true, true)?; - - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; - - state.tx_rx_refcount.store(2, Ordering::Relaxed); - - Ok(Self { + let mut this = Self { tx: UartTx { _phantom: PhantomData, info, @@ -1270,7 +1263,30 @@ impl<'d, M: Mode> Uart<'d, M> { #[cfg(any(usart_v1, usart_v2))] buffered_sr: stm32_metapac::usart::regs::Sr(0), }, - }) + }; + this.enable_and_configure(&config)?; + Ok(this) + } + + fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> { + let info = self.rx.info; + let state = self.rx.state; + + info.rcc.enable_and_reset(); + + info.regs.cr3().write(|w| { + w.set_rtse(self.rx.rts.is_some()); + w.set_ctse(self.tx.cts.is_some()); + #[cfg(not(any(usart_v1, usart_v2)))] + w.set_dem(self.tx.de.is_some()); + }); + configure(info, self.rx.kernel_clock, config, true, true)?; + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + state.tx_rx_refcount.store(2, Ordering::Relaxed); + Ok(()) } /// Perform a blocking write From 367a22cc0eef61f05061aaadc714990723c25940 Mon Sep 17 00:00:00 2001 From: Corey Schuhen Date: Sun, 2 Jun 2024 19:40:25 +1000 Subject: [PATCH 49/57] Remove more BXCAN generics. --- embassy-stm32/src/can/bxcan/filter.rs | 70 ++++++++++--------- embassy-stm32/src/can/bxcan/mod.rs | 85 +++++++++++++++--------- embassy-stm32/src/can/bxcan/registers.rs | 12 ++-- tests/stm32/src/bin/can.rs | 6 +- tests/stm32/src/bin/can_common.rs | 4 +- tests/stm32/src/bin/fdcan.rs | 4 -- 6 files changed, 99 insertions(+), 82 deletions(-) diff --git a/embassy-stm32/src/can/bxcan/filter.rs b/embassy-stm32/src/can/bxcan/filter.rs index 9940c7f50..167c6c572 100644 --- a/embassy-stm32/src/can/bxcan/filter.rs +++ b/embassy-stm32/src/can/bxcan/filter.rs @@ -2,7 +2,7 @@ use core::marker::PhantomData; -use super::{ExtendedId, Fifo, FilterOwner, Id, Instance, MasterInstance, StandardId}; +use super::{ExtendedId, Fifo, Id, StandardId}; const F32_RTR: u32 = 0b010; // set the RTR bit to match remote frames const F32_IDE: u32 = 0b100; // set the IDE bit to match extended identifiers @@ -210,24 +210,24 @@ impl From for BankConfig { } /// Interface to the filter banks of a CAN peripheral. -pub struct MasterFilters<'a, I: FilterOwner> { +pub struct MasterFilters<'a> { /// Number of assigned filter banks. /// /// On chips with splittable filter banks, this value can be dynamic. bank_count: u8, - _can: PhantomData<&'a mut I>, - canregs: crate::pac::can::Can, + _phantom: PhantomData<&'a ()>, + info: &'static crate::can::Info, } // NOTE: This type mutably borrows the CAN instance and has unique access to the registers while it // exists. -impl MasterFilters<'_, I> { - pub(crate) unsafe fn new(canregs: crate::pac::can::Can) -> Self { +impl MasterFilters<'_> { + pub(crate) unsafe fn new(info: &'static crate::can::Info) -> Self { // Enable initialization mode. - canregs.fmr().modify(|reg| reg.set_finit(true)); + info.regs.0.fmr().modify(|reg| reg.set_finit(true)); // Read the filter split value. - let bank_count = canregs.fmr().read().can2sb(); + let bank_count = info.regs.0.fmr().read().can2sb(); // (Reset value of CAN2SB is 0x0E, 14, which, in devices with 14 filter banks, assigns all // of them to the master peripheral, and in devices with 28, assigns them 50/50 to @@ -235,8 +235,8 @@ impl MasterFilters<'_, I> { Self { bank_count, - _can: PhantomData, - canregs, + _phantom: PhantomData, + info, } } @@ -244,7 +244,7 @@ impl MasterFilters<'_, I> { FilterBanks { start_idx: 0, bank_count: self.bank_count, - canregs: self.canregs, + info: self.info, } } @@ -291,49 +291,49 @@ impl MasterFilters<'_, I> { } } -impl MasterFilters<'_, I> { +impl MasterFilters<'_> { /// Sets the index at which the filter banks owned by the slave peripheral start. pub fn set_split(&mut self, split_index: u8) -> &mut Self { - assert!(split_index <= I::NUM_FILTER_BANKS); - self.canregs.fmr().modify(|reg| reg.set_can2sb(split_index)); + assert!(split_index <= self.info.num_filter_banks); + self.info.regs.0.fmr().modify(|reg| reg.set_can2sb(split_index)); self.bank_count = split_index; self } /// Accesses the filters assigned to the slave peripheral. - pub fn slave_filters(&mut self) -> SlaveFilters<'_, I> { + pub fn slave_filters(&mut self) -> SlaveFilters<'_> { // NB: This mutably borrows `self`, so it has full access to the filter bank registers. SlaveFilters { start_idx: self.bank_count, - bank_count: I::NUM_FILTER_BANKS - self.bank_count, - _can: PhantomData, - canregs: self.canregs, + bank_count: self.info.num_filter_banks - self.bank_count, + _phantom: PhantomData, + info: self.info, } } } -impl Drop for MasterFilters<'_, I> { +impl Drop for MasterFilters<'_> { #[inline] fn drop(&mut self) { // Leave initialization mode. - self.canregs.fmr().modify(|regs| regs.set_finit(false)); + self.info.regs.0.fmr().modify(|regs| regs.set_finit(false)); } } /// Interface to the filter banks assigned to a slave peripheral. -pub struct SlaveFilters<'a, I: Instance> { +pub struct SlaveFilters<'a> { start_idx: u8, bank_count: u8, - _can: PhantomData<&'a mut I>, - canregs: crate::pac::can::Can, + _phantom: PhantomData<&'a ()>, + info: &'static crate::can::Info, } -impl SlaveFilters<'_, I> { +impl SlaveFilters<'_> { fn banks_imm(&self) -> FilterBanks { FilterBanks { start_idx: self.start_idx, bank_count: self.bank_count, - canregs: self.canregs, + info: self.info, } } @@ -377,14 +377,14 @@ impl SlaveFilters<'_, I> { struct FilterBanks { start_idx: u8, bank_count: u8, - canregs: crate::pac::can::Can, + info: &'static crate::can::Info, } impl FilterBanks { fn clear(&mut self) { let mask = filter_bitmask(self.start_idx, self.bank_count); - self.canregs.fa1r().modify(|reg| { + self.info.regs.0.fa1r().modify(|reg| { for i in 0..28usize { if (0x01u32 << i) & mask != 0 { reg.set_fact(i, false); @@ -399,7 +399,11 @@ impl FilterBanks { fn disable(&mut self, index: u8) { self.assert_bank_index(index); - self.canregs.fa1r().modify(|reg| reg.set_fact(index as usize, false)) + self.info + .regs + .0 + .fa1r() + .modify(|reg| reg.set_fact(index as usize, false)) } fn enable(&mut self, index: u8, fifo: Fifo, config: BankConfig) { @@ -407,11 +411,11 @@ impl FilterBanks { // Configure mode. let mode = matches!(config, BankConfig::List16(_) | BankConfig::List32(_)); - self.canregs.fm1r().modify(|reg| reg.set_fbm(index as usize, mode)); + self.info.regs.0.fm1r().modify(|reg| reg.set_fbm(index as usize, mode)); // Configure scale. let scale = matches!(config, BankConfig::List32(_) | BankConfig::Mask32(_)); - self.canregs.fs1r().modify(|reg| reg.set_fsc(index as usize, scale)); + self.info.regs.0.fs1r().modify(|reg| reg.set_fsc(index as usize, scale)); // Configure filter register. let (fxr1, fxr2); @@ -433,12 +437,12 @@ impl FilterBanks { fxr2 = a.mask; } }; - let bank = self.canregs.fb(index as usize); + let bank = self.info.regs.0.fb(index as usize); bank.fr1().write(|w| w.0 = fxr1); bank.fr2().write(|w| w.0 = fxr2); // Assign to the right FIFO - self.canregs.ffa1r().modify(|reg| { + self.info.regs.0.ffa1r().modify(|reg| { reg.set_ffa( index as usize, match fifo { @@ -449,7 +453,7 @@ impl FilterBanks { }); // Set active. - self.canregs.fa1r().modify(|reg| reg.set_fact(index as usize, true)) + self.info.regs.0.fa1r().modify(|reg| reg.set_fact(index as usize, true)) } } diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index 9f7df1e71..53b94b9e2 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs @@ -6,7 +6,7 @@ use core::marker::PhantomData; use core::task::Poll; use embassy_hal_internal::interrupt::InterruptExt; -use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_hal_internal::into_ref; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use embassy_sync::waitqueue::AtomicWaker; @@ -91,11 +91,13 @@ impl interrupt::typelevel::Handler for SceInterrup } /// Configuration proxy returned by [`Can::modify_config`]. -pub struct CanConfig<'a, T: Instance> { - can: PhantomData<&'a mut T>, +pub struct CanConfig<'a> { + phantom: PhantomData<&'a ()>, + info: &'static Info, + periph_clock: crate::time::Hertz, } -impl CanConfig<'_, T> { +impl CanConfig<'_> { /// Configures the bit timings. /// /// You can use to calculate the `btr` parameter. Enter @@ -109,7 +111,7 @@ impl CanConfig<'_, T> { /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr` /// parameter to this method. pub fn set_bit_timing(self, bt: crate::can::util::NominalBitTiming) -> Self { - Registers(T::regs()).set_bit_timing(bt); + self.info.regs.set_bit_timing(bt); self } @@ -117,20 +119,20 @@ impl CanConfig<'_, T> { /// /// This is a helper that internally calls `set_bit_timing()`[Self::set_bit_timing]. pub fn set_bitrate(self, bitrate: u32) -> Self { - let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap(); + let bit_timing = util::calc_can_timings(self.periph_clock, bitrate).unwrap(); self.set_bit_timing(bit_timing) } /// Enables or disables loopback mode: Internally connects the TX and RX /// signals together. pub fn set_loopback(self, enabled: bool) -> Self { - Registers(T::regs()).set_loopback(enabled); + self.info.regs.set_loopback(enabled); self } /// Enables or disables silent mode: Disconnects the TX signal from the pin. pub fn set_silent(self, enabled: bool) -> Self { - Registers(T::regs()).set_silent(enabled); + self.info.regs.set_silent(enabled); self } @@ -141,23 +143,24 @@ impl CanConfig<'_, T> { /// /// Automatic retransmission is enabled by default. pub fn set_automatic_retransmit(self, enabled: bool) -> Self { - Registers(T::regs()).set_automatic_retransmit(enabled); + self.info.regs.set_automatic_retransmit(enabled); self } } -impl Drop for CanConfig<'_, T> { +impl Drop for CanConfig<'_> { #[inline] fn drop(&mut self) { - Registers(T::regs()).leave_init_mode(); + self.info.regs.leave_init_mode(); } } /// CAN driver -pub struct Can<'d, T: Instance> { - _peri: PeripheralRef<'d, T>, +pub struct Can<'d> { + phantom: PhantomData<&'d ()>, info: &'static Info, state: &'static State, + periph_clock: crate::time::Hertz, } /// Error returned by `try_write` @@ -168,11 +171,11 @@ pub enum TryWriteError { Full, } -impl<'d, T: Instance> Can<'d, T> { +impl<'d> Can<'d> { /// Creates a new Bxcan instance, keeping the peripheral in sleep mode. /// You must call [Can::enable_non_blocking] to use the peripheral. - pub fn new( - peri: impl Peripheral

+ 'd, + pub fn new( + _peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, _irqs: impl interrupt::typelevel::Binding> @@ -181,7 +184,7 @@ impl<'d, T: Instance> Can<'d, T> { + interrupt::typelevel::Binding> + 'd, ) -> Self { - into_ref!(peri, rx, tx); + into_ref!(_peri, rx, tx); let info = T::info(); let regs = &T::info().regs; @@ -226,15 +229,16 @@ impl<'d, T: Instance> Can<'d, T> { Registers(T::regs()).leave_init_mode(); Self { - _peri: peri, + phantom: PhantomData, info: T::info(), state: T::state(), + periph_clock: T::frequency(), } } /// Set CAN bit rate. pub fn set_bitrate(&mut self, bitrate: u32) { - let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap(); + let bit_timing = util::calc_can_timings(self.periph_clock, bitrate).unwrap(); self.modify_config().set_bit_timing(bit_timing); } @@ -242,10 +246,14 @@ impl<'d, T: Instance> Can<'d, T> { /// /// Calling this method will enter initialization mode. You must enable the peripheral /// again afterwards with [`enable`](Self::enable). - pub fn modify_config(&mut self) -> CanConfig<'_, T> { - Registers(T::regs()).enter_init_mode(); + pub fn modify_config(&mut self) -> CanConfig<'_> { + self.info.regs.enter_init_mode(); - CanConfig { can: PhantomData } + CanConfig { + phantom: self.phantom, + info: self.info, + periph_clock: self.periph_clock, + } } /// Enables the peripheral and synchronizes with the bus. @@ -253,7 +261,7 @@ impl<'d, T: Instance> Can<'d, T> { /// This will wait for 11 consecutive recessive bits (bus idle state). /// Contrary to enable method from bxcan library, this will not freeze the executor while waiting. pub async fn enable(&mut self) { - while Registers(T::regs()).enable_non_blocking().is_err() { + while self.info.regs.enable_non_blocking().is_err() { // SCE interrupt is only generated for entering sleep mode, but not leaving. // Yield to allow other tasks to execute while can bus is initializing. embassy_futures::yield_now().await; @@ -263,7 +271,7 @@ impl<'d, T: Instance> Can<'d, T> { /// Enables or disables the peripheral from automatically wakeup when a SOF is detected on the bus /// while the peripheral is in sleep mode pub fn set_automatic_wakeup(&mut self, enabled: bool) { - Registers(T::regs()).set_automatic_wakeup(enabled); + self.info.regs.set_automatic_wakeup(enabled); } /// Manually wake the peripheral from sleep mode. @@ -313,12 +321,12 @@ impl<'d, T: Instance> Can<'d, T> { /// /// FIFO scheduling is disabled by default. pub fn set_tx_fifo_scheduling(&mut self, enabled: bool) { - Registers(T::regs()).set_tx_fifo_scheduling(enabled) + self.info.regs.set_tx_fifo_scheduling(enabled) } /// Checks if FIFO scheduling of outgoing frames is enabled. pub fn tx_fifo_scheduling_enabled(&self) -> bool { - Registers(T::regs()).tx_fifo_scheduling_enabled() + self.info.regs.tx_fifo_scheduling_enabled() } /// Queues the message to be sent. @@ -448,13 +456,13 @@ impl<'d, T: Instance> Can<'d, T> { } } -impl<'d, T: FilterOwner> Can<'d, T> { +impl<'d> Can<'d> { /// Accesses the filter banks owned by this CAN peripheral. /// /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master /// peripheral instead. - pub fn modify_filters(&mut self) -> MasterFilters<'_, T> { - unsafe { MasterFilters::new(self.info.regs.0) } + pub fn modify_filters(&mut self) -> MasterFilters<'_> { + unsafe { MasterFilters::new(self.info) } } } @@ -819,12 +827,14 @@ impl<'d, const RX_BUF_SIZE: usize> Drop for BufferedCanRx<'d, RX_BUF_SIZE> { } } -impl<'d, T: Instance> Drop for Can<'d, T> { +impl Drop for Can<'_> { fn drop(&mut self) { // Cannot call `free()` because it moves the instance. // Manually reset the peripheral. - T::regs().mcr().write(|w| w.set_reset(true)); - rcc::disable::(); + self.info.regs.0.mcr().write(|w| w.set_reset(true)); + self.info.regs.enter_init_mode(); + self.info.regs.leave_init_mode(); + //rcc::disable::(); } } @@ -1031,6 +1041,11 @@ pub(crate) struct Info { rx1_interrupt: crate::interrupt::Interrupt, sce_interrupt: crate::interrupt::Interrupt, tx_waker: fn(), + + /// The total number of filter banks available to the instance. + /// + /// This is usually either 14 or 28, and should be specified in the chip's reference manual or datasheet. + num_filter_banks: u8, } trait SealedInstance { @@ -1095,6 +1110,7 @@ foreach_peripheral!( rx1_interrupt: crate::_generated::peripheral_interrupts::$inst::RX1::IRQ, sce_interrupt: crate::_generated::peripheral_interrupts::$inst::SCE::IRQ, tx_waker: crate::_generated::peripheral_interrupts::$inst::TX::pend, + num_filter_banks: peripherals::$inst::NUM_FILTER_BANKS, }; &INFO } @@ -1148,6 +1164,11 @@ foreach_peripheral!( } } }; + (can, CAN2) => { + unsafe impl FilterOwner for peripherals::CAN2 { + const NUM_FILTER_BANKS: u8 = 0; + } + }; (can, CAN3) => { unsafe impl FilterOwner for peripherals::CAN3 { const NUM_FILTER_BANKS: u8 = 14; diff --git a/embassy-stm32/src/can/bxcan/registers.rs b/embassy-stm32/src/can/bxcan/registers.rs index e7c7525d8..5f3d70e25 100644 --- a/embassy-stm32/src/can/bxcan/registers.rs +++ b/embassy-stm32/src/can/bxcan/registers.rs @@ -11,7 +11,7 @@ use crate::can::frame::{Envelope, Frame, Header}; pub(crate) struct Registers(pub crate::pac::can::Can); impl Registers { - pub fn enter_init_mode(&mut self) { + pub fn enter_init_mode(&self) { self.0.mcr().modify(|reg| { reg.set_sleep(false); reg.set_inrq(true); @@ -25,7 +25,7 @@ impl Registers { } // Leaves initialization mode, enters sleep mode. - pub fn leave_init_mode(&mut self) { + pub fn leave_init_mode(&self) { self.0.mcr().modify(|reg| { reg.set_sleep(true); reg.set_inrq(false); @@ -38,7 +38,7 @@ impl Registers { } } - pub fn set_bit_timing(&mut self, bt: crate::can::util::NominalBitTiming) { + pub fn set_bit_timing(&self, bt: crate::can::util::NominalBitTiming) { let prescaler = u16::from(bt.prescaler) & 0x1FF; let seg1 = u8::from(bt.seg1); let seg2 = u8::from(bt.seg2) & 0x7F; @@ -84,7 +84,7 @@ impl Registers { /// receive the frame. If enabled, [`Interrupt::Wakeup`] will also be triggered by the incoming /// frame. #[allow(dead_code)] - pub fn set_automatic_wakeup(&mut self, enabled: bool) { + pub fn set_automatic_wakeup(&self, enabled: bool) { self.0.mcr().modify(|reg| reg.set_awum(enabled)); } @@ -96,7 +96,7 @@ impl Registers { /// If this returns [`WouldBlock`][nb::Error::WouldBlock], the peripheral will enable itself /// in the background. The peripheral is enabled and ready to use when this method returns /// successfully. - pub fn enable_non_blocking(&mut self) -> nb::Result<(), Infallible> { + pub fn enable_non_blocking(&self) -> nb::Result<(), Infallible> { let msr = self.0.msr().read(); if msr.slak() { self.0.mcr().modify(|reg| { @@ -186,7 +186,7 @@ impl Registers { /// If this is enabled, mailboxes are scheduled based on the time when the transmit request bit of the mailbox was set. /// /// If this is disabled, mailboxes are scheduled based on the priority of the frame in the mailbox. - pub fn set_tx_fifo_scheduling(&mut self, enabled: bool) { + pub fn set_tx_fifo_scheduling(&self, enabled: bool) { self.0.mcr().modify(|w| w.set_txfp(enabled)) } diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs index f63076512..ba8a33e34 100644 --- a/tests/stm32/src/bin/can.rs +++ b/tests/stm32/src/bin/can.rs @@ -18,10 +18,6 @@ use {defmt_rtt as _, panic_probe as _}; mod can_common; use can_common::*; -type Can<'d> = embassy_stm32::can::Can<'d, embassy_stm32::peripherals::CAN1>; -type CanTx<'d> = embassy_stm32::can::CanTx<'d>; -type CanRx<'d> = embassy_stm32::can::CanRx<'d>; - bind_interrupts!(struct Irqs { CAN1_RX0 => Rx0InterruptHandler; CAN1_RX1 => Rx1InterruptHandler; @@ -50,7 +46,7 @@ async fn main(_spawner: Spawner) { let rx_pin = Input::new(&mut rx, Pull::Up); core::mem::forget(rx_pin); - let mut can = Can::new(can, rx, tx, Irqs); + let mut can = embassy_stm32::can::Can::new(can, rx, tx, Irqs); info!("Configuring can..."); diff --git a/tests/stm32/src/bin/can_common.rs b/tests/stm32/src/bin/can_common.rs index fbfbcdc21..4e1740ad5 100644 --- a/tests/stm32/src/bin/can_common.rs +++ b/tests/stm32/src/bin/can_common.rs @@ -8,7 +8,7 @@ pub struct TestOptions { pub max_buffered: u8, } -pub async fn run_can_tests<'d>(can: &mut crate::Can<'d>, options: &TestOptions) { +pub async fn run_can_tests<'d>(can: &mut can::Can<'d>, options: &TestOptions) { //pub async fn run_can_tests<'d, T: can::Instance>(can: &mut can::Can<'d, T>, options: &TestOptions) { let mut i: u8 = 0; loop { @@ -80,7 +80,7 @@ pub async fn run_can_tests<'d>(can: &mut crate::Can<'d>, options: &TestOptions) } } -pub async fn run_split_can_tests<'d>(tx: &mut crate::CanTx<'d>, rx: &mut crate::CanRx<'d>, options: &TestOptions) { +pub async fn run_split_can_tests<'d>(tx: &mut can::CanTx<'d>, rx: &mut can::CanRx<'d>, options: &TestOptions) { for i in 0..options.max_buffered { // Try filling up the RX FIFO0 buffers //let tx_frame = if 0 != (i & 0x01) { diff --git a/tests/stm32/src/bin/fdcan.rs b/tests/stm32/src/bin/fdcan.rs index 8a397f661..bc2b7edd4 100644 --- a/tests/stm32/src/bin/fdcan.rs +++ b/tests/stm32/src/bin/fdcan.rs @@ -15,10 +15,6 @@ use {defmt_rtt as _, panic_probe as _}; mod can_common; use can_common::*; -type Can<'d> = can::Can<'d>; -type CanTx<'d> = can::CanTx<'d>; -type CanRx<'d> = can::CanRx<'d>; - bind_interrupts!(struct Irqs2 { FDCAN2_IT0 => can::IT0InterruptHandler; FDCAN2_IT1 => can::IT1InterruptHandler; From 58ef2594e511bd5147aae504b8ff723152ab1d1b Mon Sep 17 00:00:00 2001 From: Corey Schuhen Date: Sun, 2 Jun 2024 20:16:57 +1000 Subject: [PATCH 50/57] Fix F7 example. --- examples/stm32f7/src/bin/can.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32f7/src/bin/can.rs b/examples/stm32f7/src/bin/can.rs index f4d6d8c19..a82e335a9 100644 --- a/examples/stm32f7/src/bin/can.rs +++ b/examples/stm32f7/src/bin/can.rs @@ -45,7 +45,7 @@ async fn main(spawner: Spawner) { let rx_pin = Input::new(&mut p.PA15, Pull::Up); core::mem::forget(rx_pin); - static CAN: StaticCell> = StaticCell::new(); + static CAN: StaticCell> = StaticCell::new(); let can = CAN.init(Can::new(p.CAN3, p.PA8, p.PA15, Irqs)); can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); From 900b1048606989bd3c8aba69b1488611d15ed64d Mon Sep 17 00:00:00 2001 From: Corey Schuhen Date: Sun, 2 Jun 2024 21:45:35 +1000 Subject: [PATCH 51/57] Remove generic argument from CanBuilder. --- embassy-stm32/src/can/fd/peripheral.rs | 53 +++++++++++++------------- embassy-stm32/src/can/fdcan.rs | 50 ++++++++++-------------- 2 files changed, 46 insertions(+), 57 deletions(-) diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs index 7321ab230..07e3dddad 100644 --- a/embassy-stm32/src/can/fd/peripheral.rs +++ b/embassy-stm32/src/can/fd/peripheral.rs @@ -73,7 +73,6 @@ impl Registers { pub fn put_tx_frame(&self, bufidx: usize, header: &Header, buffer: &[u8]) { let mailbox = self.tx_buffer_element(bufidx); - mailbox.reset(); put_tx_header(mailbox, header); put_tx_data(mailbox, &buffer[..header.len() as usize]); @@ -245,12 +244,12 @@ impl Registers { } #[inline] - fn reset_msg_ram(&mut self) { + fn reset_msg_ram(&self) { self.msg_ram_mut().reset(); } #[inline] - fn enter_init_mode(&mut self) { + fn enter_init_mode(&self) { self.regs.cccr().modify(|w| w.set_init(true)); while false == self.regs.cccr().read().init() {} self.regs.cccr().modify(|w| w.set_cce(true)); @@ -259,7 +258,7 @@ impl Registers { /// Enables or disables loopback mode: Internally connects the TX and RX /// signals together. #[inline] - fn set_loopback_mode(&mut self, mode: LoopbackMode) { + fn set_loopback_mode(&self, mode: LoopbackMode) { let (test, mon, lbck) = match mode { LoopbackMode::None => (false, false, false), LoopbackMode::Internal => (true, true, true), @@ -274,34 +273,34 @@ impl Registers { /// Enables or disables silent mode: Disconnects the TX signal from the pin. #[inline] - fn set_bus_monitoring_mode(&mut self, enabled: bool) { + fn set_bus_monitoring_mode(&self, enabled: bool) { self.regs.cccr().modify(|w| w.set_mon(enabled)); } #[inline] - fn set_restricted_operations(&mut self, enabled: bool) { + fn set_restricted_operations(&self, enabled: bool) { self.regs.cccr().modify(|w| w.set_asm(enabled)); } #[inline] - fn set_normal_operations(&mut self, _enabled: bool) { + fn set_normal_operations(&self, _enabled: bool) { self.set_loopback_mode(LoopbackMode::None); } #[inline] - fn set_test_mode(&mut self, enabled: bool) { + fn set_test_mode(&self, enabled: bool) { self.regs.cccr().modify(|w| w.set_test(enabled)); } #[inline] - fn set_power_down_mode(&mut self, enabled: bool) { + fn set_power_down_mode(&self, enabled: bool) { self.regs.cccr().modify(|w| w.set_csr(enabled)); while self.regs.cccr().read().csa() != enabled {} } /// Moves out of PoweredDownMode and into ConfigMode #[inline] - pub fn into_config_mode(mut self, _config: FdCanConfig) { + pub fn into_config_mode(self, _config: FdCanConfig) { self.set_power_down_mode(false); self.enter_init_mode(); self.reset_msg_ram(); @@ -328,7 +327,7 @@ impl Registers { /// Applies the settings of a new FdCanConfig See [`FdCanConfig`] #[inline] - pub fn apply_config(&mut self, config: FdCanConfig) { + pub fn apply_config(&self, config: FdCanConfig) { self.set_tx_buffer_mode(config.tx_buffer_mode); // set standard filters list size to 28 @@ -389,7 +388,7 @@ impl Registers { } #[inline] - fn leave_init_mode(&mut self, config: FdCanConfig) { + fn leave_init_mode(&self, config: FdCanConfig) { self.apply_config(config); self.regs.cccr().modify(|w| w.set_cce(false)); @@ -399,7 +398,7 @@ impl Registers { /// Moves out of ConfigMode and into specified mode #[inline] - pub fn into_mode(mut self, config: FdCanConfig, mode: crate::can::_version::OperatingMode) { + pub fn into_mode(&self, config: FdCanConfig, mode: crate::can::_version::OperatingMode) { match mode { crate::can::OperatingMode::InternalLoopbackMode => self.set_loopback_mode(LoopbackMode::Internal), crate::can::OperatingMode::ExternalLoopbackMode => self.set_loopback_mode(LoopbackMode::External), @@ -423,7 +422,7 @@ impl Registers { /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr` /// parameter to this method. #[inline] - pub fn set_nominal_bit_timing(&mut self, btr: NominalBitTiming) { + pub fn set_nominal_bit_timing(&self, btr: NominalBitTiming) { self.regs.nbtp().write(|w| { w.set_nbrp(btr.nbrp() - 1); w.set_ntseg1(btr.ntseg1() - 1); @@ -435,7 +434,7 @@ impl Registers { /// Configures the data bit timings for the FdCan Variable Bitrates. /// This is not used when frame_transmit is set to anything other than AllowFdCanAndBRS. #[inline] - pub fn set_data_bit_timing(&mut self, btr: DataBitTiming) { + pub fn set_data_bit_timing(&self, btr: DataBitTiming) { self.regs.dbtp().write(|w| { w.set_dbrp(btr.dbrp() - 1); w.set_dtseg1(btr.dtseg1() - 1); @@ -451,39 +450,39 @@ impl Registers { /// /// Automatic retransmission is enabled by default. #[inline] - pub fn set_automatic_retransmit(&mut self, enabled: bool) { + pub fn set_automatic_retransmit(&self, enabled: bool) { self.regs.cccr().modify(|w| w.set_dar(!enabled)); } /// Configures the transmit pause feature. See /// [`FdCanConfig::set_transmit_pause`] #[inline] - pub fn set_transmit_pause(&mut self, enabled: bool) { + pub fn set_transmit_pause(&self, enabled: bool) { self.regs.cccr().modify(|w| w.set_txp(!enabled)); } /// Configures non-iso mode. See [`FdCanConfig::set_non_iso_mode`] #[inline] - pub fn set_non_iso_mode(&mut self, enabled: bool) { + pub fn set_non_iso_mode(&self, enabled: bool) { self.regs.cccr().modify(|w| w.set_niso(enabled)); } /// Configures edge filtering. See [`FdCanConfig::set_edge_filtering`] #[inline] - pub fn set_edge_filtering(&mut self, enabled: bool) { + pub fn set_edge_filtering(&self, enabled: bool) { self.regs.cccr().modify(|w| w.set_efbi(enabled)); } /// Configures TX Buffer Mode #[inline] - pub fn set_tx_buffer_mode(&mut self, tbm: TxBufferMode) { + pub fn set_tx_buffer_mode(&self, tbm: TxBufferMode) { self.regs.txbc().write(|w| w.set_tfqm(tbm.into())); } /// Configures frame transmission mode. See /// [`FdCanConfig::set_frame_transmit`] #[inline] - pub fn set_frame_transmit(&mut self, fts: FrameTransmissionConfig) { + pub fn set_frame_transmit(&self, fts: FrameTransmissionConfig) { let (fdoe, brse) = match fts { FrameTransmissionConfig::ClassicCanOnly => (false, false), FrameTransmissionConfig::AllowFdCan => (true, false), @@ -501,14 +500,14 @@ impl Registers { /// Sets the protocol exception handling on/off #[inline] - pub fn set_protocol_exception_handling(&mut self, enabled: bool) { + pub fn set_protocol_exception_handling(&self, enabled: bool) { self.regs.cccr().modify(|w| w.set_pxhd(!enabled)); } /// Configures and resets the timestamp counter #[inline] #[allow(unused)] - pub fn set_timestamp_counter_source(&mut self, select: TimestampSource) { + pub fn set_timestamp_counter_source(&self, select: TimestampSource) { #[cfg(can_fdcan_h7)] let (tcp, tss) = match select { TimestampSource::None => (0, 0), @@ -532,7 +531,7 @@ impl Registers { #[cfg(not(can_fdcan_h7))] /// Configures the global filter settings #[inline] - pub fn set_global_filter(&mut self, filter: GlobalFilter) { + pub fn set_global_filter(&self, filter: GlobalFilter) { let anfs = match filter.handle_standard_frames { crate::can::fd::config::NonMatchingFilter::IntoRxFifo0 => stm32_metapac::can::vals::Anfs::ACCEPT_FIFO_0, crate::can::fd::config::NonMatchingFilter::IntoRxFifo1 => stm32_metapac::can::vals::Anfs::ACCEPT_FIFO_1, @@ -555,7 +554,7 @@ impl Registers { #[cfg(can_fdcan_h7)] /// Configures the global filter settings #[inline] - pub fn set_global_filter(&mut self, filter: GlobalFilter) { + pub fn set_global_filter(&self, filter: GlobalFilter) { let anfs = match filter.handle_standard_frames { crate::can::fd::config::NonMatchingFilter::IntoRxFifo0 => 0, crate::can::fd::config::NonMatchingFilter::IntoRxFifo1 => 1, @@ -577,10 +576,10 @@ impl Registers { } #[cfg(not(can_fdcan_h7))] - fn configure_msg_ram(&mut self) {} + fn configure_msg_ram(&self) {} #[cfg(can_fdcan_h7)] - fn configure_msg_ram(&mut self) { + fn configure_msg_ram(&self) { let r = self.regs; use crate::can::fd::message_ram::*; diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index a838d0412..f3ad718ae 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -141,13 +141,16 @@ pub enum OperatingMode { //TestMode, } -fn calc_ns_per_timer_tick(mode: crate::can::fd::config::FrameTransmissionConfig) -> u64 { +fn calc_ns_per_timer_tick( + info: &'static Info, + freq: crate::time::Hertz, + mode: crate::can::fd::config::FrameTransmissionConfig, +) -> u64 { match mode { // Use timestamp from Rx FIFO to adjust timestamp reported to user crate::can::fd::config::FrameTransmissionConfig::ClassicCanOnly => { - let freq = T::frequency(); - let prescale: u64 = ({ T::registers().regs.nbtp().read().nbrp() } + 1) as u64 - * ({ T::registers().regs.tscc().read().tcp() } + 1) as u64; + let prescale: u64 = ({ info.regs.regs.nbtp().read().nbrp() } + 1) as u64 + * ({ info.regs.regs.tscc().read().tcp() } + 1) as u64; 1_000_000_000 as u64 / (freq.0 as u64 * prescale) } // For VBR this is too hard because the FDCAN timer switches clock rate you need to configure to use @@ -158,28 +161,28 @@ fn calc_ns_per_timer_tick(mode: crate::can::fd::config::FrameTransm /// FDCAN Configuration instance instance /// Create instance of this first -pub struct CanConfigurator<'d, T: Instance> { +pub struct CanConfigurator<'d> { + _phantom: PhantomData<&'d ()>, config: crate::can::fd::config::FdCanConfig, info: &'static Info, state: &'static State, /// Reference to internals. - _instance: FdcanInstance<'d, T>, properties: Properties, periph_clock: crate::time::Hertz, } -impl<'d, T: Instance> CanConfigurator<'d, T> { +impl<'d> CanConfigurator<'d> { /// Creates a new Fdcan instance, keeping the peripheral in sleep mode. /// You must call [Fdcan::enable_non_blocking] to use the peripheral. - pub fn new( - peri: impl Peripheral

+ 'd, + pub fn new( + _peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, tx: impl Peripheral

> + 'd, _irqs: impl interrupt::typelevel::Binding> + interrupt::typelevel::Binding> + 'd, - ) -> CanConfigurator<'d, T> { - into_ref!(peri, rx, tx); + ) -> CanConfigurator<'d> { + into_ref!(_peri, rx, tx); rx.set_as_af(rx.af_num(), AFType::Input); tx.set_as_af(tx.af_num(), AFType::OutputPushPull); @@ -201,10 +204,10 @@ impl<'d, T: Instance> CanConfigurator<'d, T> { T::IT1Interrupt::enable(); } Self { + _phantom: PhantomData, config, info: T::info(), state: T::state(), - _instance: FdcanInstance(peri), properties: Properties::new(T::info()), periph_clock: T::frequency(), } @@ -255,7 +258,7 @@ impl<'d, T: Instance> CanConfigurator<'d, T> { /// Start in mode. pub fn start(self, mode: OperatingMode) -> Can<'d> { - let ns_per_timer_tick = calc_ns_per_timer_tick::(self.config.frame_transmit); + let ns_per_timer_tick = calc_ns_per_timer_tick(self.info, self.periph_clock, self.config.frame_transmit); critical_section::with(|_| { let state = self.state as *const State; unsafe { @@ -263,15 +266,14 @@ impl<'d, T: Instance> CanConfigurator<'d, T> { (*mut_state).ns_per_timer_tick = ns_per_timer_tick; } }); - T::registers().into_mode(self.config, mode); + self.info.regs.into_mode(self.config, mode); Can { _phantom: PhantomData, config: self.config, info: self.info, state: self.state, - instance: T::info().regs.regs, _mode: mode, - properties: Properties::new(T::info()), + properties: Properties::new(self.info), } } @@ -297,7 +299,6 @@ pub struct Can<'d> { config: crate::can::fd::config::FdCanConfig, info: &'static Info, state: &'static State, - instance: crate::pac::can::Fdcan, _mode: OperatingMode, properties: Properties, } @@ -360,14 +361,12 @@ impl<'d> Can<'d> { info: self.info, state: self.state, config: self.config, - _instance: self.instance, _mode: self._mode, }, CanRx { _phantom: PhantomData, info: self.info, state: self.state, - _instance: self.instance, _mode: self._mode, }, self.properties, @@ -380,7 +379,6 @@ impl<'d> Can<'d> { config: tx.config, info: tx.info, state: tx.state, - instance: tx._instance, _mode: rx._mode, properties: Properties::new(tx.info), } @@ -392,7 +390,7 @@ impl<'d> Can<'d> { tx_buf: &'static mut TxBuf, rxb: &'static mut RxBuf, ) -> BufferedCan<'d, TX_BUF_SIZE, RX_BUF_SIZE> { - BufferedCan::new(self.info, self.state, self.info.regs.regs, self._mode, tx_buf, rxb) + BufferedCan::new(self.info, self.state, self._mode, tx_buf, rxb) } /// Return a buffered instance of driver with CAN FD support. User must supply Buffers @@ -401,7 +399,7 @@ impl<'d> Can<'d> { tx_buf: &'static mut TxFdBuf, rxb: &'static mut RxFdBuf, ) -> BufferedCanFd<'d, TX_BUF_SIZE, RX_BUF_SIZE> { - BufferedCanFd::new(self.info, self.state, self.info.regs.regs, self._mode, tx_buf, rxb) + BufferedCanFd::new(self.info, self.state, self._mode, tx_buf, rxb) } } @@ -416,7 +414,6 @@ pub struct BufferedCan<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { _phantom: PhantomData<&'d ()>, info: &'static Info, state: &'static State, - _instance: crate::pac::can::Fdcan, _mode: OperatingMode, tx_buf: &'static TxBuf, rx_buf: &'static RxBuf, @@ -427,7 +424,6 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, fn new( info: &'static Info, state: &'static State, - _instance: crate::pac::can::Fdcan, _mode: OperatingMode, tx_buf: &'static TxBuf, rx_buf: &'static RxBuf, @@ -436,7 +432,6 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, _phantom: PhantomData, info, state, - _instance, _mode, tx_buf, rx_buf, @@ -549,7 +544,6 @@ pub struct BufferedCanFd<'d, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> _phantom: PhantomData<&'d ()>, info: &'static Info, state: &'static State, - _instance: crate::pac::can::Fdcan, _mode: OperatingMode, tx_buf: &'static TxFdBuf, rx_buf: &'static RxFdBuf, @@ -560,7 +554,6 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<' fn new( info: &'static Info, state: &'static State, - _instance: crate::pac::can::Fdcan, _mode: OperatingMode, tx_buf: &'static TxFdBuf, rx_buf: &'static RxFdBuf, @@ -569,7 +562,6 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<' _phantom: PhantomData, info, state, - _instance, _mode, tx_buf, rx_buf, @@ -646,7 +638,6 @@ pub struct CanRx<'d> { _phantom: PhantomData<&'d ()>, info: &'static Info, state: &'static State, - _instance: crate::pac::can::Fdcan, _mode: OperatingMode, } @@ -668,7 +659,6 @@ pub struct CanTx<'d> { info: &'static Info, state: &'static State, config: crate::can::fd::config::FdCanConfig, - _instance: crate::pac::can::Fdcan, _mode: OperatingMode, } From 348c87fc2fb640e5a27bb85dd9a8edd0c8ff3b0e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 3 Jun 2024 00:57:53 +0200 Subject: [PATCH 52/57] stm32/spi: fix blocking_write on nosck spi. Fixes #2902. --- embassy-stm32/src/spi/mod.rs | 66 +++++++++++++++++++++++++++++----- tests/stm32/src/bin/spi.rs | 18 +++------- tests/stm32/src/bin/spi_dma.rs | 23 ++++++------ 3 files changed, 72 insertions(+), 35 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 5d6277c33..720e80e0d 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -351,17 +351,46 @@ impl<'d, M: PeriMode> Spi<'d, M> { /// Blocking write. pub fn blocking_write(&mut self, words: &[W]) -> Result<(), Error> { + // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? + #[cfg(any(spi_v3, spi_v4, spi_v5))] + self.info.regs.cr1().modify(|w| w.set_spe(false)); self.info.regs.cr1().modify(|w| w.set_spe(true)); flush_rx_fifo(self.info.regs); self.set_word_size(W::CONFIG); for word in words.iter() { - let _ = transfer_word(self.info.regs, *word)?; + // this cannot use `transfer_word` because on SPIv2 and higher, + // the SPI RX state machine hangs if no physical pin is connected to the SCK AF. + // This is the case when the SPI has been created with `new_(blocking_?)txonly_nosck`. + // See https://github.com/embassy-rs/embassy/issues/2902 + // This is not documented as an errata by ST, and I've been unable to find anything online... + #[cfg(not(any(spi_v1, spi_f1)))] + write_word(self.info.regs, *word)?; + + // if we're doing tx only, after writing the last byte to FIFO we have to wait + // until it's actually sent. On SPIv1 you're supposed to use the BSY flag for this + // but apparently it's broken, it clears too soon. Workaround is to wait for RXNE: + // when it gets set you know the transfer is done, even if you don't care about rx. + // Luckily this doesn't affect SPIv2+. + // See http://efton.sk/STM32/gotcha/g68.html + // ST doesn't seem to document this in errata sheets (?) + #[cfg(any(spi_v1, spi_f1))] + transfer_word(self.info.regs, *word)?; } + + // wait until last word is transmitted. (except on v1, see above) + #[cfg(not(any(spi_v1, spi_f1, spi_v2)))] + while !self.info.regs.sr().read().txc() {} + #[cfg(spi_v2)] + while self.info.regs.sr().read().bsy() {} + Ok(()) } /// Blocking read. pub fn blocking_read(&mut self, words: &mut [W]) -> Result<(), Error> { + // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? + #[cfg(any(spi_v3, spi_v4, spi_v5))] + self.info.regs.cr1().modify(|w| w.set_spe(false)); self.info.regs.cr1().modify(|w| w.set_spe(true)); flush_rx_fifo(self.info.regs); self.set_word_size(W::CONFIG); @@ -375,6 +404,9 @@ impl<'d, M: PeriMode> Spi<'d, M> { /// /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. pub fn blocking_transfer_in_place(&mut self, words: &mut [W]) -> Result<(), Error> { + // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? + #[cfg(any(spi_v3, spi_v4, spi_v5))] + self.info.regs.cr1().modify(|w| w.set_spe(false)); self.info.regs.cr1().modify(|w| w.set_spe(true)); flush_rx_fifo(self.info.regs); self.set_word_size(W::CONFIG); @@ -391,6 +423,9 @@ impl<'d, M: PeriMode> Spi<'d, M> { /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored. /// If `write` is shorter it is padded with zero bytes. pub fn blocking_transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { + // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? + #[cfg(any(spi_v3, spi_v4, spi_v5))] + self.info.regs.cr1().modify(|w| w.set_spe(false)); self.info.regs.cr1().modify(|w| w.set_spe(true)); flush_rx_fifo(self.info.regs); self.set_word_size(W::CONFIG); @@ -465,7 +500,6 @@ impl<'d> Spi<'d, Blocking> { /// Create a new SPI driver, in TX-only mode, without SCK pin. /// /// This can be useful for bit-banging non-SPI protocols. - #[cfg(any(spi_v1, spi_f1))] // no SCK pin causes it to hang on spiv2+ for unknown reasons. pub fn new_blocking_txonly_nosck( peri: impl Peripheral

+ 'd, mosi: impl Peripheral

> + 'd, @@ -550,7 +584,6 @@ impl<'d> Spi<'d, Async> { /// Create a new SPI driver, in TX-only mode, without SCK pin. /// /// This can be useful for bit-banging non-SPI protocols. - #[cfg(any(spi_v1, spi_f1))] // no SCK pin causes it to hang on spiv2+ for unknown reasons. pub fn new_txonly_nosck( peri: impl Peripheral

+ 'd, mosi: impl Peripheral

> + 'd, @@ -900,8 +933,8 @@ impl RegsExt for Regs { } } -fn check_error_flags(sr: regs::Sr) -> Result<(), Error> { - if sr.ovr() { +fn check_error_flags(sr: regs::Sr, ovr: bool) -> Result<(), Error> { + if sr.ovr() && ovr { return Err(Error::Overrun); } #[cfg(not(any(spi_f1, spi_v3, spi_v4, spi_v5)))] @@ -927,11 +960,11 @@ fn check_error_flags(sr: regs::Sr) -> Result<(), Error> { Ok(()) } -fn spin_until_tx_ready(regs: Regs) -> Result<(), Error> { +fn spin_until_tx_ready(regs: Regs, ovr: bool) -> Result<(), Error> { loop { let sr = regs.sr().read(); - check_error_flags(sr)?; + check_error_flags(sr, ovr)?; #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] if sr.txe() { @@ -948,7 +981,7 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> { loop { let sr = regs.sr().read(); - check_error_flags(sr)?; + check_error_flags(sr, true)?; #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] if sr.rxne() { @@ -1032,7 +1065,7 @@ fn finish_dma(regs: Regs) { } fn transfer_word(regs: Regs, tx_word: W) -> Result { - spin_until_tx_ready(regs)?; + spin_until_tx_ready(regs, true)?; unsafe { ptr::write_volatile(regs.tx_ptr(), tx_word); @@ -1047,6 +1080,21 @@ fn transfer_word(regs: Regs, tx_word: W) -> Result { Ok(rx_word) } +#[allow(unused)] // unused in SPIv1 +fn write_word(regs: Regs, tx_word: W) -> Result<(), Error> { + // for write, we intentionally ignore the rx fifo, which will cause + // overrun errors that we have to ignore. + spin_until_tx_ready(regs, false)?; + + unsafe { + ptr::write_volatile(regs.tx_ptr(), tx_word); + + #[cfg(any(spi_v3, spi_v4, spi_v5))] + regs.cr1().modify(|reg| reg.set_cstart(true)); + } + Ok(()) +} + // Note: It is not possible to impl these traits generically in embedded-hal 0.2 due to a conflict with // some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289 macro_rules! impl_blocking { diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index 8be3b1a7c..0ffd0f653 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -94,19 +94,11 @@ async fn main(_spawner: Spawner) { drop(spi); // Test tx-only nosck. - #[cfg(feature = "spi-v1")] - { - let mut spi = Spi::new_blocking_txonly_nosck(&mut spi_peri, &mut mosi, spi_config); - spi.blocking_transfer(&mut buf, &data).unwrap(); - spi.blocking_transfer_in_place(&mut buf).unwrap(); - spi.blocking_write(&buf).unwrap(); - spi.blocking_read(&mut buf).unwrap(); - spi.blocking_transfer::(&mut [], &[]).unwrap(); - spi.blocking_transfer_in_place::(&mut []).unwrap(); - spi.blocking_read::(&mut []).unwrap(); - spi.blocking_write::(&[]).unwrap(); - drop(spi); - } + let mut spi = Spi::new_blocking_txonly_nosck(&mut spi_peri, &mut mosi, spi_config); + spi.blocking_write(&buf).unwrap(); + spi.blocking_write::(&[]).unwrap(); + spi.blocking_write(&buf).unwrap(); + drop(spi); info!("Test OK"); cortex_m::asm::bkpt(); diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index a8001a111..fd26d3f71 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -132,19 +132,16 @@ async fn main(_spawner: Spawner) { drop(spi); // Test tx-only nosck. - #[cfg(feature = "spi-v1")] - { - let mut spi = Spi::new_txonly_nosck(&mut spi_peri, &mut mosi, &mut tx_dma, spi_config); - spi.blocking_write(&buf).unwrap(); - spi.write(&buf).await.unwrap(); - spi.blocking_write(&buf).unwrap(); - spi.blocking_write(&buf).unwrap(); - spi.write(&buf).await.unwrap(); - spi.write(&buf).await.unwrap(); - spi.write::(&[]).await.unwrap(); - spi.blocking_write::(&[]).unwrap(); - drop(spi); - } + let mut spi = Spi::new_txonly_nosck(&mut spi_peri, &mut mosi, &mut tx_dma, spi_config); + spi.blocking_write(&buf).unwrap(); + spi.write(&buf).await.unwrap(); + spi.blocking_write(&buf).unwrap(); + spi.blocking_write(&buf).unwrap(); + spi.write(&buf).await.unwrap(); + spi.write(&buf).await.unwrap(); + spi.write::(&[]).await.unwrap(); + spi.blocking_write::(&[]).unwrap(); + drop(spi); info!("Test OK"); cortex_m::asm::bkpt(); From d2045be9f322be29fc3ac3240e11dd4a6b34e784 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 3 Jun 2024 08:45:05 +0200 Subject: [PATCH 53/57] fix broken links to embassy book --- docs/pages/faq.adoc | 2 +- docs/pages/getting_started.adoc | 2 +- embassy-boot/README.md | 2 +- examples/stm32f4/src/bin/usb_ethernet.rs | 2 +- examples/stm32f4/src/bin/usb_hid_keyboard.rs | 2 +- examples/stm32f4/src/bin/usb_hid_mouse.rs | 2 +- examples/stm32f4/src/bin/usb_raw.rs | 2 +- examples/stm32f4/src/bin/usb_serial.rs | 2 +- examples/stm32f7/src/bin/usb_serial.rs | 2 +- examples/stm32h7/src/bin/usb_serial.rs | 2 +- examples/stm32l4/src/bin/usb_serial.rs | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/pages/faq.adoc b/docs/pages/faq.adoc index bb088dfb6..a2f56a539 100644 --- a/docs/pages/faq.adoc +++ b/docs/pages/faq.adoc @@ -299,7 +299,7 @@ You can add a section to your `Cargo.toml` file like this, you'll need to patch Using `patch` will replace all direct AND indirect dependencies. -See the link:https://embassy.dev/book/dev/new_project.html#_cargo_toml[new project docs] for more details on this approach. +See the link:https://embassy.dev/book/#_starting_a_new_project[new project docs] for more details on this approach. [source,toml] ---- diff --git a/docs/pages/getting_started.adoc b/docs/pages/getting_started.adoc index 465059922..77275a857 100644 --- a/docs/pages/getting_started.adoc +++ b/docs/pages/getting_started.adoc @@ -131,7 +131,7 @@ If you’re using a raspberry pi pico-w, make sure you’re running `+cargo run If you’re using an rp2040 debug probe (e.g. the pico probe) and are having issues after running `probe-rs info`, unplug and reconnect the probe, letting it power cycle. Running `probe-rs info` is link:https://github.com/probe-rs/probe-rs/issues/1849[known to put the pico probe into an unusable state]. -If you’re still having problems, check the link:https://embassy.dev/book/dev/faq.html[FAQ], or ask for help in the link:https://matrix.to/#/#embassy-rs:matrix.org[Embassy Chat Room]. +If you’re still having problems, check the link:https://embassy.dev/book/#_frequently_asked_questions[FAQ], or ask for help in the link:https://matrix.to/#/#embassy-rs:matrix.org[Embassy Chat Room]. == What's next? diff --git a/embassy-boot/README.md b/embassy-boot/README.md index 812c43524..c893c83e1 100644 --- a/embassy-boot/README.md +++ b/embassy-boot/README.md @@ -24,7 +24,7 @@ For any partition, the following preconditions are required: The linker scripts for the application and bootloader look similar, but the FLASH region must point to the BOOTLOADER partition for the bootloader, and the ACTIVE partition for the application. -For more details on the bootloader, see [the documentation](https://embassy.dev/book/dev/bootloader.html). +For more details on the bootloader, see [the documentation](https://embassy.dev/book/#_bootloader). ## Hardware support diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index 19ae16e8b..b398c35da 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -43,7 +43,7 @@ bind_interrupts!(struct Irqs { // If you are trying this and your USB device doesn't connect, the most // common issues are the RCC config and vbus_detection // -// See https://embassy.dev/book/dev/faq.html#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure +// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure // for more information. #[embassy_executor::main] async fn main(spawner: Spawner) { diff --git a/examples/stm32f4/src/bin/usb_hid_keyboard.rs b/examples/stm32f4/src/bin/usb_hid_keyboard.rs index 537ff63ea..1270995c4 100644 --- a/examples/stm32f4/src/bin/usb_hid_keyboard.rs +++ b/examples/stm32f4/src/bin/usb_hid_keyboard.rs @@ -24,7 +24,7 @@ bind_interrupts!(struct Irqs { // If you are trying this and your USB device doesn't connect, the most // common issues are the RCC config and vbus_detection // -// See https://embassy.dev/book/dev/faq.html#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure +// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure // for more information. #[embassy_executor::main] async fn main(_spawner: Spawner) { diff --git a/examples/stm32f4/src/bin/usb_hid_mouse.rs b/examples/stm32f4/src/bin/usb_hid_mouse.rs index df4b7426c..45136f965 100644 --- a/examples/stm32f4/src/bin/usb_hid_mouse.rs +++ b/examples/stm32f4/src/bin/usb_hid_mouse.rs @@ -21,7 +21,7 @@ bind_interrupts!(struct Irqs { // If you are trying this and your USB device doesn't connect, the most // common issues are the RCC config and vbus_detection // -// See https://embassy.dev/book/dev/faq.html#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure +// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure // for more information. #[embassy_executor::main] async fn main(_spawner: Spawner) { diff --git a/examples/stm32f4/src/bin/usb_raw.rs b/examples/stm32f4/src/bin/usb_raw.rs index 1452e7c5f..b2d706208 100644 --- a/examples/stm32f4/src/bin/usb_raw.rs +++ b/examples/stm32f4/src/bin/usb_raw.rs @@ -72,7 +72,7 @@ bind_interrupts!(struct Irqs { // If you are trying this and your USB device doesn't connect, the most // common issues are the RCC config and vbus_detection // -// See https://embassy.dev/book/dev/faq.html#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure +// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure // for more information. #[embassy_executor::main] async fn main(_spawner: Spawner) { diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs index b2bd390b6..328b5effe 100644 --- a/examples/stm32f4/src/bin/usb_serial.rs +++ b/examples/stm32f4/src/bin/usb_serial.rs @@ -19,7 +19,7 @@ bind_interrupts!(struct Irqs { // If you are trying this and your USB device doesn't connect, the most // common issues are the RCC config and vbus_detection // -// See https://embassy.dev/book/dev/faq.html#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure +// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure // for more information. #[embassy_executor::main] async fn main(_spawner: Spawner) { diff --git a/examples/stm32f7/src/bin/usb_serial.rs b/examples/stm32f7/src/bin/usb_serial.rs index 0e5cc7c5c..1906b28ed 100644 --- a/examples/stm32f7/src/bin/usb_serial.rs +++ b/examples/stm32f7/src/bin/usb_serial.rs @@ -19,7 +19,7 @@ bind_interrupts!(struct Irqs { // If you are trying this and your USB device doesn't connect, the most // common issues are the RCC config and vbus_detection // -// See https://embassy.dev/book/dev/faq.html#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure +// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure // for more information. #[embassy_executor::main] async fn main(_spawner: Spawner) { diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs index 1c50fc1c8..65ae597d4 100644 --- a/examples/stm32h7/src/bin/usb_serial.rs +++ b/examples/stm32h7/src/bin/usb_serial.rs @@ -18,7 +18,7 @@ bind_interrupts!(struct Irqs { // If you are trying this and your USB device doesn't connect, the most // common issues are the RCC config and vbus_detection // -// See https://embassy.dev/book/dev/faq.html#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure +// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure // for more information. #[embassy_executor::main] async fn main(_spawner: Spawner) { diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs index ed9671d0f..c3b1211d8 100644 --- a/examples/stm32l4/src/bin/usb_serial.rs +++ b/examples/stm32l4/src/bin/usb_serial.rs @@ -19,7 +19,7 @@ bind_interrupts!(struct Irqs { // If you are trying this and your USB device doesn't connect, the most // common issues are the RCC config and vbus_detection // -// See https://embassy.dev/book/dev/faq.html#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure +// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure // for more information. #[embassy_executor::main] async fn main(_spawner: Spawner) { From f3703ff6bfed6f8342b49cfd9a65d543d1c99c27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0pa=C4=8Dek?= Date: Mon, 3 Jun 2024 20:12:33 +0200 Subject: [PATCH 54/57] stm32/usart: set refcount even if initialization failed --- embassy-stm32/src/usart/buffered.rs | 2 +- embassy-stm32/src/usart/mod.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 09d020a7b..fd79e035e 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -315,6 +315,7 @@ impl<'d> BufferedUart<'d> { ) -> Result<(), ConfigError> { let info = self.rx.info; let state = self.rx.state; + state.tx_rx_refcount.store(2, Ordering::Relaxed); info.rcc.enable_and_reset(); @@ -339,7 +340,6 @@ impl<'d> BufferedUart<'d> { info.interrupt.unpend(); unsafe { info.interrupt.enable() }; - state.tx_rx_refcount.store(2, Ordering::Relaxed); Ok(()) } diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index df5121f67..53321391a 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -446,6 +446,7 @@ impl<'d, M: Mode> UartTx<'d, M> { fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> { let info = self.info; let state = self.state; + state.tx_rx_refcount.store(1, Ordering::Relaxed); info.rcc.enable_and_reset(); @@ -454,7 +455,6 @@ impl<'d, M: Mode> UartTx<'d, M> { }); configure(info, self.kernel_clock, config, false, true)?; - state.tx_rx_refcount.store(1, Ordering::Relaxed); Ok(()) } @@ -798,6 +798,7 @@ impl<'d, M: Mode> UartRx<'d, M> { fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> { let info = self.info; let state = self.state; + state.tx_rx_refcount.store(1, Ordering::Relaxed); info.rcc.enable_and_reset(); @@ -809,7 +810,6 @@ impl<'d, M: Mode> UartRx<'d, M> { info.interrupt.unpend(); unsafe { info.interrupt.enable() }; - state.tx_rx_refcount.store(1, Ordering::Relaxed); Ok(()) } @@ -1271,6 +1271,7 @@ impl<'d, M: Mode> Uart<'d, M> { fn enable_and_configure(&mut self, config: &Config) -> Result<(), ConfigError> { let info = self.rx.info; let state = self.rx.state; + state.tx_rx_refcount.store(2, Ordering::Relaxed); info.rcc.enable_and_reset(); @@ -1285,7 +1286,6 @@ impl<'d, M: Mode> Uart<'d, M> { info.interrupt.unpend(); unsafe { info.interrupt.enable() }; - state.tx_rx_refcount.store(2, Ordering::Relaxed); Ok(()) } From 03d8f99aa57d71c27745972f2bcd9ba3450bd8c3 Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 4 Jun 2024 00:38:51 +0200 Subject: [PATCH 55/57] rp: Add zerocopy channel example --- examples/rp/src/bin/zerocopy.rs | 90 +++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 examples/rp/src/bin/zerocopy.rs diff --git a/examples/rp/src/bin/zerocopy.rs b/examples/rp/src/bin/zerocopy.rs new file mode 100644 index 000000000..b5bd11512 --- /dev/null +++ b/examples/rp/src/bin/zerocopy.rs @@ -0,0 +1,90 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::adc::{self, Adc, Async, Config, InterruptHandler}; +use embassy_rp::bind_interrupts; +use embassy_rp::gpio::Pull; +use embassy_rp::peripherals::DMA_CH0; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::zerocopy_channel::{Channel, Receiver, Sender}; +use embassy_time::{Duration, Ticker, Timer}; +use portable_atomic::{AtomicU16, Ordering}; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +type SampleBuffer = [u16; 512]; + +bind_interrupts!(struct Irqs { + ADC_IRQ_FIFO => InterruptHandler; +}); + +const BLOCK_SIZE: usize = 512; +const NUM_BLOCKS: usize = 2; +static MAX: AtomicU16 = AtomicU16::new(0); + +struct AdcParts { + adc: Adc<'static, Async>, + pin: adc::Channel<'static>, + dma: DMA_CH0, +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Here we go!"); + + let adc_parts = AdcParts { + adc: Adc::new(p.ADC, Irqs, Config::default()), + pin: adc::Channel::new_pin(p.PIN_29, Pull::None), + dma: p.DMA_CH0, + }; + + static BUF: StaticCell<[SampleBuffer; NUM_BLOCKS]> = StaticCell::new(); + let buf = BUF.init([[0; BLOCK_SIZE]; NUM_BLOCKS]); + + static CHANNEL: StaticCell> = StaticCell::new(); + let channel = CHANNEL.init(Channel::new(buf)); + let (sender, receiver) = channel.split(); + + spawner.must_spawn(consumer(receiver)); + spawner.must_spawn(producer(sender, adc_parts)); + + let mut ticker = Ticker::every(Duration::from_secs(1)); + loop { + ticker.next().await; + let max = MAX.load(Ordering::Relaxed); + info!("latest block's max value: {:?}", max); + } +} + +#[embassy_executor::task] +async fn producer(mut sender: Sender<'static, NoopRawMutex, SampleBuffer>, mut adc: AdcParts) { + loop { + // Obtain a free buffer from the channel + let buf = sender.send().await; + + // Fill it with data + adc.adc.read_many(&mut adc.pin, buf, 1, &mut adc.dma).await.unwrap(); + + // Notify the channel that the buffer is now ready to be received + sender.send_done(); + } +} + +#[embassy_executor::task] +async fn consumer(mut receiver: Receiver<'static, NoopRawMutex, SampleBuffer>) { + loop { + // Receive a buffer from the channel + let buf = receiver.receive().await; + + // Simulate using the data, while the producer is filling up the next buffer + Timer::after_micros(1000).await; + let max = buf.iter().max().unwrap(); + MAX.store(*max, Ordering::Relaxed); + + // Notify the channel that the buffer is now ready to be reused + receiver.receive_done(); + } +} From 874d5f7c65f2af21e4666bf92800d304c2d9e01d Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 4 Jun 2024 00:53:51 +0200 Subject: [PATCH 56/57] core atomic --- examples/rp/src/bin/zerocopy.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/rp/src/bin/zerocopy.rs b/examples/rp/src/bin/zerocopy.rs index b5bd11512..730a3ae0c 100644 --- a/examples/rp/src/bin/zerocopy.rs +++ b/examples/rp/src/bin/zerocopy.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] +use core::sync::atomic::{AtomicU16, Ordering}; + use defmt::*; use embassy_executor::Spawner; use embassy_rp::adc::{self, Adc, Async, Config, InterruptHandler}; @@ -10,7 +12,6 @@ use embassy_rp::peripherals::DMA_CH0; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::zerocopy_channel::{Channel, Receiver, Sender}; use embassy_time::{Duration, Ticker, Timer}; -use portable_atomic::{AtomicU16, Ordering}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; From f9d8c68fc8ed490e72e61b74d2b3b26bbbd0434c Mon Sep 17 00:00:00 2001 From: kalkyl Date: Tue, 4 Jun 2024 01:20:19 +0200 Subject: [PATCH 57/57] Add description --- examples/rp/src/bin/zerocopy.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/rp/src/bin/zerocopy.rs b/examples/rp/src/bin/zerocopy.rs index 730a3ae0c..39f03c8e4 100644 --- a/examples/rp/src/bin/zerocopy.rs +++ b/examples/rp/src/bin/zerocopy.rs @@ -1,3 +1,6 @@ +//! This example shows how to use `zerocopy_channel` from `embassy_sync` for +//! sending large values between two tasks without copying. +//! The example also shows how to use the RP2040 ADC with DMA. #![no_std] #![no_main]