Skip to content

AnyPin #384

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

Merged
merged 1 commit into from
Feb 8, 2023
Merged

AnyPin #384

merged 1 commit into from
Feb 8, 2023

Conversation

bjoernQ
Copy link
Contributor

@bjoernQ bjoernQ commented Feb 6, 2023

This is not necessarily meant to get merged but we can if we agree it's a good idea

This adds an ErasedPin type by having an enum and using the enum-dispatch pattern.
The good thing is, it's easy to use this pattern also for other heavily typed types (e.g. SPI, I2C, I2S, ...) and doesn't need any refactoring of existing code. In other places we can even use an existing macro to implement From and TryInto - but all macros I found didn't support generics which we need for the mode.

It looks like a nice solution but maybe I forgot something that makes it actually a bad thing 🤷‍♂️

Closes #115
Closes #277

@bjoernQ
Copy link
Contributor Author

bjoernQ commented Feb 6, 2023

Seems like ESP32 with its InputOnly pins causes problems here and make the build fail

One solution to this would be to have ErasedOutputPin and ErasedInputPin instead of ErasedPin

@bjoernQ bjoernQ marked this pull request as draft February 6, 2023 12:38
@MabezDev
Copy link
Member

MabezDev commented Feb 7, 2023

So I understand the need for the handle_gpio! macro, but I can't figure out what and why we need the make_enum_proxy proc macro - sorry, I'm probably just being dense :D. Also, ErasedPin is public so users could match on it, which might make things confusing? Maybe its best to hide it.

Maybe to make things simpler, something like this:

enum ErasedPin<MODE> {
    Gpio0(Gpio0<MODE>),
    Gpio1(Gpio1<MODE>),
    // ...
}

pub struct AnyPin<MODE> {
  inner: ErasedPin<MODE>
}

// .. e-h and e-h-a impls on AnyPin

@MabezDev
Copy link
Member

MabezDev commented Feb 7, 2023

Seems like ESP32 with its InputOnly pins causes problems here and make the build fail

One solution to this would be to have ErasedOutputPin and ErasedInputPin instead of ErasedPin

Can we make it panic instead if you try to use them?

@bjoernQ
Copy link
Contributor Author

bjoernQ commented Feb 7, 2023

Sorry, I probably made a bad job explaining the idea here - basically the idea is borrowed from https://crates.io/crates/enum_dispatch (but we can't use that or any of the similar macros I found, so I made our own)

So, we have that enum like this:

enum ErasedPin<MODE> {
 Gpio0(Gpio0),
 Gpio1(Gpio1),
}

To dispatch calls we need something like this

impl<MODE> SomeTraitAlreadyImplementedOnPin for ErasedPin<MODE> {
    fn method_on_trait(&self) {
        match self {
            ErasedPin::Gpio0(target) => {  target.method_on_trait()  }
            ErasedPin::Gpio1(target) => {  target.method_on_trait()  }
                 ...
        }
    }
}

That new macro generats a macro that handles all variants of the given enum.

I like that AnyPin suggestion 👍 Thanks for pointing that out!

Seems like ESP32 with its InputOnly pins causes problems here and make the build fail
One solution to this would be to have ErasedOutputPin and ErasedInputPin instead of ErasedPin

Can we make it panic instead if you try to use them?

I would like that more than ErasedOutputPin / ErasedInputPin but I don't yet have an idea how to do that since it would mean that when we generate the handle_gpio macro we would need to know if we are allowed to create the dispatch code for a variant or not. We might get away with generating that macro just for GPIO (my original idea was to create something which we can also use for other use cases like SPI etc. - but maybe GPIO is so special that there is no other way here - I will look into that)

@bjoernQ
Copy link
Contributor Author

bjoernQ commented Feb 7, 2023

This makes things like this possible

#![no_std]
#![no_main]

use esp32c3_hal::{
    clock::ClockControl,
    gpio::{AnyPin, Output, PushPull, IO},
    peripherals::Peripherals,
    prelude::*,
    timer::TimerGroup,
    Delay,
    Rtc,
};
use esp_backtrace as _;
use esp_riscv_rt::entry;

#[entry]
fn main() -> ! {
    let peripherals = Peripherals::take();
    let system = peripherals.SYSTEM.split();
    let clocks = ClockControl::boot_defaults(system.clock_control).freeze();

    // Disable the watchdog timers. For the ESP32-C3, this includes the Super WDT,
    // the RTC WDT, and the TIMG WDTs.
    let mut rtc = Rtc::new(peripherals.RTC_CNTL);
    let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
    let mut wdt0 = timer_group0.wdt;
    let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks);
    let mut wdt1 = timer_group1.wdt;

    rtc.swd.disable();
    rtc.rwdt.disable();
    wdt0.disable();
    wdt1.disable();

    // Set GPIO5 as an output, and set its state high initially.
    let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
    let mut led = io.pins.gpio3.into_push_pull_output();
    let mut led1 = io.pins.gpio4.into_push_pull_output();
    let mut led2 = io.pins.gpio5.into_push_pull_output();

    let mut pins = [led.into(), led2.into()];

    // Initialize the Delay peripheral, and use it to toggle the LED state in a
    // loop.
    let mut delay = Delay::new(&clocks);

    loop {
        toggle_em(&mut pins);
        delay.delay_ms(500u32);
    }
}

