Skip to content

Commit 6b8ee81

Browse files
committed
dynamic pin
1 parent c911651 commit 6b8ee81

File tree

7 files changed

+198
-26
lines changed

7 files changed

+198
-26
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1313

1414
### Added
1515

16+
- `DynamicPin` with dynamically changed mode, remove `AF` constants [#372]
1617
- `count_down` constructor for `Timer` -> `CountDownTimer` without start [#382]
1718
- Implementation of RTIC Monotonic for TIM2 & TIM5 under `rtic` feature [#380]
1819
- `IoPin` for `Output<OpenDrain>> <-> Input<Floating>>` [#374]

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,3 +437,7 @@ required-features = ["tim8"] # stm32f446
437437
[[example]]
438438
name = "ist7920_bidi_normal_spi"
439439
required-features = ["device-selected"]
440+
441+
[[example]]
442+
name = "dynamic_gpio"
443+
required-features = ["device-selected"]

examples/dynamic_gpio.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#![deny(unsafe_code)]
2+
#![no_std]
3+
#![no_main]
4+
5+
use panic_halt as _;
6+
7+
use nb::block;
8+
9+
use cortex_m_rt::entry;
10+
use cortex_m_semihosting::hprintln;
11+
use embedded_hal::digital::v2::{InputPin, OutputPin, PinState};
12+
use stm32f4xx_hal::{pac, prelude::*, timer::Timer};
13+
14+
#[entry]
15+
fn main() -> ! {
16+
// Get access to the core peripherals from the cortex-m crate
17+
let cp = cortex_m::Peripherals::take().unwrap();
18+
// Get access to the device specific peripherals from the peripheral access crate
19+
let dp = pac::Peripherals::take().unwrap();
20+
21+
// Take ownership over raw device and convert it into the corresponding HAL struct
22+
let rcc = dp.RCC.constrain();
23+
24+
// Freeze the configuration of all the clocks in the system and store the frozen frequencies in
25+
// `clocks`
26+
let clocks = rcc.cfgr.freeze();
27+
28+
// Acquire the GPIOC peripheral
29+
let gpioc = dp.GPIOC.split();
30+
31+
let mut pin = gpioc.pc13.into_dynamic();
32+
// Configure the syst timer to trigger an update every second
33+
let mut timer = Timer::syst(cp.SYST, &clocks).start_count_down(1.hz());
34+
35+
// Wait for the timer to trigger an update and change the state of the LED
36+
loop {
37+
pin.make_floating_input();
38+
block!(timer.wait()).unwrap();
39+
hprintln!("{}", pin.is_high().unwrap()).unwrap();
40+
41+
pin.make_push_pull_output_in_state(PinState::High);
42+
block!(timer.wait()).unwrap();
43+
pin.set_low().unwrap();
44+
block!(timer.wait()).unwrap();
45+
}
46+
}

src/gpio.rs

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
//!
2525
//! - **Alternate**: Pin mode required when the pin is driven by other peripherals
2626
//! - **Analog**: Analog input to be used with ADC.
27+
//! - **Dynamic**: Pin mode is selected at runtime. See changing configurations for more details
2728
//! - Input
2829
//! - **PullUp**: Input connected to high with a weak pull up resistor. Will be high when nothing
2930
//! is connected
@@ -42,6 +43,16 @@
4243
//! If you need a more temporary mode change, and can not use the `into_<mode>` functions for
4344
//! ownership reasons, you can use the closure based `with_<mode>` functions to temporarily change the pin type, do
4445
//! some output or input, and then have it change back once done.
46+
//!
47+
//! ### Dynamic Mode Change
48+
//! The above mode change methods guarantee that you can only call input functions when the pin is
49+
//! in input mode, and output when in output modes, but can lead to some issues. Therefore, there
50+
//! is also a mode where the state is kept track of at runtime, allowing you to change the mode
51+
//! often, and without problems with ownership, or references, at the cost of some performance and
52+
//! the risk of runtime errors.
53+
//!
54+
//! To make a pin dynamic, use the `into_dynamic` function, and then use the `make_<mode>` functions to
55+
//! change the mode
4556
4657
use core::convert::Infallible;
4758
use core::marker::PhantomData;
@@ -60,6 +71,8 @@ mod partially_erased;
6071
pub use partially_erased::{PEPin, PartiallyErasedPin};
6172
mod erased;
6273
pub use erased::{EPin, ErasedPin};
74+
mod dynamic;
75+
pub use dynamic::{Dynamic, DynamicPin};
6376

6477
/// A filler pin type
6578
pub struct NoPin;
@@ -84,24 +97,6 @@ pub trait PinExt {
8497
/// Some alternate mode (type state)
8598
pub struct Alternate<Otype, const A: u8>(PhantomData<Otype>);
8699

87-
// Compatibility constants
88-
pub const AF0: u8 = 0;
89-
pub const AF1: u8 = 1;
90-
pub const AF2: u8 = 2;
91-
pub const AF3: u8 = 3;
92-
pub const AF4: u8 = 4;
93-
pub const AF5: u8 = 5;
94-
pub const AF6: u8 = 6;
95-
pub const AF7: u8 = 7;
96-
pub const AF8: u8 = 8;
97-
pub const AF9: u8 = 9;
98-
pub const AF10: u8 = 10;
99-
pub const AF11: u8 = 11;
100-
pub const AF12: u8 = 12;
101-
pub const AF13: u8 = 13;
102-
pub const AF14: u8 = 14;
103-
pub const AF15: u8 = 15;
104-
105100
/// Input mode (type state)
106101
pub struct Input<MODE> {
107102
_mode: PhantomData<MODE>,

src/gpio/convert.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,14 @@ impl<MODE, const P: char, const N: u8> Pin<MODE, P, N> {
436436
Pin::new()
437437
}
438438

439+
/// Configures the pin as a pin that can change between input
440+
/// and output without changing the type. It starts out
441+
/// as a floating input
442+
pub fn into_dynamic(self) -> DynamicPin<P, N> {
443+
self.into_floating_input();
444+
DynamicPin::new(Dynamic::InputFloating)
445+
}
446+
439447
/// Puts `self` into mode `M`.
440448
///
441449
/// This violates the type state constraints from `MODE`, so callers must

src/gpio/dynamic.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
use super::*;
2+
3+
/// Pin type with dynamic mode
4+
///
5+
/// - `P` is port name: `A` for GPIOA, `B` for GPIOB, etc.
6+
/// - `N` is pin number: from `0` to `15`.
7+
pub struct DynamicPin<const P: char, const N: u8> {
8+
/// Current pin mode
9+
mode: Dynamic,
10+
}
11+
12+
impl<const P: char, const N: u8> DynamicPin<P, N> {
13+
pub const fn new(mode: Dynamic) -> Self {
14+
Self { mode }
15+
}
16+
}
17+
18+
/// Tracks the current pin state for dynamic pins
19+
pub enum Dynamic {
20+
InputFloating,
21+
InputPullUp,
22+
InputPullDown,
23+
OutputPushPull,
24+
OutputOpenDrain,
25+
}
26+
27+
#[derive(Debug, PartialEq)]
28+
pub enum PinModeError {
29+
IncorrectMode,
30+
}
31+
32+
impl Dynamic {
33+
fn is_input(&self) -> bool {
34+
use Dynamic::*;
35+
match self {
36+
InputFloating | InputPullUp | InputPullDown | OutputOpenDrain => true,
37+
OutputPushPull => false,
38+
}
39+
}
40+
fn is_output(&self) -> bool {
41+
use Dynamic::*;
42+
match self {
43+
InputFloating | InputPullUp | InputPullDown => false,
44+
OutputPushPull | OutputOpenDrain => true,
45+
}
46+
}
47+
}
48+
49+
impl<const P: char, const N: u8> OutputPin for DynamicPin<P, N> {
50+
type Error = PinModeError;
51+
fn set_high(&mut self) -> Result<(), Self::Error> {
52+
if self.mode.is_output() {
53+
Pin::<Output<PushPull>, P, N>::new()._set_state(PinState::High);
54+
Ok(())
55+
} else {
56+
Err(PinModeError::IncorrectMode)
57+
}
58+
}
59+
fn set_low(&mut self) -> Result<(), Self::Error> {
60+
if self.mode.is_output() {
61+
Pin::<Output<PushPull>, P, N>::new()._set_state(PinState::Low);
62+
Ok(())
63+
} else {
64+
Err(PinModeError::IncorrectMode)
65+
}
66+
}
67+
}
68+
69+
impl<const P: char, const N: u8> InputPin for DynamicPin<P, N> {
70+
type Error = PinModeError;
71+
fn is_high(&self) -> Result<bool, Self::Error> {
72+
self.is_low().map(|b| !b)
73+
}
74+
fn is_low(&self) -> Result<bool, Self::Error> {
75+
if self.mode.is_input() {
76+
Ok(Pin::<Input<Floating>, P, N>::new()._is_low())
77+
} else {
78+
Err(PinModeError::IncorrectMode)
79+
}
80+
}
81+
}
82+
83+
impl<const P: char, const N: u8> DynamicPin<P, N> {
84+
#[inline]
85+
pub fn make_pull_up_input(&mut self) {
86+
// NOTE(unsafe), we have a mutable reference to the current pin
87+
Pin::<Input<Floating>, P, N>::new().into_pull_up_input();
88+
self.mode = Dynamic::InputPullUp;
89+
}
90+
#[inline]
91+
pub fn make_pull_down_input(&mut self) {
92+
// NOTE(unsafe), we have a mutable reference to the current pin
93+
Pin::<Input<Floating>, P, N>::new().into_pull_down_input();
94+
self.mode = Dynamic::InputPullDown;
95+
}
96+
#[inline]
97+
pub fn make_floating_input(&mut self) {
98+
// NOTE(unsafe), we have a mutable reference to the current pin
99+
Pin::<Output<PushPull>, P, N>::new().into_floating_input();
100+
self.mode = Dynamic::InputFloating;
101+
}
102+
#[inline]
103+
pub fn make_push_pull_output(&mut self) {
104+
// NOTE(unsafe), we have a mutable reference to the current pin
105+
Pin::<Input<Floating>, P, N>::new().into_push_pull_output();
106+
self.mode = Dynamic::OutputPushPull;
107+
}
108+
#[inline]
109+
pub fn make_push_pull_output_in_state(&mut self, state: PinState) {
110+
// NOTE(unsafe), we have a mutable reference to the current pin
111+
Pin::<Input<Floating>, P, N>::new().into_push_pull_output_in_state(state);
112+
self.mode = Dynamic::OutputPushPull;
113+
}
114+
#[inline]
115+
pub fn make_open_drain_output(&mut self) {
116+
// NOTE(unsafe), we have a mutable reference to the current pin
117+
Pin::<Input<Floating>, P, N>::new().into_open_drain_output();
118+
self.mode = Dynamic::OutputOpenDrain;
119+
}
120+
#[inline]
121+
pub fn make_open_drain_output_in_state(&mut self, state: PinState) {
122+
// NOTE(unsafe), we have a mutable reference to the current pin
123+
Pin::<Input<Floating>, P, N>::new().into_open_drain_output_in_state(state);
124+
self.mode = Dynamic::OutputOpenDrain;
125+
}
126+
}

src/rcc/enable.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -132,16 +132,8 @@ impl RccBus for crate::pac::FSMC {
132132
type Bus = AHB3;
133133
}
134134
#[cfg(feature = "fsmc")]
135-
#[cfg(any(feature = "stm32f427", feature = "stm32f437"))]
136135
bus_enable!(FSMC => 0);
137136
#[cfg(feature = "fsmc")]
138-
#[cfg(not(any(feature = "stm32f427", feature = "stm32f437")))]
139-
bus_enable!(FSMC => 0);
140-
#[cfg(feature = "fsmc")]
141-
#[cfg(any(feature = "stm32f427", feature = "stm32f437"))]
142-
bus_reset!(FSMC => 0);
143-
#[cfg(feature = "fsmc")]
144-
#[cfg(not(any(feature = "stm32f427", feature = "stm32f437")))]
145137
bus_reset!(FSMC => 0);
146138

147139
bus! {

0 commit comments

Comments
 (0)