Skip to content

Add an API for temporary GPIO reconfiguration #74

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 2 commits into from
Feb 25, 2020
Merged
Show file tree
Hide file tree
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
268 changes: 196 additions & 72 deletions src/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,63 @@ pub struct Output<MODE> {
/// Push pull output (type state)
pub struct PushPull;

mod sealed {
pub trait Sealed {}
}

/// Marker trait for valid pin modes (type state).
///
/// It can not be implemented by outside types.
pub trait PinMode: sealed::Sealed {
// These constants are used to implement the pin configuration code.
// They are not part of public API.

#[doc(hidden)]
const PUPDR: u8;
#[doc(hidden)]
const MODER: u8;
#[doc(hidden)]
const OTYPER: Option<u8> = None;
}

impl sealed::Sealed for Input<Floating> {}
impl PinMode for Input<Floating> {
const PUPDR: u8 = 0b00;
const MODER: u8 = 0b00;
}

impl sealed::Sealed for Input<PullDown> {}
impl PinMode for Input<PullDown> {
const PUPDR: u8 = 0b10;
const MODER: u8 = 0b00;
}

impl sealed::Sealed for Input<PullUp> {}
impl PinMode for Input<PullUp> {
const PUPDR: u8 = 0b01;
const MODER: u8 = 0b00;
}

impl sealed::Sealed for Analog {}
impl PinMode for Analog {
const PUPDR: u8 = 0b00;
const MODER: u8 = 0b11;
}

impl sealed::Sealed for Output<OpenDrain> {}
impl PinMode for Output<OpenDrain> {
const PUPDR: u8 = 0b00;
const MODER: u8 = 0b01;
const OTYPER: Option<u8> = Some(0b1);
}

impl sealed::Sealed for Output<PushPull> {}
impl PinMode for Output<PushPull> {
const PUPDR: u8 = 0b00;
const MODER: u8 = 0b01;
const OTYPER: Option<u8> = Some(0b0);
}

/// GPIO Pin speed selection
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Speed {
Expand Down Expand Up @@ -85,7 +142,8 @@ macro_rules! gpio {
use crate::rcc::Rcc;
use super::{
Floating, GpioExt, Input, OpenDrain, Output, Speed,
PullDown, PullUp, PushPull, AltMode, Analog, Port
PullDown, PullUp, PushPull, AltMode, Analog, Port,
PinMode,
};

/// GPIO parts
Expand Down Expand Up @@ -218,122 +276,188 @@ macro_rules! gpio {
}
}

impl<MODE> $PXi<MODE> {
/// Configures the pin to operate as a floating input pin
pub fn into_floating_input(
self,
) -> $PXi<Input<Floating>> {
impl<MODE: PinMode> $PXi<MODE> {
/// Puts `self` into mode `M`.
///
/// This violates the type state constraints from `MODE`, so callers must
/// ensure they use this properly.
fn mode<M: PinMode>(&mut self) {
let offset = 2 * $i;
unsafe {
&(*$GPIOX::ptr()).pupdr.modify(|r, w| {
w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset))
w.bits((r.bits() & !(0b11 << offset)) | (u32::from(M::PUPDR) << offset))
});

if let Some(otyper) = M::OTYPER {
&(*$GPIOX::ptr()).otyper.modify(|r, w| {
w.bits(r.bits() & !(0b1 << $i) | (u32::from(otyper) << $i))
});
}

&(*$GPIOX::ptr()).moder.modify(|r, w| {
w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset))
})
w.bits((r.bits() & !(0b11 << offset)) | (u32::from(M::MODER) << offset))
});
}
}

fn with_mode<M, F, R>(
&mut self,
f: F
) -> R
where
M: PinMode,
F: FnOnce(&mut $PXi<M>) -> R,
{
struct ResetMode<'a, ORIG: PinMode> {
pin: &'a mut $PXi<ORIG>,
}

