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

Treat a Gpio pin as either input or output.

Open Ben-PH opened this issue 2 years ago • 3 comments

I'm trying to implement a bit-bang controller for a peripheral (ADNS5050, specifically), based an CPP arduino code.

The problem I've run into, is that I need to be able to use one of the pins as both input and output. If there's an ergonomic/intuitive way to do this as things already are, I've missed it.

Ideally, I'd be able to write something like this:

struct ADNSDriver<'a, MODE>
{
    sdio: &'a mut dyn IOPin<Error = Infallible, MODE>,
    srl_clk: &'a mut dyn OutputPin<Error = Infallible>,
    not_reset: &'a mut dyn OutputPin<Error = Infallible>,
    not_chip_sel: &'a mut dyn OutputPin<Error = Infallible>,
    pix: [u8; 360],
}

Reason being, is I have code a bit like this:

    fn read_reg(&mut self, addr: ADNSRegs, delay: &mut Delay) -> u8 {
        let stdio_writer = self.sdio.into_push_pull_output();
        let mut addr = addr as u8;
        for _ in 0..8 {
            // use bit-banging to write with the sdio pin as an output
            // in order to choose which memory address to read from
        }

        let mut res = 0u8;

        for i in 0..8 {
            // use bit-baning to read from the sdio-pin as an InputPin in order to get the value at
            // that memory address
        }

        res
    }

...the ADNSDriver can use the sdio field either an output pin XOR an input pin. If there's a way to dance between the two modes in a nice way, I been unable to come across it.

Context: https://github.com/rust-embedded/embedded-hal/issues/357 in short: embedded-hal has the IoPin trait as well as some others, but is removing it for 1.0.0 (follow link for details).

Ben-PH avatar Aug 17 '23 14:08 Ben-PH

One thing that probably would work right now already is using our internal implementation details (I'm not sure if those should be kept public in future)

struct HoldGpioPin<P>
where
    P: esp32c3_hal::gpio::InputPin
        + esp32c3_hal::gpio::OutputPin
{
    pin: Option<P>,
}

impl<P> HoldGpioPin<P>
where
    P: esp32c3_hal::gpio::InputPin
        + esp32c3_hal::gpio::OutputPin
{
    fn do_output(&mut self) {
        let mut out = self.pin.take().unwrap();
        out.set_to_push_pull_output();
        out.set_output_high(true);
        self.pin.replace(out);
    }

    fn do_input(&mut self) -> bool {
        let mut inp = self.pin.take().unwrap();
        inp.set_to_input();
        let res = inp.is_input_high();
        self.pin.replace(inp);
        res
    }
}

... and use it like this

...
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);

    let mut foo = HoldGpioPin {
        pin: Some(io.pins.gpio5),
    };

    foo.do_output();
    delay.delay_ms(3500u32);

    loop {
        println!("{}", foo.do_input());
        delay.delay_ms(500u32);
    }
...

This way certainly you cannot implement things in a HAL agnostic way

bjoernQ avatar Aug 18 '23 07:08 bjoernQ

Not sure if we should implement the more or less deprecated IoPin or better wait for its return in EH-1.x

bjoernQ avatar Aug 30 '23 10:08 bjoernQ

Not sure if we should implement the more or less deprecated IoPin or better wait for its return in EH-1.x

Just hit into this, when tried to implement a driver for my segment display. Would be great to have a working implementation for current supported embedded_hal (which doesn’t mention deprecating IoPin in doc. However, I am not very familiar with its development flow). Or at least maybe document some workarounds for this? I have found two of them: either use open_drain_output, which implements both InputPin and OutputPin, or use push_pull_output with extra traits you mentioned.

SeTSeR avatar Nov 22 '23 07:11 SeTSeR

I think with our recent GPIO changes it should be easy to implement a Flexible pin type.

MabezDev avatar May 20 '24 09:05 MabezDev