How to declare constant pins across different boards (stm32)

Hi

While doing some initial programming on stm32 (blue pill etc.) I've used example for blinky. Suppose I have a different board which has LED on a different pin and GPIO port. Since everything in stm32 HAL crates return structs with different types, how I can declare which port and pon to use when compiling on different board ?

Comming from C++ I'd just include header responsible for given board in which I'd define pin and ports. I can use cfg macro in rust but that's only half way done.

As you mention, conditional compilation (#[cfg(...)]) is probably the best way to do this.

The stm32f1xx-hal crate's blinky example shows how this can be done:

    #[cfg(feature = "stm32f100")]
    gpioc
        .pc9
        .into_push_pull_output(&mut gpioc.crh)
        .set_high()
        .unwrap();

    #[cfg(feature = "stm32f101")]
    gpioc
        .pc9
        .into_push_pull_output(&mut gpioc.crh)
        .set_high()
        .unwrap();

    #[cfg(any(feature = "stm32f103", feature = "stm32f105", feature = "stm32f107"))]
    gpioc
        .pc13
        .into_push_pull_output(&mut gpioc.crh)
        .set_low()
        .unwrap();

As you start to do more complex things, this kind of duplication can get annoying but in my experience it's usually possible to factor out the bits that are board specific so you don't have to duplicate as much stuff.

For example:

#[cfg(any(feature = "stm32f100", feature = "stm32f101"))]
type LedPin = PC9<Output<PushPull>>;

#[cfg(any(feature = "stm32f100", feature = "stm32f101"))]
fn get_pin(parts: &gpioc::Parts) -> LedPin { ... }


#[cfg(any(feature = "stm32f103", feature = "stm32f105", feature = "stm32f107"))]
type LedPin = PC13<Output<PushPull>>;

#[cfg(any(feature = "stm32f103", feature = "stm32f105", feature = "stm32f107"))]
fn get_pin(parts: &gpioc::Parts) -> LedPin { ... }

fn turn_on(pin: &mut LedPin) { pin.set_low().unwrap(); }

let mut pin = get_pin();
turn_on(&mut pin);

There are also traits in embedded-hal and in HAL crates that make it easy to generalize over pins in a zero-cost fashion without having to manually get all the types to align like we do above. For example:

use embedded_hal::digital::v2::OutputPin;
use core::convert::Infallible;

#[cfg(any(feature = "stm32f100", feature = "stm32f101"))]
fn get_pin(parts: &gpioc::Parts) -> PC9<Output<PushPull>> { ... }

#[cfg(any(feature = "stm32f103", feature = "stm32f105", feature = "stm32f107"))]
fn get_pin(parts: &gpioc::Parts) -> PC13<Output<PushPull>> { ... }

fn turn_on(pin: &mut impl OutputPin<Error = Infallible>) { pin.set_low().unwrap(); }

let mut pin = get_pin();
turn_on(&mut pin);

Finally, some HAL crates (like stm32f1xx-hal) offer type erased pin types (like this) that let you sidestep the problem altogether.