stm32h7xx-hal icon indicating copy to clipboard operation
stm32h7xx-hal copied to clipboard

No generic way to create IO pin

Open jg2562 opened this issue 3 years ago • 7 comments

With the new pin rewrite to mimic the f4 api, I havent found a way to save several generic pins into a struct and use them as IO pins.

Heres an example struct

struct IOBus {
	io0: PA0,
	io1: PB1,
	io2: PC2,
	io3: PD3,
}

But I'm not sure how to make to it generic over the pin types.

At first I tried to use erased pins, but with the latest change, there's no pin mode manipulation for erased pins.

I also found this post on stack overflow describing a similar situation. But from what I can tell, theres no traits to abstract over for the pins nor a way to use the associated types for the pins directly.

My real world use case has over 40 pins in the same struct, so duplication of the struct isn't very feasible or ergonomic.

Im not sure if this is just a shortcoming of my knowledge or if its just not feasible yet, either way help would be greatly appreciated! Thanks!

jg2562 avatar Apr 08 '22 03:04 jg2562

Do you actually want to make it generic over the pin types? If its just that you can have a bunch of generics <A, B, C, D> that implement InputPin and OutputPin. If you want to use the concrete Pin types later on to e.g. instantiate an I2C bus or whatever that just won't cut it, you need the concrete pin types for that since the API will enforce that type safety constraint

hargoniX avatar Apr 08 '22 07:04 hargoniX

I believe I do, they won't be used to initialize anything later, but I need to be able to switch between Output<PushPull> and Input<Floating> in order to both get data from the pins and set data on the pins.

For more context, I'm interfacing with 2 chips similar to this sram. They are mounted on a custom daughter card that mounts on top of a nucleo board. In this case, I'm not sure how to use the data pins for both reading and writing, while keeping them in a struct that is generic for both chips on the device.

jg2562 avatar Apr 08 '22 09:04 jg2562

@jg2562 I think the DynamicPin API is what you're looking for here

richardeoin avatar Apr 10 '22 15:04 richardeoin

@richardeoin Thanks for replying richard! So, the DynamicPin is still generic over the port and pin, so doesn't it have the same problem as the regular Pin? I didnt see a way to erase the dynamic pin either. However, if I can use a generic pin, I can always use the with_<mode> methods. Thankfully for my use case it stays in the same mode most of the time, so I'm not rapidly switching between modes, meaning the with_<mode> methods work very nicely for this use case. Am I missing something that allows me to use the DynamicPin generically over a regular pin?

Also, as a side note, when i was experimenting with the pins in OUTPUT mode and I used the with_input method it gave back values that were not he current state of the pins at the time. I want to recheck it first, but that might a bug.

jg2562 avatar Apr 10 '22 23:04 jg2562

I don't think you're missing anything, it just seems that a combination of Dynamic and Erased pin is needed for your use-case

richardeoin avatar Apr 19 '22 21:04 richardeoin

So I ended up creating a few traits and then created implementations for the different pins. It seems like there should be a built-in trait for these, but I'm not sure if that's too complicated. Hopefully the below example can show their potential use case.

Here's an example of the traits I did (along with a use case to see what I mean). They work pretty well for my use case, so im not sure if others want it. It does allow for quite a bit of flexability but a lot of boilerplate.

// Traits
pub trait DevicePin {
    fn set_high(&mut self);
    fn set_low(&mut self);
    fn set_state(&mut self, state: PinState);
}

pub trait DeviceIOPin: DevicePin {
    fn is_high(&mut self) -> bool;
}


impl<const P: char, const N: u8> DevicePin for Pin<P, N, Output> {
    fn set_high(&mut self) {
        Pin::set_high(self);
    }

    fn set_low(&mut self) {
        Pin::set_low(self);
    }

    fn set_state(&mut self, state: PinState) {
        Pin::set_state(self, state)
    }
}

impl DevicePin for EPin<Output> {
    fn set_high(&mut self) {
        EPin::set_high(self);
    }

    fn set_low(&mut self) {
        EPin::set_low(self);
    }

    fn set_state(&mut self, state: PinState) {
        EPin::set_state(self, state);
    }
}

impl<const P: char, const N: u8> DeviceIOPin for Pin<P, N, Output> {
    fn is_high(&mut self) -> bool {
        self.with_input(|x| x.is_high())
    }
}

// Heres a complicated use case
pub trait DevicePins{
	type IO0: DeviceIOPin,
	type IO1: DeviceIOPin,
	type IO2: DeviceIOPin,
	type IO3: DeviceIOPin,

	type A0: DevicePin,
	type A1: DevicePin,
	type A2: DevicePin,
	type A3: DevicePin,
}

struct RightDevicePins{};
impl DevicePins for RightDevice{
	type IO0 = PA0<Output>;
	type IO1 = PB1<Output>;
	type IO2 = PC2<Output>;
	type IO3 = PD3<Output>;

	type A0 = PA10<Output>;
	type A1 = PB11<Output>;
	type A2 = PC12<Output>;
	type A3 = PD13<Output>;
}

struct LeftDevicePins{};
impl DevicePins for LeftDevice{
	type IO0 = PD0<Output>;
	type IO1 = PC1<Output>;
	type IO2 = PB2<Output>;
	type IO3 = PA3<Output>;

	type A0 = PD10<Output>;
	type A1 = PC11<Output>;
	type A2 = PB12<Output>;
	type A3 = PA13<Output>;
}

struct Device<DevicePins> {
	io0: DevicePins::IO0,
	io1: DevicePins::IO1,
	io2: DevicePins::IO2,
	io3: DevicePins::IO3,

	a0: DevicePins::A0,
	a1: DevicePins::A1,
	a2: DevicePins::A2,
	a3: DevicePins::A3,
}

// Actual usable device
type LeftDevice = Device<LeftDevicePins>;
type RightDevice = Device<RightDevicePins>;

jg2562 avatar Apr 20 '22 00:04 jg2562

I will report, I am getting very weird behavior with the with_input method, as I've seen the reads not being accurately reported, and they're stuck reported at the low state. I've also seen pin writes before the with_input read call no longer happen, and it seems that they're optimized out.

EDIT: I was able to get the write behavior consistently, so I opened an issue for it #358 along with a minimal example.

jg2562 avatar Apr 21 '22 01:04 jg2562