嵌入式HAL结构体中泛型的使用

7

我正在为STM32微控制器编写嵌入式程序。我希望能够将代表按钮的GPIO引脚存储在一个漂亮、整合的结构体中。

由于embedded-hal使用泛型编码"Pin"结构的状态,每个GPIO都有不同的Rust类型,但是对于我的情况(按钮),它们都实现了embedded_hal::digital::v2::InputPin特征。

我可以显式地指定每个成员的类型,但是当进行配置时,我还必须编写每个按钮的引脚和端口,即:

    let mut middle = gpioa
        .pa6
        .into_pull_up_input(&mut gpioa.moder, &mut gpioa.pupdr);

这意味着在结构体中指定类型会导致冗余,并且如果我需要在硬件中更改引脚,我将需要在多个地方更改代码。
存储这些引脚的规范方法是什么?当然,在嵌入式上下文中无法使用特性对象。
目前我有以下内容:
use embedded_hal::digital::v2::InputPin;

struct Buttons<M, R, L, U, D>
where
    M: InputPin,
    R: InputPin,
    L: InputPin,
    U: InputPin,
    D: InputPin,
{
    middle: M,
    right: R,
    left: L,
    up: U,
    down: D
}

然而,这似乎是潜在的代码异味。

此外,如果我想将此按钮结构存储在另一个结构中,除非我显式地指定(因此写两次)引脚类型,否则父结构中的泛型数量很可能非常大。

是否有更好的方法来处理这个问题并且同时保持高效?


这将是一个重复的问题A good way to pack GPIO_pin and GPIO_port in STM32,但我刚意识到你正在使用Rust... - Lundin
3个回答

4

你可以使用关联类型将这5种类型封装在单个类型中,如下所示:

trait InputPin {}

trait InputPins {
    type M: InputPin;
    type R: InputPin;
    type L: InputPin;
    type U: InputPin;
    type D: InputPin;
}

struct Buttons<Pins: InputPins> {
    middle: Pins::M,
    right: Pins::R,
    left: Pins::L,
    up: Pins::U,
    down: Pins::D
}

然后,为了实例化Buttons,您将使用一个实现所有必需类型的单一类型,并使用InputPins trait。


谢谢!这绝对是迄今为止最简洁的方法,没有任何不必要的检查/开销。然而,我仍然有一个问题,那就是类型必须重复,因为设置引脚的函数调用也涉及到类型。我想现在只能硬编码它们了。 - Gary Allen

1

假设有一些结构体都实现了 InputPin 接口,

struct M;
struct R;
struct L;
struct U;
struct D;

你可以构造一个同时实现InputPin的枚举类型:
enum Pin {
    M(M),
    R(R),
    L(L),
    U(U),
    D(D),
}

impl InputPin for Pin {
    type Error = ...;

    fn is_high(&self) -> Result<bool, Self::Error> {
        match self {
            Pin::M(pin) => pin.is_high(),
            Pin::R(pin) => pin.is_high(),
            Pin::L(pin) => pin.is_high(),
            Pin::U(pin) => pin.is_high(),
            Pin::D(pin) => pin.is_high(),
        }
    }

    fn is_low(&self) -> Result<bool, Self::Error> {
        match self {
            Pin::M(pin) => pin.is_low(),
            Pin::R(pin) => pin.is_low(),
            Pin::L(pin) => pin.is_low(),
            Pin::U(pin) => pin.is_low(),
            Pin::D(pin) => pin.is_low(),
        }
    }
}

接着,您可以在无需泛型的情况下随处使用Pin枚举。

由于这可能变得繁琐而重复,因此有一个方便的crate可为您执行此操作:enum_dispatch。 它甚至声称比手写代码性能更好。


0

你可以使用into_dynamic。它会移除一些类型信息,即引脚,但仍然保留了端口的类型。

atsamd-halDynPin,它完全移除了所有类型信息。像这样的东西是你需要的,但我对ST HALs不太熟悉。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接