Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No generic way to create IO pin #349

Open
jg2562 opened this issue Apr 8, 2022 · 7 comments
Open

No generic way to create IO pin #349

jg2562 opened this issue Apr 8, 2022 · 7 comments
Labels
enhancement New feature or request question Further information is requested

Comments

@jg2562
Copy link
Contributor

jg2562 commented Apr 8, 2022

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!

@hargoniX
Copy link
Member

hargoniX commented Apr 8, 2022

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

@jg2562
Copy link
Contributor Author

jg2562 commented Apr 8, 2022

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.

@richardeoin
Copy link
Member

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

@jg2562
Copy link
Contributor Author

jg2562 commented Apr 10, 2022

@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.

@richardeoin
Copy link
Member

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 richardeoin added enhancement New feature or request question Further information is requested labels Apr 19, 2022
@jg2562
Copy link
Contributor Author

jg2562 commented Apr 20, 2022

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
Copy link
Contributor Author

jg2562 commented Apr 21, 2022

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants