Skip to content

How to wrap a pluckable HCons in a struct with outher fields? #221

@Ben-PH

Description

@Ben-PH

I would like to refactor this:

/// General Purpose Input/Output driver
pub struct IO {
    _io_mux: IO_MUX,
    pub pins: Pins,
}
(A reduced version of the `Pins` struct:)
struct Pins {
    gpio0: GpioPin<Unknown, 0>,
    gpio1: GpioPin<Unknown, 1>,
    gpio2: GpioPin<Unknown, 2>,
    gpio3: GpioPin<Unknown, 3>,
}

// MODE is a series of ZST structs. `Input<PullDown>`, for example
pub struct GpioPin<MODE, const GPIONUM: u8> {
    _mode: PhantomData<MODE>,
}

into this:

/// General Purpose Input/Output driver
pub struct IO<???> {
    _io_mux: IO_MUX,
    pub pins: PinHList<???>,
}
I've built macros to create the HList type and to construct an initial pin-list:
type InitialPins = gpiopin_HList!(0, 1, 2);
// -> 
type InitialPins = HCons<GpioPin<Unknown, { 0 }>,
                   HCons<GpioPin<Unknown, { 1 }>, 
                   HCons<GpioPin<Unknown, { 2 }>,
                   HNil>>>;


let pin_list = gpiopin_hlist!(0, 1, 2);
// -> 
let pin_list = frunk::hlist!(
    GpioPin::<Unknown>, 0>::new(),
    GpioPin::<Unknown>, 1>::new(),
    GpioPin::<Unknown>, 2>::new()
);
...and go from this:
// blinky example: https://github.com/esp-rs/esp-hal/blob/422eb2118676d4c1c938fa6cb2b8211fb177c232/esp32s3-hal/examples/blinky.rs
// Borrow GPIO4, and set it as an output
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
let mut led = io.pins.gpio4.into_push_pull_output();

// initialize the gpio4 pin by setting it to high
led.set_high().unwrap();

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

loop {
    led.toggle().unwrap();
    delay.delay_ms(500u32);
}
to something like this:
// Give pin 4 to an initialized, ready to go, blinker implementation.
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
let (blinky, io) = Blinker::<4>::init(io);

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

blinky.blink_loop(&mut delay, 500);
unreachable!();
And `Blinker` might look something like this:
struct Blinker<const PIN: u8> {
    pin: GpioPin<Output<PushPull>, PIN>
}

impl<const PIN: u8> Blinker<PIN>
{
    fn initialize<???, ???>(io: IO<???>) -> (Self, IO<???>)
    {
        let (pin, io) = io.pins.pluck();
        let mut pin = pin.pin.into_push_pull_output();
        pin.set_high().unwrap();
        (Self{pin}, io)
    }

    fn toggle(&mut self) {
        self.pin.toggle().unwrap();
    }
    
    fn blink_loop(mut self, delay: &mut Delay, rest_period: u16) -> ! {
        loop {
            self.toggle();
            delay.delay_ms(rest_period);
        }
    }
}

Setting up the IO struct is difficult:

  • It seems I need to implement Plucker for IO?
  • is it possible to implement Plucker in such a way that a const NUM: u8 can be used, and pluck solely based on the GpioNums const?
  • I'm not sure the kind of approach I'd need: should I just re-implement HCons from the ground up, copy-pasting a lot of the code? Is there a way I'd be able to re-use the frunk implementations (e.g. by setting the pins field to be HCons<???, ???>)?
  • Are there any other projects out there that I've missed, that I could use as an example. Closest I found was usbd-human-interface-device, but that doesn't take advantage of plucking (which is a core feature I'm after)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions