Correct type for representing a Pin in avr-hal

I am trying to create a generic library interface for a piece of hardware that can use several GPIO pins, but I can't seem to figure out how to define them to pack into a struct for handling them. I have several output pins defined as such:

let dp = arduino_uno::Peripherals::take().unwrap();
let mut pins = arduino_uno::Pins::new(dp.PORTB, dp.PORTC, dp.PORTD);

let mut en_pin = pins.d7.into_output(&mut pins.ddr);
let mut rs_pin = pins.d6.into_output(&mut pins.ddr);
let mut rw_pin = pins.d5.into_output(&mut pins.ddr);

Now I'd like to drop them in a struct and allow them to be chosen as needed for each project. The pins en, rs, and rw won't necessarily always be d7, d6, and d5. However, I can't seem to figure out the correct type to store them as.

I wasn’t able to find docs for avr-hal, but dug around a bit in the repository. It exports a bunch of embedded_hal pin-related traits in the avr-hal-generic prelude, and it appears to be currently built against version 0.2.3. The device-specific crates also appear to re-export the generic prelude in their own.

So you can read the trait docs here and, if you’ve imported the prelude, you should be able to use them something like this:

struct MyDevice<EN, RS, RW> {
    en: EN,
    rs: RS,
    rw: RW
}

impl<EN: OutputPin, RS: OutputPin, RW: OutputPin>
MyDevice<EN, RS, RW> {
    pub fn new(en: EN, rs: RS, rw: RW) -> Self {
        MyDevice { en, rs, rw }
    }

    pub fn enable(&mut self) {
        self.rs.set_low();
        self.rw.set_low();
        self.en.set_high();
    }
}

Thanks! That got me a little closer. There is a small error in the argument to new, one of those colons should be a comma. OK, well, I imported OutputPin with use avr_hal_generic::hal::digital::OutputPin, but now I've hit a new error:

the trait arduino_uno::prelude::_embedded_hal_digital_OutputPin is not implemented for arduino_uno::atmega328p_hal::port::portd::PD6<avr_hal_generic::port::mode::Output>

l'm wondering if there's something further I need to use in order to pull in the missing trait. I'll have to play with this a little further.

Nevermind, it's working. Turns out, I had the wrong OutputPin. I need to use this:
avr_hal_generic::hal::digital::v2::OutputPin

1 Like

Just for completeness, there is another solution to your problem. First a bit of background:

Each pin has its own type, because that way it can be compiled down into a single instruction to set it high or low. It also allows other abstractions which depend on the exact pins used (e.g. because a peripheral is hard-wired to certain pins), to know at compile-time that the correct pins are passed in.

Of course, this makes it difficult for use-cases where you don't care about the exact pin. For those cases, you can .downgrade() your pins which gives you a variable that has the same type for all pins. Instead of having separate types and doing the differentiation between pins at compile-time, it is now done with a single type and a tiny amount of overhead at run-time. The tradeoff for this is that you get much cleaner code that isn't full of generics ...

If you want to know more, the details are in the avr-hal documentation. Hope this helps!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.