From a6562c4f033432e40970aafe82f33c5138adf84e Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Mon, 21 Jul 2025 07:57:49 +0200 Subject: [PATCH 1/5] Add STM32F1 AFIO remap --- ci.sh | 6 +- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/build.rs | 125 +- embassy-stm32/src/can/bxcan/mod.rs | 12 +- embassy-stm32/src/eth/mod.rs | 32 +- embassy-stm32/src/eth/v1/mod.rs | 54 +- embassy-stm32/src/eth/v2/mod.rs | 50 +- embassy-stm32/src/gpio.rs | 11 + embassy-stm32/src/i2c/mod.rs | 16 +- embassy-stm32/src/i2s.rs | 46 +- embassy-stm32/src/macros.rs | 63 +- embassy-stm32/src/spi/mod.rs | 62 +- embassy-stm32/src/timer/complementary_pwm.rs | 34 +- embassy-stm32/src/timer/input_capture.rs | 21 +- embassy-stm32/src/timer/mod.rs | 31 +- embassy-stm32/src/timer/one_pulse.rs | 10 +- embassy-stm32/src/timer/pwm_input.rs | 4 + embassy-stm32/src/timer/qei.rs | 12 +- embassy-stm32/src/timer/simple_pwm.rs | 23 +- embassy-stm32/src/usart/buffered.rs | 48 +- embassy-stm32/src/usart/mod.rs | 116 +- tests/stm32/Cargo.toml | 12 +- tests/stm32/src/bin/afio.rs | 1156 ++++++++++++++++++ tests/stm32/src/common.rs | 2 +- 24 files changed, 1679 insertions(+), 268 deletions(-) create mode 100644 tests/stm32/src/bin/afio.rs 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 a473834dd..db0c1e89c 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -32,6 +32,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..f55e1e0c9 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1391,9 +1391,60 @@ fn main() { }) } - g.extend(quote! { - pin_trait_impl!(#tr, #peri, #pin_name, #af); - }) + let pin_trait_impl = p + .afio + .as_ref() + .and_then(|afio| { + if p.name.starts_with("TIM") { + // timers are handled by timer_afio_impl!() + return None; + } + + let values = afio + .values + .iter() + .filter(|v| v.pins.contains(&pin.pin)) + .map(|v| v.value) + .collect::>(); + + if values.is_empty() { + None + } else { + 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, {#setter, #type_and_values}); + }) + } + }) + .unwrap_or_else(|| { + let peripherals_with_afio = [ + "CAN", + "CEC", + "ETH", + "I2C", + "SPI", + "SUBGHZSPI", + "USART", + "UART", + "LPUART", + ]; + let af_or_not_applicable = if peripherals_with_afio.iter().any(|&x| p.name.starts_with(x)) { + quote!(0, crate::gpio::AfioRemapNotApplicable) + } else { + quote!(#af) + }; + + quote!(pin_trait_impl!(#tr, #peri, #pin_name, #af_or_not_applicable);) + }); + + g.extend(pin_trait_impl); } // ADC is special @@ -1588,17 +1639,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 { @@ -1928,6 +1969,48 @@ fn main() { pub(crate) const DMA_CHANNELS: &[crate::dma::ChannelInfo] = &[#dmas]; }); + // ======== + // Generate timer AFIO impls + + for p in METADATA.peripherals { + if p.name.starts_with("TIM") { + let pname = format_ident!("{}", p.name); + let afio = if let Some(afio) = &p.afio { + let register = format_ident!("{}", afio.register.to_lowercase()); + let setter = format_ident!("set_{}", afio.field.to_lowercase()); + + let swj_cfg = if afio.register == "MAPR" { + quote!(w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP);) + } else { + quote!() + }; + let bool_eval = if is_bool_field("AFIO", &afio.register, &afio.field) { + quote!(> 0) + } else { + quote!() + }; + + let values = afio.values.iter().map(|v| { + let mapr_value = v.value; + let pin = v.pins.iter().map(|p| { + let port_num = p.chars().nth(1).unwrap() as u8 - b'A'; + let pin_num = p[2..].parse::().unwrap(); + port_num * 16 + pin_num + }); + quote!(#mapr_value, [#(#pin),*]) + }); + + quote! { + , |v| crate::pac::AFIO.#register().modify(|w| { #swj_cfg w.#setter(v #bool_eval); }), #({#values}),* + } + } else { + quote!() + }; + + g.extend(quote!(timer_afio_impl!(#pname #afio);)); + } + } + // ======== // Generate gpio_block() function @@ -2300,3 +2383,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..9ebfeb42f 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, impl RxPin>, + tx: Peri<'d, impl TxPin>, _irqs: impl interrupt::typelevel::Binding> + interrupt::typelevel::Binding> + interrupt::typelevel::Binding> @@ -195,6 +195,8 @@ impl<'d> Can<'d> { let regs = &T::info().regs; rx.set_as_af(rx.af_num(), AfType::input(Pull::None)); + #[cfg(afio)] + rx.afio_remap(); tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); rcc::enable_and_reset::(); @@ -1218,8 +1220,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/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..45be65c5f 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -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, 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>, phy: P, mac_addr: [u8; 6], ) -> Self { @@ -150,6 +150,8 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { { config_in_pins!(ref_clk, rx_d0, rx_d1); config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en); + #[cfg(afio)] + rx_d0.afio_remap(); } #[cfg(any(eth_v1b, eth_v1c))] @@ -289,24 +291,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, 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>, phy: P, mac_addr: [u8; 6], ) -> Self { @@ -347,6 +349,8 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { { config_in_pins!(rx_clk, tx_clk, rx_d0, rx_d1, rx_d2, rx_d3, rxdv); config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); + #[cfg(afio)] + rx_d0.afio_remap(); } #[cfg(any(eth_v1b, eth_v1c))] diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 034c5dd88..96ad42c7f 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -65,19 +65,19 @@ macro_rules! config_pins { impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { /// Create a new RMII ethernet driver using 9 pins. - 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, 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>, phy: P, mac_addr: [u8; 6], ) -> Self { @@ -110,24 +110,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, 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>, phy: P, mac_addr: [u8; 6], ) -> Self { diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index bb37c4194..ef631bbdc 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -710,6 +710,17 @@ 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; + +/// Placeholder for a peripheral's pin which cannot be remapped via AFIO. +pub struct AfioRemapNotApplicable; + pub(crate) trait SealedPin { fn pin_port(&self) -> u8; diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 5fb49f943..be0ae2f5f 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, impl SclPin>, + sda: Peri<'d, 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, impl SclPin>, + sda: Peri<'d, 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..4c634aa17 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, impl MosiPin>, + ws: Peri<'d, impl WsPin>, + ck: Peri<'d, impl CkPin>, + mck: Peri<'d, 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, impl MosiPin>, + ws: Peri<'d, impl WsPin>, + ck: Peri<'d, 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, impl MisoPin>, + ws: Peri<'d, impl WsPin>, + ck: Peri<'d, impl CkPin>, + mck: Peri<'d, 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, impl MosiPin>, + rxsd: Peri<'d, impl MisoPin>, + ws: Peri<'d, impl WsPin>, + ck: Peri<'d, impl CkPin>, + mck: Peri<'d, impl MckPin>, txdma: Peri<'d, impl TxDma>, txdma_buf: &'d mut [W], rxdma: Peri<'d, impl RxDma>, @@ -459,12 +459,12 @@ 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, impl WsPin>, + ck: Peri<'d, impl CkPin>, mck: Option>, txdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>, rxdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>, diff --git a/embassy-stm32/src/macros.rs b/embassy-stm32/src/macros.rs index 3a0b490ba..f97843a7d 100644 --- a/embassy-stm32/src/macros.rs +++ b/embassy-stm32/src/macros.rs @@ -41,25 +41,54 @@ 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 { #[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) => { - impl crate::$mod::$trait for crate::peripherals::$pin { + (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $pin:ident, $af:expr $(, $afio:path)?) => { + impl crate::$mod::$trait for crate::peripherals::$pin { fn af_num(&self) -> u8 { $af } + + #[cfg(afio)] + fn afio_remap(&self) { + // nothing + } } }; } +#[cfg(afio)] +macro_rules! pin_trait_afio_impl { + (crate::$mod:ident::$trait:ident, $instance:ident, $pin:ident, {$setter:ident, $type:ident, [$($val:expr),+]}) => { + $( + impl crate::$mod::$trait> for crate::peripherals::$pin { + fn af_num(&self) -> u8 { + 0 + } + + fn afio_remap(&self) { + crate::pac::AFIO.mapr().modify(|w| { + w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); + w.$setter($val); + }); + } + } + )+ + }; +} + #[allow(unused_macros)] macro_rules! sel_trait_impl { (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $pin:ident, $sel:expr) => { @@ -73,6 +102,30 @@ macro_rules! sel_trait_impl { // ==================== +macro_rules! timer_afio_impl { + ($instance:ident $(, $set_afio:expr)? $(,{$mapr_value:literal, [$($pin:literal),*]})*) => { + impl crate::timer::Afio for crate::peripherals::$instance { + fn afio_mappings() -> &'static [crate::timer::AfioMapping] { + &[ + $( + crate::timer::AfioMapping { + value: $mapr_value, + pins: &[$($pin),*], + } + ),* + ] + } + + #[allow(unused)] + fn set_afio(value: u8) { + $($set_afio(value);)? + } + } + }; +} + +// ==================== + macro_rules! dma_trait { ($signal:ident, $instance:path$(, $mode:path)?) => { #[doc = concat!(stringify!($signal), " DMA request trait")] @@ -134,6 +187,8 @@ macro_rules! new_dma { macro_rules! new_pin { ($name:ident, $af_type:expr) => {{ let pin = $name; + #[cfg(afio)] + pin.afio_remap(); pin.set_as_af(pin.af_num(), $af_type); Some(pin.into()) }}; diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index a49ebcbee..105c617d8 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, impl SckPin>, + mosi: Peri<'d, impl MosiPin>, + miso: Peri<'d, 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, impl SckPin>, + miso: Peri<'d, 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, impl SckPin>, + mosi: Peri<'d, 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, 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, impl SckPin>, + mosi: Peri<'d, impl MosiPin>, + miso: Peri<'d, 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, impl SckPin>, + miso: Peri<'d, 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, impl SckPin>, + mosi: Peri<'d, 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, 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..08404cdd6 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -17,7 +17,8 @@ use crate::Peri; /// /// This wraps a pin to make it usable with PWM. pub struct ComplementaryPwmPin<'d, T, C> { - _pin: Peri<'d, AnyPin>, + #[allow(unused)] + pin: Peri<'d, AnyPin>, phantom: PhantomData<(T, C)>, } @@ -32,7 +33,7 @@ impl<'d, T: AdvancedInstance4Channel, C: TimerChannel> ComplementaryPwmPin<'d, T ); }); ComplementaryPwmPin { - _pin: pin.into(), + pin: pin.into(), phantom: PhantomData, } } @@ -54,20 +55,31 @@ pub enum IdlePolarity { impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// Create a new complementary PWM driver. - #[allow(clippy::too_many_arguments)] + #[allow(clippy::too_many_arguments, unused)] pub fn new( 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 { + #[cfg(afio)] + super::set_afio::(&[ + ch1.map(|p| p.pin), + ch1n.map(|p| p.pin), + ch2.map(|p| p.pin), + ch2n.map(|p| p.pin), + ch3.map(|p| p.pin), + ch3n.map(|p| p.pin), + ch4.map(|p| p.pin), + ch4n.map(|p| p.pin), + ]); Self::new_inner(tim, freq, counting_mode) } diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index dda33e7f1..b717e6eac 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -18,7 +18,8 @@ use crate::Peri; /// /// This wraps a pin to make it usable with capture. pub struct CapturePin<'d, T, C> { - _pin: Peri<'d, AnyPin>, + #[allow(unused)] + pin: Peri<'d, AnyPin>, phantom: PhantomData<(T, C)>, } impl<'d, T: GeneralInstance4Channel, C: TimerChannel> CapturePin<'d, T, C> { @@ -26,7 +27,7 @@ impl<'d, T: GeneralInstance4Channel, C: TimerChannel> CapturePin<'d, T, C> { pub fn new(pin: Peri<'d, impl TimerPin>, pull: Pull) -> Self { pin.set_as_af(pin.af_num(), AfType::input(pull)); CapturePin { - _pin: pin.into(), + pin: pin.into(), phantom: PhantomData, } } @@ -39,16 +40,24 @@ pub struct InputCapture<'d, T: GeneralInstance4Channel> { impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { /// Create a new input capture driver. + #[allow(unused)] pub fn new( 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, ) -> Self { + #[cfg(afio)] + super::set_afio::(&[ + ch1.map(|p| p.pin), + ch2.map(|p| p.pin), + ch3.map(|p| p.pin), + ch4.map(|p| p.pin), + ]); Self::new_inner(tim, freq, counting_mode) } diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 7062f5f4c..38f4a1a51 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -14,6 +14,8 @@ pub mod pwm_input; pub mod qei; pub mod simple_pwm; +#[cfg(afio)] +use crate::gpio::SealedPin; use crate::interrupt; use crate::rcc::RccPeripheral; @@ -155,9 +157,15 @@ trait SealedInstance: RccPeripheral + PeripheralType { fn state() -> &'static State; } +#[allow(unused)] +pub(crate) trait Afio { + fn afio_mappings() -> &'static [AfioMapping]; + fn set_afio(value: u8); +} + /// Core timer instance. #[allow(private_bounds)] -pub trait CoreInstance: SealedInstance + 'static { +pub trait CoreInstance: SealedInstance + Afio + 'static { /// Update Interrupt for this timer. type UpdateInterrupt: interrupt::typelevel::Interrupt; @@ -450,3 +458,24 @@ impl interrupt::typelevel::Handler(pins: &[Option>]) { + let mapping = T::afio_mappings() + .iter() + .find(|m| { + pins.iter() + .flatten() + .map(|p| (*p).pin_port()) + .all(|p| m.pins.contains(&p)) + }) + .expect("Should be called with a combination of timer pins supported by the hardware"); + + T::set_afio(mapping.value); +} diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs index 498d9c082..0267749e1 100644 --- a/embassy-stm32/src/timer/one_pulse.rs +++ b/embassy-stm32/src/timer/one_pulse.rs @@ -42,7 +42,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)>, } @@ -113,7 +114,7 @@ impl<'d, T: GeneralInstance4Channel, C: TriggerSource> TriggerPin<'d, T, C> { pub fn new(pin: Peri<'d, impl TimerTriggerPin>, pull: Pull) -> Self { pin.set_as_af(pin.af_num(), AfType::input(pull)); TriggerPin { - _pin: pin.into(), + pin: pin.into(), phantom: PhantomData, } } @@ -131,14 +132,17 @@ 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, counting_mode: CountingMode, ) -> Self { + #[cfg(afio)] + super::set_afio::(&[Some(pin.pin)]); let mut this = Self { inner: Timer::new(tim) }; this.inner.set_trigger_source(Ts::TI1F_ED); diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index 1e55f2919..c537e5268 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -20,6 +20,8 @@ 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)); + #[cfg(afio)] + super::set_afio::(&[Some(pin.into())]); Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2) } @@ -27,6 +29,8 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { /// 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)); + #[cfg(afio)] + super::set_afio::(&[Some(pin.into())]); 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..4e5a309ac 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs @@ -21,19 +21,20 @@ pub enum Direction { /// Wrapper for using a pin with QEI. pub struct QeiPin<'d, T, Channel> { - _pin: Peri<'d, AnyPin>, + #[allow(unused)] + pin: Peri<'d, AnyPin>, phantom: PhantomData<(T, Channel)>, } impl<'d, T: GeneralInstance4Channel, C: QeiChannel> QeiPin<'d, T, C> { - /// Create a new QEI pin instance. + /// Create a new QEI pin instance. pub fn new(pin: Peri<'d, impl TimerPin>) -> Self { critical_section::with(|_| { pin.set_low(); pin.set_as_af(pin.af_num(), AfType::input(Pull::None)); }); QeiPin { - _pin: pin.into(), + pin: pin.into(), phantom: PhantomData, } } @@ -58,7 +59,10 @@ 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(tim: Peri<'d, T>, ch1: QeiPin<'d, T, Ch1>, ch2: QeiPin<'d, T, Ch2>) -> Self { + #[cfg(afio)] + super::set_afio::(&[Some(ch1.pin), Some(ch2.pin)]); Self::new_inner(tim) } diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index c04b1ab97..df86859fe 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -15,7 +15,8 @@ use crate::Peri; /// /// This wraps a pin to make it usable with PWM. pub struct PwmPin<'d, T, C> { - _pin: Peri<'d, AnyPin>, + #[allow(unused)] + pub(crate) pin: Peri<'d, AnyPin>, phantom: PhantomData<(T, C)>, } @@ -42,7 +43,7 @@ impl<'d, T: GeneralInstance4Channel, C: TimerChannel> PwmPin<'d, T, C> { pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh)); }); PwmPin { - _pin: pin.into(), + pin: pin.into(), phantom: PhantomData, } } @@ -60,7 +61,7 @@ impl<'d, T: GeneralInstance4Channel, C: TimerChannel> PwmPin<'d, T, C> { ); }); PwmPin { - _pin: pin.into(), + pin: pin.into(), phantom: PhantomData, } } @@ -178,15 +179,23 @@ pub struct SimplePwm<'d, T: GeneralInstance4Channel> { impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Create a new simple PWM driver. + #[allow(unused)] pub fn new( 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 { + #[cfg(afio)] + super::set_afio::(&[ + ch1.map(|p| p.pin), + ch2.map(|p| p.pin), + ch3.map(|p| p.pin), + ch4.map(|p| p.pin), + ]); Self::new_inner(tim, freq, counting_mode) } diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 729440c46..72aeb8357 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, impl RxPin>, + tx: Peri<'d, 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, impl RxPin>, + tx: Peri<'d, impl TxPin>, + rts: Peri<'d, impl RtsPin>, + cts: Peri<'d, 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, impl RxPin>, + tx: Peri<'d, impl TxPin>, + rts: Peri<'d, 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, impl RxPin>, + tx: Peri<'d, impl TxPin>, + rts: Peri<'d, 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, impl RxPin>, + tx: Peri<'d, impl TxPin>, + de: Peri<'d, 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, 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, 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..21d174bf0 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, 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, impl TxPin>, + cts: Peri<'d, 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, 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, impl TxPin>, + cts: Peri<'d, 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, 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, impl RxPin>, + rts: Peri<'d, 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, 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, impl RxPin>, + rts: Peri<'d, 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, impl RxPin>, + tx: Peri<'d, 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, impl RxPin>, + tx: Peri<'d, impl TxPin>, _irq: impl interrupt::typelevel::Binding> + 'd, - rts: Peri<'d, impl RtsPin>, - cts: Peri<'d, impl CtsPin>, + rts: Peri<'d, impl RtsPin>, + cts: Peri<'d, 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, impl RxPin>, + tx: Peri<'d, impl TxPin>, _irq: impl interrupt::typelevel::Binding> + 'd, - de: Peri<'d, impl DePin>, + de: Peri<'d, 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, 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, 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, impl RxPin>, + tx: Peri<'d, 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, impl RxPin>, + tx: Peri<'d, impl TxPin>, + rts: Peri<'d, impl RtsPin>, + cts: Peri<'d, 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, impl RxPin>, + tx: Peri<'d, impl TxPin>, + de: Peri<'d, 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, 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, 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/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..c684b7b3e --- /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::{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, From 7419b398bf7cc5c1ff164c504f4a4027cd6bcd3b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 5 Sep 2025 23:00:31 +0200 Subject: [PATCH 2/5] stm32/afio: use type inference for timer remaps as well. --- embassy-stm32/build.rs | 131 ++++++------------- embassy-stm32/src/macros.rs | 55 ++++---- embassy-stm32/src/timer/complementary_pwm.rs | 39 +++--- embassy-stm32/src/timer/input_capture.rs | 25 ++-- embassy-stm32/src/timer/mod.rs | 43 +----- embassy-stm32/src/timer/one_pulse.rs | 72 +++------- embassy-stm32/src/timer/pwm_input.rs | 8 +- embassy-stm32/src/timer/qei.rs | 14 +- embassy-stm32/src/timer/simple_pwm.rs | 31 ++--- examples/stm32f1/src/bin/input_capture.rs | 5 +- examples/stm32f1/src/bin/pwm_input.rs | 4 +- tests/stm32/src/bin/afio.rs | 24 ++-- 12 files changed, 159 insertions(+), 292 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index f55e1e0c9..0d3582c9d 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1391,58 +1391,51 @@ fn main() { }) } - let pin_trait_impl = p - .afio - .as_ref() - .and_then(|afio| { - if p.name.starts_with("TIM") { - // timers are handled by timer_afio_impl!() - return None; - } + 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::>(); - let values = afio - .values - .iter() - .filter(|v| v.pins.contains(&pin.pin)) - .map(|v| v.value) - .collect::>(); - - if values.is_empty() { - None + 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 { - 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, {#setter, #type_and_values}); - }) - } - }) - .unwrap_or_else(|| { - let peripherals_with_afio = [ - "CAN", - "CEC", - "ETH", - "I2C", - "SPI", - "SUBGHZSPI", - "USART", - "UART", - "LPUART", - ]; - let af_or_not_applicable = if peripherals_with_afio.iter().any(|&x| p.name.starts_with(x)) { - quote!(0, crate::gpio::AfioRemapNotApplicable) - } else { - quote!(#af) + quote!(AfioRemap, [#(#values),*]) }; - quote!(pin_trait_impl!(#tr, #peri, #pin_name, #af_or_not_applicable);) - }); + 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 af_or_not_applicable = if peripherals_with_afio.iter().any(|&x| p.name.starts_with(x)) { + quote!(0, crate::gpio::AfioRemapNotApplicable) + } else { + quote!(#af) + }; + + Some(quote!(pin_trait_impl!(#tr, #peri, #pin_name, #af_or_not_applicable);)) + }; g.extend(pin_trait_impl); } @@ -1969,48 +1962,6 @@ fn main() { pub(crate) const DMA_CHANNELS: &[crate::dma::ChannelInfo] = &[#dmas]; }); - // ======== - // Generate timer AFIO impls - - for p in METADATA.peripherals { - if p.name.starts_with("TIM") { - let pname = format_ident!("{}", p.name); - let afio = if let Some(afio) = &p.afio { - let register = format_ident!("{}", afio.register.to_lowercase()); - let setter = format_ident!("set_{}", afio.field.to_lowercase()); - - let swj_cfg = if afio.register == "MAPR" { - quote!(w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP);) - } else { - quote!() - }; - let bool_eval = if is_bool_field("AFIO", &afio.register, &afio.field) { - quote!(> 0) - } else { - quote!() - }; - - let values = afio.values.iter().map(|v| { - let mapr_value = v.value; - let pin = v.pins.iter().map(|p| { - let port_num = p.chars().nth(1).unwrap() as u8 - b'A'; - let pin_num = p[2..].parse::().unwrap(); - port_num * 16 + pin_num - }); - quote!(#mapr_value, [#(#pin),*]) - }); - - quote! { - , |v| crate::pac::AFIO.#register().modify(|w| { #swj_cfg w.#setter(v #bool_eval); }), #({#values}),* - } - } else { - quote!() - }; - - g.extend(quote!(timer_afio_impl!(#pname #afio);)); - } - } - // ======== // Generate gpio_block() function diff --git a/embassy-stm32/src/macros.rs b/embassy-stm32/src/macros.rs index f97843a7d..8a3abe1ee 100644 --- a/embassy-stm32/src/macros.rs +++ b/embassy-stm32/src/macros.rs @@ -71,7 +71,31 @@ macro_rules! pin_trait_impl { #[cfg(afio)] macro_rules! pin_trait_afio_impl { - (crate::$mod:ident::$trait:ident, $instance:ident, $pin:ident, {$setter:ident, $type:ident, [$($val:expr),+]}) => { + (@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 af_num(&self) -> u8 { + 0 + } + + 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 af_num(&self) -> u8 { @@ -79,10 +103,7 @@ macro_rules! pin_trait_afio_impl { } fn afio_remap(&self) { - crate::pac::AFIO.mapr().modify(|w| { - w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); - w.$setter($val); - }); + pin_trait_afio_impl!(@set $reg, $setter, $val); } } )+ @@ -102,30 +123,6 @@ macro_rules! sel_trait_impl { // ==================== -macro_rules! timer_afio_impl { - ($instance:ident $(, $set_afio:expr)? $(,{$mapr_value:literal, [$($pin:literal),*]})*) => { - impl crate::timer::Afio for crate::peripherals::$instance { - fn afio_mappings() -> &'static [crate::timer::AfioMapping] { - &[ - $( - crate::timer::AfioMapping { - value: $mapr_value, - pins: &[$($pin),*], - } - ),* - ] - } - - #[allow(unused)] - fn set_afio(value: u8) { - $($set_afio(value);)? - } - } - }; -} - -// ==================== - macro_rules! dma_trait { ($signal:ident, $instance:path$(, $mode:path)?) => { #[doc = concat!(stringify!($signal), " DMA request trait")] diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 08404cdd6..d3b84ed16 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -16,21 +16,23 @@ use crate::Peri; /// Complementary PWM pin wrapper. /// /// This wraps a pin to make it usable with PWM. -pub struct ComplementaryPwmPin<'d, T, C> { +pub struct ComplementaryPwmPin<'d, T, C, A> { #[allow(unused)] pin: Peri<'d, AnyPin>, - phantom: PhantomData<(T, C)>, + phantom: PhantomData<(T, C, A)>, } -impl<'d, T: AdvancedInstance4Channel, C: TimerChannel> ComplementaryPwmPin<'d, T, C> { +impl<'d, T: AdvancedInstance4Channel, C: TimerChannel, A> 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, 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), ); + #[cfg(afio)] + pin.afio_remap(); }); ComplementaryPwmPin { pin: pin.into(), @@ -56,30 +58,19 @@ pub enum IdlePolarity { impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// Create a new complementary PWM driver. #[allow(clippy::too_many_arguments, unused)] - pub fn new( + pub fn new( 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 { - #[cfg(afio)] - super::set_afio::(&[ - ch1.map(|p| p.pin), - ch1n.map(|p| p.pin), - ch2.map(|p| p.pin), - ch2n.map(|p| p.pin), - ch3.map(|p| p.pin), - ch3n.map(|p| p.pin), - ch4.map(|p| p.pin), - ch4n.map(|p| p.pin), - ]); Self::new_inner(tim, freq, counting_mode) } diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index b717e6eac..262f9d067 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -17,14 +17,14 @@ use crate::Peri; /// Capture pin wrapper. /// /// This wraps a pin to make it usable with capture. -pub struct CapturePin<'d, T, C> { +pub struct CapturePin<'d, T, C, A> { #[allow(unused)] pin: Peri<'d, AnyPin>, - phantom: PhantomData<(T, C)>, + phantom: PhantomData<(T, C, A)>, } -impl<'d, T: GeneralInstance4Channel, C: TimerChannel> CapturePin<'d, T, C> { +impl<'d, T: GeneralInstance4Channel, C: TimerChannel, A> CapturePin<'d, T, C, A> { /// Create a new capture pin instance. - pub fn new(pin: Peri<'d, impl TimerPin>, pull: Pull) -> Self { + pub fn new(pin: Peri<'d, impl TimerPin>, pull: Pull) -> Self { pin.set_as_af(pin.af_num(), AfType::input(pull)); CapturePin { pin: pin.into(), @@ -41,23 +41,16 @@ pub struct InputCapture<'d, T: GeneralInstance4Channel> { impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { /// Create a new input capture driver. #[allow(unused)] - pub fn new( + pub fn new( 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, ) -> Self { - #[cfg(afio)] - super::set_afio::(&[ - ch1.map(|p| p.pin), - ch2.map(|p| p.pin), - ch3.map(|p| p.pin), - ch4.map(|p| p.pin), - ]); Self::new_inner(tim, freq, counting_mode) } diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 38f4a1a51..b09bc7166 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -14,8 +14,6 @@ pub mod pwm_input; pub mod qei; pub mod simple_pwm; -#[cfg(afio)] -use crate::gpio::SealedPin; use crate::interrupt; use crate::rcc::RccPeripheral; @@ -157,15 +155,9 @@ trait SealedInstance: RccPeripheral + PeripheralType { fn state() -> &'static State; } -#[allow(unused)] -pub(crate) trait Afio { - fn afio_mappings() -> &'static [AfioMapping]; - fn set_afio(value: u8); -} - /// Core timer instance. #[allow(private_bounds)] -pub trait CoreInstance: SealedInstance + Afio + 'static { +pub trait CoreInstance: SealedInstance + 'static { /// Update Interrupt for this timer. type UpdateInterrupt: interrupt::typelevel::Interrupt; @@ -231,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); @@ -458,24 +450,3 @@ impl interrupt::typelevel::Handler(pins: &[Option>]) { - let mapping = T::afio_mappings() - .iter() - .find(|m| { - pins.iter() - .flatten() - .map(|p| (*p).pin_port()) - .all(|p| m.pins.contains(&p)) - }) - .expect("Should be called with a combination of timer pins supported by the hardware"); - - T::set_afio(mapping.value); -} diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs index 0267749e1..b15cea679 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. @@ -61,58 +62,25 @@ 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 { +impl<'d, T: GeneralInstance4Channel, C: TriggerSource + TimerChannel> TriggerPin<'d, T, C> { + /// Create a new Channel trigger pin instance. + pub fn new(pin: Peri<'d, impl TimerPin>, pull: Pull) -> Self { pin.set_as_af(pin.af_num(), AfType::input(pull)); + #[cfg(afio)] + pin.afio_remap(); + TriggerPin { + pin: pin.into(), + phantom: PhantomData, + } + } +} + +impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, Ext> { + /// Create a new external trigger pin instance. + pub fn new_external(pin: Peri<'d, impl ExternalTriggerPin>, pull: Pull) -> Self { + pin.set_as_af(pin.af_num(), AfType::input(pull)); + #[cfg(afio)] + pin.afio_remap(); TriggerPin { pin: pin.into(), phantom: PhantomData, @@ -141,8 +109,6 @@ impl<'d, T: GeneralInstance4Channel> OnePulse<'d, T> { pulse_end: u32, counting_mode: CountingMode, ) -> Self { - #[cfg(afio)] - super::set_afio::(&[Some(pin.pin)]); let mut this = Self { inner: Timer::new(tim) }; this.inner.set_trigger_source(Ts::TI1F_ED); diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index c537e5268..62d7a8550 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -18,19 +18,19 @@ 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 { + 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)); #[cfg(afio)] - super::set_afio::(&[Some(pin.into())]); + pin.afio_remap(); 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 { + 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)); #[cfg(afio)] - super::set_afio::(&[Some(pin.into())]); + pin.afio_remap(); 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 4e5a309ac..39d051294 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs @@ -20,18 +20,20 @@ pub enum Direction { } /// Wrapper for using a pin with QEI. -pub struct QeiPin<'d, T, Channel> { +pub struct QeiPin<'d, T, Channel, A> { #[allow(unused)] pin: Peri<'d, AnyPin>, - phantom: PhantomData<(T, Channel)>, + phantom: PhantomData<(T, Channel, A)>, } -impl<'d, T: GeneralInstance4Channel, C: QeiChannel> QeiPin<'d, T, C> { +impl<'d, T: GeneralInstance4Channel, C: QeiChannel, A> QeiPin<'d, T, C, A> { /// Create a new QEI pin instance. - pub fn new(pin: Peri<'d, impl TimerPin>) -> Self { + pub fn new(pin: Peri<'d, impl TimerPin>) -> Self { critical_section::with(|_| { pin.set_low(); pin.set_as_af(pin.af_num(), AfType::input(Pull::None)); + #[cfg(afio)] + pin.afio_remap(); }); QeiPin { pin: pin.into(), @@ -60,9 +62,7 @@ pub struct Qei<'d, T: GeneralInstance4Channel> { impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { /// Create a new quadrature decoder driver. #[allow(unused)] - pub fn new(tim: Peri<'d, T>, ch1: QeiPin<'d, T, Ch1>, ch2: QeiPin<'d, T, Ch2>) -> Self { - #[cfg(afio)] - super::set_afio::(&[Some(ch1.pin), Some(ch2.pin)]); + pub fn new(tim: Peri<'d, T>, ch1: QeiPin<'d, T, Ch1, A>, ch2: 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 df86859fe..53f7cdd22 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -14,10 +14,10 @@ use crate::Peri; /// PWM pin wrapper. /// /// This wraps a pin to make it usable with PWM. -pub struct PwmPin<'d, T, C> { +pub struct PwmPin<'d, T, C, A> { #[allow(unused)] pub(crate) pin: Peri<'d, AnyPin>, - phantom: PhantomData<(T, C)>, + phantom: PhantomData<(T, C, A)>, } /// PWM pin config @@ -35,12 +35,14 @@ pub struct PwmPinConfig { pub pull: Pull, } -impl<'d, T: GeneralInstance4Channel, C: TimerChannel> PwmPin<'d, T, C> { +impl<'d, T: GeneralInstance4Channel, C: TimerChannel, A> 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, 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)); + #[cfg(afio)] + pin.afio_remap(); }); PwmPin { pin: pin.into(), @@ -49,7 +51,7 @@ impl<'d, T: GeneralInstance4Channel, C: TimerChannel> PwmPin<'d, T, C> { } /// Create a new PWM pin instance with config. - pub fn new_with_config(pin: Peri<'d, impl TimerPin>, pin_config: PwmPinConfig) -> Self { + pub fn new_with_config(pin: Peri<'d, impl TimerPin>, pin_config: PwmPinConfig) -> Self { critical_section::with(|_| { pin.set_low(); pin.set_as_af( @@ -59,6 +61,8 @@ impl<'d, T: GeneralInstance4Channel, C: TimerChannel> PwmPin<'d, T, C> { #[cfg(gpio_v2)] AfType::output_pull(pin_config.output_type, pin_config.speed, pin_config.pull), ); + #[cfg(afio)] + pin.afio_remap(); }); PwmPin { pin: pin.into(), @@ -180,22 +184,15 @@ pub struct SimplePwm<'d, T: GeneralInstance4Channel> { impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Create a new simple PWM driver. #[allow(unused)] - pub fn new( + pub fn new( 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 { - #[cfg(afio)] - super::set_afio::(&[ - ch1.map(|p| p.pin), - ch2.map(|p| p.pin), - ch3.map(|p| p.pin), - ch4.map(|p| p.pin), - ]); Self::new_inner(tim, freq, counting_mode) } 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/src/bin/afio.rs b/tests/stm32/src/bin/afio.rs index c684b7b3e..cc44dc59c 100644 --- a/tests/stm32/src/bin/afio.rs +++ b/tests/stm32/src/bin/afio.rs @@ -6,7 +6,7 @@ mod common; use common::*; use embassy_executor::Spawner; -use embassy_stm32::gpio::{OutputType, Pull}; +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}; @@ -173,7 +173,7 @@ async fn main(_spawner: Spawner) { { // no remap afio_registers_set_remap(); - SimplePwm::new( + SimplePwm::new::>( p.TIM1.reborrow(), Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)), Some(PwmPin::new(p.PA9.reborrow(), OutputType::PushPull)), @@ -187,7 +187,7 @@ async fn main(_spawner: Spawner) { { // no remap afio_registers_set_remap(); - SimplePwm::new( + SimplePwm::new::>( p.TIM1.reborrow(), Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)), None, @@ -201,7 +201,7 @@ async fn main(_spawner: Spawner) { { // partial remap reset_afio_registers(); - ComplementaryPwm::new( + ComplementaryPwm::new::>( p.TIM1.reborrow(), Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)), None, @@ -219,7 +219,7 @@ async fn main(_spawner: Spawner) { { // partial remap reset_afio_registers(); - ComplementaryPwm::new( + ComplementaryPwm::new::>( p.TIM1.reborrow(), Some(PwmPin::new(p.PA8.reborrow(), OutputType::PushPull)), Some(ComplementaryPwmPin::new(p.PA7.reborrow(), OutputType::PushPull)), @@ -237,7 +237,7 @@ async fn main(_spawner: Spawner) { { // partial remap reset_afio_registers(); - InputCapture::new( + InputCapture::new::>( p.TIM1.reborrow(), Some(CapturePin::new(p.PA8.reborrow(), Pull::Down)), None, @@ -252,13 +252,13 @@ async fn main(_spawner: Spawner) { { // partial remap reset_afio_registers(); - PwmInput::new_ch1(p.TIM1.reborrow(), p.PA8.reborrow(), Pull::Down, khz(10)); + 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( + Qei::new::>( p.TIM1.reborrow(), QeiPin::new(p.PA8.reborrow()), QeiPin::new(p.PA9.reborrow()), @@ -270,7 +270,7 @@ async fn main(_spawner: Spawner) { { // no remap afio_registers_set_remap(); - SimplePwm::new( + SimplePwm::new::>( p.TIM2.reborrow(), Some(PwmPin::new(p.PA0.reborrow(), OutputType::PushPull)), Some(PwmPin::new(p.PA1.reborrow(), OutputType::PushPull)), @@ -284,7 +284,7 @@ async fn main(_spawner: Spawner) { { // partial remap 1 reset_afio_registers(); - SimplePwm::new( + SimplePwm::new::>( p.TIM2.reborrow(), Some(PwmPin::new(p.PA15.reborrow(), OutputType::PushPull)), Some(PwmPin::new(p.PB3.reborrow(), OutputType::PushPull)), @@ -298,7 +298,7 @@ async fn main(_spawner: Spawner) { { // partial remap 2 reset_afio_registers(); - SimplePwm::new( + SimplePwm::new::>( p.TIM2.reborrow(), Some(PwmPin::new(p.PA0.reborrow(), OutputType::PushPull)), Some(PwmPin::new(p.PA1.reborrow(), OutputType::PushPull)), @@ -312,7 +312,7 @@ async fn main(_spawner: Spawner) { { // full remap reset_afio_registers(); - SimplePwm::new( + SimplePwm::new::>( p.TIM2.reborrow(), Some(PwmPin::new(p.PA15.reborrow(), OutputType::PushPull)), Some(PwmPin::new(p.PB3.reborrow(), OutputType::PushPull)), From 35f4ae378cbc9a1263e46baaeac536cae2337896 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 5 Sep 2025 23:44:25 +0200 Subject: [PATCH 3/5] stm32/afio: make the A generic param only appear in chips with AFIO. --- embassy-stm32/src/can/bxcan/mod.rs | 6 +- embassy-stm32/src/eth/v1/mod.rs | 50 ++++----- embassy-stm32/src/eth/v2/mod.rs | 50 ++++----- embassy-stm32/src/gpio.rs | 1 + embassy-stm32/src/i2c/mod.rs | 12 +-- embassy-stm32/src/i2s.rs | 46 ++++---- embassy-stm32/src/macros.rs | 54 +++++++++- embassy-stm32/src/spi/mod.rs | 48 ++++----- embassy-stm32/src/timer/complementary_pwm.rs | 26 ++--- embassy-stm32/src/timer/input_capture.rs | 18 ++-- embassy-stm32/src/timer/one_pulse.rs | 4 +- embassy-stm32/src/timer/pwm_input.rs | 14 ++- embassy-stm32/src/timer/qei.rs | 14 ++- embassy-stm32/src/timer/simple_pwm.rs | 22 ++-- embassy-stm32/src/usart/buffered.rs | 48 ++++----- embassy-stm32/src/usart/mod.rs | 104 +++++++++---------- 16 files changed, 291 insertions(+), 226 deletions(-) diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index 9ebfeb42f..663b34501 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> diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 45be65c5f..2ae451902 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -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 { @@ -291,24 +291,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 96ad42c7f..034c5dd88 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -65,19 +65,19 @@ macro_rules! config_pins { impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { /// Create a new RMII ethernet driver using 9 pins. - 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, 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>, phy: P, mac_addr: [u8; 6], ) -> Self { @@ -110,24 +110,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, 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>, phy: P, mac_addr: [u8; 6], ) -> Self { diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index ef631bbdc..83fd08e23 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -718,6 +718,7 @@ pub struct AfioRemap; /// 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; diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index be0ae2f5f..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( diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index 4c634aa17..1b885ec54 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,12 +459,12 @@ 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])>, diff --git a/embassy-stm32/src/macros.rs b/embassy-stm32/src/macros.rs index 8a3abe1ee..def8dcf49 100644 --- a/embassy-stm32/src/macros.rs +++ b/embassy-stm32/src/macros.rs @@ -43,7 +43,7 @@ macro_rules! peri_trait_impl { macro_rules! pin_trait { ($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 { #[doc = concat!("Get the AF number needed to use this pin as ", stringify!($signal))] fn af_num(&self) -> u8; @@ -56,16 +56,23 @@ macro_rules! pin_trait { macro_rules! pin_trait_impl { (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 af_num(&self) -> u8 { $af } - #[cfg(afio)] fn afio_remap(&self) { // nothing } } + + #[cfg(not(afio))] + impl crate::$mod::$trait for crate::peripherals::$pin { + fn af_num(&self) -> u8 { + $af + } + } }; } @@ -190,3 +197,46 @@ macro_rules! new_pin { Some(pin.into()) }}; } + +#[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/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 105c617d8..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 { diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index d3b84ed16..693eb3456 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -16,15 +16,15 @@ use crate::Peri; /// Complementary PWM pin wrapper. /// /// This wraps a pin to make it usable with PWM. -pub struct ComplementaryPwmPin<'d, T, C, A> { +pub struct ComplementaryPwmPin<'d, T, C, #[cfg(afio)] A> { #[allow(unused)] pin: Peri<'d, AnyPin>, - phantom: PhantomData<(T, C, A)>, + phantom: PhantomData, } -impl<'d, T: AdvancedInstance4Channel, C: TimerChannel, A> ComplementaryPwmPin<'d, T, C, A> { +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( @@ -58,16 +58,16 @@ pub enum IdlePolarity { impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// Create a new complementary PWM driver. #[allow(clippy::too_many_arguments, unused)] - pub fn new( + 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 262f9d067..41391bd6d 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -17,14 +17,14 @@ use crate::Peri; /// Capture pin wrapper. /// /// This wraps a pin to make it usable with capture. -pub struct CapturePin<'d, T, C, A> { +pub struct CapturePin<'d, T, C, #[cfg(afio)] A> { #[allow(unused)] pin: Peri<'d, AnyPin>, - phantom: PhantomData<(T, C, A)>, + phantom: PhantomData, } -impl<'d, T: GeneralInstance4Channel, C: TimerChannel, A> CapturePin<'d, T, C, A> { +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 { + pub fn new(pin: Peri<'d, if_afio!(impl TimerPin)>, pull: Pull) -> Self { pin.set_as_af(pin.af_num(), AfType::input(pull)); CapturePin { pin: pin.into(), @@ -41,12 +41,12 @@ pub struct InputCapture<'d, T: GeneralInstance4Channel> { impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { /// Create a new input capture driver. #[allow(unused)] - pub fn new( + 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/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs index b15cea679..edab38022 100644 --- a/embassy-stm32/src/timer/one_pulse.rs +++ b/embassy-stm32/src/timer/one_pulse.rs @@ -64,7 +64,7 @@ impl SealedTriggerSource for Ext {} impl<'d, T: GeneralInstance4Channel, C: TriggerSource + TimerChannel> TriggerPin<'d, T, C> { /// Create a new Channel trigger pin instance. - pub fn new(pin: Peri<'d, impl TimerPin>, pull: Pull) -> Self { + pub fn new<#[cfg(afio)] A>(pin: Peri<'d, if_afio!(impl TimerPin)>, pull: Pull) -> Self { pin.set_as_af(pin.af_num(), AfType::input(pull)); #[cfg(afio)] pin.afio_remap(); @@ -77,7 +77,7 @@ impl<'d, T: GeneralInstance4Channel, C: TriggerSource + TimerChannel> TriggerPin impl<'d, T: GeneralInstance4Channel> TriggerPin<'d, T, Ext> { /// Create a new external trigger pin instance. - pub fn new_external(pin: Peri<'d, impl ExternalTriggerPin>, pull: Pull) -> Self { + pub fn new_external<#[cfg(afio)] A>(pin: Peri<'d, if_afio!(impl ExternalTriggerPin)>, pull: Pull) -> Self { pin.set_as_af(pin.af_num(), AfType::input(pull)); #[cfg(afio)] pin.afio_remap(); diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index 62d7a8550..4c1df0316 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -18,7 +18,12 @@ 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 { + pub fn new_ch1<#[cfg(afio)] A>( + tim: Peri<'d, T>, + pin: Peri<'d, if_afio!(impl TimerPin)>, + pull: Pull, + freq: Hertz, + ) -> Self { pin.set_as_af(pin.af_num(), AfType::input(pull)); #[cfg(afio)] pin.afio_remap(); @@ -27,7 +32,12 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { } /// Create a new PWM input driver. - pub fn new_ch2(tim: Peri<'d, T>, pin: Peri<'d, impl TimerPin>, pull: Pull, freq: Hertz) -> Self { + pub fn new_ch2<#[cfg(afio)] A>( + tim: Peri<'d, T>, + pin: Peri<'d, if_afio!(impl TimerPin)>, + pull: Pull, + freq: Hertz, + ) -> Self { pin.set_as_af(pin.af_num(), AfType::input(pull)); #[cfg(afio)] pin.afio_remap(); diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index 39d051294..528c4a904 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs @@ -20,15 +20,15 @@ pub enum Direction { } /// Wrapper for using a pin with QEI. -pub struct QeiPin<'d, T, Channel, A> { +pub struct QeiPin<'d, T, Channel, #[cfg(afio)] A> { #[allow(unused)] pin: Peri<'d, AnyPin>, - phantom: PhantomData<(T, Channel, A)>, + phantom: PhantomData, } -impl<'d, T: GeneralInstance4Channel, C: QeiChannel, A> QeiPin<'d, T, C, A> { +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, impl TimerPin>) -> Self { + 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)); @@ -62,7 +62,11 @@ pub struct Qei<'d, T: GeneralInstance4Channel> { impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { /// Create a new quadrature decoder driver. #[allow(unused)] - pub fn new(tim: Peri<'d, T>, ch1: QeiPin<'d, T, Ch1, A>, ch2: QeiPin<'d, T, Ch2, A>) -> Self { + 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 53f7cdd22..c08a3939f 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -14,10 +14,10 @@ use crate::Peri; /// PWM pin wrapper. /// /// This wraps a pin to make it usable with PWM. -pub struct PwmPin<'d, T, C, A> { +pub struct PwmPin<'d, T, C, #[cfg(afio)] A> { #[allow(unused)] pub(crate) pin: Peri<'d, AnyPin>, - phantom: PhantomData<(T, C, A)>, + phantom: PhantomData, } /// PWM pin config @@ -35,9 +35,9 @@ pub struct PwmPinConfig { pub pull: Pull, } -impl<'d, T: GeneralInstance4Channel, C: TimerChannel, A> PwmPin<'d, T, C, A> { +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)); @@ -50,8 +50,8 @@ impl<'d, T: GeneralInstance4Channel, C: TimerChannel, A> PwmPin<'d, T, C, A> { } } - /// 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( @@ -184,12 +184,12 @@ pub struct SimplePwm<'d, T: GeneralInstance4Channel> { impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Create a new simple PWM driver. #[allow(unused)] - pub fn new( + 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/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 72aeb8357..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 21d174bf0..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 { From a23c4b7bca15bc00f4b5c4af200f17eb0097e94b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 6 Sep 2025 00:11:59 +0200 Subject: [PATCH 4/5] stm32/afio: make af_num() unavailable in afio chips. --- .vscode/settings.json | 2 +- embassy-stm32/src/can/bxcan/mod.rs | 10 ++- embassy-stm32/src/can/fdcan.rs | 4 +- embassy-stm32/src/dcmi.rs | 2 +- embassy-stm32/src/dsihost.rs | 2 +- embassy-stm32/src/eth/v1/mod.rs | 10 +-- embassy-stm32/src/eth/v2/mod.rs | 2 +- embassy-stm32/src/fmc.rs | 2 +- embassy-stm32/src/gpio.rs | 19 +++-- embassy-stm32/src/hrtim/mod.rs | 10 +-- embassy-stm32/src/i2s.rs | 4 +- embassy-stm32/src/lptim/pwm.rs | 17 ++--- embassy-stm32/src/macros.rs | 36 ++++++---- embassy-stm32/src/rcc/mco.rs | 2 +- embassy-stm32/src/sai/mod.rs | 10 +-- embassy-stm32/src/sdmmc/mod.rs | 76 ++++++++++---------- embassy-stm32/src/spdifrx/mod.rs | 2 +- embassy-stm32/src/timer/complementary_pwm.rs | 8 +-- embassy-stm32/src/timer/input_capture.rs | 2 +- embassy-stm32/src/timer/one_pulse.rs | 8 +-- embassy-stm32/src/timer/pwm_input.rs | 8 +-- embassy-stm32/src/timer/qei.rs | 4 +- embassy-stm32/src/timer/simple_pwm.rs | 18 ++--- embassy-stm32/src/tsc/pin_groups.rs | 2 +- embassy-stm32/src/usb/otg.rs | 10 +-- embassy-stm32/src/usb/usb.rs | 6 +- 26 files changed, 132 insertions(+), 144 deletions(-) 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/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index 663b34501..8eb188560 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs @@ -194,10 +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)); - #[cfg(afio)] - rx.afio_remap(); - 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::(); @@ -231,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(); 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/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 2ae451902..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)); )* }) }; @@ -150,8 +150,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { { config_in_pins!(ref_clk, rx_d0, rx_d1); config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en); - #[cfg(afio)] - rx_d0.afio_remap(); } #[cfg(any(eth_v1b, eth_v1c))] @@ -349,8 +347,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { { config_in_pins!(rx_clk, tx_clk, rx_d0, rx_d1, rx_d2, rx_d3, rxdv); config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); - #[cfg(afio)] - rx_d0.afio_remap(); } #[cfg(any(eth_v1b, eth_v1c))] 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 83fd08e23..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; @@ -755,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/i2s.rs b/embassy-stm32/src/i2s.rs index 1b885ec54..b6d3daf54 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs @@ -471,8 +471,8 @@ impl<'d, W: Word> I2S<'d, 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 def8dcf49..22cc2e049 100644 --- a/embassy-stm32/src/macros.rs +++ b/embassy-stm32/src/macros.rs @@ -44,6 +44,7 @@ macro_rules! pin_trait { ($signal:ident, $instance:path $(, $mode:path)? $(, @$afio:ident)?) => { #[doc = concat!(stringify!($signal), " pin trait")] 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; @@ -58,10 +59,6 @@ macro_rules! pin_trait_impl { (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 af_num(&self) -> u8 { - $af - } - fn afio_remap(&self) { // nothing } @@ -92,10 +89,6 @@ macro_rules! pin_trait_afio_impl { (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 af_num(&self) -> u8 { - 0 - } - fn afio_remap(&self) { pin_trait_afio_impl!(@set $reg, $setter, $val); } @@ -105,10 +98,6 @@ macro_rules! pin_trait_afio_impl { (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 af_num(&self) -> u8 { - 0 - } - fn afio_remap(&self) { pin_trait_afio_impl!(@set $reg, $setter, $val); } @@ -193,11 +182,32 @@ macro_rules! new_pin { let pin = $name; #[cfg(afio)] pin.afio_remap(); - pin.set_as_af(pin.af_num(), $af_type); + 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)*) => { 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/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 693eb3456..484aae1d0 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -27,12 +27,10 @@ impl<'d, T: AdvancedInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!( 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) ); - #[cfg(afio)] - pin.afio_remap(); }); ComplementaryPwmPin { pin: pin.into(), diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index 41391bd6d..7a25e6c21 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -25,7 +25,7 @@ pub struct CapturePin<'d, T, C, #[cfg(afio)] A> { 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, if_afio!(impl TimerPin)>, pull: Pull) -> Self { - pin.set_as_af(pin.af_num(), AfType::input(pull)); + set_as_af!(pin, AfType::input(pull)); CapturePin { pin: pin.into(), phantom: PhantomData, diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs index edab38022..a75b41bd7 100644 --- a/embassy-stm32/src/timer/one_pulse.rs +++ b/embassy-stm32/src/timer/one_pulse.rs @@ -65,9 +65,7 @@ impl SealedTriggerSource for Ext {} 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 { - pin.set_as_af(pin.af_num(), AfType::input(pull)); - #[cfg(afio)] - pin.afio_remap(); + set_as_af!(pin, AfType::input(pull)); TriggerPin { pin: pin.into(), phantom: PhantomData, @@ -78,9 +76,7 @@ impl<'d, T: GeneralInstance4Channel, C: TriggerSource + TimerChannel> TriggerPin 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 { - pin.set_as_af(pin.af_num(), AfType::input(pull)); - #[cfg(afio)] - pin.afio_remap(); + set_as_af!(pin, AfType::input(pull)); TriggerPin { pin: pin.into(), phantom: PhantomData, diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index 4c1df0316..159b5a177 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -24,9 +24,7 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { pull: Pull, freq: Hertz, ) -> Self { - pin.set_as_af(pin.af_num(), AfType::input(pull)); - #[cfg(afio)] - pin.afio_remap(); + set_as_af!(pin, AfType::input(pull)); Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2) } @@ -38,9 +36,7 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { pull: Pull, freq: Hertz, ) -> Self { - pin.set_as_af(pin.af_num(), AfType::input(pull)); - #[cfg(afio)] - pin.afio_remap(); + 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 528c4a904..82b5968b0 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs @@ -31,9 +31,7 @@ impl<'d, T: GeneralInstance4Channel, C: QeiChannel, #[cfg(afio)] A> if_afio!(Qei 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)); - #[cfg(afio)] - pin.afio_remap(); + set_as_af!(pin, AfType::input(Pull::None)); }); QeiPin { pin: pin.into(), diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index c08a3939f..e6165e42b 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -40,9 +40,7 @@ impl<'d, T: GeneralInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!(P 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)); - #[cfg(afio)] - pin.afio_remap(); + set_as_af!(pin, AfType::output(output_type, Speed::VeryHigh)); }); PwmPin { pin: pin.into(), @@ -54,15 +52,13 @@ impl<'d, T: GeneralInstance4Channel, C: TimerChannel, #[cfg(afio)] A> if_afio!(P 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) ); - #[cfg(afio)] - pin.afio_remap(); }); PwmPin { pin: pin.into(), 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/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. From 23d5c7efd99e7422c63c6e12143856257d1ee651 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 6 Sep 2025 00:39:18 +0200 Subject: [PATCH 5/5] stm32/afio: fix accidentally always using AF number 0 on non-AFIO chips. --- embassy-stm32/build.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 0d3582c9d..b5f1261fe 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1428,13 +1428,13 @@ fn main() { "LPUART", "TIM", ]; - let af_or_not_applicable = if peripherals_with_afio.iter().any(|&x| p.name.starts_with(x)) { - quote!(0, crate::gpio::AfioRemapNotApplicable) + let not_applicable = if peripherals_with_afio.iter().any(|&x| p.name.starts_with(x)) { + quote!(, crate::gpio::AfioRemapNotApplicable) } else { - quote!(#af) + quote!() }; - Some(quote!(pin_trait_impl!(#tr, #peri, #pin_name, #af_or_not_applicable);)) + Some(quote!(pin_trait_impl!(#tr, #peri, #pin_name, #af #not_applicable);)) }; g.extend(pin_trait_impl);