impl<'a, ORIG: PinMode> Drop for ResetMode<'a, ORIG> {
fn drop(&mut self) {
self.pin.mode::<ORIG>();
}
}

self.mode::<M>();

// This will reset the pin back to the original mode when dropped.
// (so either when `with_mode` returns or when `f` unwinds)
let _resetti = ResetMode { pin: self };

let mut witness = $PXi {
_mode: PhantomData
};

f(&mut witness)
}

/// Configures the pin to operate as a floating input pin.
pub fn into_floating_input(
mut self,
) -> $PXi<Input<Floating>> {
self.mode::<Input<Floating>>();
$PXi {
_mode: PhantomData
}
}

/// Configures the pin to operate as a pulled down input pin
/// Temporarily configures this pin as a floating input.
///
/// The closure `f` is called with the reconfigured pin. After it returns,
/// the pin will be configured back.
pub fn with_floating_input<R>(
&mut self,
f: impl FnOnce(&mut $PXi<Input<Floating>>) -> R,
) -> R {
self.with_mode(f)
}

/// Configures the pin to operate as a pulled-down input pin.
pub fn into_pull_down_input(
self,
) -> $PXi<Input<PullDown>> {
let offset = 2 * $i;
unsafe {
&(*$GPIOX::ptr()).pupdr.modify(|r, w| {
w.bits((r.bits() & !(0b11 << offset)) | (0b10 << offset))
});
&(*$GPIOX::ptr()).moder.modify(|r, w| {
w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset))
})
};
mut self,
) -> $PXi<Input<PullDown>> {
self.mode::<Input<Floating>>();
$PXi {
_mode: PhantomData
}
}

/// Configures the pin to operate as a pulled up input pin
/// Temporarily configures this pin as a pulled-down input.
///
/// The closure `f` is called with the reconfigured pin. After it returns,
/// the pin will be configured back.
pub fn with_pull_down_input<R>(
&mut self,
f: impl FnOnce(&mut $PXi<Input<PullDown>>) -> R,
) -> R {
self.with_mode(f)
}

/// Configures the pin to operate as a pulled-up input pin.
pub fn into_pull_up_input(
self,
mut self,
) -> $PXi<Input<PullUp>> {
let offset = 2 * $i;
unsafe {
&(*$GPIOX::ptr()).pupdr.modify(|r, w| {
w.bits((r.bits() & !(0b11 << offset)) | (0b01 << offset))
});
&(*$GPIOX::ptr()).moder.modify(|r, w| {
w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset))
})
};
self.mode::<Input<PullUp>>();
$PXi {
_mode: PhantomData
}
}

/// Configures the pin to operate as an analog pin
/// Temporarily configures this pin as a pulled-up input.
///
/// The closure `f` is called with the reconfigured pin. After it returns,
/// the pin will be configured back.
pub fn with_pull_up_input<R>(
&mut self,
f: impl FnOnce(&mut $PXi<Input<PullUp>>) -> R,
) -> R {
self.with_mode(f)
}

/// Configures the pin to operate as an analog pin.
pub fn into_analog(
self,
mut self,
) -> $PXi<Analog> {
let offset = 2 * $i;
unsafe {
&(*$GPIOX::ptr()).pupdr.modify(|r, w| {
w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset))
});
&(*$GPIOX::ptr()).moder.modify(|r, w| {
w.bits((r.bits() & !(0b11 << offset)) | (0b11 << offset))
});
}
self.mode::<Analog>();
$PXi {
_mode: PhantomData
}
}

/// Configures the pin to operate as an open drain output pin
/// Temporarily configures this pin as an analog pin.
///
/// The closure `f` is called with the reconfigured pin. After it returns,
/// the pin will be configured back.
pub fn with_analog<R>(
&mut self,
f: impl FnOnce(&mut $PXi<Analog>) -> R,
) -> R {
self.with_mode(f)
}