fn toggle_em(pins: &mut [AnyPin<Output<PushPull>>]) {
    for p in pins.iter_mut() {
        p.toggle().unwrap();
    }
}

@bjoernQ
Copy link
Contributor Author

bjoernQ commented Feb 7, 2023

Can we make it panic instead if you try to use them?
I would like that more than ErasedOutputPin / ErasedInputPin but I don't yet have an idea how to do that since it would mean that when we generate the handle_gpio macro we would need to know if we are allowed to create the dispatch code for a variant or not. We might get away with generating that macro just for GPIO (my original idea was to create something which we can also use for other use cases like SPI etc. - but maybe GPIO is so special that there is no other way here - I will look into that)

It's now very GPIO specific but shouldn't fail in CI anymore (besides currently crates.io seem to have trouble)

@bjoernQ
Copy link
Contributor Author

bjoernQ commented Feb 8, 2023

I guess this is done so far. Would it make sense to add a simple example especially for this?

@MabezDev
Copy link
Member

MabezDev commented Feb 8, 2023

I think I'm on board with this approach, another thing I have in my head is can we use these macros to hide the implementation details of the typed Gpio structs? Maybe this is asking too much of these macros :D.

@MabezDev
Copy link
Member

MabezDev commented Feb 8, 2023

I guess this is done so far. Would it make sense to add a simple example especially for this?

Yeah I think it would be worthwhile adding an example on this. I think there's a lot of use-cases for it.

@MabezDev
Copy link
Member

MabezDev commented Feb 8, 2023

let mut pins = [led.into(), led2.into()];

Maybe we could add a degrade (seems to be the common name across other hals) as well as the From<> implementation? Might help with some type inference stuff.

@bjoernQ
Copy link
Contributor Author

bjoernQ commented Feb 8, 2023

I think I'm on board with this approach, another thing I have in my head is can we use these macros to hide the implementation details of the typed Gpio structs? Maybe this is asking too much of these macros :D.

🎉 Currently I can't think of using this to hide the implementation details of the typed Gpio struct but maybe we'll explore ways to do that

But for now I think this is useful and working fine

@bjoernQ bjoernQ marked this pull request as ready for review February 8, 2023 13:45
Copy link
Member

@MabezDev MabezDev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I was just getting ahead of myself :D.

LGTM! Nice to finally have something like this :)

@bjoernQ bjoernQ merged commit df891b4 into esp-rs:main Feb 8, 2023
@bjoernQ bjoernQ deleted the erased-pin branch February 8, 2023 14:00
@MabezDev MabezDev changed the title Erased Pin AnyPin Feb 8, 2023
@jessebraham
Copy link
Member

I didn't have time to get to this over the last couple days so I guess I missed out on my chance for input, but had we not decided on FlexPin for the naming previously? Or am I just imagining things?

@bjoernQ
Copy link
Contributor Author

bjoernQ commented Feb 8, 2023

I didn't have time to get to this over the last couple days so I guess I missed out on my chance for input, but had we not decided on FlexPin for the naming previously? Or am I just imagining things?

The name FlexPin rings a bell ... I guess it's not too late 😆

@MabezDev
Copy link
Member

MabezDev commented Feb 8, 2023

FlexPin would be slightly different, as our current AnyPin still has the mode information encoded in the type parameter. AnyPin in this context is essentially removing the numbered type, but keeping the mode type, allowing, well, any pin to be used. AnyPin exists in embassy hals: https://github.com/search?q=repo%3Aembassy-rs%2Fembassy%20AnyPin&type=code.

@jessebraham
Copy link
Member

Can we get some documentation for our GPIO then please? I don't even understand how it works anymore, apparently.

@bjoernQ
Copy link
Contributor Author

bjoernQ commented Feb 8, 2023

Ah I think I remember we discussed pins that can change their mode dynamically - and there we discussed FlexPin

@bjoernQ
Copy link
Contributor Author

bjoernQ commented Feb 8, 2023

Can we get some documentation for our GPIO then please? I don't even understand how it works anymore, apparently.

Do you mean API docs or developer docs?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Object safe error when making array of Gpio digital output pins Generic Pin Type
3 participants