Rahix / avr-hal

embedded-hal abstractions for AVR microcontrollers
Apache License 2.0
1.3k stars 220 forks source link

Making a tri-state pin (switching between input and output), perhaps like `embedded_hal::digital::IoPin` #583

Open jaqx0r opened 2 weeks ago

jaqx0r commented 2 weeks ago

Hi,

(Please excuse the terminology and knowledge, I'm new to both Rust and MCU programming.)

I am trying to write some code that drives a charlieplexed LED array. In order to do so, I need to switch pins from input (high impedance) to output hi/lo, and then return them to input when switching the circuit off.

I have tried badly at moving the pin into an output pin with a small scoped block, and then back into an input pin, pseudocode like this:

let out_hi = pin[index].into_output_high();
delay(...);
pin[index] = out_hi.into_input();

but I'm struggling with managing the types for all the pins attached to the matrix, and using Dynamic doesn't help as you can't change the pin mode afterwards.

Now, because I also want to use the arduino_hal::pins!() pins in order to commuinicate with I2C devices and the serial port, I don't want to simply unsafe to the pins directly with PORTB.ddrb and friends, but I think that's my only option at the moment.

What I would like is an interface that is in-between the bit-bang layer and the full safety of static pin modes, which I think the PinOps trait offers with its unsafe methods out_set, out_clear, make_input.

Is this a reasonable request? Once I get the pins out of arduino_hal::pins!*(), unsafely turning specific pins into different modes is an easier bit of code to reason about than sharing all the pins from PORTB et al after safely sharing some of them for serial and i2c access.

Maybe it's easier than this and I just need to understand how to move a pin safely, generic across the PinOps?

Finally, if avr-hal supported embedded_hal::digital::IoPin, I think that's close to what I want, but https://github.com/rust-embedded/embedded-hal/issues/340 makes me think this is also not a trivial request either.

Thanks for any advice you can offer.

Rahix commented 2 weeks ago

Hi,

this is a known open topic. There is #205 with a concept implementation that could suit such situations. Would the API from #205 work for you?

jaqx0r commented 1 week ago

Yes, that looks useful! If I can use two pins in nested closures, like so:

let hi_pin = pins.X.into_floating_input();
let lo_pin = pins.Y.into_floating_input();

...

hi_pin.as_output_high(|_| {
   lo_pin.as_output(|_| {
      delay(...);
   })
})

then that suits my needs perfectly.

jaqx0r commented 1 week ago

I was able to borrow your ideas from that PR and ended up with https://github.com/jaqx0r/bulbdialclock/blob/main/src/main.rs#L237-L243 which solves my immediate problem but I look forward to something like #205 being merged so I can avoid the unsafe blocks.

Thanks!

Rahix commented 1 week ago

Going to keep this issue open until #205 is resolved.