Description
The IoPin trait - added in #29, seems like it would be impractical to implement in a system with multiple input / output pin types. The STM32 HALs that I'm familiar with (g0, l4, h7) use different types for OpenDrain and PushPull pin types. Because IoPin is parameterized on both input and output pin types, it appears that you would need to implement every combination NxM (where N is the number of input types and M is the number of output types).
Is there a reason why it's one trait rather than two? I guess having a single trait can be good if you want to construct a trait object - although I don't think you can do that in this case anyway, since the methods take self by value.
The only implementation I managed to find was in linux-embedded-hal which seems to only have a single type for pins.
Activity
Dirbaio commentedon Dec 21, 2021
There's one in stm32f4xx-hal. I agree the trait is quite unwieldy, yes: https://github.com/stm32-rs/stm32f4xx-hal/blob/dc1552b2fc2e14901203a13ab42e76a5121a08eb/src/gpio.rs#L574-L683
It also feels strange to me
TInput, TOutput
are trait generic params instead of associated types.Also, taking
self
interacts badly with having driver structs own the pin, because it changes type on the fly so you have to store an enum in the struct to store all the possible states...I wonder if it'd be better to have a trait for a pin that you can switch at runtime without changing type.
ryankurte commentedon Jan 11, 2022
this seems preferable to me, maybe with
set_(input|output)
andis_(input|output)
?i tried to do some trickier type-based stuff but, i think there's elegance in this simplicity
kelnos commentedon Jun 14, 2022
I've run into some difficulties with
IoPin
as well, but from a different angle: a crate that uses anIoPin
and wants to hold onto it for the life of a struct.Initially I implemented the struct (simplified) as
But then while using
linux-embedded-hal
, I found that the Linux gpiochip interface doesn't allow you to have a pin handle that is both an input and output at the same time, and has to be explicitly switched back and forth (unlike the ESP32 I'm also using for this project). So I foundIoPin
, but I'm at a loss for how to do this in a reasonable way.I've switched to (again, simplified):
And then using it looks something like this:
Ok, so on the face of it that's not terrible, but what happens if there is an error anywhere? Welp, the pin gets dropped and is now gone for good, and if someone calls
read()
again it'll panic. So now I have to get rid of all the nice, easy?
error handling and manually check for error states and re-set the pin inself
if there's an error before bailing.I think the proposed alternative in #340 (comment) is nice from the perspective of simplicity, but the big downside is that the
IoPin
impl would still expose theset_high()
andset_low()
functions while the pin is in input mode. Another option:The idea here is that the two
as_
functions would return a completely different struct that implements either the input or output pin functionality. The issue here is that there is nothing keeping you from calling (for example)as_output_pin()
while there's an instance of the input pin still alive (which could return an error, but it's a shame to force something to be a runtime error if we can make it a compile time error). I guess perhaps this could work too:The idea there would be to keep the mutable borrow on the
IoPin
open for the lifetime of the input/output pin impl. Downside here is that makes things a bit harder on implementors, as they'd have to stash the input/output struct instance insideself
. Not completely terrible, though.Merge #393
Finomnis commentedon Sep 20, 2022
I also have doubts about
IoPin
. Questions that are unclear to me:Input
andOutput
? Is it expected that switching toInput
will disable the output, or just enable read capability? (nrf chips, for example, can read/write simultaneously, and for energy consumption the input part can be switched off)InputPin + OutputPin
implementIoPin<Self, Self>
? (This would mean that the output should not necessarily be disabled during read.)IoPin<Self, Self>
)?InputPin + OutputPin
and also implementIoPin<Self, Self>
, one could theoretically call.try_into_input_pin()
, followed by.set_high()
. How should this behave?I know it now got removed for
1.0.0
, but if it ever gets reintroduced, a thorough explanation what the expected pin behaviour is would absolutely be necessary. But I feel like this trait just introduces way too many corner cases and ambiguities to be re-introduced.rumatoest commentedon Nov 10, 2023
I've tried to implement a HAL based drive once and stuck into an issue that some MCU (AVR for example) are not supporting open/drain pins and similar stuff. But still you could communicate with some sensors via single pin by simply switching between input/output modes like it done for Arduino (see DHT11 DHT22).
In order to do it using HAL I need to have some interface that would not consume itself while toggling between input/output.
The thing here is that implementing
InputPin + OutputPin
trait for pins is not a mandatory. Which means that driver developers could not rely thatInputPin + OutputPin
will be in every HAL implementation for different MCU. Thus such drivers would not work everywhere while it is possible to force such behavior in HAL by introducing something like IoPin trait that must be implemented.1 remaining item