将仿制药用于嵌入式HAL结构

发布于 2025-01-17 10:30:15 字数 867 浏览 3 评论 0原文

我正在为STM32微控制器编写一个嵌入式程序。我希望能够存储代表按钮的GPIO引脚,以一个不错的合并结构。

由于嵌入式式嵌入式用仿制药编码“引脚”结构的状态,因此每个GPIO都有不同的锈类型,但是对于我的情况(按钮),它们都实现了embedded_hal :: digital :: v2 :: v2 :: inputpin特性。

我可以明确指定每个成员的类型,但是在进行配置时,我还必须为每个按钮编写PIN和端口,即:

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

这意味着指定结构中的类型会导致冗余,并且如果我需要更改一个PIN在硬件中,我需要在多个地方以代码更改它。

存储这些销钉的规范方法是什么?当然,我不能像在嵌入式上下文中那样使用特质对象。

目前,我有以下内容:

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
}

但是这似乎是潜在的代码气味。

另外,如果我想将此按钮结构存储在另一个结构中,则除非我明确指定(并因此写两次)PIN类型,否则仿制药的数量很可能很大。

是否有更好的方法可以有效?

I am writing an embedded program for an STM32 microcontroller. I would like to be able to store the GPIO pins that represent buttons in a nice, consolidated struct.

Because embedded-hal encodes the state of a "Pin" struct with generics, each GPIO has a different Rust type, however for my case (buttons) they all implement the embedded_hal::digital::v2::InputPin trait.

I could explicitly specify the type for each member, however I also have to write the pin and port for each button when doing configuration, i.e.:

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

This means that specifying the type in the struct would cause redundancy, and if ever I need to change a pin in hardware, I will need to change it in code in more than one place.

What is the canonical way to store these pins? Of course I cannot use trait objects as it is in an embedded context.

At the moment I have the following:

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
}

However this seems like a potential code smell.

Also, if I want to store this button struct inside another struct, the number of generics will most likely be very big in this parent struct unless I explicitly specify (and therefore write twice) the pin type.

Is there a better way to do this which is also efficient?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

蛮可爱 2025-01-24 10:30:16

您可以使用 into_dynamic< /代码>。它删除了一些类型信息,即引脚,但类型中仍然包含端口。

atsamd-halDynPin,这会完全删除所有类型信息。您需要这样的东西,但我对 ST HAL 不太熟悉。

You could use into_dynamic. It removes some type information, namely the pin, but still has the port in the type.

atsamd-hal has DynPin, which does fully remove all type information. Something like this is what you need, but I'm not as familiar with the ST HALs.

北方的韩爷 2025-01-24 10:30:15

您可以使用关联类型将单个类型封装在单个类型中:

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
}

然后,要实例化按钮您将使用单个类型,该类型使用inputpins特质。

You can encapsulate the 5 types within a single one using associated types as such:

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
}

Then to instantiate the Buttons you would use a single type that implements all of your necessary types using the InputPins trait.

开始看清了 2025-01-24 10:30:15

给定一些全部实现 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。它甚至声称比手写代码有更好的性能。

Given some structs that all implement InputPin,

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

you can construct an enum that also implements 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(),
        }
    }
}

Then, you can use the Pin enum everywhere without generics.

As this can become cumbersome and repetitive, there is a handy crate to do it for you: enum_dispatch. It even claims to result in better performance than the handwritten code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文