Embedded_hal generic types verbosity

#1

I have a question about the embedded_hals design. I am currently implementing a device driver based on the embedded_hal, which uses lots of GPIO peripherals. I have a hard time abstracting over InputPin + OutputPin, because my device driver is supposed to reconfigure some pins from output to input.

Here is my PIN configuration:
8 or 4 databits: IN / OUT
3 dedicated only OUT pins.

I came up with the following idea: Create an internal PinHolder which can be used to change the pin mode between in and out configurations. The actual conversion must be implemented by the driver user. For that the trait PinModeConverter is used. The driver user supplies the conversion functions (by implementing the trait on a type), and all the pins in an initial state (OUT for all).
At the top level there is Driver::new_4bit, and Driver::new_8bit, which sets up the Driver, and then there is a generic over all the CFN, and generic over DataBits protocol implementation.


pub trait PinModeConverter {
    type Output;
    type Input;
    fn into_output(pin: Self::Input) -> Self::Output;
    fn into_input(pin: Self::Output) -> Self::Input;
}

enum PinHolder<IN, OUT> {
    Output(OUT),
    Input(IN),
}

impl<IN, OUT, CFN> PinHolder<IN, OUT>
where
    CFN: PinModeConverter<Input = IN, Output = OUT>,
{
    fn is_out(&self) -> bool {...}
    fn is_input(&self) -> bool {...}
    fn toggle_mode(&mut self) {
        let is_out = self.is_out();
        let mut swapper = unsafe {mem::uninitialized()};
        mem::swap(self, &mut swapper);
        swapper = if is_out {
            PinHolder::Input(CFN::into_input(swapper.output().unwrap()))
        } else {
            PinHolder::Output(CFN::into_output(swapper.input().unwrap()))
        };
        mem::swap(self, &mut swapper);
        mem::forget(swapper);
    }
    fn to_input(&mut self) {...}
    fn to_output(&mut self) {...}
}

// Databit trait (implements 4 / 8bit mode of the peripheral) 
trait DataBits {
    fn raw_cmd(&self, cmd: RawCmd);
}
struct Data4bit<RO, R1, R2, R3, W0, W1, W2, W3>(PinHolder<R0, W0>, PinHolder<R1, W1>, ...);
struct Data8bit<...>(...);

impl<...> DataBits for Data4bit<...> {...}

// Private internal type to hold pins
struct OutPins<EN, RW, RS> {
    en: EN,
    rw: RW,
    rs: RS,
}

pub struct Driver<EN, RW, RS, DATA> {
    out_pins: OutPins<EN, RW, RS>,
    data_pins: DATA,
}

impl<EN, RW, RS, RO, R1, R2, R3, W0, W1, W2, W3> Driver<EN, RW, RS, Data4bit<RO, R1, R2, R3, W0, W1, W2, W3>>
where
    EN: OutputPin,
    RW: OutputPin,
    RS: OutputPin,
{
    pub fn new_4bit() -> Self {
        
    }
}

As you can see, this gets messy because of all the generics + constraints. I think the root of the problem is, that in embedded_hal each and every pin may be its own type. Is there some kind of generic solution to this problem? I am currently experimenting with macros to get rid of all the boilerplate.

0 Likes

#2

I have a related issue, I haven’t been using rust for long so I’m probably not understanding something with the type system. I’m using the embedded_hal on stm32f103 and trying to use RTFM. Declaring resources in RTFM appears to require explicit typing. This gets very verbose and specific for example:

static mut LED : stm32f1xx_hal::gpio::gpioc::PC13&lt;stm32f1xx_hal::gpio::Output&lt;stm32f1xx_hal::gpio::PushPull&gt;&gt; = ();

If I try to use a driver that uses a hal peripheral (mpu9250 for example) and intentionally mis-type the resource declaration I get a compile error saying the type should be something like:

mpu9250::Mpu9250<mpu9250::device::SpiDevice<hal::spi::Spi<hal::stm32::SPI1, (hal::gpio::gpioa::PA5<hal::gpio::Alternate<hal::gpio::PushPull>>, hal::gpio::gpioa::PA6<hal::gpio::Input<hal::gpio::Floating>>, hal::gpio::gpioa::PA7<hal::gpio::Alternate<hal::gpio::PushPull>>)>, hal::gpio::gpioa::PA4<hal::gpio::Output<hal::gpio::PushPull>>>, mpu9250::types::Imu>

however this is not usable for for my variable type since it contains private types. I’m not sure how this is supposed to work… Is there a more generic way to do this?

0 Likes

#3

I am not sure, what you mean with contains private types. Can you please elaborate a little more, I might be able to help you out, since this seems to be your first post :smiley:.

0 Likes

#4

Turns out I was not looking carefully enough. I can just use https://docs.rs/stm32f4xx-hal/0.3.0/stm32f4xx_hal/gpio/gpioa/struct.PA0.html#method.downgrade from my hal impl crate, and collect pins in an array (with all having the same type). I think this is somewhat not obvious, since it is not mentioned in the embedded-hal crate anywhere.

0 Likes

#5

i had quite some issues with this as well, it’s so much different from doing things in C, i’m sure the deep type magic makes some great things possible (and just takes getting used to it) but it’s fairly intimidating to newbies :slight_smile:

0 Likes