/// Configures the pin to operate as an open drain output pin.
pub fn into_open_drain_output(
self,
mut self,
) -> $PXi<Output<OpenDrain>> {
let offset = 2 * $i;
unsafe {
&(*$GPIOX::ptr()).pupdr.modify(|r, w| {
w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset))
});
&(*$GPIOX::ptr()).otyper.modify(|r, w| {
w.bits(r.bits() | (0b1 << $i))
});
&(*$GPIOX::ptr()).moder.modify(|r, w| {
w.bits((r.bits() & !(0b11 << offset)) | (0b01 << offset))
})
};
self.mode::<Output<OpenDrain>>();
$PXi {
_mode: PhantomData
}
}

/// Configures the pin to operate as an push pull output pin
/// Temporarily configures this pin as an open drain output.
///
/// The closure `f` is called with the reconfigured pin. After it returns,
/// the pin will be configured back.
pub fn with_open_drain_output<R>(
&mut self,
f: impl FnOnce(&mut $PXi<Output<OpenDrain>>) -> R,
) -> R {
self.with_mode(f)
}

/// Configures the pin to operate as an push-pull output pin.
pub fn into_push_pull_output(
self,
mut self,
) -> $PXi<Output<PushPull>> {
let offset = 2 * $i;
unsafe {
&(*$GPIOX::ptr()).pupdr.modify(|r, w| {
w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset))
});
&(*$GPIOX::ptr()).otyper.modify(|r, w| {
w.bits(r.bits() & !(0b1 << $i))
});
&(*$GPIOX::ptr()).moder.modify(|r, w| {
w.bits((r.bits() & !(0b11 << offset)) | (0b01 << offset))
})
};
self.mode::<Output<PushPull>>();
$PXi {
_mode: PhantomData
}
}

/// Set pin speed
/// Temporarily configures this pin as a push-pull output.
///
/// The closure `f` is called with the reconfigured pin. After it returns,
/// the pin will be configured back.
pub fn with_push_pull_output<R>(
&mut self,
f: impl FnOnce(&mut $PXi<Output<PushPull>>) -> R,
) -> R {
self.with_mode(f)
}

/// Set pin speed.
pub fn set_speed(self, speed: Speed) -> Self {
let offset = 2 * $i;
unsafe {
Expand Down
4 changes: 2 additions & 2 deletions src/pwm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use core::ops::Deref;
use cortex_m::interrupt;

use crate::gpio::gpioa::{PA0, PA1, PA2, PA3};
use crate::gpio::AltMode;
use crate::gpio::{AltMode, PinMode};
use crate::hal;
use crate::pac::{tim2, TIM2, TIM3};
use crate::rcc::Rcc;
Expand Down Expand Up @@ -251,7 +251,7 @@ macro_rules! impl_pin {
) => {
$(
$(
impl<State> Pin<$instance, $channel> for $name<State> {
impl<State: PinMode> Pin<$instance, $channel> for $name<State> {
fn setup(&self) {
self.set_alt_mode(AltMode::$alternate_function);
}
Expand Down
4 changes: 2 additions & 2 deletions src/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use core::marker::PhantomData;
use core::ptr;

use crate::gpio::gpioa::*;
use crate::gpio::AltMode;
use crate::gpio::{PinMode, AltMode};
use crate::hal;
use crate::hal::prelude::*;
pub use crate::pac::USART2;
Expand Down Expand Up @@ -159,7 +159,7 @@ pub trait Pins<USART> {
macro_rules! impl_pins {
($($instance:ty, $tx:ident, $rx:ident, $alt:ident;)*) => {
$(
impl<Tx, Rx> Pins<$instance> for ($tx<Tx>, $rx<Rx>) {
impl<Tx: PinMode, Rx: PinMode> Pins<$instance> for ($tx<Tx>, $rx<Rx>) {
fn setup(&self) {
self.0.set_alt_mode(AltMode::$alt);
self.1.set_alt_mode(AltMode::$alt);
Expand Down