diff --git a/.vscode/settings.json b/.vscode/settings.json index 6edd9312a..c504f3ccd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,7 +17,7 @@ //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", "rust-analyzer.cargo.features": [ // Comment out these features when working on the examples. Most example crates do not have any cargo features. - "stm32f446re", + "stm32f107rb", "time-driver-any", "unstable-pac", "exti", diff --git a/ci.sh b/ci.sh index e1197f397..50fb3e13d 100755 --- a/ci.sh +++ b/ci.sh @@ -311,7 +311,9 @@ cargo batch \ --- build --release --manifest-path examples/boot/bootloader/stm32wba-dfu/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-stm32/stm32wba65ri,verify \ --- build --release --manifest-path examples/boot/bootloader/stm32-dual-bank/Cargo.toml --target thumbv7em-none-eabi --features embassy-stm32/stm32h743zi \ --- build --release --manifest-path examples/wasm/Cargo.toml --target wasm32-unknown-unknown --artifact-dir out/examples/wasm \ + --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f100rd --artifact-dir out/tests/stm32f100rd \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f103c8 --artifact-dir out/tests/stm32f103c8 \ + --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f107vc --artifact-dir out/tests/stm32f107vc \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi --artifact-dir out/tests/stm32f429zi \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f446re --artifact-dir out/tests/stm32f446re \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re --artifact-dir out/tests/stm32g491re \ @@ -398,8 +400,10 @@ rm out/tests/pimoroni-pico-plus-2/pwm rm out/tests/rpi-pico/pwm rm out/tests/rpi-pico/cyw43-perf -# tests are implemented but the HIL test farm doesn't actually have this board yet +# tests are implemented but the HIL test farm doesn't actually have these boards, yet rm -rf out/tests/stm32c071rb +rm -rf out/tests/stm32f100rd +rm -rf out/tests/stm32f107vc if [[ -z "${TELEPROBE_TOKEN-}" ]]; then echo No teleprobe token found, skipping running HIL tests diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 5635481fa..ba565f663 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fix: Fix stm32h7rs init when using external flash via XSPI - feat: Add Adc::new_with_clock() to configure analog clock - feat: Add GPDMA linked-list + ringbuffer support ([#3923](https://github.com/embassy-rs/embassy/pull/3923)) +- feat: Added support for STM32F1 peripheral pin remapping (AFIO) ([#4430](https://github.com/embassy-rs/embassy/pull/4430)) ## 0.3.0 - 2025-08-12 diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index b731012c6..b5f1261fe 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1391,9 +1391,53 @@ fn main() { }) } - g.extend(quote! { - pin_trait_impl!(#tr, #peri, #pin_name, #af); - }) + let pin_trait_impl = if let Some(afio) = &p.afio { + let values = afio + .values + .iter() + .filter(|v| v.pins.contains(&pin.pin)) + .map(|v| v.value) + .collect::>(); + + if values.is_empty() { + None + } else { + let reg = format_ident!("{}", afio.register.to_lowercase()); + let setter = format_ident!("set_{}", afio.field.to_lowercase()); + let type_and_values = if is_bool_field("AFIO", afio.register, afio.field) { + let values = values.iter().map(|&v| v > 0); + quote!(AfioRemapBool, [#(#values),*]) + } else { + quote!(AfioRemap, [#(#values),*]) + }; + + Some(quote! { + pin_trait_afio_impl!(#tr, #peri, #pin_name, {#reg, #setter, #type_and_values}); + }) + } + } else { + let peripherals_with_afio = [ + "CAN", + "CEC", + "ETH", + "I2C", + "SPI", + "SUBGHZSPI", + "USART", + "UART", + "LPUART", + "TIM", + ]; + let not_applicable = if peripherals_with_afio.iter().any(|&x| p.name.starts_with(x)) { + quote!(, crate::gpio::AfioRemapNotApplicable) + } else { + quote!() + }; + + Some(quote!(pin_trait_impl!(#tr, #peri, #pin_name, #af #not_applicable);)) + }; + + g.extend(pin_trait_impl); } // ADC is special @@ -1588,17 +1632,7 @@ fn main() { let register = format_ident!("{}", remap_info.register.to_lowercase()); let setter = format_ident!("set_{}", remap_info.field.to_lowercase()); - let field_metadata = METADATA - .peripherals - .iter() - .filter(|p| p.name == "SYSCFG") - .flat_map(|p| p.registers.as_ref().unwrap().ir.fieldsets.iter()) - .filter(|f| f.name.eq_ignore_ascii_case(remap_info.register)) - .flat_map(|f| f.fields.iter()) - .find(|f| f.name.eq_ignore_ascii_case(remap_info.field)) - .unwrap(); - - let value = if field_metadata.bit_size == 1 { + let value = if is_bool_field("SYSCFG", &remap_info.register, &remap_info.field) { let bool_value = format_ident!("{}", remap_info.value > 0); quote!(#bool_value) } else { @@ -2300,3 +2334,17 @@ fn gcd(a: u32, b: u32) -> u32 { } gcd(b, a % b) } + +fn is_bool_field(peripheral: &str, register: &str, field: &str) -> bool { + let field_metadata = METADATA + .peripherals + .iter() + .filter(|p| p.name == peripheral) + .flat_map(|p| p.registers.as_ref().unwrap().ir.fieldsets.iter()) + .filter(|f| f.name.eq_ignore_ascii_case(register)) + .flat_map(|f| f.fields.iter()) + .find(|f| f.name.eq_ignore_ascii_case(field)) + .unwrap(); + + field_metadata.bit_size == 1 +} diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index 4c0795a2a..8eb188560 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs @@ -181,10 +181,10 @@ pub enum TryWriteError { 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( + pub fn new( _peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - tx: Peri<'d, impl TxPin>, + rx: Peri<'d, if_afio!(impl RxPin)>, + tx: Peri<'d, if_afio!(impl TxPin)>, _irqs: impl interrupt::typelevel::Binding> + interrupt::typelevel::Binding> + interrupt::typelevel::Binding> @@ -194,8 +194,8 @@ impl<'d> Can<'d> { let info = T::info(); let regs = &T::info().regs; - rx.set_as_af(rx.af_num(), AfType::input(Pull::None)); - tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); + set_as_af!(rx, AfType::input(Pull::None)); + set_as_af!(tx, AfType::output(OutputType::PushPull, Speed::VeryHigh)); rcc::enable_and_reset::(); @@ -229,8 +229,8 @@ impl<'d> Can<'d> { info.sce_interrupt.enable(); } - rx.set_as_af(rx.af_num(), AfType::input(Pull::None)); - tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); + set_as_af!(rx, AfType::input(Pull::None)); + set_as_af!(tx, AfType::output(OutputType::PushPull, Speed::VeryHigh)); Registers(T::regs()).leave_init_mode(); @@ -1218,8 +1218,8 @@ foreach_peripheral!( }; ); -pin_trait!(RxPin, Instance); -pin_trait!(TxPin, Instance); +pin_trait!(RxPin, Instance, @A); +pin_trait!(TxPin, Instance, @A); trait Index { fn index(&self) -> usize; diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index 99e40ba62..d8f71e03e 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -185,8 +185,8 @@ impl<'d> CanConfigurator<'d> { + interrupt::typelevel::Binding> + 'd, ) -> CanConfigurator<'d> { - rx.set_as_af(rx.af_num(), AfType::input(Pull::None)); - tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); + set_as_af!(rx, AfType::input(Pull::None)); + set_as_af!(tx, AfType::output(OutputType::PushPull, Speed::VeryHigh)); rcc::enable_and_reset::(); diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index d05faee21..bd03f1e00 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -108,7 +108,7 @@ macro_rules! config_pins { ($($pin:ident),*) => { critical_section::with(|_| { $( - $pin.set_as_af($pin.af_num(), AfType::input(Pull::None)); + set_as_af!($pin, AfType::input(Pull::None)); )* }) }; diff --git a/embassy-stm32/src/dsihost.rs b/embassy-stm32/src/dsihost.rs index e97ccd9d0..deda956af 100644 --- a/embassy-stm32/src/dsihost.rs +++ b/embassy-stm32/src/dsihost.rs @@ -78,7 +78,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { rcc::enable_and_reset::(); // Set Tearing Enable pin according to CubeMx example - te.set_as_af(te.af_num(), AfType::output(OutputType::PushPull, Speed::Low)); + set_as_af!(te, AfType::output(OutputType::PushPull, Speed::Low)); /* T::regs().wcr().modify(|w| { w.set_dsien(true); diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 97d7b4347..10b3a0517 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -209,19 +209,19 @@ impl SealedInstance for crate::peripherals::ETH { } impl Instance for crate::peripherals::ETH {} -pin_trait!(RXClkPin, Instance); -pin_trait!(TXClkPin, Instance); -pin_trait!(RefClkPin, Instance); -pin_trait!(MDIOPin, Instance); -pin_trait!(MDCPin, Instance); -pin_trait!(RXDVPin, Instance); -pin_trait!(CRSPin, Instance); -pin_trait!(RXD0Pin, Instance); -pin_trait!(RXD1Pin, Instance); -pin_trait!(RXD2Pin, Instance); -pin_trait!(RXD3Pin, Instance); -pin_trait!(TXD0Pin, Instance); -pin_trait!(TXD1Pin, Instance); -pin_trait!(TXD2Pin, Instance); -pin_trait!(TXD3Pin, Instance); -pin_trait!(TXEnPin, Instance); +pin_trait!(RXClkPin, Instance, @A); +pin_trait!(TXClkPin, Instance, @A); +pin_trait!(RefClkPin, Instance, @A); +pin_trait!(MDIOPin, Instance, @A); +pin_trait!(MDCPin, Instance, @A); +pin_trait!(RXDVPin, Instance, @A); +pin_trait!(CRSPin, Instance, @A); +pin_trait!(RXD0Pin, Instance, @A); +pin_trait!(RXD1Pin, Instance, @A); +pin_trait!(RXD2Pin, Instance, @A); +pin_trait!(RXD3Pin, Instance, @A); +pin_trait!(TXD0Pin, Instance, @A); +pin_trait!(TXD1Pin, Instance, @A); +pin_trait!(TXD2Pin, Instance, @A); +pin_trait!(TXD3Pin, Instance, @A); +pin_trait!(TXEnPin, Instance, @A); diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index b9746231f..5be1c9739 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -69,7 +69,7 @@ macro_rules! config_in_pins { critical_section::with(|_| { $( // TODO properly create a set_as_input function - $pin.set_as_af($pin.af_num(), AfType::input(Pull::None)); + set_as_af!($pin, AfType::input(Pull::None)); )* }) } @@ -80,7 +80,7 @@ macro_rules! config_af_pins { ($($pin:ident),*) => { critical_section::with(|_| { $( - $pin.set_as_af($pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); + set_as_af!($pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); )* }) }; @@ -91,7 +91,7 @@ macro_rules! config_pins { ($($pin:ident),*) => { critical_section::with(|_| { $( - $pin.set_as_af($pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); + set_as_af!($pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); )* }) }; @@ -99,19 +99,19 @@ macro_rules! config_pins { impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { /// safety: the returned instance is not leak-safe - pub fn new( + pub fn new( queue: &'d mut PacketQueue, peri: Peri<'d, T>, irq: impl interrupt::typelevel::Binding + 'd, - ref_clk: Peri<'d, impl RefClkPin>, - mdio: Peri<'d, impl MDIOPin>, - mdc: Peri<'d, impl MDCPin>, - crs: Peri<'d, impl CRSPin>, - rx_d0: Peri<'d, impl RXD0Pin>, - rx_d1: Peri<'d, impl RXD1Pin>, - tx_d0: Peri<'d, impl TXD0Pin>, - tx_d1: Peri<'d, impl TXD1Pin>, - tx_en: Peri<'d, impl TXEnPin>, + ref_clk: Peri<'d, if_afio!(impl RefClkPin)>, + mdio: Peri<'d, if_afio!(impl MDIOPin)>, + mdc: Peri<'d, if_afio!(impl MDCPin)>, + crs: Peri<'d, if_afio!(impl CRSPin)>, + rx_d0: Peri<'d, if_afio!(impl RXD0Pin)>, + rx_d1: Peri<'d, if_afio!(impl RXD1Pin)>, + tx_d0: Peri<'d, if_afio!(impl TXD0Pin)>, + tx_d1: Peri<'d, if_afio!(impl TXD1Pin)>, + tx_en: Peri<'d, if_afio!(impl TXEnPin)>, phy: P, mac_addr: [u8; 6], ) -> Self { @@ -289,24 +289,24 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { } /// Create a new MII ethernet driver using 14 pins. - pub fn new_mii( + pub fn new_mii( queue: &'d mut PacketQueue, peri: Peri<'d, T>, irq: impl interrupt::typelevel::Binding + 'd, - rx_clk: Peri<'d, impl RXClkPin>, - tx_clk: Peri<'d, impl TXClkPin>, - mdio: Peri<'d, impl MDIOPin>, - mdc: Peri<'d, impl MDCPin>, - rxdv: Peri<'d, impl RXDVPin>, - rx_d0: Peri<'d, impl RXD0Pin>, - rx_d1: Peri<'d, impl RXD1Pin>, - rx_d2: Peri<'d, impl RXD2Pin>, - rx_d3: Peri<'d, impl RXD3Pin>, - tx_d0: Peri<'d, impl TXD0Pin>, - tx_d1: Peri<'d, impl TXD1Pin>, - tx_d2: Peri<'d, impl TXD2Pin>, - tx_d3: Peri<'d, impl TXD3Pin>, - tx_en: Peri<'d, impl TXEnPin>, + rx_clk: Peri<'d, if_afio!(impl RXClkPin)>, + tx_clk: Peri<'d, if_afio!(impl TXClkPin)>, + mdio: Peri<'d, if_afio!(impl MDIOPin)>, + mdc: Peri<'d, if_afio!(impl MDCPin)>, + rxdv: Peri<'d, if_afio!(impl RXDVPin)>, + rx_d0: Peri<'d, if_afio!(impl RXD0Pin)>, + rx_d1: Peri<'d, if_afio!(impl RXD1Pin)>, + rx_d2: Peri<'d, if_afio!(impl RXD2Pin)>, + rx_d3: Peri<'d, if_afio!(impl RXD3Pin)>, + tx_d0: Peri<'d, if_afio!(impl TXD0Pin)>, + tx_d1: Peri<'d, if_afio!(impl TXD1Pin)>, + tx_d2: Peri<'d, if_afio!(impl TXD2Pin)>, + tx_d3: Peri<'d, if_afio!(impl TXD3Pin)>, + tx_en: Peri<'d, if_afio!(impl TXEnPin)>, phy: P, mac_addr: [u8; 6], ) -> Self { diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 034c5dd88..cf7a9901b 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -57,7 +57,7 @@ macro_rules! config_pins { critical_section::with(|_| { $( // TODO: shouldn't some pins be configured as inputs? - $pin.set_as_af($pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); + set_as_af!($pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); )* }) }; diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs index 71ca775cb..ff18a8bee 100644 --- a/embassy-stm32/src/fmc.rs +++ b/embassy-stm32/src/fmc.rs @@ -75,7 +75,7 @@ where macro_rules! config_pins { ($($pin:ident),*) => { $( - $pin.set_as_af($pin.af_num(), AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)); + set_as_af!($pin, AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)); )* }; } diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index bb37c4194..5a8d23183 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -150,9 +150,13 @@ impl<'d> Flex<'d> { /// This puts the pin into the AF mode, with the requested number and AF type. This is /// completely unchecked, it can attach the pin to literally any peripheral, so use with care. #[inline] - pub fn set_as_af_unchecked(&mut self, af_num: u8, af_type: AfType) { + pub fn set_as_af_unchecked(&mut self, #[cfg(not(afio))] af_num: u8, af_type: AfType) { critical_section::with(|_| { - self.pin.set_as_af(af_num, af_type); + self.pin.set_as_af( + #[cfg(not(afio))] + af_num, + af_type, + ); }); } @@ -588,7 +592,7 @@ impl AfType { #[inline(never)] #[cfg(gpio_v1)] -fn set_as_af(pin_port: u8, _af_num: u8, af_type: AfType) { +fn set_as_af(pin_port: u8, af_type: AfType) { let pin = unsafe { AnyPin::steal(pin_port) }; let r = pin.block(); let n = pin._pin() as usize; @@ -710,6 +714,18 @@ fn get_pull(pin_port: u8) -> Pull { }; } +#[cfg(afio)] +/// Holds the AFIO remap value for a peripheral's pin +pub struct AfioRemap; + +#[cfg(afio)] +/// Holds the AFIO remap value for a peripheral's pin +pub struct AfioRemapBool; + +#[cfg(afio)] +/// Placeholder for a peripheral's pin which cannot be remapped via AFIO. +pub struct AfioRemapNotApplicable; + pub(crate) trait SealedPin { fn pin_port(&self) -> u8; @@ -743,8 +759,13 @@ pub(crate) trait SealedPin { } #[inline] - fn set_as_af(&self, af_num: u8, af_type: AfType) { - set_as_af(self.pin_port(), af_num, af_type) + fn set_as_af(&self, #[cfg(not(afio))] af_num: u8, af_type: AfType) { + set_as_af( + self.pin_port(), + #[cfg(not(afio))] + af_num, + af_type, + ) } #[inline] diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index 1d0594125..6fece5eb2 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs @@ -79,10 +79,7 @@ macro_rules! advanced_channel_impl { pub fn $new_chx(pin: Peri<'d, impl $pin_trait>) -> Self { critical_section::with(|_| { pin.set_low(); - pin.set_as_af( - pin.af_num(), - AfType::output(OutputType::PushPull, Speed::VeryHigh), - ); + set_as_af!(pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); }); PwmPin { _pin: pin.into(), @@ -96,10 +93,7 @@ macro_rules! advanced_channel_impl { pub fn $new_chx(pin: Peri<'d, impl $complementary_pin_trait>) -> Self { critical_section::with(|_| { pin.set_low(); - pin.set_as_af( - pin.af_num(), - AfType::output(OutputType::PushPull, Speed::VeryHigh), - ); + set_as_af!(pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); }); ComplementaryPwmPin { _pin: pin.into(), diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 5fb49f943..249bac41c 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -149,10 +149,10 @@ pub struct I2c<'d, M: Mode, IM: MasterMode> { impl<'d> I2c<'d, Async, Master> { /// Create a new I2C driver. - pub fn new( + pub fn new( peri: Peri<'d, T>, - scl: Peri<'d, impl SclPin>, - sda: Peri<'d, impl SdaPin>, + scl: Peri<'d, if_afio!(impl SclPin)>, + sda: Peri<'d, if_afio!(impl SdaPin)>, _irq: impl interrupt::typelevel::Binding> + interrupt::typelevel::Binding> + 'd, @@ -173,10 +173,10 @@ impl<'d> I2c<'d, Async, Master> { impl<'d> I2c<'d, Blocking, Master> { /// Create a new blocking I2C driver. - pub fn new_blocking( + pub fn new_blocking( peri: Peri<'d, T>, - scl: Peri<'d, impl SclPin>, - sda: Peri<'d, impl SdaPin>, + scl: Peri<'d, if_afio!(impl SclPin)>, + sda: Peri<'d, if_afio!(impl SdaPin)>, config: Config, ) -> Self { Self::new_inner( @@ -296,8 +296,8 @@ peri_trait!( irqs: [EventInterrupt, ErrorInterrupt], ); -pin_trait!(SclPin, Instance); -pin_trait!(SdaPin, Instance); +pin_trait!(SclPin, Instance, @A); +pin_trait!(SdaPin, Instance, @A); dma_trait!(RxDma, Instance); dma_trait!(TxDma, Instance); diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index 0c4ab56e3..b6d3daf54 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs @@ -237,12 +237,12 @@ pub struct I2S<'d, W: Word> { impl<'d, W: Word> I2S<'d, W> { /// Create a transmitter driver. - pub fn new_txonly( + pub fn new_txonly( peri: Peri<'d, T>, - sd: Peri<'d, impl MosiPin>, - ws: Peri<'d, impl WsPin>, - ck: Peri<'d, impl CkPin>, - mck: Peri<'d, impl MckPin>, + sd: Peri<'d, if_afio!(impl MosiPin)>, + ws: Peri<'d, if_afio!(impl WsPin)>, + ck: Peri<'d, if_afio!(impl CkPin)>, + mck: Peri<'d, if_afio!(impl MckPin)>, txdma: Peri<'d, impl TxDma>, txdma_buf: &'d mut [W], config: Config, @@ -262,11 +262,11 @@ impl<'d, W: Word> I2S<'d, W> { } /// Create a transmitter driver without a master clock pin. - pub fn new_txonly_nomck( + pub fn new_txonly_nomck( peri: Peri<'d, T>, - sd: Peri<'d, impl MosiPin>, - ws: Peri<'d, impl WsPin>, - ck: Peri<'d, impl CkPin>, + sd: Peri<'d, if_afio!(impl MosiPin)>, + ws: Peri<'d, if_afio!(impl WsPin)>, + ck: Peri<'d, if_afio!(impl CkPin)>, txdma: Peri<'d, impl TxDma>, txdma_buf: &'d mut [W], config: Config, @@ -286,12 +286,12 @@ impl<'d, W: Word> I2S<'d, W> { } /// Create a receiver driver. - pub fn new_rxonly( + pub fn new_rxonly( peri: Peri<'d, T>, - sd: Peri<'d, impl MisoPin>, - ws: Peri<'d, impl WsPin>, - ck: Peri<'d, impl CkPin>, - mck: Peri<'d, impl MckPin>, + sd: Peri<'d, if_afio!(impl MisoPin)>, + ws: Peri<'d, if_afio!(impl WsPin)>, + ck: Peri<'d, if_afio!(impl CkPin)>, + mck: Peri<'d, if_afio!(impl MckPin)>, rxdma: Peri<'d, impl RxDma>, rxdma_buf: &'d mut [W], config: Config, @@ -313,13 +313,13 @@ impl<'d, W: Word> I2S<'d, W> { #[cfg(any(spi_v4, spi_v5))] /// Create a full duplex driver. - pub fn new_full_duplex( + pub fn new_full_duplex( peri: Peri<'d, T>, - txsd: Peri<'d, impl MosiPin>, - rxsd: Peri<'d, impl MisoPin>, - ws: Peri<'d, impl WsPin>, - ck: Peri<'d, impl CkPin>, - mck: Peri<'d, impl MckPin>, + txsd: Peri<'d, if_afio!(impl MosiPin)>, + rxsd: Peri<'d, if_afio!(impl MisoPin)>, + ws: Peri<'d, if_afio!(impl WsPin)>, + ck: Peri<'d, if_afio!(impl CkPin)>, + mck: Peri<'d, if_afio!(impl MckPin)>, txdma: Peri<'d, impl TxDma>, txdma_buf: &'d mut [W], rxdma: Peri<'d, impl RxDma>, @@ -459,20 +459,20 @@ impl<'d, W: Word> I2S<'d, W> { } } - fn new_inner( + fn new_inner( peri: Peri<'d, T>, txsd: Option>, rxsd: Option>, - ws: Peri<'d, impl WsPin>, - ck: Peri<'d, impl CkPin>, + ws: Peri<'d, if_afio!(impl WsPin)>, + ck: Peri<'d, if_afio!(impl CkPin)>, mck: Option>, txdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>, rxdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>, config: Config, function: Function, ) -> Self { - ws.set_as_af(ws.af_num(), AfType::output(OutputType::PushPull, config.gpio_speed)); - ck.set_as_af(ck.af_num(), AfType::output(OutputType::PushPull, config.gpio_speed)); + set_as_af!(ws, AfType::output(OutputType::PushPull, config.gpio_speed)); + set_as_af!(ck, AfType::output(OutputType::PushPull, config.gpio_speed)); let spi = Spi::new_internal(peri, None, None, { let mut spi_config = SpiConfig::default(); diff --git a/embassy-stm32/src/lptim/pwm.rs b/embassy-stm32/src/lptim/pwm.rs index 2f2d7ba01..96af9f4d9 100644 --- a/embassy-stm32/src/lptim/pwm.rs +++ b/embassy-stm32/src/lptim/pwm.rs @@ -50,10 +50,7 @@ macro_rules! channel_impl { pub fn $new_chx(pin: Peri<'d, impl $pin_trait>) -> Self { critical_section::with(|_| { pin.set_low(); - pin.set_as_af( - pin.af_num(), - AfType::output(OutputType::PushPull, Speed::VeryHigh), - ); + set_as_af!(pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); }); PwmPin { _pin: pin.into(), @@ -64,12 +61,12 @@ macro_rules! channel_impl { pub fn $new_chx_with_config(pin: Peri<'d, impl $pin_trait>, pin_config: PwmPinConfig) -> Self { critical_section::with(|_| { pin.set_low(); - pin.set_as_af( - pin.af_num(), - #[cfg(gpio_v1)] - AfType::output(pin_config.output_type, pin_config.speed), - #[cfg(gpio_v2)] - AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull), + #[cfg(gpio_v1)] + set_as_af!(pin, AfType::output(pin_config.output_type, pin_config.speed)); + #[cfg(gpio_v2)] + set_as_af!( + pin, + AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull) ); }); PwmPin { diff --git a/embassy-stm32/src/macros.rs b/embassy-stm32/src/macros.rs index 3a0b490ba..22cc2e049 100644 --- a/embassy-stm32/src/macros.rs +++ b/embassy-stm32/src/macros.rs @@ -41,17 +41,30 @@ macro_rules! peri_trait_impl { } macro_rules! pin_trait { - ($signal:ident, $instance:path $(, $mode:path)?) => { + ($signal:ident, $instance:path $(, $mode:path)? $(, @$afio:ident)?) => { #[doc = concat!(stringify!($signal), " pin trait")] - pub trait $signal: crate::gpio::Pin { + pub trait $signal: crate::gpio::Pin { + #[cfg(not(afio))] #[doc = concat!("Get the AF number needed to use this pin as ", stringify!($signal))] fn af_num(&self) -> u8; + + #[cfg(afio)] + #[doc = concat!("Configures AFIO_MAPR to use this pin as ", stringify!($signal))] + fn afio_remap(&self); } }; } macro_rules! pin_trait_impl { - (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $pin:ident, $af:expr) => { + (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $pin:ident, $af:expr $(, $afio:path)?) => { + #[cfg(afio)] + impl crate::$mod::$trait for crate::peripherals::$pin { + fn afio_remap(&self) { + // nothing + } + } + + #[cfg(not(afio))] impl crate::$mod::$trait for crate::peripherals::$pin { fn af_num(&self) -> u8 { $af @@ -60,6 +73,39 @@ macro_rules! pin_trait_impl { }; } +#[cfg(afio)] +macro_rules! pin_trait_afio_impl { + (@set mapr, $setter:ident, $val:expr) => { + crate::pac::AFIO.mapr().modify(|w| { + w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); + w.$setter($val); + }); + }; + (@set mapr2, $setter:ident, $val:expr) => { + crate::pac::AFIO.mapr2().modify(|w| { + w.$setter($val); + }); + }; + (crate::$mod:ident::$trait:ident<$mode:ident>, $instance:ident, $pin:ident, {$reg:ident, $setter:ident, $type:ident, [$($val:expr),+]}) => { + $( + impl crate::$mod::$trait> for crate::peripherals::$pin { + fn afio_remap(&self) { + pin_trait_afio_impl!(@set $reg, $setter, $val); + } + } + )+ + }; + (crate::$mod:ident::$trait:ident, $instance:ident, $pin:ident, {$reg:ident, $setter:ident, $type:ident, [$($val:expr),+]}) => { + $( + impl crate::$mod::$trait> for crate::peripherals::$pin { + fn afio_remap(&self) { + pin_trait_afio_impl!(@set $reg, $setter, $val); + } + } + )+ + }; +} + #[allow(unused_macros)] macro_rules! sel_trait_impl { (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $pin:ident, $sel:expr) => { @@ -134,7 +180,73 @@ macro_rules! new_dma { macro_rules! new_pin { ($name:ident, $af_type:expr) => {{ let pin = $name; - pin.set_as_af(pin.af_num(), $af_type); + #[cfg(afio)] + pin.afio_remap(); + pin.set_as_af( + #[cfg(not(afio))] + pin.af_num(), + $af_type, + ); Some(pin.into()) }}; } + +/// Macro to configure a pin for alternate function use. +/// For AFIO chips (STM32F1), it calls afio_remap(). +/// For non-AFIO chips, it calls set_as_af() with the pin's af_num(). +macro_rules! set_as_af { + ($pin:expr, $af_type:expr) => { + #[cfg(afio)] + { + $pin.set_as_af($af_type); + $pin.afio_remap(); + } + #[cfg(not(afio))] + { + $pin.set_as_af($pin.af_num(), $af_type); + } + }; +} + +#[cfg(afio)] +macro_rules! if_afio { + ($($t:tt)*) => { + $($t)* + } +} +#[cfg(not(afio))] +macro_rules! if_afio { + (($a:ty, A)) => { + ($a,) + }; + (($a:ty, $b:ty, A)) => { + ($a,$b) + }; + (($a:ty, $b:ty, $c:ty, A)) => { + ($a,$b, $c) + }; + ($type:ident<$lt:lifetime, $a:ty, $b:ty, A>) => { + $type<$lt, $a, $b> + }; + ($type:ident<$lt:lifetime, $a:ty, $b:ty, $c:ty, A>) => { + $type<$lt, $a, $b, $c> + }; + ($type:ident<$a:ty, A>) => { + $type<$a> + }; + ($type:ident<$a:ty, $b:ty, A>) => { + $type<$a, $b> + }; + ($type:ident<$a:ty, $b:ty, $c:ty, A>) => { + $type<$a, $b, $c> + }; + (impl $trait:ident<$a:ty, A>) => { + impl $trait<$a> + }; + (impl $trait:ident<$a:ty, $b:ty, A>) => { + impl $trait<$a, $b> + }; + (impl $trait:ident<$a:ty, $b:ty, $c:ty, A>) => { + impl $trait<$a, $b, $c> + }; +} diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs index 96e628b1a..59ccc8cb5 100644 --- a/embassy-stm32/src/rcc/mco.rs +++ b/embassy-stm32/src/rcc/mco.rs @@ -94,7 +94,7 @@ impl<'d, T: McoInstance> Mco<'d, T> { pub fn new(_peri: Peri<'d, T>, pin: Peri<'d, impl McoPin>, source: T::Source, prescaler: McoPrescaler) -> Self { critical_section::with(|_| unsafe { T::_apply_clock_settings(source, prescaler); - pin.set_as_af(pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); + set_as_af!(pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); }); Self { phantom: PhantomData } diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index cde2a56c2..fb8b23b79 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs @@ -558,7 +558,7 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { config: Config, ) -> Self { let (_sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx); - mclk.set_as_af(mclk.af_num(), ck_af_type); + set_as_af!(mclk, ck_af_type); Self::new_asynchronous(peri, sck, sd, fs, dma, dma_buf, config) } @@ -578,9 +578,9 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { let peri = peri.peri; let (sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx); - sd.set_as_af(sd.af_num(), sd_af_type); - sck.set_as_af(sck.af_num(), ck_af_type); - fs.set_as_af(fs.af_num(), ck_af_type); + set_as_af!(sd, sd_af_type); + set_as_af!(sck, ck_af_type); + set_as_af!(fs, ck_af_type); let sub_block = S::WHICH; let request = dma.request(); @@ -612,7 +612,7 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { let peri = peri.peri; let (sd_af_type, _ck_af_type) = get_af_types(config.mode, config.tx_rx); - sd.set_as_af(sd.af_num(), sd_af_type); + set_as_af!(sd, sd_af_type); let sub_block = S::WHICH; let request = dma.request(); diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 6e5d735d7..ccbd16cbf 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -428,9 +428,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> { config: Config, ) -> Self { critical_section::with(|_| { - clk.set_as_af(clk.af_num(), CLK_AF); - cmd.set_as_af(cmd.af_num(), CMD_AF); - d0.set_as_af(d0.af_num(), DATA_AF); + set_as_af!(clk, CLK_AF); + set_as_af!(cmd, CMD_AF); + set_as_af!(d0, DATA_AF); }); Self::new_inner( @@ -464,12 +464,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { config: Config, ) -> Self { critical_section::with(|_| { - clk.set_as_af(clk.af_num(), CLK_AF); - cmd.set_as_af(cmd.af_num(), CMD_AF); - d0.set_as_af(d0.af_num(), DATA_AF); - d1.set_as_af(d1.af_num(), DATA_AF); - d2.set_as_af(d2.af_num(), DATA_AF); - d3.set_as_af(d3.af_num(), DATA_AF); + set_as_af!(clk, CLK_AF); + set_as_af!(cmd, CMD_AF); + set_as_af!(d0, DATA_AF); + set_as_af!(d1, DATA_AF); + set_as_af!(d2, DATA_AF); + set_as_af!(d3, DATA_AF); }); Self::new_inner( @@ -510,16 +510,16 @@ impl<'d, T: Instance> Sdmmc<'d, T> { config: Config, ) -> Self { critical_section::with(|_| { - clk.set_as_af(clk.af_num(), CLK_AF); - cmd.set_as_af(cmd.af_num(), CMD_AF); - d0.set_as_af(d0.af_num(), DATA_AF); - d1.set_as_af(d1.af_num(), DATA_AF); - d2.set_as_af(d2.af_num(), DATA_AF); - d3.set_as_af(d3.af_num(), DATA_AF); - d4.set_as_af(d4.af_num(), DATA_AF); - d5.set_as_af(d5.af_num(), DATA_AF); - d6.set_as_af(d6.af_num(), DATA_AF); - d7.set_as_af(d7.af_num(), DATA_AF); + set_as_af!(clk, CLK_AF); + set_as_af!(cmd, CMD_AF); + set_as_af!(d0, DATA_AF); + set_as_af!(d1, DATA_AF); + set_as_af!(d2, DATA_AF); + set_as_af!(d3, DATA_AF); + set_as_af!(d4, DATA_AF); + set_as_af!(d5, DATA_AF); + set_as_af!(d6, DATA_AF); + set_as_af!(d7, DATA_AF); }); Self::new_inner( @@ -552,9 +552,9 @@ impl<'d, T: Instance> Sdmmc<'d, T> { config: Config, ) -> Self { critical_section::with(|_| { - clk.set_as_af(clk.af_num(), CLK_AF); - cmd.set_as_af(cmd.af_num(), CMD_AF); - d0.set_as_af(d0.af_num(), DATA_AF); + set_as_af!(clk, CLK_AF); + set_as_af!(cmd, CMD_AF); + set_as_af!(d0, DATA_AF); }); Self::new_inner( @@ -586,12 +586,12 @@ impl<'d, T: Instance> Sdmmc<'d, T> { config: Config, ) -> Self { critical_section::with(|_| { - clk.set_as_af(clk.af_num(), CLK_AF); - cmd.set_as_af(cmd.af_num(), CMD_AF); - d0.set_as_af(d0.af_num(), DATA_AF); - d1.set_as_af(d1.af_num(), DATA_AF); - d2.set_as_af(d2.af_num(), DATA_AF); - d3.set_as_af(d3.af_num(), DATA_AF); + set_as_af!(clk, CLK_AF); + set_as_af!(cmd, CMD_AF); + set_as_af!(d0, DATA_AF); + set_as_af!(d1, DATA_AF); + set_as_af!(d2, DATA_AF); + set_as_af!(d3, DATA_AF); }); Self::new_inner( @@ -630,16 +630,16 @@ impl<'d, T: Instance> Sdmmc<'d, T> { config: Config, ) -> Self { critical_section::with(|_| { - clk.set_as_af(clk.af_num(), CLK_AF); - cmd.set_as_af(cmd.af_num(), CMD_AF); - d0.set_as_af(d0.af_num(), DATA_AF); - d1.set_as_af(d1.af_num(), DATA_AF); - d2.set_as_af(d2.af_num(), DATA_AF); - d3.set_as_af(d3.af_num(), DATA_AF); - d4.set_as_af(d4.af_num(), DATA_AF); - d5.set_as_af(d5.af_num(), DATA_AF); - d6.set_as_af(d6.af_num(), DATA_AF); - d7.set_as_af(d7.af_num(), DATA_AF); + set_as_af!(clk, CLK_AF); + set_as_af!(cmd, CMD_AF); + set_as_af!(d0, DATA_AF); + set_as_af!(d1, DATA_AF); + set_as_af!(d2, DATA_AF); + set_as_af!(d3, DATA_AF); + set_as_af!(d4, DATA_AF); + set_as_af!(d5, DATA_AF); + set_as_af!(d6, DATA_AF); + set_as_af!(d7, DATA_AF); }); Self::new_inner( diff --git a/embassy-stm32/src/spdifrx/mod.rs b/embassy-stm32/src/spdifrx/mod.rs index 466639e83..b0a32d5d1 100644 --- a/embassy-stm32/src/spdifrx/mod.rs +++ b/embassy-stm32/src/spdifrx/mod.rs @@ -35,7 +35,7 @@ macro_rules! new_spdifrx_pin { ($name:ident, $af_type:expr) => {{ let pin = $name; let input_sel = pin.input_sel(); - pin.set_as_af(pin.af_num(), $af_type); + set_as_af!(pin, $af_type); (Some(pin.into()), input_sel) }}; } diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index a49ebcbee..c5373a54d 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -471,11 +471,11 @@ impl<'d, M: PeriMode> Spi<'d, M> { impl<'d> Spi<'d, Blocking> { /// Create a new blocking SPI driver. - pub fn new_blocking( + pub fn new_blocking( peri: Peri<'d, T>, - sck: Peri<'d, impl SckPin>, - mosi: Peri<'d, impl MosiPin>, - miso: Peri<'d, impl MisoPin>, + sck: Peri<'d, if_afio!(impl SckPin)>, + mosi: Peri<'d, if_afio!(impl MosiPin)>, + miso: Peri<'d, if_afio!(impl MisoPin)>, config: Config, ) -> Self { Self::new_inner( @@ -490,10 +490,10 @@ impl<'d> Spi<'d, Blocking> { } /// Create a new blocking SPI driver, in RX-only mode (only MISO pin, no MOSI). - pub fn new_blocking_rxonly( + pub fn new_blocking_rxonly( peri: Peri<'d, T>, - sck: Peri<'d, impl SckPin>, - miso: Peri<'d, impl MisoPin>, + sck: Peri<'d, if_afio!(impl SckPin)>, + miso: Peri<'d, if_afio!(impl MisoPin)>, config: Config, ) -> Self { Self::new_inner( @@ -508,10 +508,10 @@ impl<'d> Spi<'d, Blocking> { } /// Create a new blocking SPI driver, in TX-only mode (only MOSI pin, no MISO). - pub fn new_blocking_txonly( + pub fn new_blocking_txonly( peri: Peri<'d, T>, - sck: Peri<'d, impl SckPin>, - mosi: Peri<'d, impl MosiPin>, + sck: Peri<'d, if_afio!(impl SckPin)>, + mosi: Peri<'d, if_afio!(impl MosiPin)>, config: Config, ) -> Self { Self::new_inner( @@ -528,9 +528,9 @@ 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. - pub fn new_blocking_txonly_nosck( + pub fn new_blocking_txonly_nosck( peri: Peri<'d, T>, - mosi: Peri<'d, impl MosiPin>, + mosi: Peri<'d, if_afio!(impl MosiPin)>, config: Config, ) -> Self { Self::new_inner( @@ -547,11 +547,11 @@ impl<'d> Spi<'d, Blocking> { impl<'d> Spi<'d, Async> { /// Create a new SPI driver. - pub fn new( + pub fn new( peri: Peri<'d, T>, - sck: Peri<'d, impl SckPin>, - mosi: Peri<'d, impl MosiPin>, - miso: Peri<'d, impl MisoPin>, + sck: Peri<'d, if_afio!(impl SckPin)>, + mosi: Peri<'d, if_afio!(impl MosiPin)>, + miso: Peri<'d, if_afio!(impl MisoPin)>, tx_dma: Peri<'d, impl TxDma>, rx_dma: Peri<'d, impl RxDma>, config: Config, @@ -568,10 +568,10 @@ impl<'d> Spi<'d, Async> { } /// Create a new SPI driver, in RX-only mode (only MISO pin, no MOSI). - pub fn new_rxonly( + pub fn new_rxonly( peri: Peri<'d, T>, - sck: Peri<'d, impl SckPin>, - miso: Peri<'d, impl MisoPin>, + sck: Peri<'d, if_afio!(impl SckPin)>, + miso: Peri<'d, if_afio!(impl MisoPin)>, #[cfg(any(spi_v1, spi_v2, spi_v3))] tx_dma: Peri<'d, impl TxDma>, rx_dma: Peri<'d, impl RxDma>, config: Config, @@ -591,10 +591,10 @@ impl<'d> Spi<'d, Async> { } /// Create a new SPI driver, in TX-only mode (only MOSI pin, no MISO). - pub fn new_txonly( + pub fn new_txonly( peri: Peri<'d, T>, - sck: Peri<'d, impl SckPin>, - mosi: Peri<'d, impl MosiPin>, + sck: Peri<'d, if_afio!(impl SckPin)>, + mosi: Peri<'d, if_afio!(impl MosiPin)>, tx_dma: Peri<'d, impl TxDma>, config: Config, ) -> Self { @@ -612,9 +612,9 @@ 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. - pub fn new_txonly_nosck( + pub fn new_txonly_nosck( peri: Peri<'d, T>, - mosi: Peri<'d, impl MosiPin>, + mosi: Peri<'d, if_afio!(impl MosiPin)>, tx_dma: Peri<'d, impl TxDma>, config: Config, ) -> Self { @@ -1309,13 +1309,13 @@ impl State { peri_trait!(); -pin_trait!(SckPin, Instance); -pin_trait!(MosiPin, Instance); -pin_trait!(MisoPin, Instance); -pin_trait!(CsPin, Instance); -pin_trait!(MckPin, Instance); -pin_trait!(CkPin, Instance); -pin_trait!(WsPin, Instance); +pin_trait!(SckPin, Instance, @A); +pin_trait!(MosiPin, Instance, @A); +pin_trait!(MisoPin, Instance, @A); +pin_trait!(CsPin, Instance, @A); +pin_trait!(MckPin, Instance, @A); +pin_trait!(CkPin, Instance, @A); +pin_trait!(WsPin, Instance, @A); dma_trait!(RxDma, Instance); dma_trait!(TxDma, Instance); diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 68cdec302..484aae1d0 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -16,23 +16,24 @@ use crate::Peri; /// Complementary PWM pin wrapper. /// /// This wraps a pin to make it usable with PWM. -pub struct ComplementaryPwmPin<'d, T, C> { - _pin: Peri<'d, AnyPin>, - phantom: PhantomData<(T, C)>, +pub struct ComplementaryPwmPin<'d, T, C, #[cfg(afio)] A> { + #[allow(unused)] + pin: Peri<'d, AnyPin>, + phantom: PhantomData, } -impl<'d, T: AdvancedInstance4Channel, C: TimerChannel> ComplementaryPwmPin<'d, T, C> { +impl<'d, T: AdvancedInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!(ComplementaryPwmPin<'d, T, C, A>) { /// Create a new complementary PWM pin instance. - pub fn new(pin: Peri<'d, impl TimerComplementaryPin>, output_type: OutputType) -> Self { + pub fn new(pin: Peri<'d, if_afio!(impl TimerComplementaryPin)>, output_type: OutputType) -> Self { critical_section::with(|_| { pin.set_low(); - pin.set_as_af( - pin.af_num(), - crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh), + set_as_af!( + pin, + crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh) ); }); ComplementaryPwmPin { - _pin: pin.into(), + pin: pin.into(), phantom: PhantomData, } } @@ -54,17 +55,17 @@ pub enum IdlePolarity { impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// Create a new complementary PWM driver. - #[allow(clippy::too_many_arguments)] - pub fn new( + #[allow(clippy::too_many_arguments, unused)] + pub fn new<#[cfg(afio)] A>( tim: Peri<'d, T>, - _ch1: Option>, - _ch1n: Option>, - _ch2: Option>, - _ch2n: Option>, - _ch3: Option>, - _ch3n: Option>, - _ch4: Option>, - _ch4n: Option>, + ch1: Option)>, + ch1n: Option)>, + ch2: Option)>, + ch2n: Option)>, + ch3: Option)>, + ch3n: Option)>, + ch4: Option)>, + ch4n: Option)>, freq: Hertz, counting_mode: CountingMode, ) -> Self { diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index dda33e7f1..7a25e6c21 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -17,16 +17,17 @@ use crate::Peri; /// Capture pin wrapper. /// /// This wraps a pin to make it usable with capture. -pub struct CapturePin<'d, T, C> { - _pin: Peri<'d, AnyPin>, - phantom: PhantomData<(T, C)>, +pub struct CapturePin<'d, T, C, #[cfg(afio)] A> { + #[allow(unused)] + pin: Peri<'d, AnyPin>, + phantom: PhantomData, } -impl<'d, T: GeneralInstance4Channel, C: TimerChannel> CapturePin<'d, T, C> { +impl<'d, T: GeneralInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!(CapturePin<'d, T, C, A>) { /// Create a new capture pin instance. - pub fn new(pin: Peri<'d, impl TimerPin>, pull: Pull) -> Self { - pin.set_as_af(pin.af_num(), AfType::input(pull)); + pub fn new(pin: Peri<'d, if_afio!(impl TimerPin)>, pull: Pull) -> Self { + set_as_af!(pin, AfType::input(pull)); CapturePin { - _pin: pin.into(), + pin: pin.into(), phantom: PhantomData, } } @@ -39,12 +40,13 @@ pub struct InputCapture<'d, T: GeneralInstance4Channel> { impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { /// Create a new input capture driver. - pub fn new( + #[allow(unused)] + pub fn new<#[cfg(afio)] A>( tim: Peri<'d, T>, - _ch1: Option>, - _ch2: Option>, - _ch3: Option>, - _ch4: Option>, + ch1: Option)>, + ch2: Option)>, + ch3: Option)>, + ch4: Option)>, _irq: impl Binding> + 'd, freq: Hertz, counting_mode: CountingMode, diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 7062f5f4c..b09bc7166 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -223,15 +223,15 @@ pub trait AdvancedInstance2Channel: BasicInstance + GeneralInstance2Channel + Ad /// Advanced 16-bit timer with 4 channels instance. pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel {} -pin_trait!(TimerPin, GeneralInstance4Channel, TimerChannel); -pin_trait!(ExternalTriggerPin, GeneralInstance4Channel); +pin_trait!(TimerPin, GeneralInstance4Channel, TimerChannel, @A); +pin_trait!(ExternalTriggerPin, GeneralInstance4Channel, @A); -pin_trait!(TimerComplementaryPin, AdvancedInstance4Channel, TimerChannel); +pin_trait!(TimerComplementaryPin, AdvancedInstance4Channel, TimerChannel, @A); -pin_trait!(BreakInputPin, AdvancedInstance4Channel, BreakInput); +pin_trait!(BreakInputPin, AdvancedInstance4Channel, BreakInput, @A); -pin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel, BreakInput); -pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel, BreakInput); +pin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel, BreakInput, @A); +pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel, BreakInput, @A); // Update Event trigger DMA for every timer dma_trait!(UpDma, BasicInstance); diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs index 498d9c082..a75b41bd7 100644 --- a/embassy-stm32/src/timer/one_pulse.rs +++ b/embassy-stm32/src/timer/one_pulse.rs @@ -15,6 +15,7 @@ use crate::gpio::{AfType, AnyPin, Pull}; use crate::interrupt::typelevel::{Binding, Interrupt}; use crate::pac::timer::vals::Etp; use crate::time::Hertz; +use crate::timer::TimerChannel; use crate::Peri; /// External input marker type. @@ -42,7 +43,8 @@ impl From for Etp { /// /// This wraps a pin to make it usable as a timer trigger. pub struct TriggerPin<'d, T, C> { - _pin: Peri<'d, AnyPin>, + #[allow(unused)] + pin: Peri<'d, AnyPin>, phantom: PhantomData<(T, C)>, } @@ -60,60 +62,23 @@ impl SealedTriggerSource for Ch1 {} impl SealedTriggerSource for Ch2 {} impl SealedTriggerSource for Ext {} -trait SealedTimerTriggerPin: crate::gpio::Pin {} - -/// Marker trait for a trigger pin. -#[expect(private_bounds)] -// TODO: find better naming scheme than prefixing all pin traits with "Timer". -// The trait name cannot conflict with the corresponding type's name. -// Applies to other timer submodules as well. -pub trait TimerTriggerPin: SealedTimerTriggerPin { - /// Get the AF number needed to use this pin as a trigger source. - fn af_num(&self) -> u8; -} - -impl TimerTriggerPin for P -where - T: GeneralInstance4Channel, - P: TimerPin, - C: super::TimerChannel + TriggerSource, -{ - fn af_num(&self) -> u8 { - TimerPin::af_num(self) - } -} - -impl TimerTriggerPin for P -where - T: GeneralInstance4Channel, - P: ExternalTriggerPin, -{ - fn af_num(&self) -> u8 { - ExternalTriggerPin::af_num(self) - } -} - -impl SealedTimerTriggerPin for P -where - T: GeneralInstance4Channel, - P: TimerPin, - C: super::TimerChannel + TriggerSource, -{ -} - -impl SealedTimerTriggerPin for P -where - T: GeneralInstance4Channel, - P: ExternalTriggerPin, -{ -} - -impl<'d, T: GeneralInstance4Channel, C: TriggerSource> TriggerPin<'d, T, C> { - /// "Create a new Ch1 trigger pin instance. - pub fn new(pin: Peri<'d, impl TimerTriggerPin>, pull: Pull) -> Self { - pin.set_as_af(pin.af_num(), AfType::input(pull)); +impl<'d, T: GeneralInstance4Channel, C: TriggerSource + TimerChannel> TriggerPin<'d, T, C> { + /// Create a new Channel trigger pin instance. + pub fn new<#[cfg(afio)] A>(pin: Peri<'d, if_afio!(impl TimerPin)>, pull: Pull) -> Self { + set_as_af!(pin, AfType::input(pull)); TriggerPin { - _pin: pin.into(), + pin: pin.into(), + phantom: PhantomData, + } + } +} + +impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, Ext> { + /// Create a new external trigger pin instance. + pub fn new_external<#[cfg(afio)] A>(pin: Peri<'d, if_afio!(impl ExternalTriggerPin)>, pull: Pull) -> Self { + set_as_af!(pin, AfType::input(pull)); + TriggerPin { + pin: pin.into(), phantom: PhantomData, } } @@ -131,9 +96,10 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { /// /// The pulse is triggered by a channel 1 input pin on both rising and /// falling edges. Channel 1 will unusable as an output. + #[allow(unused)] pub fn new_ch1_edge_detect( tim: Peri<'d, T>, - _pin: TriggerPin<'d, T, Ch1>, + pin: TriggerPin<'d, T, Ch1>, _irq: impl Binding> + 'd, freq: Hertz, pulse_end: u32, diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index 1e55f2919..159b5a177 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -18,15 +18,25 @@ pub struct PwmInput<'d, T: GeneralInstance4Channel> { impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { /// Create a new PWM input driver. - pub fn new_ch1(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin>, pull: Pull, freq: Hertz) -> Self { - pin.set_as_af(pin.af_num(), AfType::input(pull)); + pub fn new_ch1<#[cfg(afio)] A>( + tim: Peri<'d, T>, + pin: Peri<'d, if_afio!(impl TimerPin)>, + pull: Pull, + freq: Hertz, + ) -> Self { + set_as_af!(pin, AfType::input(pull)); Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2) } /// Create a new PWM input driver. - pub fn new_ch2(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin>, pull: Pull, freq: Hertz) -> Self { - pin.set_as_af(pin.af_num(), AfType::input(pull)); + pub fn new_ch2<#[cfg(afio)] A>( + tim: Peri<'d, T>, + pin: Peri<'d, if_afio!(impl TimerPin)>, + pull: Pull, + freq: Hertz, + ) -> Self { + set_as_af!(pin, AfType::input(pull)); Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1) } diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index eabe1b22a..82b5968b0 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs @@ -20,20 +20,21 @@ pub enum Direction { } /// Wrapper for using a pin with QEI. -pub struct QeiPin<'d, T, Channel> { - _pin: Peri<'d, AnyPin>, - phantom: PhantomData<(T, Channel)>, +pub struct QeiPin<'d, T, Channel, #[cfg(afio)] A> { + #[allow(unused)] + pin: Peri<'d, AnyPin>, + phantom: PhantomData, } -impl<'d, T: GeneralInstance4Channel, C: QeiChannel> QeiPin<'d, T, C> { - /// Create a new QEI pin instance. - pub fn new(pin: Peri<'d, impl TimerPin>) -> Self { +impl<'d, T: GeneralInstance4Channel, C: QeiChannel, #[cfg(afio)] A> if_afio!(QeiPin<'d, T, C, A>) { + /// Create a new QEI pin instance. + pub fn new(pin: Peri<'d, if_afio!(impl TimerPin)>) -> Self { critical_section::with(|_| { pin.set_low(); - pin.set_as_af(pin.af_num(), AfType::input(Pull::None)); + set_as_af!(pin, AfType::input(Pull::None)); }); QeiPin { - _pin: pin.into(), + pin: pin.into(), phantom: PhantomData, } } @@ -58,7 +59,12 @@ pub struct Qei<'d, T: GeneralInstance4Channel> { impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { /// Create a new quadrature decoder driver. - pub fn new(tim: Peri<'d, T>, _ch1: QeiPin<'d, T, Ch1>, _ch2: QeiPin<'d, T, Ch2>) -> Self { + #[allow(unused)] + pub fn new<#[cfg(afio)] A>( + tim: Peri<'d, T>, + ch1: if_afio!(QeiPin<'d, T, Ch1, A>), + ch2: if_afio!(QeiPin<'d, T, Ch2, A>), + ) -> Self { Self::new_inner(tim) } diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index c04b1ab97..e6165e42b 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -14,9 +14,10 @@ use crate::Peri; /// PWM pin wrapper. /// /// This wraps a pin to make it usable with PWM. -pub struct PwmPin<'d, T, C> { - _pin: Peri<'d, AnyPin>, - phantom: PhantomData<(T, C)>, +pub struct PwmPin<'d, T, C, #[cfg(afio)] A> { + #[allow(unused)] + pub(crate) pin: Peri<'d, AnyPin>, + phantom: PhantomData, } /// PWM pin config @@ -34,33 +35,33 @@ pub struct PwmPinConfig { pub pull: Pull, } -impl<'d, T: GeneralInstance4Channel, C: TimerChannel> PwmPin<'d, T, C> { +impl<'d, T: GeneralInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!(PwmPin<'d, T, C, A>) { /// Create a new PWM pin instance. - pub fn new(pin: Peri<'d, impl TimerPin>, output_type: OutputType) -> Self { + pub fn new(pin: Peri<'d, if_afio!(impl TimerPin)>, output_type: OutputType) -> Self { critical_section::with(|_| { pin.set_low(); - pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh)); + set_as_af!(pin, AfType::output(output_type, Speed::VeryHigh)); }); PwmPin { - _pin: pin.into(), + pin: pin.into(), phantom: PhantomData, } } - /// Create a new PWM pin instance with config. - pub fn new_with_config(pin: Peri<'d, impl TimerPin>, pin_config: PwmPinConfig) -> Self { + /// Create a new PWM pin instance with a specific configuration. + pub fn new_with_config(pin: Peri<'d, if_afio!(impl TimerPin)>, pin_config: PwmPinConfig) -> Self { critical_section::with(|_| { pin.set_low(); - pin.set_as_af( - pin.af_num(), - #[cfg(gpio_v1)] - AfType::output(pin_config.output_type, pin_config.speed), - #[cfg(gpio_v2)] - AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull), + #[cfg(gpio_v1)] + set_as_af!(pin, AfType::output(pin_config.output_type, pin_config.speed)); + #[cfg(gpio_v2)] + set_as_af!( + pin, + AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull) ); }); PwmPin { - _pin: pin.into(), + pin: pin.into(), phantom: PhantomData, } } @@ -178,12 +179,13 @@ pub struct SimplePwm<'d, T: GeneralInstance4Channel> { impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Create a new simple PWM driver. - pub fn new( + #[allow(unused)] + pub fn new<#[cfg(afio)] A>( tim: Peri<'d, T>, - _ch1: Option>, - _ch2: Option>, - _ch3: Option>, - _ch4: Option>, + ch1: Option)>, + ch2: Option)>, + ch3: Option)>, + ch4: Option)>, freq: Hertz, counting_mode: CountingMode, ) -> Self { diff --git a/embassy-stm32/src/tsc/pin_groups.rs b/embassy-stm32/src/tsc/pin_groups.rs index 6f914a94e..84421f7ff 100644 --- a/embassy-stm32/src/tsc/pin_groups.rs +++ b/embassy-stm32/src/tsc/pin_groups.rs @@ -427,7 +427,7 @@ macro_rules! impl_set_io { pub fn $method(&mut self, pin: Peri<'d, impl $trait>) -> IOPinWithRole<$group, Role> { critical_section::with(|_| { pin.set_low(); - pin.set_as_af(pin.af_num(), AfType::output(Role::output_type(), Speed::VeryHigh)); + set_as_af!(pin, AfType::output(Role::output_type(), Speed::VeryHigh)); let tsc_io_pin = trait_to_io_pin!($trait); let new_pin = Pin { _pin: pin.into(), diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 729440c46..890c8a80e 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -208,10 +208,10 @@ impl<'d> SetConfig for BufferedUartTx<'d> { impl<'d> BufferedUart<'d> { /// Create a new bidirectional buffered UART driver - pub fn new( + pub fn new( peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - tx: Peri<'d, impl TxPin>, + rx: Peri<'d, if_afio!(impl RxPin)>, + tx: Peri<'d, if_afio!(impl TxPin)>, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], _irq: impl interrupt::typelevel::Binding> + 'd, @@ -231,12 +231,12 @@ impl<'d> BufferedUart<'d> { } /// Create a new bidirectional buffered UART driver with request-to-send and clear-to-send pins - pub fn new_with_rtscts( + pub fn new_with_rtscts( peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - tx: Peri<'d, impl TxPin>, - rts: Peri<'d, impl RtsPin>, - cts: Peri<'d, impl CtsPin>, + rx: Peri<'d, if_afio!(impl RxPin)>, + tx: Peri<'d, if_afio!(impl TxPin)>, + rts: Peri<'d, if_afio!(impl RtsPin)>, + cts: Peri<'d, if_afio!(impl CtsPin)>, _irq: impl interrupt::typelevel::Binding> + 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], @@ -256,11 +256,11 @@ impl<'d> BufferedUart<'d> { } /// Create a new bidirectional buffered UART driver with only the RTS pin as the DE pin - pub fn new_with_rts_as_de( + pub fn new_with_rts_as_de( peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - tx: Peri<'d, impl TxPin>, - rts: Peri<'d, impl RtsPin>, + rx: Peri<'d, if_afio!(impl RxPin)>, + tx: Peri<'d, if_afio!(impl TxPin)>, + rts: Peri<'d, if_afio!(impl RtsPin)>, _irq: impl interrupt::typelevel::Binding> + 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], @@ -280,11 +280,11 @@ impl<'d> BufferedUart<'d> { } /// Create a new bidirectional buffered UART driver with only the request-to-send pin - pub fn new_with_rts( + pub fn new_with_rts( peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - tx: Peri<'d, impl TxPin>, - rts: Peri<'d, impl RtsPin>, + rx: Peri<'d, if_afio!(impl RxPin)>, + tx: Peri<'d, if_afio!(impl TxPin)>, + rts: Peri<'d, if_afio!(impl RtsPin)>, _irq: impl interrupt::typelevel::Binding> + 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], @@ -305,11 +305,11 @@ impl<'d> BufferedUart<'d> { /// Create a new bidirectional buffered UART driver with a driver-enable pin #[cfg(not(any(usart_v1, usart_v2)))] - pub fn new_with_de( + pub fn new_with_de( peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - tx: Peri<'d, impl TxPin>, - de: Peri<'d, impl DePin>, + rx: Peri<'d, if_afio!(impl RxPin)>, + tx: Peri<'d, if_afio!(impl TxPin)>, + de: Peri<'d, if_afio!(impl DePin)>, _irq: impl interrupt::typelevel::Binding> + 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], @@ -340,9 +340,9 @@ impl<'d> BufferedUart<'d> { /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict /// on the line must be managed by software (for instance by using a centralized arbiter). #[doc(alias("HDSEL"))] - pub fn new_half_duplex( + pub fn new_half_duplex( peri: Peri<'d, T>, - tx: Peri<'d, impl TxPin>, + tx: Peri<'d, if_afio!(impl TxPin)>, _irq: impl interrupt::typelevel::Binding> + 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], @@ -379,9 +379,9 @@ impl<'d> BufferedUart<'d> { /// on the line must be managed by software (for instance by using a centralized arbiter). #[cfg(not(any(usart_v1, usart_v2)))] #[doc(alias("HDSEL"))] - pub fn new_half_duplex_on_rx( + pub fn new_half_duplex_on_rx( peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, + rx: Peri<'d, if_afio!(impl RxPin)>, _irq: impl interrupt::typelevel::Binding> + 'd, tx_buffer: &'d mut [u8], rx_buffer: &'d mut [u8], diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 3d95de897..ff211e0c9 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -429,9 +429,9 @@ impl<'d, M: Mode> SetConfig for UartRx<'d, M> { impl<'d> UartTx<'d, Async> { /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. - pub fn new( + pub fn new( peri: Peri<'d, T>, - tx: Peri<'d, impl TxPin>, + tx: Peri<'d, if_afio!(impl TxPin)>, tx_dma: Peri<'d, impl TxDma>, config: Config, ) -> Result { @@ -439,10 +439,10 @@ impl<'d> UartTx<'d, Async> { } /// Create a new tx-only UART with a clear-to-send pin - pub fn new_with_cts( + pub fn new_with_cts( peri: Peri<'d, T>, - tx: Peri<'d, impl TxPin>, - cts: Peri<'d, impl CtsPin>, + tx: Peri<'d, if_afio!(impl TxPin)>, + cts: Peri<'d, if_afio!(impl CtsPin)>, tx_dma: Peri<'d, impl TxDma>, config: Config, ) -> Result { @@ -482,19 +482,19 @@ impl<'d> UartTx<'d, Blocking> { /// Create a new blocking tx-only UART with no hardware flow control. /// /// Useful if you only want Uart Tx. It saves 1 pin and consumes a little less power. - pub fn new_blocking( + pub fn new_blocking( peri: Peri<'d, T>, - tx: Peri<'d, impl TxPin>, + tx: Peri<'d, if_afio!(impl TxPin)>, config: Config, ) -> Result { Self::new_inner(peri, new_pin!(tx, config.tx_af()), None, None, config) } /// Create a new blocking tx-only UART with a clear-to-send pin - pub fn new_blocking_with_cts( + pub fn new_blocking_with_cts( peri: Peri<'d, T>, - tx: Peri<'d, impl TxPin>, - cts: Peri<'d, impl CtsPin>, + tx: Peri<'d, if_afio!(impl TxPin)>, + cts: Peri<'d, if_afio!(impl CtsPin)>, config: Config, ) -> Result { Self::new_inner( @@ -662,10 +662,10 @@ impl<'d> UartRx<'d, Async> { /// Create a new rx-only UART with no hardware flow control. /// /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. - pub fn new( + pub fn new( peri: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, - rx: Peri<'d, impl RxPin>, + rx: Peri<'d, if_afio!(impl RxPin)>, rx_dma: Peri<'d, impl RxDma>, config: Config, ) -> Result { @@ -673,11 +673,11 @@ impl<'d> UartRx<'d, Async> { } /// Create a new rx-only UART with a request-to-send pin - pub fn new_with_rts( + pub fn new_with_rts( peri: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding> + 'd, - rx: Peri<'d, impl RxPin>, - rts: Peri<'d, impl RtsPin>, + rx: Peri<'d, if_afio!(impl RxPin)>, + rts: Peri<'d, if_afio!(impl RtsPin)>, rx_dma: Peri<'d, impl RxDma>, config: Config, ) -> Result { @@ -913,19 +913,19 @@ impl<'d> UartRx<'d, Blocking> { /// Create a new rx-only UART with no hardware flow control. /// /// Useful if you only want Uart Rx. It saves 1 pin and consumes a little less power. - pub fn new_blocking( + pub fn new_blocking( peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, + rx: Peri<'d, if_afio!(impl RxPin)>, config: Config, ) -> Result { Self::new_inner(peri, new_pin!(rx, config.rx_af()), None, None, config) } /// Create a new rx-only UART with a request-to-send pin - pub fn new_blocking_with_rts( + pub fn new_blocking_with_rts( peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - rts: Peri<'d, impl RtsPin>, + rx: Peri<'d, if_afio!(impl RxPin)>, + rts: Peri<'d, if_afio!(impl RtsPin)>, config: Config, ) -> Result { Self::new_inner( @@ -1109,10 +1109,10 @@ fn drop_tx_rx(info: &Info, state: &State) { impl<'d> Uart<'d, Async> { /// Create a new bidirectional UART - pub fn new( + pub fn new( peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - tx: Peri<'d, impl TxPin>, + rx: Peri<'d, if_afio!(impl RxPin)>, + tx: Peri<'d, if_afio!(impl TxPin)>, _irq: impl interrupt::typelevel::Binding> + 'd, tx_dma: Peri<'d, impl TxDma>, rx_dma: Peri<'d, impl RxDma>, @@ -1132,13 +1132,13 @@ impl<'d> Uart<'d, Async> { } /// Create a new bidirectional UART with request-to-send and clear-to-send pins - pub fn new_with_rtscts( + pub fn new_with_rtscts( peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - tx: Peri<'d, impl TxPin>, + rx: Peri<'d, if_afio!(impl RxPin)>, + tx: Peri<'d, if_afio!(impl TxPin)>, _irq: impl interrupt::typelevel::Binding> + 'd, - rts: Peri<'d, impl RtsPin>, - cts: Peri<'d, impl CtsPin>, + rts: Peri<'d, if_afio!(impl RtsPin)>, + cts: Peri<'d, if_afio!(impl CtsPin)>, tx_dma: Peri<'d, impl TxDma>, rx_dma: Peri<'d, impl RxDma>, config: Config, @@ -1158,12 +1158,12 @@ impl<'d> Uart<'d, Async> { #[cfg(not(any(usart_v1, usart_v2)))] /// Create a new bidirectional UART with a driver-enable pin - pub fn new_with_de( + pub fn new_with_de( peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - tx: Peri<'d, impl TxPin>, + rx: Peri<'d, if_afio!(impl RxPin)>, + tx: Peri<'d, if_afio!(impl TxPin)>, _irq: impl interrupt::typelevel::Binding> + 'd, - de: Peri<'d, impl DePin>, + de: Peri<'d, if_afio!(impl DePin)>, tx_dma: Peri<'d, impl TxDma>, rx_dma: Peri<'d, impl RxDma>, config: Config, @@ -1193,9 +1193,9 @@ impl<'d> Uart<'d, Async> { /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict /// on the line must be managed by software (for instance by using a centralized arbiter). #[doc(alias("HDSEL"))] - pub fn new_half_duplex( + pub fn new_half_duplex( peri: Peri<'d, T>, - tx: Peri<'d, impl TxPin>, + tx: Peri<'d, if_afio!(impl TxPin)>, _irq: impl interrupt::typelevel::Binding> + 'd, tx_dma: Peri<'d, impl TxDma>, rx_dma: Peri<'d, impl RxDma>, @@ -1232,9 +1232,9 @@ impl<'d> Uart<'d, Async> { /// on the line must be managed by software (for instance by using a centralized arbiter). #[cfg(not(any(usart_v1, usart_v2)))] #[doc(alias("HDSEL"))] - pub fn new_half_duplex_on_rx( + pub fn new_half_duplex_on_rx( peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, + rx: Peri<'d, if_afio!(impl RxPin)>, _irq: impl interrupt::typelevel::Binding> + 'd, tx_dma: Peri<'d, impl TxDma>, rx_dma: Peri<'d, impl RxDma>, @@ -1280,10 +1280,10 @@ impl<'d> Uart<'d, Async> { impl<'d> Uart<'d, Blocking> { /// Create a new blocking bidirectional UART. - pub fn new_blocking( + pub fn new_blocking( peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - tx: Peri<'d, impl TxPin>, + rx: Peri<'d, if_afio!(impl RxPin)>, + tx: Peri<'d, if_afio!(impl TxPin)>, config: Config, ) -> Result { Self::new_inner( @@ -1300,12 +1300,12 @@ impl<'d> Uart<'d, Blocking> { } /// Create a new bidirectional UART with request-to-send and clear-to-send pins - pub fn new_blocking_with_rtscts( + pub fn new_blocking_with_rtscts( peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - tx: Peri<'d, impl TxPin>, - rts: Peri<'d, impl RtsPin>, - cts: Peri<'d, impl CtsPin>, + rx: Peri<'d, if_afio!(impl RxPin)>, + tx: Peri<'d, if_afio!(impl TxPin)>, + rts: Peri<'d, if_afio!(impl RtsPin)>, + cts: Peri<'d, if_afio!(impl CtsPin)>, config: Config, ) -> Result { Self::new_inner( @@ -1323,11 +1323,11 @@ impl<'d> Uart<'d, Blocking> { #[cfg(not(any(usart_v1, usart_v2)))] /// Create a new bidirectional UART with a driver-enable pin - pub fn new_blocking_with_de( + pub fn new_blocking_with_de( peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, - tx: Peri<'d, impl TxPin>, - de: Peri<'d, impl DePin>, + rx: Peri<'d, if_afio!(impl RxPin)>, + tx: Peri<'d, if_afio!(impl TxPin)>, + de: Peri<'d, if_afio!(impl DePin)>, config: Config, ) -> Result { Self::new_inner( @@ -1354,9 +1354,9 @@ impl<'d> Uart<'d, Blocking> { /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict /// on the line must be managed by software (for instance by using a centralized arbiter). #[doc(alias("HDSEL"))] - pub fn new_blocking_half_duplex( + pub fn new_blocking_half_duplex( peri: Peri<'d, T>, - tx: Peri<'d, impl TxPin>, + tx: Peri<'d, if_afio!(impl TxPin)>, mut config: Config, readback: HalfDuplexReadback, ) -> Result { @@ -1390,9 +1390,9 @@ impl<'d> Uart<'d, Blocking> { /// on the line must be managed by software (for instance by using a centralized arbiter). #[cfg(not(any(usart_v1, usart_v2)))] #[doc(alias("HDSEL"))] - pub fn new_blocking_half_duplex_on_rx( + pub fn new_blocking_half_duplex_on_rx( peri: Peri<'d, T>, - rx: Peri<'d, impl RxPin>, + rx: Peri<'d, if_afio!(impl RxPin)>, mut config: Config, readback: HalfDuplexReadback, ) -> Result { @@ -2055,12 +2055,12 @@ pub trait Instance: SealedInstance + PeripheralType + 'static + Send { type Interrupt: interrupt::typelevel::Interrupt; } -pin_trait!(RxPin, Instance); -pin_trait!(TxPin, Instance); -pin_trait!(CtsPin, Instance); -pin_trait!(RtsPin, Instance); -pin_trait!(CkPin, Instance); -pin_trait!(DePin, Instance); +pin_trait!(RxPin, Instance, @A); +pin_trait!(TxPin, Instance, @A); +pin_trait!(CtsPin, Instance, @A); +pin_trait!(RtsPin, Instance, @A); +pin_trait!(CkPin, Instance, @A); +pin_trait!(DePin, Instance, @A); dma_trait!(TxDma, Instance); dma_trait!(RxDma, Instance); diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index 1c3b99b93..5ce81b131 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -34,7 +34,7 @@ macro_rules! config_ulpi_pins { ($($pin:ident),*) => { critical_section::with(|_| { $( - $pin.set_as_af($pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); + set_as_af!($pin, AfType::output(OutputType::PushPull, Speed::VeryHigh)); )* }) }; @@ -68,8 +68,8 @@ impl<'d, T: Instance> Driver<'d, T> { ep_out_buffer: &'d mut [u8], config: Config, ) -> Self { - dp.set_as_af(dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); - dm.set_as_af(dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); + set_as_af!(dp, AfType::output(OutputType::PushPull, Speed::VeryHigh)); + set_as_af!(dm, AfType::output(OutputType::PushPull, Speed::VeryHigh)); let regs = T::regs(); @@ -107,8 +107,8 @@ impl<'d, T: Instance> Driver<'d, T> { // For STM32U5 High speed pins need to be left in analog mode #[cfg(not(any(all(stm32u5, peri_usb_otg_hs), all(stm32wba, peri_usb_otg_hs))))] { - _dp.set_as_af(_dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); - _dm.set_as_af(_dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); + set_as_af!(_dp, AfType::output(OutputType::PushPull, Speed::VeryHigh)); + set_as_af!(_dm, AfType::output(OutputType::PushPull, Speed::VeryHigh)); } let instance = OtgInstance { diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 54596aeae..9e08d99b3 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -298,7 +298,7 @@ impl<'d, T: Instance> Driver<'d, T> { ) -> Self { { use crate::gpio::{AfType, OutputType, Speed}; - sof.set_as_af(sof.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); + set_as_af!(sof, AfType::output(OutputType::PushPull, Speed::VeryHigh)); } Self::new(_usb, _irq, dp, dm) @@ -329,8 +329,8 @@ impl<'d, T: Instance> Driver<'d, T> { #[cfg(not(stm32l1))] { use crate::gpio::{AfType, OutputType, Speed}; - dp.set_as_af(dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); - dm.set_as_af(dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); + set_as_af!(dp, AfType::output(OutputType::PushPull, Speed::VeryHigh)); + set_as_af!(dm, AfType::output(OutputType::PushPull, Speed::VeryHigh)); } #[cfg(stm32l1)] let _ = (dp, dm); // suppress "unused" warnings. diff --git a/examples/stm32f1/src/bin/input_capture.rs b/examples/stm32f1/src/bin/input_capture.rs index d747a43c2..b5b26938d 100644 --- a/examples/stm32f1/src/bin/input_capture.rs +++ b/examples/stm32f1/src/bin/input_capture.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::gpio::{AfioRemap, Level, Output, Pull, Speed}; use embassy_stm32::time::khz; use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; use embassy_stm32::timer::{self, Channel}; @@ -40,7 +40,8 @@ async fn main(spawner: Spawner) { spawner.spawn(unwrap!(blinky(p.PC13))); let ch3 = CapturePin::new(p.PA2, Pull::None); - let mut ic = InputCapture::new(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); + let mut ic = + InputCapture::new::>(p.TIM2, None, None, Some(ch3), None, Irqs, khz(1000), Default::default()); loop { info!("wait for rising edge"); diff --git a/examples/stm32f1/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/pwm_input.rs index 63b899767..9ae747018 100644 --- a/examples/stm32f1/src/bin/pwm_input.rs +++ b/examples/stm32f1/src/bin/pwm_input.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::gpio::{AfioRemap, Level, Output, Pull, Speed}; use embassy_stm32::time::khz; use embassy_stm32::timer::pwm_input::PwmInput; use embassy_stm32::{bind_interrupts, peripherals, timer, Peri}; @@ -38,7 +38,7 @@ async fn main(spawner: Spawner) { spawner.spawn(unwrap!(blinky(p.PC13))); - let mut pwm_input = PwmInput::new_ch1(p.TIM2, p.PA0, Pull::None, khz(10)); + let mut pwm_input = PwmInput::new_ch1::>(p.TIM2, p.PA0, Pull::None, khz(10)); pwm_input.enable(); loop { diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index aeca67659..891ec93fd 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -9,7 +9,9 @@ publish = false [features] stm32c031c6 = ["embassy-stm32/stm32c031c6", "cm0", "not-gpdma"] stm32c071rb = ["embassy-stm32/stm32c071rb", "cm0", "not-gpdma"] -stm32f103c8 = ["embassy-stm32/stm32f103c8", "spi-v1", "not-gpdma"] +stm32f100rd = ["embassy-stm32/stm32f100rd", "spi-v1", "not-gpdma", "afio", "afio-value-line"] +stm32f103c8 = ["embassy-stm32/stm32f103c8", "spi-v1", "not-gpdma", "afio"] +stm32f107vc = ["embassy-stm32/stm32f107vc", "spi-v1", "not-gpdma", "afio", "afio-connectivity-line"] stm32f207zg = ["embassy-stm32/stm32f207zg", "spi-v1", "chrono", "not-gpdma", "eth", "rng"] stm32f303ze = ["embassy-stm32/stm32f303ze", "chrono", "not-gpdma"] stm32f429zi = ["embassy-stm32/stm32f429zi", "spi-v1", "chrono", "eth", "stop", "can", "not-gpdma", "dac", "rng"] @@ -59,6 +61,9 @@ cordic = ["dep:num-traits"] dual-bank = ["embassy-stm32/dual-bank"] single-bank = ["embassy-stm32/single-bank"] eeprom = [] +afio = [] +afio-connectivity-line = [] +afio-value-line = [] cm0 = ["portable-atomic/unsafe-assume-single-core"] @@ -98,6 +103,11 @@ num-traits = { version="0.2", default-features = false,features = ["libm"], opti # BEGIN TESTS # Generated by gen_test.py. DO NOT EDIT. +[[bin]] +name = "afio" +path = "src/bin/afio.rs" +required-features = [ "afio",] + [[bin]] name = "can" path = "src/bin/can.rs" diff --git a/tests/stm32/src/bin/afio.rs b/tests/stm32/src/bin/afio.rs new file mode 100644 index 000000000..cc44dc59c --- /dev/null +++ b/tests/stm32/src/bin/afio.rs @@ -0,0 +1,1156 @@ +// required-features: afio +#![no_std] +#![no_main] +#[path = "../common.rs"] +mod common; + +use common::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{AfioRemap, OutputType, Pull}; +use embassy_stm32::pac::AFIO; +use embassy_stm32::time::khz; +use embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin}; +use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; +use embassy_stm32::timer::pwm_input::PwmInput; +use embassy_stm32::timer::qei::{Qei, QeiPin}; +use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; +use embassy_stm32::usart::{Uart, UartRx, UartTx}; +use embassy_stm32::{bind_interrupts, Peripherals}; + +#[cfg(not(feature = "afio-connectivity-line"))] +bind_interrupts!(struct Irqs { + USART3 => embassy_stm32::usart::InterruptHandler; + TIM1_CC => embassy_stm32::timer::CaptureCompareInterruptHandler; +}); + +#[cfg(feature = "afio-connectivity-line")] +bind_interrupts!(struct Irqs { + CAN1_RX0 => embassy_stm32::can::Rx0InterruptHandler; + CAN1_RX1 => embassy_stm32::can::Rx1InterruptHandler; + CAN1_SCE => embassy_stm32::can::SceInterruptHandler; + CAN1_TX => embassy_stm32::can::TxInterruptHandler; + + CAN2_RX0 => embassy_stm32::can::Rx0InterruptHandler; + CAN2_RX1 => embassy_stm32::can::Rx1InterruptHandler; + CAN2_SCE => embassy_stm32::can::SceInterruptHandler; + CAN2_TX => embassy_stm32::can::TxInterruptHandler; + + ETH => embassy_stm32::eth::InterruptHandler; + USART3 => embassy_stm32::usart::InterruptHandler; + TIM1_CC => embassy_stm32::timer::CaptureCompareInterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut p = init(); + info!("Hello World!"); + + // USART3 + { + // no remap RX/TX/RTS/CTS + afio_registers_set_remap(); + Uart::new_blocking_with_rtscts( + p.USART3.reborrow(), + p.PB11.reborrow(), + p.PB10.reborrow(), + p.PB14.reborrow(), + p.PB13.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0); + } + { + // no remap RX/TX + afio_registers_set_remap(); + Uart::new_blocking( + p.USART3.reborrow(), + p.PB11.reborrow(), + p.PB10.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0); + } + { + // no remap TX + afio_registers_set_remap(); + Uart::new_blocking_half_duplex( + p.USART3.reborrow(), + p.PB10.reborrow(), + Default::default(), + embassy_stm32::usart::HalfDuplexReadback::NoReadback, + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0); + } + { + // no remap TX async + afio_registers_set_remap(); + UartTx::new( + p.USART3.reborrow(), + p.PB10.reborrow(), + p.DMA1_CH2.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0); + } + { + // no remap TX/CTS async + afio_registers_set_remap(); + UartTx::new_with_cts( + p.USART3.reborrow(), + p.PB10.reborrow(), + p.PB13.reborrow(), + p.DMA1_CH2.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0); + } + { + // no remap RX async + afio_registers_set_remap(); + UartRx::new( + p.USART3.reborrow(), + Irqs, + p.PB11.reborrow(), + p.DMA1_CH3.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0); + } + { + // no remap RX async + afio_registers_set_remap(); + UartRx::new_with_rts( + p.USART3.reborrow(), + Irqs, + p.PB11.reborrow(), + p.PB14.reborrow(), + p.DMA1_CH3.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0); + } + { + // no remap RX/TX async + afio_registers_set_remap(); + Uart::new( + p.USART3.reborrow(), + p.PB11.reborrow(), + p.PB10.reborrow(), + Irqs, + p.DMA1_CH2.reborrow(), + p.DMA1_CH3.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0); + } + { + // no remap RX/TX/RTS/CTS async + afio_registers_set_remap(); + Uart::new_with_rtscts( + p.USART3.reborrow(), + p.PB11.reborrow(), + p.PB10.reborrow(), + Irqs, + p.PB14.reborrow(), + p.PB13.reborrow(), + p.DMA1_CH2.reborrow(), + p.DMA1_CH3.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 0); + } + + // TIM1 + { + // no remap + afio_registers_set_remap(); + SimplePwm::new::>( + p.TIM1.reborrow(), + Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)), + Some(PwmPin::new(p.PA9.reborrow(), OutputType::PushPull)), + Some(PwmPin::new(p.PA10.reborrow(), OutputType::PushPull)), + Some(PwmPin::new(p.PA11.reborrow(), OutputType::PushPull)), + khz(10), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 0); + } + { + // no remap + afio_registers_set_remap(); + SimplePwm::new::>( + p.TIM1.reborrow(), + Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)), + None, + None, + None, + khz(10), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 0); + } + { + // partial remap + reset_afio_registers(); + ComplementaryPwm::new::>( + p.TIM1.reborrow(), + Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)), + None, + None, + None, + None, + None, + None, + None, + khz(10), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1); + } + { + // partial remap + reset_afio_registers(); + ComplementaryPwm::new::>( + p.TIM1.reborrow(), + Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)), + Some(ComplementaryPwmPin::new(p.PA7.reborrow(), OutputType::PushPull)), + Some(PwmPin::new(p.PA9.reborrow(), OutputType::PushPull)), + Some(ComplementaryPwmPin::new(p.PB0.reborrow(), OutputType::PushPull)), + Some(PwmPin::new(p.PA10.reborrow(), OutputType::PushPull)), + None, // pin does not exist on medium-density devices + Some(PwmPin::new(p.PA11.reborrow(), OutputType::PushPull)), + None, // signal does not exist + khz(10), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1); + } + { + // partial remap + reset_afio_registers(); + InputCapture::new::>( + p.TIM1.reborrow(), + Some(CapturePin::new(p.PA8.reborrow(), Pull::Down)), + None, + None, + None, + Irqs, + khz(10), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1); + } + { + // partial remap + reset_afio_registers(); + PwmInput::new_ch1::>(p.TIM1.reborrow(), p.PA8.reborrow(), Pull::Down, khz(10)); + defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1); + } + { + // partial remap + reset_afio_registers(); + Qei::new::>( + p.TIM1.reborrow(), + QeiPin::new(p.PA8.reborrow()), + QeiPin::new(p.PA9.reborrow()), + ); + defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 1); + } + + // TIM2 + { + // no remap + afio_registers_set_remap(); + SimplePwm::new::>( + p.TIM2.reborrow(), + Some(PwmPin::new(p.PA0.reborrow(), OutputType::PushPull)), + Some(PwmPin::new(p.PA1.reborrow(), OutputType::PushPull)), + Some(PwmPin::new(p.PA2.reborrow(), OutputType::PushPull)), + Some(PwmPin::new(p.PA3.reborrow(), OutputType::PushPull)), + khz(10), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().tim2_remap(), 0); + } + { + // partial remap 1 + reset_afio_registers(); + SimplePwm::new::>( + p.TIM2.reborrow(), + Some(PwmPin::new(p.PA15.reborrow(), OutputType::PushPull)), + Some(PwmPin::new(p.PB3.reborrow(), OutputType::PushPull)), + Some(PwmPin::new(p.PA2.reborrow(), OutputType::PushPull)), + Some(PwmPin::new(p.PA3.reborrow(), OutputType::PushPull)), + khz(10), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().tim2_remap(), 1); + } + { + // partial remap 2 + reset_afio_registers(); + SimplePwm::new::>( + p.TIM2.reborrow(), + Some(PwmPin::new(p.PA0.reborrow(), OutputType::PushPull)), + Some(PwmPin::new(p.PA1.reborrow(), OutputType::PushPull)), + Some(PwmPin::new(p.PB10.reborrow(), OutputType::PushPull)), + Some(PwmPin::new(p.PB11.reborrow(), OutputType::PushPull)), + khz(10), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().tim2_remap(), 2); + } + { + // full remap + reset_afio_registers(); + SimplePwm::new::>( + p.TIM2.reborrow(), + Some(PwmPin::new(p.PA15.reborrow(), OutputType::PushPull)), + Some(PwmPin::new(p.PB3.reborrow(), OutputType::PushPull)), + Some(PwmPin::new(p.PB10.reborrow(), OutputType::PushPull)), + Some(PwmPin::new(p.PB11.reborrow(), OutputType::PushPull)), + khz(10), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().tim2_remap(), 3); + } + + connectivity_line::run(&mut p); + value_line::run(&mut p); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +#[cfg(feature = "afio-connectivity-line")] +mod connectivity_line { + use embassy_stm32::can::Can; + use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; + use embassy_stm32::i2s::I2S; + use embassy_stm32::spi::Spi; + + use super::*; + + pub fn run(p: &mut Peripherals) { + // USART3 + { + // partial remap RX/TX/RTS/CTS + reset_afio_registers(); + Uart::new_blocking_with_rtscts( + p.USART3.reborrow(), + p.PC11.reborrow(), + p.PC10.reborrow(), + p.PB14.reborrow(), + p.PB13.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1); + } + { + // partial remap RX/TX + reset_afio_registers(); + Uart::new_blocking( + p.USART3.reborrow(), + p.PC11.reborrow(), + p.PC10.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1); + } + { + // partial remap TX + reset_afio_registers(); + Uart::new_blocking_half_duplex( + p.USART3.reborrow(), + p.PC10.reborrow(), + Default::default(), + embassy_stm32::usart::HalfDuplexReadback::NoReadback, + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1); + } + { + // partial remap TX async + reset_afio_registers(); + UartTx::new( + p.USART3.reborrow(), + p.PC10.reborrow(), + p.DMA1_CH2.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1); + } + { + // partial remap TX/CTS async + reset_afio_registers(); + UartTx::new_with_cts( + p.USART3.reborrow(), + p.PC10.reborrow(), + p.PB13.reborrow(), + p.DMA1_CH2.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1); + } + { + // partial remap RX async + reset_afio_registers(); + UartRx::new( + p.USART3.reborrow(), + Irqs, + p.PC11.reborrow(), + p.DMA1_CH3.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1); + } + { + // partial remap RX async + reset_afio_registers(); + UartRx::new_with_rts( + p.USART3.reborrow(), + Irqs, + p.PC11.reborrow(), + p.PB14.reborrow(), + p.DMA1_CH3.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1); + } + { + // partial remap RX/TX async + reset_afio_registers(); + Uart::new( + p.USART3.reborrow(), + p.PC11.reborrow(), + p.PC10.reborrow(), + Irqs, + p.DMA1_CH2.reborrow(), + p.DMA1_CH3.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1); + } + { + // partial remap RX/TX/RTS/CTS async + reset_afio_registers(); + Uart::new_with_rtscts( + p.USART3.reborrow(), + p.PC11.reborrow(), + p.PC10.reborrow(), + Irqs, + p.PB14.reborrow(), + p.PB13.reborrow(), + p.DMA1_CH2.reborrow(), + p.DMA1_CH3.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 1); + } + { + // full remap RX/TX/RTS/CTS + reset_afio_registers(); + Uart::new_blocking_with_rtscts( + p.USART3.reborrow(), + p.PD9.reborrow(), + p.PD8.reborrow(), + p.PD12.reborrow(), + p.PD11.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3); + } + { + // full remap RX/TX + reset_afio_registers(); + Uart::new_blocking( + p.USART3.reborrow(), + p.PD9.reborrow(), + p.PD8.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3); + } + { + // full remap TX + reset_afio_registers(); + Uart::new_blocking_half_duplex( + p.USART3.reborrow(), + p.PD8.reborrow(), + Default::default(), + embassy_stm32::usart::HalfDuplexReadback::NoReadback, + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3); + } + { + // full remap TX async + reset_afio_registers(); + UartTx::new( + p.USART3.reborrow(), + p.PD8.reborrow(), + p.DMA1_CH2.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3); + } + { + // full remap TX/CTS async + reset_afio_registers(); + UartTx::new_with_cts( + p.USART3.reborrow(), + p.PD8.reborrow(), + p.PD11.reborrow(), + p.DMA1_CH2.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3); + } + { + // full remap RX async + reset_afio_registers(); + UartRx::new( + p.USART3.reborrow(), + Irqs, + p.PD9.reborrow(), + p.DMA1_CH3.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3); + } + { + // full remap RX async + reset_afio_registers(); + UartRx::new_with_rts( + p.USART3.reborrow(), + Irqs, + p.PD9.reborrow(), + p.PD12.reborrow(), + p.DMA1_CH3.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3); + } + { + // full remap RX/TX async + reset_afio_registers(); + Uart::new( + p.USART3.reborrow(), + p.PD9.reborrow(), + p.PD8.reborrow(), + Irqs, + p.DMA1_CH2.reborrow(), + p.DMA1_CH3.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3); + } + { + // full remap RX/TX/RTS/CTS async + reset_afio_registers(); + Uart::new_with_rtscts( + p.USART3.reborrow(), + p.PD9.reborrow(), + p.PD8.reborrow(), + Irqs, + p.PD12.reborrow(), + p.PD11.reborrow(), + p.DMA1_CH2.reborrow(), + p.DMA1_CH3.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart3_remap(), 3); + } + + // SPI3 + { + // no remap SCK/MISO/MOSI + afio_registers_set_remap(); + Spi::new_blocking( + p.SPI3.reborrow(), + p.PB3.reborrow(), + p.PB5.reborrow(), + p.PB4.reborrow(), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false); + } + { + // no remap SCK/MOSI + afio_registers_set_remap(); + Spi::new_blocking_txonly( + p.SPI3.reborrow(), + p.PB3.reborrow(), + p.PB5.reborrow(), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false); + } + { + // no remap MOSI + afio_registers_set_remap(); + Spi::new_blocking_txonly_nosck(p.SPI3.reborrow(), p.PB5.reborrow(), Default::default()); + defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false); + } + { + // no remap SCK/MISO + afio_registers_set_remap(); + Spi::new_blocking_rxonly( + p.SPI3.reborrow(), + p.PB3.reborrow(), + p.PB4.reborrow(), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false); + } + { + // remap SCK/MISO/MOSI + reset_afio_registers(); + Spi::new_blocking( + p.SPI3.reborrow(), + p.PC10.reborrow(), + p.PC12.reborrow(), + p.PC11.reborrow(), + Default::default(), + ); + + defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true); + } + { + // remap SCK/MOSI + reset_afio_registers(); + Spi::new_blocking_txonly( + p.SPI3.reborrow(), + p.PC10.reborrow(), + p.PC12.reborrow(), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true); + } + { + // remap MOSI + reset_afio_registers(); + Spi::new_blocking_txonly_nosck(p.SPI3.reborrow(), p.PB5.reborrow(), Default::default()); + defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true); + } + { + // remap SCK/MISO + reset_afio_registers(); + Spi::new_blocking_rxonly( + p.SPI3.reborrow(), + p.PC10.reborrow(), + p.PC11.reborrow(), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true); + } + + // I2S3 + { + // no remap SD/WS/CK/MCK + afio_registers_set_remap(); + I2S::new_txonly( + p.SPI3.reborrow(), + p.PB5.reborrow(), + p.PA15.reborrow(), + p.PB3.reborrow(), + p.PC7.reborrow(), + p.DMA2_CH2.reborrow(), + &mut [0u16; 0], + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false); + } + { + // no remap SD/WS/CK + afio_registers_set_remap(); + I2S::new_txonly_nomck( + p.SPI3.reborrow(), + p.PB5.reborrow(), + p.PA15.reborrow(), + p.PB3.reborrow(), + p.DMA2_CH2.reborrow(), + &mut [0u16; 0], + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), false); + } + { + // no remap SD/WS/CK/MCK + afio_registers_set_remap(); + I2S::new_rxonly( + p.SPI3.reborrow(), + p.PB4.reborrow(), + p.PA15.reborrow(), + p.PB3.reborrow(), + p.PC7.reborrow(), + p.DMA2_CH1.reborrow(), + &mut [0u16; 0], + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true); + } + { + // remap SD/WS/CK/MCK + reset_afio_registers(); + I2S::new_txonly( + p.SPI3.reborrow(), + p.PC12.reborrow(), + p.PA4.reborrow(), + p.PC10.reborrow(), + p.PC7.reborrow(), + p.DMA2_CH2.reborrow(), + &mut [0u16; 0], + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true); + } + { + // remap SD/WS/CK + reset_afio_registers(); + I2S::new_txonly_nomck( + p.SPI3.reborrow(), + p.PC12.reborrow(), + p.PA4.reborrow(), + p.PC10.reborrow(), + p.DMA2_CH2.reborrow(), + &mut [0u16; 0], + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true); + } + { + // remap SD/WS/CK/MCK + reset_afio_registers(); + I2S::new_rxonly( + p.SPI3.reborrow(), + p.PC11.reborrow(), + p.PA4.reborrow(), + p.PC10.reborrow(), + p.PC7.reborrow(), + p.DMA2_CH1.reborrow(), + &mut [0u16; 0], + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().spi3_remap(), true); + } + + // CAN2 + { + // no remap + afio_registers_set_remap(); + Can::new(p.CAN2.reborrow(), p.PB12.reborrow(), p.PB13.reborrow(), Irqs); + defmt::assert_eq!(AFIO.mapr().read().can2_remap(), false); + } + { + // remap + reset_afio_registers(); + Can::new(p.CAN2.reborrow(), p.PB5.reborrow(), p.PB6.reborrow(), Irqs); + defmt::assert_eq!(AFIO.mapr().read().can2_remap(), true); + } + + // Ethernet + { + // no remap RMII + afio_registers_set_remap(); + Ethernet::new( + &mut PacketQueue::<1, 1>::new(), + p.ETH.reborrow(), + Irqs, + p.PA1.reborrow(), + p.PA2.reborrow(), + p.PC1.reborrow(), + p.PA7.reborrow(), + p.PC4.reborrow(), + p.PC5.reborrow(), + p.PB12.reborrow(), + p.PB13.reborrow(), + p.PB11.reborrow(), + GenericPhy::new_auto(), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().eth_remap(), false); + } + { + // no remap MII + afio_registers_set_remap(); + Ethernet::new_mii( + &mut PacketQueue::<1, 1>::new(), + p.ETH.reborrow(), + Irqs, + p.PA1.reborrow(), + p.PC3.reborrow(), + p.PA2.reborrow(), + p.PC1.reborrow(), + p.PA7.reborrow(), + p.PC4.reborrow(), + p.PC5.reborrow(), + p.PB0.reborrow(), + p.PB1.reborrow(), + p.PB12.reborrow(), + p.PB13.reborrow(), + p.PC2.reborrow(), + p.PB8.reborrow(), + p.PB11.reborrow(), + GenericPhy::new_auto(), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().eth_remap(), false); + } + { + // remap RMII + reset_afio_registers(); + Ethernet::new( + &mut PacketQueue::<1, 1>::new(), + p.ETH.reborrow(), + Irqs, + p.PA1.reborrow(), + p.PA2.reborrow(), + p.PC1.reborrow(), + p.PD8.reborrow(), + p.PD9.reborrow(), + p.PD10.reborrow(), + p.PB12.reborrow(), + p.PB13.reborrow(), + p.PB11.reborrow(), + GenericPhy::new_auto(), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().eth_remap(), true); + } + { + // remap MII + reset_afio_registers(); + Ethernet::new_mii( + &mut PacketQueue::<1, 1>::new(), + p.ETH.reborrow(), + Irqs, + p.PA1.reborrow(), + p.PC3.reborrow(), + p.PA2.reborrow(), + p.PC1.reborrow(), + p.PD8.reborrow(), + p.PD9.reborrow(), + p.PD10.reborrow(), + p.PD11.reborrow(), + p.PD12.reborrow(), + p.PB12.reborrow(), + p.PB13.reborrow(), + p.PC2.reborrow(), + p.PB8.reborrow(), + p.PB11.reborrow(), + GenericPhy::new_auto(), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().eth_remap(), true); + } + + // CAN1 + { + // no remap + afio_registers_set_remap(); + Can::new(p.CAN1.reborrow(), p.PA11.reborrow(), p.PA12.reborrow(), Irqs); + defmt::assert_eq!(AFIO.mapr().read().can1_remap(), 0); + } + { + // partial remap + reset_afio_registers(); + Can::new(p.CAN1.reborrow(), p.PB8.reborrow(), p.PB9.reborrow(), Irqs); + defmt::assert_eq!(AFIO.mapr().read().can1_remap(), 2); + } + { + // full remap + reset_afio_registers(); + Can::new(p.CAN1.reborrow(), p.PD0.reborrow(), p.PD1.reborrow(), Irqs); + defmt::assert_eq!(AFIO.mapr().read().can1_remap(), 3); + } + + // USART2 + { + // no remap RX/TX/RTS/CTS + afio_registers_set_remap(); + Uart::new_blocking_with_rtscts( + p.USART2.reborrow(), + p.PA3.reborrow(), + p.PA2.reborrow(), + p.PA1.reborrow(), + p.PA0.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false); + } + { + // no remap RX/TX + afio_registers_set_remap(); + Uart::new_blocking( + p.USART2.reborrow(), + p.PA3.reborrow(), + p.PA2.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false); + } + { + // no remap TX + afio_registers_set_remap(); + Uart::new_blocking_half_duplex( + p.USART2.reborrow(), + p.PA2.reborrow(), + Default::default(), + embassy_stm32::usart::HalfDuplexReadback::NoReadback, + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false); + } + { + // full remap RX/TX/RTS/CTS + reset_afio_registers(); + Uart::new_blocking_with_rtscts( + p.USART2.reborrow(), + p.PD6.reborrow(), + p.PD5.reborrow(), + p.PD4.reborrow(), + p.PD3.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false); + } + { + // full remap RX/TX + reset_afio_registers(); + Uart::new_blocking( + p.USART2.reborrow(), + p.PD6.reborrow(), + p.PD5.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), false); + } + { + // full remap TX + reset_afio_registers(); + Uart::new_blocking_half_duplex( + p.USART2.reborrow(), + p.PD5.reborrow(), + Default::default(), + embassy_stm32::usart::HalfDuplexReadback::NoReadback, + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart2_remap(), true); + } + + // USART1 + { + // no remap RX/TX/RTS/CTS + afio_registers_set_remap(); + Uart::new_blocking_with_rtscts( + p.USART1.reborrow(), + p.PA10.reborrow(), + p.PA9.reborrow(), + p.PA12.reborrow(), + p.PA11.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), false); + } + { + // no remap RX/TX + afio_registers_set_remap(); + Uart::new_blocking( + p.USART1.reborrow(), + p.PA10.reborrow(), + p.PA9.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), false); + } + { + // no remap TX + afio_registers_set_remap(); + Uart::new_blocking_half_duplex( + p.USART1.reborrow(), + p.PA9.reborrow(), + Default::default(), + embassy_stm32::usart::HalfDuplexReadback::NoReadback, + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), false); + } + { + // remap RX/TX/RTS/CTS + reset_afio_registers(); + Uart::new_blocking_with_rtscts( + p.USART1.reborrow(), + p.PB7.reborrow(), + p.PB6.reborrow(), + p.PA12.reborrow(), + p.PA11.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), true); + } + { + // remap RX/TX + reset_afio_registers(); + Uart::new_blocking( + p.USART1.reborrow(), + p.PB7.reborrow(), + p.PB6.reborrow(), + Default::default(), + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), true); + } + { + // remap TX + reset_afio_registers(); + Uart::new_blocking_half_duplex( + p.USART1.reborrow(), + p.PB6.reborrow(), + Default::default(), + embassy_stm32::usart::HalfDuplexReadback::NoReadback, + ) + .unwrap(); + defmt::assert_eq!(AFIO.mapr().read().usart1_remap(), true); + } + + // TIM1 + { + // full remap + reset_afio_registers(); + SimplePwm::new( + p.TIM1.reborrow(), + Some(PwmPin::new(p.PE9.reborrow(), OutputType::PushPull)), + Some(PwmPin::new(p.PE11.reborrow(), OutputType::PushPull)), + None, + None, + khz(10), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr().read().tim1_remap(), 3); + } + } +} + +#[cfg(feature = "afio-value-line")] +mod value_line { + use super::*; + + pub fn run(p: &mut Peripherals) { + // TIM13 + { + // no remap + reset_afio_registers(); + SimplePwm::new( + p.TIM13.reborrow(), + Some(PwmPin::new(p.PC8.reborrow(), OutputType::PushPull)), + None, + None, + None, + khz(10), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr2().read().tim13_remap(), false); + } + { + // remap + reset_afio_registers(); + SimplePwm::new( + p.TIM13.reborrow(), + Some(PwmPin::new(p.PB0.reborrow(), OutputType::PushPull)), + None, + None, + None, + khz(10), + Default::default(), + ); + defmt::assert_eq!(AFIO.mapr2().read().tim13_remap(), true); + } + } +} + +#[cfg(not(feature = "afio-connectivity-line"))] +mod connectivity_line { + use super::*; + + pub fn run(_: &mut Peripherals) {} +} + +#[cfg(not(feature = "afio-value-line"))] +mod value_line { + use super::*; + + pub fn run(_: &mut Peripherals) {} +} + +fn reset_afio_registers() { + set_afio_registers(false, 0); +} + +fn afio_registers_set_remap() { + set_afio_registers(true, 1); +} + +fn set_afio_registers(bool_val: bool, num_val: u8) { + AFIO.mapr().modify(|w| { + w.set_swj_cfg(embassy_stm32::pac::afio::vals::SwjCfg::NO_OP); + w.set_can1_remap(num_val); + w.set_can2_remap(bool_val); + w.set_eth_remap(bool_val); + w.set_i2c1_remap(bool_val); + w.set_spi1_remap(bool_val); + w.set_spi3_remap(bool_val); + w.set_tim1_remap(num_val); + w.set_tim2_remap(num_val); + w.set_tim3_remap(num_val); + w.set_tim4_remap(bool_val); + w.set_usart1_remap(bool_val); + w.set_usart2_remap(bool_val); + w.set_usart3_remap(num_val); + }); + + AFIO.mapr2().modify(|w| { + w.set_cec_remap(bool_val); + w.set_tim9_remap(bool_val); + w.set_tim10_remap(bool_val); + w.set_tim11_remap(bool_val); + w.set_tim12_remap(bool_val); + w.set_tim13_remap(bool_val); + w.set_tim14_remap(bool_val); + w.set_tim15_remap(bool_val); + w.set_tim16_remap(bool_val); + w.set_tim17_remap(bool_val); + }); +} diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index cb63b3374..f800769ab 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -103,7 +103,7 @@ define_peris!( SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler;}, ); -#[cfg(feature = "stm32f103c8")] +#[cfg(any(feature = "stm32f100rd", feature = "stm32f103c8", feature = "stm32f107vc"))] define_peris!( UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5, SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2,