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.

Thx for reply, although I must say that's not what I was looking for. Namely

This:

type LedPin = PC9<Output<PushPull>>;
fn get_pin(parts: &gpioc::Parts) -> LedPin { ... }

Solves one problem. Which pin to use. However I still need to pass which pin (struct field) I want to use

gpioc
        .pc9
        .into_push_pull_output(&mut gpioc.crh)

As far as I know, there is no way to do it based on LedPin.
Correct me if I am wrong because it seems to me that I have to copy and paste code for each implementation. I have to hardcode each pin pin9 in each function along with it's method to get output. pin. Or use macros to generate this code.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.