Skip to content
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

port: Add methods to temporarily switch a pin to a different mode #205

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions avr-hal-generic/src/port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,91 @@ impl<PIN: PinOps, MODE: mode::Io> Pin<MODE, PIN> {
}
}


impl<PIN: PinOps> Pin<mode::Output, PIN> {
/// Temporarily put this pin into input mode (with pull-up enabled) and revert back to output afterwards.
pub fn with_pin_as_pull_up_input<F, R>(&mut self, f: F) -> R
where
F: FnOnce(&mut Pin<mode::Input<mode::PullUp>, PIN>) -> R,
{
// Save state so we can restore it afterwards
let state = self.is_set_high();
// To "move" the pin out of current "owner" variable without actually moving
// SAFETY: This is okay because the pins are trivial structs.
let pin: Self = unsafe { core::ptr::read(self) };
let mut pin = pin.into_pull_up_input();
let res = f(&mut pin);
*self = if state {
pin.into_output_high()
} else {
pin.into_output()
};
Comment on lines +229 to +236
Copy link
Owner Author

Choose a reason for hiding this comment

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

This implementation is still quite ugly and not 100% sound either. I quite like the approach in stm32f4xx-hal: https://docs.rs/stm32f4xx-hal/0.11.1/src/stm32f4xx_hal/gpio/convert.rs.html#476-490

This is at least more sound than my hack. So I'll try to reimplement that pattern here.

Copy link
Owner Author

@Rahix Rahix Oct 8, 2022

Choose a reason for hiding this comment

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

Source: https://github.com/stm32-rs/stm32h7xx-hal/blob/21d9c06a57169e92cf5745e582dca171c73294c9/src/gpio/convert.rs

It looks like this will unfortunately require a larger change to make it work. But I think following that route is still a good idea and will improve the remainder of the port module as well. Will just need a bit more time...

res
}

/// Temporarily put this pin into input mode (floating) and revert back to output afterwards.
pub fn with_pin_as_floating_input<F, R>(&mut self, f: F) -> R
where
F: FnOnce(&mut Pin<mode::Input<mode::Floating>, PIN>) -> R,
{
// Save state so we can restore it afterwards
let state = self.is_set_high();
// To "move" the pin out of current "owner" variable without actually moving
// SAFETY: This is okay because the pins are trivial structs.
let pin: Self = unsafe { core::ptr::read(self) };
let mut pin = pin.into_floating_input();
let res = f(&mut pin);
*self = if state {
pin.into_output_high()
} else {
pin.into_output()
};
res
}
}

impl<PIN: PinOps, IMODE: mode::InputMode> Pin<mode::Input<IMODE>, PIN> {
pub fn with_pin_as_output<F, R>(&mut self, f: F) -> R
where
F: FnOnce(&mut Pin<mode::Output, PIN>) -> R,
{
// Find out if pull-up was enabled
let was_pull_up = unsafe { self.pin.out_get() };
// To "move" the pin out of current "owner" variable without actually moving
// SAFETY: This is okay because the pins are trivial structs.
let pin: Self = unsafe { core::ptr::read(self) };
let mut pin = pin.into_output();
let res = f(&mut pin);
// TODO: Here it would be nice to write back to *self, but should work without for now.
if was_pull_up {
pin.into_pull_up_input();
} else {
pin.into_floating_input();
};
res
}

pub fn with_pin_as_output_high<F, R>(&mut self, f: F) -> R
where
F: FnOnce(&mut Pin<mode::Output, PIN>) -> R,
{
// Find out if pull-up was enabled
let was_pull_up = unsafe { self.pin.out_get() };
// To "move" the pin out of current "owner" variable without actually moving
// SAFETY: This is okay because the pins are trivial structs.
let pin: Self = unsafe { core::ptr::read(self) };
let mut pin = pin.into_output_high();
let res = f(&mut pin);
// TODO: Here it would be nice to write back to *self, but should work without for now.
if was_pull_up {
pin.into_pull_up_input();
} else {
pin.into_floating_input();
};
res
}
}

/// # Downgrading
/// For applications where the exact pin is irrelevant, a specific pin can be downgraded to a
/// "dynamic pin" which can represent any pin:
Expand Down