esp-hal
esp-hal copied to clipboard
Inverting GPIOs
Why?
Today I came across a SPI device that has an inverted CS line. With the current HAL implementation, this situation cannot easily be handled: The SpiDevice trait as implemented on SpiBusDevice requires an IO that implements the OutputPin trait from the esp-hal, hence something like inverted-pin doesn't work. This leaves me with having to forego SpiBusDevice in favor of manually handling the CS line which isn't nice.
What to do about it?
The esp32 has hardware support for inverting both input and output GPIOs in hardware, via the GPIO_FUNCx_(OUT|IN)_INV_SEL bit.
Hence, I think it would be possible to add GPIO inversion at zero cost by extending the Pin trait from esp-hal. What are your thoughts on this? Would you accept a PR that implements this?
I think in general if the hardware supports it we will accept PRs, so go nuts :)
Just as a note I am in the process of completely rewriting the GPIO module, however I'm not sure how long this will take so you can likely implement this and get it merged before I'm done. I will of course propagate your changes to my rewrite if this does happen.
We have everything needed to do that in
- https://github.com/esp-rs/esp-hal/blob/9fa59fe12754b367cb0b75668dbd896e8a78fba8/esp-hal-common/src/gpio.rs#L158-L163
- https://github.com/esp-rs/esp-hal/blob/9fa59fe12754b367cb0b75668dbd896e8a78fba8/esp-hal-common/src/gpio.rs#L196-L203
Only thing we are missing is a way to expose it in the API. That is everywhere we want to have this feature we need a special constructor for the peripheral driver that allows to specify if a pin should be inverted or not.
@bjoernQ How we can invoke the inversion?
E.g. here it's used in SmartLED which should have inverted signal: https://github.com/georgik/esp32-buddy-rs/blob/feature/update-hal-2023-05/examples/rainbow.rs#L89
// Configure RMT peripheral globally
let pulse = PulseControl::new(
peripherals.RMT,
&mut system.peripheral_clock_control,
ClockSource::APB,
0,
0,
0,
)
.unwrap();
let mut led = <smartLedAdapter!(1)>::new(pulse.channel1, io.pins.gpio25);
Should be just GPIO25 womehow wrapped or do we need to make whole driver inverted?
Pins get passed into the drivers and the drivers configure the pins accordingly (e.g. here https://github.com/esp-rs/esp-hal/blob/7b3e19c4c6bbca778f3ac77c036635bb1a919e75/esp-hal-common/src/pulse_control.rs#L761-L763 )
In this case we would need to call connect_peripheral_to_output_with_options instead and set the options to invert the pin.
This then needs to be reflected in the user-facing API (e.g. alternative constructors with an option to invert a pin)